Creating Plugins

The standard library has a few nodes (import nodes, export nodes and calculator nodes) that make use of plugins. Plugins can be added to third-party libraries to extend the functionality of these nodes in some intended ways.

Plugins are installed by adding python files inside the python package of a library (<some-library>/Common/<some-library-package>) or some of its subfolders that conform to the following format: plugin_*.py. These python files needs to contain a class which is a subclass of the plugin class specified by the node. Installed plugins are automatcally loaded and made ready for use in nodes.

Import node plugins

The specific base classes used for implementing plugins for import nodes inherit the following base:

class sympathy.api.importers.IDataImporter(fq_infilename, parameters)[source]

Interface for a DataImporter. It’s important to set IMPORTER_NAME to a unique name otherwise errors will occur.

classmethod display_name()[source]
Returns:Display name for importer.
Return type:six.text_type
classmethod identifier()[source]
Returns:Unique identifier for importer.
Return type:six.text_type
import_data(out_file, parameters=None, progress=None)[source]

Fill out_file with data.

is_type()[source]
Returns:True if self._fq_infilename points to a native “sydata file.
Return type:bool
classmethod name()[source]
Returns:Name for importer.
Return type:six.text_type
parameter_view(**kwargs)
Returns:GUI widget for importer.
Return type:QtGui.QWidget
valid_for_file()[source]
Returns:True if plugin handles self._fq_infilename.
Return type:bool

It serves as a common point for standardization and documentation but should not be subclassed directly. Instead subclass the base class specified by the node.

Example

This example will serve to illustrate the fundamentals of creating import plugins with custom parameters and GUI.

To extend Table with support for a json subformat of table-like structure, for example, the following:

[{"a": 1, "b": 2, "c": 3},
 {"a": 4, "b": 5, "c": 6},
 {"a": 7, "b": 8, "c": 9}]

Create a new file called plugin_json_table_importer.py with the following code:

from __future__ import (print_function, division, unicode_literals,
                        absolute_import)
import os
import json
import numpy as np
from sympathy.api import qt as qt_compat
from sympathy.api import importers
QtGui = qt_compat.import_module('QtGui')

class JsonTable(importers.TableDataImporterBase):
    IMPORTER_NAME = 'JSON-TABLE'

    def __init__(self, fq_infilename, parameters):
        super(JsonTable, self).__init__(fq_infilename, parameters)
        if parameters is not None:
            if 'set_name' not in self._parameters:
                parameters.set_boolean('set_name', value=True,
                                       label='Set table name from filename')

    def valid_for_file(self):
        # Inefficient for large files, it would be better
        # to check some part of the content or perhaps even the file
        # extension. Additionally, this accepts all valid json files.
        try:
            with open(self._fq_infilename, 'rb') as f:
                json.load(f)
                return True
        except Exception:
            return False

    def import_data(self, out_table, parameters=None, progress=None):
        cols = {}
        with open(self._fq_infilename, 'rb') as f:
            for row in json.load(f):
                for col_name, cell in row.items():
                    cols.setdefault(col_name, []).append(cell)
        for col_name, col_data in cols.items():
            out_table[col_name] = np.array(col_data)

        if parameters['set_name']:
            out_table.set_name(os.path.basename(self._fq_infilename))

    def parameter_view(self, parameters):
        # For importers without custom parameters, this need not
        # be implemented.
        if not self.valid_for_file():
            return QtGui.QWidget()
        return parameters['set_name'].gui()

Note that we are subclassing TableDataImporterBase which is the base class for plugins specified in Table.

Create a new library and move the file into Common/<some-library-package>/

To try it out, create a new text file with data following the structure example above, called table.json.

Configure Table to import using JSON-TABLE and select table.json using the datasource node.

Now we have created a custom importer plugin for Table. Plugins for ADAF, Text, etc. should follow the same structure and should be straight-forward to implement so long as you can handle the source format (possibly by using a third-party library) and know the sympathy datatype.

Export node plugins

The specific base classes used for implementing plugins for export nodes inherit the following base:

class sympathy.api.exporters.IDataExporter(parameters)[source]

Interface for a DataExporter. It’s important to set EXPORTER_NAME to a unique name otherwise errors will occur.

cardinality()[source]

Relation between input elements and output elements created.

Returns:Cardinality enum. IDataExporter.one_to_one, IDataExporter.one_to_many IDataExporter.many_to_one.
Return type:int
create_filenames(data, filename, ext=True)[source]
Parameters:
  • data (list of sydata) – Items to export.
  • filename (six.text_type) – Base filename without extension.
  • ext (bool or six.text_type) – Extensions string or True to add default extension, False for no extension.
Returns:

relative filenames with extension.

Return type:

list of six.text_type

classmethod display_name()[source]
Returns:Display name for return.
Return type:six.text_type
static file_based()[source]
Returns:True if exporter is file based (creates files on disk) instead.
Return type:bool
static hide_filename()[source]
Returns:True if filename preview should be hidden from view.
Return type:bool
classmethod identifier()[source]
Returns:Unique identifier for importer.
Return type:six.text_type
parameter_view(*args)[source]
Returns:GUI widget for exporter.
Return type:QtGui.QWidget

It serves as a common point for standardization and documentation but should not be subclassed directly. Instead subclass the base class specified by the node.

Calculator node plugins

All calculator nodes use the following base class for implementing plugins (it can subclassed directly):

class sylib.calculator.plugins.ICalcPlugin[source]

Interface for calculator plugins.

static globals_dict()[source]

Return a dictionary that will be added to the globals dictionary when executing calculations.

static gui_dict(generic)[source]

Return a dictionary with functions that will be shown in the configuration gui for the calculator node.

static imports()[source]

Return a dictionary with extra imports that will be available in the calculator. For example, add the imports:

import math
import scipy.integrate
import scipy.signal as sig

to your plugin, and return a dictionary like this:

return {'math': math,
        'spint': scipy.integrate,
        'sig': sig}

and math, spint and sig will be available as modules in the calculator.

static signals_dict()[source]

Define signals that are needed to run the functions as written in eval texts, when running the tests. Must have the same length.

static variables_dict()[source]

Define variables that are needed to run the functions as written in eval texts, when running the tests.