pluginify 1.0.0.post16.dev0__py3-none-any.whl → 1.0.0.post41.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.post41.dev0.dist-info}/METADATA +3 -1
- pluginify-1.0.0.post41.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.post41.dev0.dist-info}/WHEEL +0 -0
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post41.dev0.dist-info}/entry_points.txt +0 -0
- {pluginify-1.0.0.post16.dev0.dist-info → pluginify-1.0.0.post41.dev0.dist-info}/licenses/LICENSE +0 -0
|
@@ -31,6 +31,7 @@ from pathlib import Path
|
|
|
31
31
|
import re
|
|
32
32
|
import tempfile
|
|
33
33
|
import warnings
|
|
34
|
+
from typing import Any
|
|
34
35
|
|
|
35
36
|
import yaml
|
|
36
37
|
|
|
@@ -40,7 +41,7 @@ from pluginify.errors import PluginRegistryError
|
|
|
40
41
|
LOG = logging.getLogger(__name__)
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
def format_docstring(docstring, use_regex=True):
|
|
44
|
+
def format_docstring(docstring: str, use_regex: bool = True) -> str:
|
|
44
45
|
"""Format the provided docstring placement in the plugin registry.
|
|
45
46
|
|
|
46
47
|
Found when using the CLI and inspecting the registry, some docstrings are formatted
|
|
@@ -50,12 +51,12 @@ def format_docstring(docstring, use_regex=True):
|
|
|
50
51
|
|
|
51
52
|
Parameters
|
|
52
53
|
----------
|
|
53
|
-
docstring: str
|
|
54
|
-
|
|
55
|
-
use_regex: bool, optional (default=False)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
docstring : str
|
|
55
|
+
The docstring which we are going to format.
|
|
56
|
+
use_regex : bool, optional (default=False)
|
|
57
|
+
Whether or not we want to apply regex formatting to the docstring. Usually
|
|
58
|
+
recommended as it will replace 'newline' chars but not purposeful
|
|
59
|
+
'.newline' strings.
|
|
59
60
|
"""
|
|
60
61
|
if docstring:
|
|
61
62
|
if use_regex:
|
|
@@ -71,23 +72,19 @@ def format_docstring(docstring, use_regex=True):
|
|
|
71
72
|
return docstring
|
|
72
73
|
|
|
73
74
|
|
|
74
|
-
def remove_registries(namespace, plugin_packages):
|
|
75
|
+
def remove_registries(namespace: str, plugin_packages: list) -> None:
|
|
75
76
|
"""Remove all plugin registries if a PluginRegistryError is raised.
|
|
76
77
|
|
|
77
78
|
Parameters
|
|
78
79
|
----------
|
|
79
|
-
namespace: str
|
|
80
|
+
namespace : str
|
|
80
81
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
81
82
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
82
83
|
namespaces if developing interfaces outside of pluginify.
|
|
83
|
-
plugin_packages: list[EntryPoints]
|
|
84
|
+
plugin_packages : list[EntryPoints]
|
|
84
85
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
85
86
|
[EntryPoint(name='pluginify', value='pluginify',
|
|
86
87
|
group='pluginify.plugin_packages'), ...]
|
|
87
|
-
|
|
88
|
-
Returns
|
|
89
|
-
-------
|
|
90
|
-
None
|
|
91
88
|
"""
|
|
92
89
|
# Remove registries is called whenever an improperly formatted plugin or
|
|
93
90
|
# package is encountered. This is not called until all errors have been
|
|
@@ -111,7 +108,9 @@ def remove_registries(namespace, plugin_packages):
|
|
|
111
108
|
remove(json_plug_path)
|
|
112
109
|
|
|
113
110
|
|
|
114
|
-
def registry_sanity_check(
|
|
111
|
+
def registry_sanity_check(
|
|
112
|
+
plugin_packages: list, save_type: str, namespace: str
|
|
113
|
+
) -> None:
|
|
115
114
|
"""Check that each plugin package registry has no duplicate lowest depth entries.
|
|
116
115
|
|
|
117
116
|
If it does, raise a `PluginRegistryError` for that specific package, then remove all
|
|
@@ -121,19 +120,15 @@ def registry_sanity_check(plugin_packages, save_type, namespace):
|
|
|
121
120
|
|
|
122
121
|
Parameters
|
|
123
122
|
----------
|
|
124
|
-
plugin_packages: list EntryPoints
|
|
123
|
+
plugin_packages : list EntryPoints
|
|
125
124
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
126
125
|
`[EntryPoint(name='pluginify', value='pluginify',
|
|
127
126
|
group='pluginify.plugin_packages')]`
|
|
128
|
-
save_type: str
|
|
127
|
+
save_type : str
|
|
129
128
|
The file format to save to `[json, yaml]`
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
No returns
|
|
134
|
-
|
|
135
|
-
Exceptions
|
|
136
|
-
----------
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
137
132
|
PluginRegistryError
|
|
138
133
|
If `error_message` has contents, then raise PluginRegistryError(error_message).
|
|
139
134
|
The `error_message` string will collect and report on all errors within
|
|
@@ -263,7 +258,9 @@ def registry_sanity_check(plugin_packages, save_type, namespace):
|
|
|
263
258
|
raise PluginRegistryError(error_message)
|
|
264
259
|
|
|
265
260
|
|
|
266
|
-
def check_plugin_exists(
|
|
261
|
+
def check_plugin_exists(
|
|
262
|
+
package: str, plugins: dict, interface_name: str, plugin_name: str, relpath: str
|
|
263
|
+
) -> str:
|
|
267
264
|
"""Check if plugin already exists. If it does raise a `PluginRegistryError`.
|
|
268
265
|
|
|
269
266
|
Note this only checks for duplicate plugins within a single plugin package.
|
|
@@ -272,14 +269,16 @@ def check_plugin_exists(package, plugins, interface_name, plugin_name, relpath):
|
|
|
272
269
|
|
|
273
270
|
Parameters
|
|
274
271
|
----------
|
|
275
|
-
package: str
|
|
272
|
+
package : str
|
|
276
273
|
The package being tested against
|
|
277
|
-
plugins: dict
|
|
274
|
+
plugins : dict
|
|
278
275
|
A dictionary object of all installed plugins in the current plugin package.
|
|
279
|
-
interface_name: str
|
|
276
|
+
interface_name : str
|
|
280
277
|
A string representing the interface being checked against
|
|
281
|
-
plugin_name: str
|
|
278
|
+
plugin_name : str
|
|
282
279
|
A string representing the name of the plugin within the interface
|
|
280
|
+
relpath : str
|
|
281
|
+
The relative path to the plugin being checked.
|
|
283
282
|
|
|
284
283
|
Returns
|
|
285
284
|
-------
|
|
@@ -300,21 +299,19 @@ def check_plugin_exists(package, plugins, interface_name, plugin_name, relpath):
|
|
|
300
299
|
return ""
|
|
301
300
|
|
|
302
301
|
|
|
303
|
-
def write_registered_plugins(
|
|
302
|
+
def write_registered_plugins(
|
|
303
|
+
pkg_dir: str | Path, plugins: dict, save_type: str
|
|
304
|
+
) -> None:
|
|
304
305
|
"""Write dictionary of all plugins available from installed packages.
|
|
305
306
|
|
|
306
307
|
Parameters
|
|
307
308
|
----------
|
|
308
|
-
pkg_dir: str
|
|
309
|
+
pkg_dir : str
|
|
309
310
|
Path in which to write registered_plugins
|
|
310
|
-
plugins: dict
|
|
311
|
+
plugins : dict
|
|
311
312
|
A dictionary object of all installed package plugins
|
|
312
|
-
save_type: str
|
|
313
|
+
save_type : str
|
|
313
314
|
The file format to save to [json, yaml]
|
|
314
|
-
|
|
315
|
-
Returns
|
|
316
|
-
-------
|
|
317
|
-
No returns, file written to `pkg_dir`
|
|
318
315
|
"""
|
|
319
316
|
if save_type == "yaml":
|
|
320
317
|
reg_plug_abspath = os_join(pkg_dir, "registered_plugins.yaml")
|
|
@@ -347,7 +344,9 @@ def write_registered_plugins(pkg_dir, plugins, save_type):
|
|
|
347
344
|
pass
|
|
348
345
|
|
|
349
346
|
|
|
350
|
-
def create_plugin_registries(
|
|
347
|
+
def create_plugin_registries(
|
|
348
|
+
plugin_packages: list, save_type: str, namespace: str
|
|
349
|
+
) -> None:
|
|
351
350
|
"""Generate all plugin paths associated with every installed packages.
|
|
352
351
|
|
|
353
352
|
These paths include text plugins, class_based plugins
|
|
@@ -357,13 +356,13 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
357
356
|
|
|
358
357
|
Parameters
|
|
359
358
|
----------
|
|
360
|
-
plugin_packages: list EntryPoints
|
|
359
|
+
plugin_packages : list EntryPoints
|
|
361
360
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
362
361
|
[EntryPoint(name='pluginify', value='pluginify',
|
|
363
362
|
group='pluginify.plugin_packages'), ...]
|
|
364
|
-
save_type: str
|
|
363
|
+
save_type : str
|
|
365
364
|
The file format to save to [json, yaml]
|
|
366
|
-
namespace: str
|
|
365
|
+
namespace : str
|
|
367
366
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
368
367
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
369
368
|
namespaces if developing interfaces outside of pluginify.
|
|
@@ -371,8 +370,8 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
371
370
|
# It appears when there is *.egg-info directory, it picks that package up
|
|
372
371
|
# twice in the list. If the same package path exists twice, only keep one
|
|
373
372
|
# of them. This appears to be a bug with Python 3.9 entry points.
|
|
374
|
-
pkg_dirs = []
|
|
375
|
-
unique_package_entry_points = []
|
|
373
|
+
pkg_dirs: list = []
|
|
374
|
+
unique_package_entry_points: list = []
|
|
376
375
|
# Note we only use ep.value (resources.files finds the actual plugins),
|
|
377
376
|
# so we do not need to worry about saving the "wrong" package here.
|
|
378
377
|
# We are actually looping through all the files in each package, so
|
|
@@ -391,7 +390,7 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
391
390
|
for pkg in unique_package_entry_points:
|
|
392
391
|
# This is passed by reference and populated with each call to parse
|
|
393
392
|
# plugin packages.
|
|
394
|
-
plugins = {
|
|
393
|
+
plugins: dict = {
|
|
395
394
|
"text_based": {},
|
|
396
395
|
"yaml_based": {},
|
|
397
396
|
"class_based": {},
|
|
@@ -408,7 +407,7 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
408
407
|
# that holds all plugins, and the package_schema entry point directly
|
|
409
408
|
# to the directory that holds all scheam, so the subdirectories are
|
|
410
409
|
# not hard coded here in the create_plugin_registries code.
|
|
411
|
-
pkg_plugin_path = resources.files(package) / "plugins"
|
|
410
|
+
pkg_plugin_path = Path(str(resources.files(package) / "plugins"))
|
|
412
411
|
pkg_dir = str(resources.files(package))
|
|
413
412
|
# Generate a path to the parent directory where we should write the plugin
|
|
414
413
|
# registry file for this package to.
|
|
@@ -465,7 +464,13 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
465
464
|
registry_sanity_check(unique_package_entry_points, save_type, namespace)
|
|
466
465
|
|
|
467
466
|
|
|
468
|
-
def parse_plugin_paths(
|
|
467
|
+
def parse_plugin_paths(
|
|
468
|
+
plugin_paths: dict,
|
|
469
|
+
package: str,
|
|
470
|
+
package_dir: str,
|
|
471
|
+
plugins: dict,
|
|
472
|
+
namespace: str,
|
|
473
|
+
) -> str:
|
|
469
474
|
"""Parse the plugin_paths provided from the current installed package.
|
|
470
475
|
|
|
471
476
|
Then, add them to the plugins dictionary based on the path of the plugin.
|
|
@@ -474,22 +479,22 @@ def parse_plugin_paths(plugin_paths, package, package_dir, plugins, namespace):
|
|
|
474
479
|
|
|
475
480
|
Parameters
|
|
476
481
|
----------
|
|
477
|
-
plugin_paths: dict
|
|
482
|
+
plugin_paths : dict
|
|
478
483
|
A dictionary of filepaths, with keys referring to the type of plugin
|
|
479
|
-
package: str
|
|
484
|
+
package : str
|
|
480
485
|
The current package being parsed
|
|
481
|
-
package_dir: str
|
|
486
|
+
package_dir : str
|
|
482
487
|
The path to the current package (for determining relative paths)
|
|
483
|
-
plugins: dict
|
|
488
|
+
plugins : dict
|
|
484
489
|
A dictionary object of all installed package plugins
|
|
485
|
-
namespace: str
|
|
490
|
+
namespace : str
|
|
486
491
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
487
492
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
488
493
|
namespaces if developing interfaces outside of pluginify.
|
|
489
494
|
|
|
490
495
|
Returns
|
|
491
496
|
-------
|
|
492
|
-
error_message: str
|
|
497
|
+
error_message : str
|
|
493
498
|
String containing informative error messages from any plugins that
|
|
494
499
|
were improperly formatted. An exception will be raised at the
|
|
495
500
|
very end if error_message is not the empty string - this allows
|
|
@@ -541,27 +546,29 @@ def parse_plugin_paths(plugin_paths, package, package_dir, plugins, namespace):
|
|
|
541
546
|
return error_message
|
|
542
547
|
|
|
543
548
|
|
|
544
|
-
def add_yaml_plugin(
|
|
549
|
+
def add_yaml_plugin(
|
|
550
|
+
filepath: str, relpath: str, package: str, plugins: dict, namespace: str
|
|
551
|
+
) -> str:
|
|
545
552
|
"""Add the yaml plugin associated with the filepaths and package to plugins.
|
|
546
553
|
|
|
547
554
|
Parameters
|
|
548
555
|
----------
|
|
549
|
-
filepath: str
|
|
556
|
+
filepath : str
|
|
550
557
|
The path of the plugin derived from resouces.files(package) / plugin
|
|
551
|
-
relpath: str
|
|
558
|
+
relpath : str
|
|
552
559
|
The relative path to the filepath provided
|
|
553
|
-
package: str
|
|
560
|
+
package : str
|
|
554
561
|
The current package being parsed
|
|
555
|
-
plugins: dict
|
|
562
|
+
plugins : dict
|
|
556
563
|
A dictionary object of all installed package plugins
|
|
557
|
-
namespace: str
|
|
564
|
+
namespace : str
|
|
558
565
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
559
566
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
560
567
|
namespaces if developing interfaces outside of pluginify.
|
|
561
568
|
|
|
562
569
|
Returns
|
|
563
570
|
-------
|
|
564
|
-
error_message: str
|
|
571
|
+
error_message : str
|
|
565
572
|
String containing informative error messages from any plugins that
|
|
566
573
|
were improperly formatted. An exception will be raised at the
|
|
567
574
|
very end if error_message is not the empty string - this allows
|
|
@@ -696,7 +703,7 @@ def add_yaml_plugin(filepath, relpath, package, plugins, namespace):
|
|
|
696
703
|
# if not family:
|
|
697
704
|
# family = plugins["product_defaults"][pd]["family"]
|
|
698
705
|
plugins[interface_name][subplg_source][subplg_product] = {
|
|
699
|
-
"docstring": str(format_docstring(docstring)),
|
|
706
|
+
"docstring": str(format_docstring(docstring or "")),
|
|
700
707
|
"family": family,
|
|
701
708
|
"interface": interface_module.name,
|
|
702
709
|
"package": plugin["package"],
|
|
@@ -737,21 +744,21 @@ def add_yaml_plugin(filepath, relpath, package, plugins, namespace):
|
|
|
737
744
|
return error_message
|
|
738
745
|
|
|
739
746
|
|
|
740
|
-
def add_text_plugin(package, relpath, plugins):
|
|
747
|
+
def add_text_plugin(package: str, relpath: str, plugins: dict) -> str:
|
|
741
748
|
"""Add all text plugins into plugin registries.
|
|
742
749
|
|
|
743
750
|
Parameters
|
|
744
751
|
----------
|
|
745
|
-
package: str
|
|
752
|
+
package : str
|
|
746
753
|
The current package being parsed
|
|
747
|
-
relpath: str
|
|
754
|
+
relpath : str
|
|
748
755
|
The relpath path to the text plugin
|
|
749
|
-
plugins: dict
|
|
756
|
+
plugins : dict
|
|
750
757
|
A dictionary object of all installed package plugins
|
|
751
758
|
|
|
752
759
|
Returns
|
|
753
760
|
-------
|
|
754
|
-
error_message: str
|
|
761
|
+
error_message : str
|
|
755
762
|
String containing informative error messages from any plugins that
|
|
756
763
|
were improperly formatted. An exception will be raised at the
|
|
757
764
|
very end if error_message is not the empty string - this allows
|
|
@@ -780,8 +787,12 @@ def add_text_plugin(package, relpath, plugins):
|
|
|
780
787
|
|
|
781
788
|
|
|
782
789
|
def collect_module_plugin_metadata(
|
|
783
|
-
module
|
|
784
|
-
|
|
790
|
+
module: Any,
|
|
791
|
+
module_name: str,
|
|
792
|
+
package: str,
|
|
793
|
+
relpath: str,
|
|
794
|
+
error_message: str,
|
|
795
|
+
) -> dict | str:
|
|
785
796
|
"""Collect metadata for the module-based plugin for the plugin registry.
|
|
786
797
|
|
|
787
798
|
Metadata necessary to collect here includes the interface of the plugin, the
|
|
@@ -793,27 +804,27 @@ def collect_module_plugin_metadata(
|
|
|
793
804
|
|
|
794
805
|
Parameters
|
|
795
806
|
----------
|
|
796
|
-
module: ModuleType
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
module_name: str
|
|
800
|
-
|
|
801
|
-
package: str
|
|
802
|
-
|
|
803
|
-
relpath: str
|
|
804
|
-
|
|
805
|
-
error_message: str
|
|
806
|
-
|
|
807
|
-
|
|
807
|
+
module : ModuleType
|
|
808
|
+
The object of the module which has been loaded in realtime. This should have
|
|
809
|
+
top level attributes such as 'interface', 'name', 'family' and so on.
|
|
810
|
+
module_name : str
|
|
811
|
+
The name of the .py file.
|
|
812
|
+
package : str
|
|
813
|
+
The name of the package this plugin comes from
|
|
814
|
+
relpath : str
|
|
815
|
+
The relative path to the plugin found in 'package'
|
|
816
|
+
error_message : str
|
|
817
|
+
The current state of errors found during building the registry, stored as a
|
|
818
|
+
string.
|
|
808
819
|
|
|
809
820
|
Returns
|
|
810
821
|
-------
|
|
811
|
-
metadata: dict
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
error_message: str
|
|
815
|
-
|
|
816
|
-
|
|
822
|
+
metadata : dict
|
|
823
|
+
A dictionary of metadata for the provided plugin class to store in the plugin
|
|
824
|
+
registry.
|
|
825
|
+
error_message : str
|
|
826
|
+
The current state of errors found during building the registry, stored as a
|
|
827
|
+
string.
|
|
817
828
|
"""
|
|
818
829
|
# Try to get "interface" variable from the module. This is required
|
|
819
830
|
# on ALL files within the python module based plugins directory, to
|
|
@@ -884,7 +895,7 @@ def collect_module_plugin_metadata(
|
|
|
884
895
|
return metadata
|
|
885
896
|
|
|
886
897
|
|
|
887
|
-
def collect_class_plugin_metadata(plugin_class):
|
|
898
|
+
def collect_class_plugin_metadata(plugin_class, package, relpath, error_message):
|
|
888
899
|
"""Collect metadata linked to the class plugin in 'module' for the plugin registry.
|
|
889
900
|
|
|
890
901
|
Metadata necessary to collect here includes the interface of the plugin, the
|
|
@@ -893,24 +904,60 @@ def collect_class_plugin_metadata(plugin_class):
|
|
|
893
904
|
|
|
894
905
|
Parameters
|
|
895
906
|
----------
|
|
896
|
-
plugin_class: Object, default=None
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
907
|
+
plugin_class : Object, default=None
|
|
908
|
+
The plugin class from 'module' to collect metadata from. If None, this
|
|
909
|
+
function will attempt to locate the correct plugin class from the provided
|
|
910
|
+
module.
|
|
900
911
|
|
|
901
912
|
Returns
|
|
902
913
|
-------
|
|
903
|
-
metadata: dict
|
|
904
|
-
|
|
905
|
-
|
|
914
|
+
metadata : dict
|
|
915
|
+
A dictionary of metadata for the provided plugin class to store in the plugin
|
|
916
|
+
registry.
|
|
906
917
|
"""
|
|
907
|
-
|
|
908
|
-
|
|
918
|
+
error_occurred = False
|
|
919
|
+
|
|
920
|
+
try:
|
|
921
|
+
family = plugin_class.family
|
|
922
|
+
except AttributeError:
|
|
923
|
+
if not getattr(plugin_class, "data_tree"):
|
|
924
|
+
error_occurred = True
|
|
925
|
+
error_message += (
|
|
926
|
+
f"Error: missing 'family' attribute in non-data-tree class based "
|
|
927
|
+
f"plugin found at '{relpath}' in package '{package}'. This attribute "
|
|
928
|
+
"must be specified for non-data-tree class-based plugins."
|
|
929
|
+
)
|
|
930
|
+
else:
|
|
931
|
+
family = "N/A: data tree based plugin"
|
|
932
|
+
|
|
933
|
+
try:
|
|
934
|
+
name = plugin_class.name
|
|
935
|
+
except AttributeError:
|
|
936
|
+
error_occurred = True
|
|
937
|
+
error_message += (
|
|
938
|
+
f"Error: missing 'name' attribute in class based "
|
|
939
|
+
f"plugin found at '{relpath}' in package '{package}'. This attribute must "
|
|
940
|
+
"be specified in class-based plugins."
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
try:
|
|
944
|
+
interface = plugin_class.interface
|
|
945
|
+
except AttributeError:
|
|
946
|
+
error_occurred = True
|
|
947
|
+
error_message += (
|
|
948
|
+
f"Error: missing 'interface' attribute in class based "
|
|
949
|
+
f"plugin found at '{relpath}' in package '{package}'. This attribute must "
|
|
950
|
+
"be specified in class-based plugins."
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
if error_occurred:
|
|
954
|
+
return error_message
|
|
955
|
+
|
|
909
956
|
metadata = {
|
|
910
957
|
"docstring": format_docstring(plugin_class.__doc__),
|
|
911
|
-
"name":
|
|
912
|
-
"family":
|
|
913
|
-
"interface":
|
|
958
|
+
"name": name,
|
|
959
|
+
"family": family,
|
|
960
|
+
"interface": interface,
|
|
914
961
|
"plugin_type": "class_based",
|
|
915
962
|
"signature": str(signature(plugin_class.call)),
|
|
916
963
|
"is_derived_from_module": False,
|
|
@@ -919,7 +966,7 @@ def collect_class_plugin_metadata(plugin_class):
|
|
|
919
966
|
return metadata
|
|
920
967
|
|
|
921
968
|
|
|
922
|
-
def add_class_plugin(package, relpath, plugins):
|
|
969
|
+
def add_class_plugin(package: str, relpath: str, plugins: dict) -> str:
|
|
923
970
|
"""Add the class-based plugin associated with the filepaths and package to plugins.
|
|
924
971
|
|
|
925
972
|
NOTE: This function will work for 'legacy' module-based plugins that are dynamically
|
|
@@ -930,16 +977,16 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
930
977
|
|
|
931
978
|
Parameters
|
|
932
979
|
----------
|
|
933
|
-
package: str
|
|
980
|
+
package : str
|
|
934
981
|
The current package being parsed
|
|
935
|
-
relpath: str
|
|
982
|
+
relpath : str
|
|
936
983
|
The relpath path to the class-based plugin
|
|
937
|
-
plugins: dict
|
|
984
|
+
plugins : dict
|
|
938
985
|
A dictionary object of all installed package plugins
|
|
939
986
|
|
|
940
987
|
Returns
|
|
941
988
|
-------
|
|
942
|
-
error_message: str
|
|
989
|
+
error_message : str
|
|
943
990
|
String containing informative error messages from any plugins that
|
|
944
991
|
were improperly formatted. An exception will be raised at the
|
|
945
992
|
very end if error_message is not the empty string - this allows
|
|
@@ -970,7 +1017,11 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
970
1017
|
module_path = f"{package}.{module_path}"
|
|
971
1018
|
abspath = resources.files(package) / relpath
|
|
972
1019
|
|
|
973
|
-
spec = util.spec_from_file_location(module_path, abspath)
|
|
1020
|
+
spec = util.spec_from_file_location(module_path, str(abspath))
|
|
1021
|
+
if spec is None or spec.loader is None:
|
|
1022
|
+
raise PluginRegistryError(
|
|
1023
|
+
f"Could not create a module spec for '{module_path}' at '{abspath}'."
|
|
1024
|
+
)
|
|
974
1025
|
module = util.module_from_spec(spec)
|
|
975
1026
|
# Attempting importing module, catch ImportError
|
|
976
1027
|
# We have to fix these to be able to import the module to
|
|
@@ -1022,7 +1073,14 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
1022
1073
|
error_message += str(plugin_class_error_message)
|
|
1023
1074
|
return error_message
|
|
1024
1075
|
|
|
1025
|
-
plugin_metadata = collect_class_plugin_metadata(
|
|
1076
|
+
plugin_metadata = collect_class_plugin_metadata(
|
|
1077
|
+
PLUGIN_CLASS, package, relpath, error_message
|
|
1078
|
+
)
|
|
1079
|
+
if isinstance(plugin_metadata, str):
|
|
1080
|
+
# Error message was returned instead of a metadata dictionary. Just return
|
|
1081
|
+
# that message now.
|
|
1082
|
+
return plugin_metadata
|
|
1083
|
+
|
|
1026
1084
|
interface_name = plugin_metadata["interface"]
|
|
1027
1085
|
name = plugin_metadata.pop("name")
|
|
1028
1086
|
|
pluginify/errors.py
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# # # This source code is subject to the license referenced at
|
|
2
2
|
# # # https://github.com/NRLMMD-GEOIPS.
|
|
3
3
|
|
|
4
|
-
"""
|
|
4
|
+
"""Exceptions raised by the pluginify package.
|
|
5
|
+
|
|
6
|
+
Provides two error classes:
|
|
7
|
+
|
|
8
|
+
- ``PluginError`` — raised when an individual plugin is malformed, missing
|
|
9
|
+
required attributes, or otherwise invalid.
|
|
10
|
+
- ``PluginRegistryError`` — raised when a plugin registry itself is missing,
|
|
11
|
+
corrupt, or contains duplicate/conflicting entries across packages.
|
|
12
|
+
"""
|
|
5
13
|
|
|
6
14
|
|
|
7
15
|
class PluginError(Exception):
|
|
8
|
-
"""Exception
|
|
16
|
+
"""Exception raised for errors related to an individual plugin."""
|
|
9
17
|
|
|
10
18
|
pass
|
|
11
19
|
|
|
12
20
|
|
|
13
21
|
class PluginRegistryError(Exception):
|
|
14
|
-
"""Exception
|
|
22
|
+
"""Exception raised for errors related to a plugin registry."""
|
|
15
23
|
|
|
16
24
|
pass
|
pluginify/interfaces/__init__.py
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
# # # This source code is subject to the license referenced at
|
|
2
2
|
# # # https://github.com/NRLMMD-GEOIPS.
|
|
3
3
|
|
|
4
|
-
"""
|
|
4
|
+
"""Interface discovery and initialization module for pluginify.
|
|
5
|
+
|
|
6
|
+
Imports all available class-based and YAML-based interface instances so that
|
|
7
|
+
other parts of the pluginify ecosystem can discover them via
|
|
8
|
+
``from pluginify.interfaces import <interface_name>``.
|
|
9
|
+
|
|
10
|
+
The ``class_based_interfaces`` and ``yaml_based_interfaces`` lists are used by
|
|
11
|
+
``PluginRegistryValidator.validate_registry_interfaces`` to validate that
|
|
12
|
+
registry entries reference known interface names.
|
|
13
|
+
"""
|
|
5
14
|
|
|
6
15
|
from pluginify.interfaces.class_based.data_modifiers import data_modifiers # noqa: F401
|
|
7
16
|
from pluginify.interfaces.yaml_based.configs import configs # noqa: F401
|