BaseDataPlugin¶
class BaseDataPlugin(settings: Optional[dict] = None)
Bases: ABC
This class, BaseDataPlugin, is an abstraction of the functionality and interface that is common to all data plugins. What this means practically is that there is a chain of inheritance: all data plugins inherits from their respective base class, all of which inherit from BaseDataPlugin.
It handles stuff like instantiating the plugins, constructing settings dictionaries, and sanity checking the inputs, as well as a handful of bookkeeping functions used by the poriscope GUI to manage interactions between the MVC architecture and the data plugins themselves - basically, anything that involves interaction with the nuts and bolts of the poriscope GUI.
What You Get by Inheriting from Base Data Plugin¶
Warning
You probably do not need to inherit directly from BaseDataPlugin, as this is a general base class for the specific base classes from which Data Plugins are built. If your intention is to build a data plugin that fits one of the existing subtypes, you should inherit instead refer to one of the following pages:
If you are planning to build an entirely new type of Data Plugin not in the list above, we strongly suggest contacting the poriscope developers first.
As soon as you subclass a base class from BaseDataPlugin, the following happens:
The
poriscopeGUI will know how to interact with this plugin type, and will manage its relationship to other plugin classes on which it might dependYour plugin will handle basic sanity checks on settings at instantiation without any extra work needed
Several abstract functions are defined that can be realized either at the base class or subclass level that define a common API for all data plugins
Note
For the most part, users will not have to worry much about anything in this base class, as all other abstract base classes for data plugins inherit from this one and will define the relevant interface at the subclass level. However, in the unlikely event that you are defining an entirely new class of data plugin, it will need to inherit from this base in order to fully integrate into poriscope. Because integrating a new base into poriscope requires registration in core app elements, it is strongly encouraged that you contact the repository managers before trying in order to assess whether there is a simpler solution.
- Attributes:
logger (logging.Logger): Logger instance for logging messages. lock (threading.RLock): Per-instance reentrant lock, used by
serialize_channel_operations()to serialize this plugin’s own operations across channels when it declares that it must not run concurrently. One lock per plugin instance, so two different plugins never contend with each other. A plugin needing process-wide serialization (a non-reentrant native library, say) must declare its own class-level lock rather than reusing this one - seeWaveletFilter.
Public Methods¶
Abstract Methods¶
These methods must be implemented by subclasses.
- abstractmethod BaseDataPlugin.close_resources(channel: int | None = None) None¶
Perform any actions necessary to gracefully close resources before app exit. If channel is not None, handle only that channel, else close all of them.
- Parameters:
channel (Optional[int]) – channel ID
- abstractmethod BaseDataPlugin.get_empty_settings(globally_available_plugins: Dict[str, List[str]] | None = None, standalone: bool = False) Dict[str, Dict[str, Any]]¶
Get a dict populated with keys needed to initialize the filter if they are not set yet. This dict must have the following structure, but Min, Max, and Options can be skipped or explicitly set to None if they are not used. Type is required; Value may be omitted or set to None, both meaning there is no default and the user must supply one. All values provided must be consistent with Type.
settings = {'Parameter 1': {'Type': <int, float, str, bool>, 'Value': <value> or None, 'Options': [<option_1>, <option_2>, ... ] or None, 'Min': <min_value> or None, 'Max': <max_value> or None }, ... }
The base implementations here do omit Value - a reader’s schema is literally
{"Input File": {"Type": str}}. Note that a settings dict supplied back toapply_settings()does need every Value present, because_validate_param_typesreads it by subscript; the GUI’s settings dialog fills them in before that point.Run
python scripts/check_plugin_schemas.pyto check a schema you have written for self-consistency, or seeporiscope.utils.settings_schemato call the same check directly.- Parameters:
globally_available_plugins (Optional[ Dict[str, List[str]]]) – a dict containing all data plugins that exist to date, keyed by metaclass. Must include “MetaReader” as a key, with explicitly set Type MetaReader.
standalone (bool) – False if this is called as part of a GUI, True otherwise. Default False
- Returns:
the dict that must be filled in to initialize the filter
- Return type:
Concrete Methods¶
- BaseDataPlugin.apply_settings(settings: dict) None¶
Validate that settings are correct and reasonable, and set params if the check passes
- Parameters:
settings (dict) – a dict containing the information needed
- BaseDataPlugin.force_serial_channel_operations() bool¶
Purpose: Indicate whether operations on different channels must be serialized (not run in parallel).
For plugins that do not depend on other data plugins, by default this simply returns
False, meaning that it is acceptable and thread-safe to run operations on different channels in different threads on this plugin. If such operation is not thread-safe, this function should be overridden to simply returnTrue. In the case where your plugin depends on another plugin (for example, event finder plugins depend on reader plugins), then your plugin should defer thread safety considerations to the plugin on which it depends.- Returns:
True if only one channel can run at a time, False otherwise
- Return type:
- BaseDataPlugin.get_dependents() Set[Tuple[str, str]]¶
Get the set of (metaclass, key) tuples representing this plugin’s dependents.
- BaseDataPlugin.get_key() str¶
Get the key used to identify this plugin within the global app scope
- Returns:
the key of the reader
- Return type:
- BaseDataPlugin.get_parents() Set[Tuple[str, str]]¶
Get the set of (metaclass, key) tuples representing this plugin’s parents.
- BaseDataPlugin.get_raw_settings() dict¶
Get a copy of the settings that were applied during initialization of the instance
The returned dict is a snapshot rather than a view: the outer dict, each parameter’s dict, and any list-valued entry within one - notably
Options- are all copied. A caller that mutates what it gets back therefore cannot write into this plugin’s internal state.update_raw_settings()andreplace_raw_settings_option()are the only supported writers.The copy is deliberately shallow below that level rather than a
copy.deepcopy():Typeentries hold classes, andValuecan transiently hold a live plugin instance while settings are being applied, neither of which is safe or meaningful to deep-copy.- Returns:
a copy of the dict that must be filled in to initialize the plugin
- Return type:
- BaseDataPlugin.register_dependent(metaclass: str, key: str) None¶
Record that another plugin, identified by (metaclass, key), depends on this one.
- BaseDataPlugin.register_parent(metaclass: str, key: str) None¶
Record that this plugin depends on another plugin, identified by (metaclass, key).
- BaseDataPlugin.replace_raw_settings_option(key: str, old_value: Any, new_value: Any) None¶
Replace one entry in a setting’s list of allowed options
Needed when a parent plugin is renamed: a dependent carries the parent’s key both as the
Valueand in theOptionslist of the setting named after the parent’s metaclass, and the list must track the rename or_validate_param_ranges()will later reject the new key as not being an allowed option. This exists as a method becauseget_raw_settings()hands out a copy, so a caller cannot maintain the list by mutating what it gets back.Does nothing if the setting is absent or declares no options.
- Parameters:
key (str) – the settings key whose options should be updated
old_value (Any) – the option to remove, if it is present
new_value (Any) – the option to add, if it is not already present
- BaseDataPlugin.serialize_channel_operations() Iterator[None]¶
Hold this plugin’s own lock for the duration of the block, but only if the plugin declares that its channels must not run concurrently.
This is where
force_serial_channel_operations()is actually enforced. The declaration is a statement about this instance - “my own operations must not overlap across channels” - so the lock taken is this instance’slock. Two different plugin instances never contend with each other, and a plugin that returnsFalsepays nothing.Enforcement lives here, on the object that makes the declaration, rather than in the caller. It used to live in
MetaModel, which asked the plugin over the signal bus and then handed its own model-scoped lock to the worker - and since every analysis tab builds its own model, two tabs driving the same plugin instance took different locks and ran it concurrently anyway, while unrelated plugins within one tab serialized against each other for nothing.The lock is held across
yield, so a generator guarded by this is serialized for its whole run and releases when it is exhausted or closed.discard_generator()closes spent generators explicitly so that release does not depend on garbage-collection timing.- Yield:
None; the block runs with the lock held if serialization was requested.
- Ytype:
None
- BaseDataPlugin.set_key(key: str) None¶
Set the key used to identify this plugin within the global app scope
- Parameters:
key (str) – the key of the plugin
- BaseDataPlugin.unregister_dependent(metaclass: str, key: str) None¶
Remove a previously registered dependent, identified by (metaclass, key), if present.
Private Methods¶
Abstract Methods¶
These methods must be implemented by subclasses.
- abstractmethod BaseDataPlugin._finalize_initialization() None¶
Apply the provided paramters and intialize any internal structures needed Should Raise if initialization fails.
This function is called at the end of the class constructor to perform additional initialization specific to the algorithm being implemented. kwargs provided to the base class constructor are available as class attributes.
- abstractmethod BaseDataPlugin._validate_settings(settings: dict) None¶
Validate that the settings dict contains the correct information for use by the subclass.
- Parameters:
settings (dict) – Parameters required to configure this plugin.
- Raises:
ValueError – If the settings dict does not contain the correct information.
Concrete Methods¶
- BaseDataPlugin.__enter__() BaseDataPlugin¶
Enter the context management. Return self to be used within a ‘with’ statement.
- BaseDataPlugin.__exit__(exc_type: Type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) None¶
Exit the context management. Close resources.
- BaseDataPlugin.__init__(settings: dict | None = None) None¶
Construct the plugin and, if settings are provided, apply them immediately.
- Parameters:
settings (Optional[dict]) – A dict specifying the parameters of the plugin to be created. Required keys depend on subclass. If None, the plugin is left unconfigured until
apply_settings()is called.
- BaseDataPlugin._resolve_metaclass_name(cls: type) str¶
Walk the MRO of a plugin class to find its direct Meta* base (e.g. MetaEventFinder, MetaReader), regardless of how many concrete-subclass layers sit between it and that base.
Using
cls.__bases__[0]instead only works ifclssubclasses its Meta* base directly. For a plugin that subclasses another concrete plugin (e.g.BoundedBlockageFinder(ClassicBlockageFinder), itself a subclass ofMetaEventFinder), that would return the intermediate concrete class’s name instead, which does not match any key in DataPluginModel’s per-metaclass plugin registry.
- BaseDataPlugin._validate_param_ranges(settings: dict) None¶
Validate that every parameter has a value and that it lies within any declared bounds
A parameter with no value - no
Valuekey, orValue: None- is rejected here rather than in_validate_param_types(), so that the user is told the value is required instead of being told it has the wrong type. This also keepsNoneaway from the bound comparisons below, which would otherwise raiseTypeErrorfrom a method whose contract promisesValueError.- Parameters:
settings (dict) – A dict specifying the parameters of the filter to be created. Required keys depend on subclass.
- Raises:
ValueError – If a required value is missing, out of range, or not an allowed option
- BaseDataPlugin._validate_param_types(settings: Dict[str, Setting]) None¶
Validate that the filter_params dict contains correct data types, but only checks primitives. More detailed parameter checking should follow a call to super() in an override.
A parameter with no value at all - either no
Valuekey orValue: None- is left for_validate_param_ranges()to reject, which reports it as a missing required value rather than as a type error. Reading the key with.getmatters: a schema straight out ofget_empty_settings()legitimately omitsValue, and subscripting it raisedKeyErrorwhere the caller expectedTypeError.