pluginify 1.0.0.post16.dev0__py3-none-any.whl → 1.0.0.post48.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pluginify/__init__.py +15 -2
- pluginify/_version.py +2 -2
- pluginify/commandline_typer.py +21 -19
- pluginify/config.py +18 -11
- pluginify/create_plugin_registries.py +162 -104
- pluginify/errors.py +11 -3
- pluginify/interfaces/__init__.py +10 -1
- pluginify/interfaces/base.py +168 -100
- pluginify/interfaces/class_based/data_modifiers.py +4 -1
- pluginify/interfaces/class_based_plugin.py +167 -63
- pluginify/interfaces/yaml_based/configs.py +2 -0
- pluginify/plugin_registry.py +213 -172
- pluginify/plugins/classes/data_modifiers/cuboid.py +10 -8
- pluginify/pydantic_models/v1/configs.py +8 -2
- pluginify/utils/__init__.py +37 -17
- pluginify/utils/context_managers.py +18 -6
- pluginify/utils/validators.py +87 -14
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post48.dev0.dist-info}/METADATA +3 -1
- pluginify-1.0.0.post48.dev0.dist-info/RECORD +23 -0
- pluginify-1.0.0.post16.dev0.dist-info/RECORD +0 -23
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post48.dev0.dist-info}/WHEEL +0 -0
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post48.dev0.dist-info}/entry_points.txt +0 -0
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post48.dev0.dist-info}/licenses/LICENSE +0 -0
pluginify/interfaces/base.py
CHANGED
|
@@ -3,18 +3,24 @@
|
|
|
3
3
|
|
|
4
4
|
"""Base classes for interfaces, plugins, and plugin validation machinery."""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import abc
|
|
7
9
|
from glob import glob
|
|
8
10
|
from importlib.resources import files
|
|
9
11
|
import inspect
|
|
10
12
|
import logging
|
|
11
13
|
from os.path import basename
|
|
14
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
12
15
|
|
|
13
16
|
from jsonschema.exceptions import ValidationError
|
|
14
17
|
|
|
15
18
|
from pluginify.config import REBUILD_REGISTRIES
|
|
16
19
|
from pluginify.errors import PluginError
|
|
17
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from pluginify.plugin_registry import PluginRegistry
|
|
23
|
+
|
|
18
24
|
LOG = logging.getLogger(__name__)
|
|
19
25
|
|
|
20
26
|
|
|
@@ -41,7 +47,7 @@ class BaseInterface(abc.ABC):
|
|
|
41
47
|
# This can be overridden by setting them in child interface classes
|
|
42
48
|
apiVersion = "pluginify/v1"
|
|
43
49
|
|
|
44
|
-
def __new__(cls):
|
|
50
|
+
def __new__(cls) -> BaseInterface:
|
|
45
51
|
"""Plugin interface new method."""
|
|
46
52
|
if not hasattr(cls, "name") or not cls.name:
|
|
47
53
|
raise AttributeError(
|
|
@@ -54,7 +60,7 @@ class BaseInterface(abc.ABC):
|
|
|
54
60
|
return super(BaseInterface, cls).__new__(cls)
|
|
55
61
|
|
|
56
62
|
@property
|
|
57
|
-
def namespace(self):
|
|
63
|
+
def namespace(self) -> str:
|
|
58
64
|
"""Default namespace used for the plugin registry associated with this class.
|
|
59
65
|
|
|
60
66
|
By default, we use 'pluginify.plugin_packages' as the namespace for interface
|
|
@@ -69,7 +75,7 @@ class BaseInterface(abc.ABC):
|
|
|
69
75
|
return self._namespace
|
|
70
76
|
|
|
71
77
|
@property
|
|
72
|
-
def plugin_registry(self):
|
|
78
|
+
def plugin_registry(self) -> PluginRegistry:
|
|
73
79
|
"""The plugin registry associated with this interface.
|
|
74
80
|
|
|
75
81
|
By default, we use 'pluginify.plugin_packages' as the namespace for interface
|
|
@@ -89,17 +95,17 @@ class BaseInterface(abc.ABC):
|
|
|
89
95
|
|
|
90
96
|
Parameters
|
|
91
97
|
----------
|
|
92
|
-
name: str or tuple(str)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
rebuild_registries: bool (default=True)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
name : str or tuple(str)
|
|
99
|
+
The name of the yaml-based plugin. Either a single string or a tuple of
|
|
100
|
+
strings for product plugins.
|
|
101
|
+
rebuild_registries : bool (default=True)
|
|
102
|
+
Whether or not to rebuild the registries if get_plugin fails. If set to
|
|
103
|
+
true and get_plugin fails, rebuild the plugin registry, call then call
|
|
104
|
+
get_plugin once more with rebuild_registries toggled off, so it only gets
|
|
105
|
+
rebuilt once.
|
|
106
|
+
By default, the value of rebuild_registries is set to True if not
|
|
107
|
+
explicitly set to False as an configuration variable under the name
|
|
108
|
+
`REBUILD_REGISTRIES` in `~/.config/pluginify/config.yaml`.
|
|
103
109
|
"""
|
|
104
110
|
pass
|
|
105
111
|
|
|
@@ -108,7 +114,7 @@ class BaseInterface(abc.ABC):
|
|
|
108
114
|
"""Abstract function for retrieving all plugins under a certain interface."""
|
|
109
115
|
pass
|
|
110
116
|
|
|
111
|
-
def get_plugin_metadata(self, name):
|
|
117
|
+
def get_plugin_metadata(self, name: str | tuple[str, str]) -> dict:
|
|
112
118
|
"""Retrieve a plugin's metadata.
|
|
113
119
|
|
|
114
120
|
Where the metadata of the plugin matches the plugin's corresponding entry in the
|
|
@@ -116,13 +122,13 @@ class BaseInterface(abc.ABC):
|
|
|
116
122
|
|
|
117
123
|
Parameters
|
|
118
124
|
----------
|
|
119
|
-
name: str or tuple(str)
|
|
120
|
-
|
|
125
|
+
name : str or tuple(str)
|
|
126
|
+
The name of the plugin whose metadata we want.
|
|
121
127
|
|
|
122
128
|
Returns
|
|
123
129
|
-------
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
dict
|
|
131
|
+
A dictionary of metadata for the requested plugin.
|
|
126
132
|
"""
|
|
127
133
|
return self.plugin_registry.get_plugin_metadata(self, name)
|
|
128
134
|
|
|
@@ -130,11 +136,11 @@ class BaseInterface(abc.ABC):
|
|
|
130
136
|
class BaseYamlPlugin(dict):
|
|
131
137
|
"""Base class for YAML plugins."""
|
|
132
138
|
|
|
133
|
-
def __init__(self, *args, **kwargs):
|
|
139
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
134
140
|
"""Class BaseYamlPlugin init method."""
|
|
135
141
|
super().__init__(*args, **kwargs)
|
|
136
142
|
|
|
137
|
-
def __repr__(self):
|
|
143
|
+
def __repr__(self) -> str:
|
|
138
144
|
"""Class BaseYamlPlugin repr method."""
|
|
139
145
|
val = super().__repr__()
|
|
140
146
|
return f"{self.__class__.__name__}({val})"
|
|
@@ -161,13 +167,11 @@ class BaseYamlInterface(BaseInterface):
|
|
|
161
167
|
name = "BaseYamlInterface"
|
|
162
168
|
use_pydantic = False
|
|
163
169
|
|
|
164
|
-
def __new__(cls):
|
|
170
|
+
def __new__(cls) -> BaseYamlInterface:
|
|
165
171
|
"""YAML plugin interface new method."""
|
|
166
|
-
|
|
172
|
+
return cast(BaseYamlInterface, super(BaseInterface, cls).__new__(cls))
|
|
167
173
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def __init__(self):
|
|
174
|
+
def __init__(self) -> None:
|
|
171
175
|
"""YAML plugin interface init method."""
|
|
172
176
|
try:
|
|
173
177
|
self.supported_families = [
|
|
@@ -181,11 +185,21 @@ class BaseYamlInterface(BaseInterface):
|
|
|
181
185
|
# fix and will be removed entirely once families are removed.
|
|
182
186
|
self.supported_families = ["standard"]
|
|
183
187
|
|
|
184
|
-
def _create_registered_plugin_names(self, yaml_plugin):
|
|
188
|
+
def _create_registered_plugin_names(self, yaml_plugin: dict) -> list[str]:
|
|
185
189
|
"""Create a plugin name for plugin registry.
|
|
186
190
|
|
|
187
191
|
Some interfaces need to override this (e.g. products) because they
|
|
188
192
|
need a more complex name for retrieval.
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
yaml_plugin : dict
|
|
197
|
+
The YAML plugin dictionary.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
list of str
|
|
202
|
+
A list of registered plugin names derived from the plugin.
|
|
189
203
|
"""
|
|
190
204
|
return [yaml_plugin["name"]]
|
|
191
205
|
|
|
@@ -200,7 +214,9 @@ class BaseYamlInterface(BaseInterface):
|
|
|
200
214
|
pass
|
|
201
215
|
|
|
202
216
|
@classmethod
|
|
203
|
-
def _plugin_yaml_to_obj(
|
|
217
|
+
def _plugin_yaml_to_obj(
|
|
218
|
+
cls, name: str, yaml_plugin: dict, obj_attrs: dict | None = None
|
|
219
|
+
) -> BaseYamlPlugin:
|
|
204
220
|
"""Convert a yaml plugin to an object.
|
|
205
221
|
|
|
206
222
|
Convert the passed YAML plugin into an object and return it. The returned
|
|
@@ -222,6 +238,8 @@ class BaseYamlInterface(BaseInterface):
|
|
|
222
238
|
- name: The name of the plugin which must be unique within the interface.
|
|
223
239
|
- docstring: A string to be used as the object's docstring.
|
|
224
240
|
"""
|
|
241
|
+
if obj_attrs is None:
|
|
242
|
+
obj_attrs = {}
|
|
225
243
|
obj_attrs["id"] = name
|
|
226
244
|
obj_attrs["yaml"] = yaml_plugin
|
|
227
245
|
|
|
@@ -239,7 +257,7 @@ class BaseYamlInterface(BaseInterface):
|
|
|
239
257
|
obj_attrs[attr] = yaml_plugin[attr]
|
|
240
258
|
# This should be removed once we fully switch to pydantic models
|
|
241
259
|
except TypeError:
|
|
242
|
-
yaml_plugin = yaml_plugin.model_dump()
|
|
260
|
+
yaml_plugin = cast(Any, yaml_plugin).model_dump()
|
|
243
261
|
obj_attrs[attr] = yaml_plugin[attr]
|
|
244
262
|
except KeyError:
|
|
245
263
|
missing.append(attr)
|
|
@@ -257,13 +275,18 @@ class BaseYamlInterface(BaseInterface):
|
|
|
257
275
|
plugin_base_class = BaseYamlPlugin
|
|
258
276
|
if hasattr(cls, "plugin_class") and cls.plugin_class:
|
|
259
277
|
plugin_base_class = cls.plugin_class
|
|
260
|
-
return
|
|
278
|
+
return cast(
|
|
279
|
+
BaseYamlPlugin,
|
|
280
|
+
type(plugin_type, (plugin_base_class,), obj_attrs)(yaml_plugin),
|
|
281
|
+
)
|
|
261
282
|
|
|
262
|
-
def __repr__(self):
|
|
283
|
+
def __repr__(self) -> str:
|
|
263
284
|
"""Plugin interface repr method."""
|
|
264
285
|
return f"{self.__class__.__name__}()"
|
|
265
286
|
|
|
266
|
-
def get_plugin(
|
|
287
|
+
def get_plugin(
|
|
288
|
+
self, name: str | tuple[str, str], rebuild_registries: bool | None = None
|
|
289
|
+
) -> Any:
|
|
267
290
|
"""Get a plugin by its name.
|
|
268
291
|
|
|
269
292
|
This default method can be overridden to provide different search
|
|
@@ -273,42 +296,59 @@ class BaseYamlInterface(BaseInterface):
|
|
|
273
296
|
|
|
274
297
|
Parameters
|
|
275
298
|
----------
|
|
276
|
-
name: str or tuple(str)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
rebuild_registries: bool (default=None)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
299
|
+
name : str or tuple(str)
|
|
300
|
+
The name of the yaml-based plugin. Either a single string or a tuple of
|
|
301
|
+
strings for product plugins.
|
|
302
|
+
rebuild_registries : bool (default=None)
|
|
303
|
+
Whether or not to rebuild the registries if get_plugin fails. If set to
|
|
304
|
+
None, default to the value of
|
|
305
|
+
`~/.config/pluginify/config.yaml:REBUILD_REGISTRIES`. If that
|
|
306
|
+
configuration variable is not set, we default to True.
|
|
307
|
+
If specified, use the input value of rebuild_registries, which should be a
|
|
308
|
+
boolean value. If rebuild registries is true and get_plugin fails, rebuild
|
|
309
|
+
the plugin registry, call then call get_plugin once more with
|
|
310
|
+
rebuild_registries toggled off, so it only gets rebuilt once.
|
|
288
311
|
"""
|
|
289
312
|
return self.plugin_registry.get_yaml_plugin(self, name, rebuild_registries)
|
|
290
313
|
|
|
291
|
-
def get_plugins(self):
|
|
314
|
+
def get_plugins(self) -> list:
|
|
292
315
|
"""Retrieve all yaml plugin objects."""
|
|
293
316
|
return self.plugin_registry.get_yaml_plugins(self)
|
|
294
317
|
|
|
295
|
-
def plugin_is_valid(self, name):
|
|
296
|
-
"""
|
|
318
|
+
def plugin_is_valid(self, name: str | tuple[str, str]) -> bool:
|
|
319
|
+
"""Check if a plugin is valid by name.
|
|
320
|
+
|
|
321
|
+
Parameters
|
|
322
|
+
----------
|
|
323
|
+
name : str or tuple(str)
|
|
324
|
+
The name of the plugin to validate.
|
|
325
|
+
|
|
326
|
+
Returns
|
|
327
|
+
-------
|
|
328
|
+
bool
|
|
329
|
+
True if the plugin is valid, False otherwise.
|
|
330
|
+
"""
|
|
297
331
|
try:
|
|
298
332
|
self.get_plugin(name)
|
|
299
333
|
return True
|
|
300
334
|
except ValidationError:
|
|
301
335
|
return False
|
|
302
336
|
|
|
303
|
-
def plugins_all_valid(self):
|
|
304
|
-
"""
|
|
337
|
+
def plugins_all_valid(self) -> bool:
|
|
338
|
+
"""Check if all plugins are valid.
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
bool
|
|
343
|
+
True if all plugins are valid, False otherwise.
|
|
344
|
+
"""
|
|
305
345
|
try:
|
|
306
346
|
self.get_plugins()
|
|
307
347
|
return True
|
|
308
348
|
except ValidationError:
|
|
309
349
|
return False
|
|
310
350
|
|
|
311
|
-
def test_interface(self):
|
|
351
|
+
def test_interface(self) -> dict:
|
|
312
352
|
"""Test interface method.
|
|
313
353
|
|
|
314
354
|
Note this is currently only called via the
|
|
@@ -319,7 +359,7 @@ class BaseYamlInterface(BaseInterface):
|
|
|
319
359
|
plugins = self.get_plugins()
|
|
320
360
|
all_valid = self.plugins_all_valid()
|
|
321
361
|
family_list = []
|
|
322
|
-
plugin_ids = {}
|
|
362
|
+
plugin_ids: dict = {}
|
|
323
363
|
for plugin in plugins:
|
|
324
364
|
# TODO: Refactor to remove this `if` block after full migration from JSON
|
|
325
365
|
# to Pydantic schemas.
|
|
@@ -342,7 +382,7 @@ class BaseYamlInterface(BaseInterface):
|
|
|
342
382
|
plugin_ids[plugin_family] = []
|
|
343
383
|
plugin_ids[plugin_family].append(plugin_name)
|
|
344
384
|
|
|
345
|
-
output = {
|
|
385
|
+
output: dict[str, Any] = {
|
|
346
386
|
"all_valid": all_valid,
|
|
347
387
|
"by_family": plugin_ids,
|
|
348
388
|
"validity_check": {},
|
|
@@ -376,17 +416,20 @@ class BaseClassInterface(BaseInterface):
|
|
|
376
416
|
interface_type = "class_based"
|
|
377
417
|
name = "BaseClassInterface"
|
|
378
418
|
required_args: dict[str, list[str]] = {}
|
|
419
|
+
required_kwargs: dict[str, list[str]] = {}
|
|
379
420
|
|
|
380
|
-
def __repr__(self):
|
|
421
|
+
def __repr__(self) -> str:
|
|
381
422
|
"""Plugin interface repr method."""
|
|
382
423
|
return f"{self.__class__.__name__}()"
|
|
383
424
|
|
|
384
|
-
def __init__(self):
|
|
425
|
+
def __init__(self) -> None:
|
|
385
426
|
"""Initialize module plugin interface."""
|
|
386
427
|
self.supported_families = list(self.required_args.keys())
|
|
387
428
|
|
|
388
429
|
@classmethod
|
|
389
|
-
def _plugin_module_to_obj(
|
|
430
|
+
def _plugin_module_to_obj(
|
|
431
|
+
cls, name: str, module, obj_attrs: dict | None = None
|
|
432
|
+
) -> Any:
|
|
390
433
|
"""Convert a module plugin to an object.
|
|
391
434
|
|
|
392
435
|
Convert the passed module plugin into an object and return it. The returned
|
|
@@ -414,19 +457,39 @@ class BaseClassInterface(BaseInterface):
|
|
|
414
457
|
Parameters
|
|
415
458
|
----------
|
|
416
459
|
module : module
|
|
417
|
-
|
|
460
|
+
The imported plugin module.
|
|
418
461
|
obj_attrs : dict, optional
|
|
419
|
-
|
|
462
|
+
Additional attributes to be assigned to the plugin object.
|
|
420
463
|
|
|
421
464
|
Returns
|
|
422
465
|
-------
|
|
423
|
-
|
|
424
|
-
|
|
466
|
+
<interface>InterfacePlugin
|
|
467
|
+
An object of type ``<interface>InterfacePlugin`` where ``<interface>``
|
|
468
|
+
is the name of the interface that the desired plugin belongs to.
|
|
425
469
|
"""
|
|
470
|
+
if obj_attrs is None:
|
|
471
|
+
obj_attrs = {}
|
|
426
472
|
obj_attrs["id"] = name
|
|
427
473
|
|
|
428
474
|
missing = []
|
|
429
|
-
|
|
475
|
+
required_attrs = ["interface", "name"]
|
|
476
|
+
|
|
477
|
+
# Always require 'plugin_class' from each class-based interface
|
|
478
|
+
# This is enforced in the 'test_interfaces' unit test.
|
|
479
|
+
if not hasattr(cls, "plugin_class") or cls.plugin_class is None:
|
|
480
|
+
raise PluginError(
|
|
481
|
+
f"Error: interface '{cls.name}' is missing required "
|
|
482
|
+
"attribute 'plugin_class'. Please create a base class plugin for this "
|
|
483
|
+
"interface and assign that object to the 'plugin_class' attribute of "
|
|
484
|
+
"this interface before continuing."
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# If the base plugin class doesn't support data tree native plugins, make sure
|
|
488
|
+
# to require the family attribute
|
|
489
|
+
if not getattr(cls.plugin_class, "data_tree", False):
|
|
490
|
+
required_attrs += ["family"]
|
|
491
|
+
|
|
492
|
+
for attr in required_attrs:
|
|
430
493
|
try:
|
|
431
494
|
obj_attrs[attr] = getattr(module, attr)
|
|
432
495
|
except AttributeError:
|
|
@@ -459,55 +522,46 @@ class BaseClassInterface(BaseInterface):
|
|
|
459
522
|
plugin_interface_name = obj_attrs["interface"].title().replace("_", "")
|
|
460
523
|
plugin_type = f"{plugin_interface_name}Plugin"
|
|
461
524
|
|
|
462
|
-
# Always require 'plugin_class' from each class-based interface
|
|
463
|
-
# This is enforced in the 'test_interfaces' unit test.
|
|
464
|
-
if not hasattr(cls, "plugin_class") or cls.plugin_class is None:
|
|
465
|
-
raise PluginError(
|
|
466
|
-
f"Error: interface '{obj_attrs['interface']}' is missing required "
|
|
467
|
-
"attribute 'plugin_class'. Please create a base class plugin for this "
|
|
468
|
-
"interface and assign that object to the 'plugin_class' attribute of "
|
|
469
|
-
"this interface before continuing."
|
|
470
|
-
)
|
|
471
|
-
|
|
472
525
|
plugin_base_class = cls.plugin_class
|
|
473
526
|
|
|
474
527
|
# Create an object of type ``plugin_type`` with attributes from ``obj_attrs``
|
|
475
528
|
return type(plugin_type, (plugin_base_class,), obj_attrs)(module)
|
|
476
529
|
|
|
477
|
-
def get_plugin(self, name, rebuild_registries=None):
|
|
530
|
+
def get_plugin(self, name: str, rebuild_registries: bool | None = None) -> Any:
|
|
478
531
|
"""Retrieve a plugin from this interface by name.
|
|
479
532
|
|
|
480
533
|
Parameters
|
|
481
534
|
----------
|
|
482
535
|
name : str
|
|
483
|
-
|
|
484
|
-
rebuild_registries: bool (default=None)
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
536
|
+
The name of the desired plugin.
|
|
537
|
+
rebuild_registries : bool (default=None)
|
|
538
|
+
Whether or not to rebuild the registries if get_plugin fails. If set to
|
|
539
|
+
None, default to the value of
|
|
540
|
+
`~/.config/pluginify/config.yaml:REBUILD_REGISTRIES`. If that
|
|
541
|
+
configuration variable is not set, we default to True.
|
|
542
|
+
If specified, use the input value of rebuild_registries, which should be a
|
|
543
|
+
boolean value. If rebuild registries is true and get_plugin fails, rebuild
|
|
544
|
+
the plugin registry, call then call get_plugin once more with
|
|
545
|
+
rebuild_registries toggled off, so it only gets rebuilt once.
|
|
493
546
|
|
|
494
547
|
Returns
|
|
495
548
|
-------
|
|
496
|
-
|
|
497
|
-
|
|
549
|
+
<interface>Plugin
|
|
550
|
+
An object of type ``<interface>Plugin`` where ``<interface>`` is the name
|
|
551
|
+
of this interface.
|
|
498
552
|
|
|
499
553
|
Raises
|
|
500
554
|
------
|
|
501
555
|
PluginError
|
|
502
|
-
|
|
556
|
+
If the specified plugin isn't found within the interface.
|
|
503
557
|
"""
|
|
504
558
|
return self.plugin_registry.get_class_plugin(self, name, rebuild_registries)
|
|
505
559
|
|
|
506
|
-
def get_plugins(self):
|
|
560
|
+
def get_plugins(self) -> list:
|
|
507
561
|
"""Retrieve all module plugins for this interface."""
|
|
508
562
|
return self.plugin_registry.get_class_plugins(self)
|
|
509
563
|
|
|
510
|
-
def plugin_is_valid(self, plugin):
|
|
564
|
+
def plugin_is_valid(self, plugin) -> bool:
|
|
511
565
|
"""Check that an interface is valid.
|
|
512
566
|
|
|
513
567
|
Check that the requested interface function has the correct call signature.
|
|
@@ -523,14 +577,17 @@ class BaseClassInterface(BaseInterface):
|
|
|
523
577
|
Parameters
|
|
524
578
|
----------
|
|
525
579
|
plugin : PluginObject
|
|
526
|
-
|
|
580
|
+
A plugin object coming from _plugin_module_to_obj that needs to be
|
|
581
|
+
validated.
|
|
527
582
|
|
|
528
583
|
Returns
|
|
529
584
|
-------
|
|
530
585
|
bool
|
|
531
|
-
|
|
586
|
+
True if valid, False if invalid
|
|
532
587
|
"""
|
|
533
|
-
|
|
588
|
+
family_required = not getattr(plugin, "data_tree", False)
|
|
589
|
+
|
|
590
|
+
if family_required and plugin.family not in self.required_args:
|
|
534
591
|
raise PluginError(
|
|
535
592
|
f"'{plugin.family}' must be added to required args list"
|
|
536
593
|
f"\nfor '{self.name}' interface,"
|
|
@@ -538,7 +595,7 @@ class BaseClassInterface(BaseInterface):
|
|
|
538
595
|
f"\nin '{plugin.module_name}' module"
|
|
539
596
|
f"\nat '{plugin.module_path}'\n"
|
|
540
597
|
)
|
|
541
|
-
if plugin.family not in self.required_kwargs:
|
|
598
|
+
if family_required and plugin.family not in self.required_kwargs:
|
|
542
599
|
raise PluginError(
|
|
543
600
|
f"'{plugin.family}' must be added to required kwargs list"
|
|
544
601
|
f"\nfor '{self.name}' interface,"
|
|
@@ -546,8 +603,14 @@ class BaseClassInterface(BaseInterface):
|
|
|
546
603
|
f"\nin '{plugin.module_name}' module"
|
|
547
604
|
f"\nat '{plugin.module_path}'\n"
|
|
548
605
|
)
|
|
549
|
-
|
|
550
|
-
|
|
606
|
+
|
|
607
|
+
# NOTE: Likely will need to modify this; we need to discuss how we want to
|
|
608
|
+
# require specific sets of arguments for data_tree based plugins that do not
|
|
609
|
+
# require a family attribute
|
|
610
|
+
family_name = plugin.family if family_required else "data_tree"
|
|
611
|
+
|
|
612
|
+
expected_args = self.required_args[family_name]
|
|
613
|
+
expected_kwargs = self.required_kwargs[family_name]
|
|
551
614
|
|
|
552
615
|
sig = inspect.signature(plugin.call)
|
|
553
616
|
arg_list = []
|
|
@@ -596,11 +659,12 @@ class BaseClassInterface(BaseInterface):
|
|
|
596
659
|
|
|
597
660
|
return True
|
|
598
661
|
|
|
599
|
-
def plugins_all_valid(self):
|
|
662
|
+
def plugins_all_valid(self) -> bool:
|
|
600
663
|
"""Test the current interface by validating every Plugin.
|
|
601
664
|
|
|
602
665
|
Returns
|
|
603
666
|
-------
|
|
667
|
+
bool
|
|
604
668
|
True if all plugins are valid, False if any plugin is invalid.
|
|
605
669
|
"""
|
|
606
670
|
plugins = self.get_plugins()
|
|
@@ -609,7 +673,7 @@ class BaseClassInterface(BaseInterface):
|
|
|
609
673
|
return False
|
|
610
674
|
return True
|
|
611
675
|
|
|
612
|
-
def test_interface(self):
|
|
676
|
+
def test_interface(self) -> dict:
|
|
613
677
|
"""Test the current interface by validating each Plugin and testing each method.
|
|
614
678
|
|
|
615
679
|
Test this interface by opening every Plugin available to the interface,
|
|
@@ -623,6 +687,7 @@ class BaseClassInterface(BaseInterface):
|
|
|
623
687
|
|
|
624
688
|
Returns
|
|
625
689
|
-------
|
|
690
|
+
dict
|
|
626
691
|
A dictionary containing three keys:
|
|
627
692
|
'by_family', 'validity_check', 'func', and 'family'. The value for each
|
|
628
693
|
of these keys is a dictionary whose keys are the names of the Plugins.
|
|
@@ -640,14 +705,17 @@ class BaseClassInterface(BaseInterface):
|
|
|
640
705
|
plugins = self.get_plugins()
|
|
641
706
|
all_valid = self.plugins_all_valid()
|
|
642
707
|
family_list = []
|
|
643
|
-
plugin_ids = {}
|
|
708
|
+
plugin_ids: dict = {}
|
|
644
709
|
for plugin in plugins:
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
710
|
+
family_name = (
|
|
711
|
+
plugin.family if not getattr(plugin, "data_tree") else "data_tree"
|
|
712
|
+
)
|
|
713
|
+
if family_name not in family_list:
|
|
714
|
+
family_list.append(family_name)
|
|
715
|
+
plugin_ids[family_name] = []
|
|
716
|
+
plugin_ids[family_name].append(plugin.id)
|
|
649
717
|
|
|
650
|
-
output = {
|
|
718
|
+
output: dict[str, Any] = {
|
|
651
719
|
"all_valid": all_valid,
|
|
652
720
|
"by_family": plugin_ids,
|
|
653
721
|
"validity_check": {},
|
|
@@ -12,7 +12,10 @@ from pluginify.interfaces.base import BaseClassInterface
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class BaseDataModifierPlugin(BaseClassPlugin, abstract=True):
|
|
15
|
-
"""Dummy base class for data_modifier plugins.
|
|
15
|
+
"""Dummy base class for data_modifier plugins.
|
|
16
|
+
|
|
17
|
+
This base class is strictly used for testing pluginify.
|
|
18
|
+
"""
|
|
16
19
|
|
|
17
20
|
pass
|
|
18
21
|
|