pluginify 1.0.0.post11.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 +179 -111
- 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.post11.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.post11.dev0.dist-info/RECORD +0 -23
- {pluginify-1.0.0.post11.dev0.dist-info → pluginify-1.0.0.post41.dev0.dist-info}/WHEEL +0 -0
- {pluginify-1.0.0.post11.dev0.dist-info → pluginify-1.0.0.post41.dev0.dist-info}/entry_points.txt +0 -0
- {pluginify-1.0.0.post11.dev0.dist-info → pluginify-1.0.0.post41.dev0.dist-info}/licenses/LICENSE +0 -0
|
@@ -17,7 +17,7 @@ import logging
|
|
|
17
17
|
from importlib import metadata, resources, util, import_module
|
|
18
18
|
from inspect import signature
|
|
19
19
|
import json
|
|
20
|
-
from os import
|
|
20
|
+
from os import makedirs, remove, replace
|
|
21
21
|
from os.path import (
|
|
22
22
|
basename,
|
|
23
23
|
dirname,
|
|
@@ -29,7 +29,9 @@ from os.path import (
|
|
|
29
29
|
)
|
|
30
30
|
from pathlib import Path
|
|
31
31
|
import re
|
|
32
|
+
import tempfile
|
|
32
33
|
import warnings
|
|
34
|
+
from typing import Any
|
|
33
35
|
|
|
34
36
|
import yaml
|
|
35
37
|
|
|
@@ -39,7 +41,7 @@ from pluginify.errors import PluginRegistryError
|
|
|
39
41
|
LOG = logging.getLogger(__name__)
|
|
40
42
|
|
|
41
43
|
|
|
42
|
-
def format_docstring(docstring, use_regex=True):
|
|
44
|
+
def format_docstring(docstring: str, use_regex: bool = True) -> str:
|
|
43
45
|
"""Format the provided docstring placement in the plugin registry.
|
|
44
46
|
|
|
45
47
|
Found when using the CLI and inspecting the registry, some docstrings are formatted
|
|
@@ -49,12 +51,12 @@ def format_docstring(docstring, use_regex=True):
|
|
|
49
51
|
|
|
50
52
|
Parameters
|
|
51
53
|
----------
|
|
52
|
-
docstring: str
|
|
53
|
-
|
|
54
|
-
use_regex: bool, optional (default=False)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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.
|
|
58
60
|
"""
|
|
59
61
|
if docstring:
|
|
60
62
|
if use_regex:
|
|
@@ -70,23 +72,19 @@ def format_docstring(docstring, use_regex=True):
|
|
|
70
72
|
return docstring
|
|
71
73
|
|
|
72
74
|
|
|
73
|
-
def remove_registries(namespace, plugin_packages):
|
|
75
|
+
def remove_registries(namespace: str, plugin_packages: list) -> None:
|
|
74
76
|
"""Remove all plugin registries if a PluginRegistryError is raised.
|
|
75
77
|
|
|
76
78
|
Parameters
|
|
77
79
|
----------
|
|
78
|
-
namespace: str
|
|
80
|
+
namespace : str
|
|
79
81
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
80
82
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
81
83
|
namespaces if developing interfaces outside of pluginify.
|
|
82
|
-
plugin_packages: list[EntryPoints]
|
|
84
|
+
plugin_packages : list[EntryPoints]
|
|
83
85
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
84
86
|
[EntryPoint(name='pluginify', value='pluginify',
|
|
85
87
|
group='pluginify.plugin_packages'), ...]
|
|
86
|
-
|
|
87
|
-
Returns
|
|
88
|
-
-------
|
|
89
|
-
None
|
|
90
88
|
"""
|
|
91
89
|
# Remove registries is called whenever an improperly formatted plugin or
|
|
92
90
|
# package is encountered. This is not called until all errors have been
|
|
@@ -110,7 +108,9 @@ def remove_registries(namespace, plugin_packages):
|
|
|
110
108
|
remove(json_plug_path)
|
|
111
109
|
|
|
112
110
|
|
|
113
|
-
def registry_sanity_check(
|
|
111
|
+
def registry_sanity_check(
|
|
112
|
+
plugin_packages: list, save_type: str, namespace: str
|
|
113
|
+
) -> None:
|
|
114
114
|
"""Check that each plugin package registry has no duplicate lowest depth entries.
|
|
115
115
|
|
|
116
116
|
If it does, raise a `PluginRegistryError` for that specific package, then remove all
|
|
@@ -120,19 +120,15 @@ def registry_sanity_check(plugin_packages, save_type, namespace):
|
|
|
120
120
|
|
|
121
121
|
Parameters
|
|
122
122
|
----------
|
|
123
|
-
plugin_packages: list EntryPoints
|
|
123
|
+
plugin_packages : list EntryPoints
|
|
124
124
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
125
125
|
`[EntryPoint(name='pluginify', value='pluginify',
|
|
126
126
|
group='pluginify.plugin_packages')]`
|
|
127
|
-
save_type: str
|
|
127
|
+
save_type : str
|
|
128
128
|
The file format to save to `[json, yaml]`
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
No returns
|
|
133
|
-
|
|
134
|
-
Exceptions
|
|
135
|
-
----------
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
136
132
|
PluginRegistryError
|
|
137
133
|
If `error_message` has contents, then raise PluginRegistryError(error_message).
|
|
138
134
|
The `error_message` string will collect and report on all errors within
|
|
@@ -262,7 +258,9 @@ def registry_sanity_check(plugin_packages, save_type, namespace):
|
|
|
262
258
|
raise PluginRegistryError(error_message)
|
|
263
259
|
|
|
264
260
|
|
|
265
|
-
def check_plugin_exists(
|
|
261
|
+
def check_plugin_exists(
|
|
262
|
+
package: str, plugins: dict, interface_name: str, plugin_name: str, relpath: str
|
|
263
|
+
) -> str:
|
|
266
264
|
"""Check if plugin already exists. If it does raise a `PluginRegistryError`.
|
|
267
265
|
|
|
268
266
|
Note this only checks for duplicate plugins within a single plugin package.
|
|
@@ -271,14 +269,16 @@ def check_plugin_exists(package, plugins, interface_name, plugin_name, relpath):
|
|
|
271
269
|
|
|
272
270
|
Parameters
|
|
273
271
|
----------
|
|
274
|
-
package: str
|
|
272
|
+
package : str
|
|
275
273
|
The package being tested against
|
|
276
|
-
plugins: dict
|
|
274
|
+
plugins : dict
|
|
277
275
|
A dictionary object of all installed plugins in the current plugin package.
|
|
278
|
-
interface_name: str
|
|
276
|
+
interface_name : str
|
|
279
277
|
A string representing the interface being checked against
|
|
280
|
-
plugin_name: str
|
|
278
|
+
plugin_name : str
|
|
281
279
|
A string representing the name of the plugin within the interface
|
|
280
|
+
relpath : str
|
|
281
|
+
The relative path to the plugin being checked.
|
|
282
282
|
|
|
283
283
|
Returns
|
|
284
284
|
-------
|
|
@@ -299,21 +299,19 @@ def check_plugin_exists(package, plugins, interface_name, plugin_name, relpath):
|
|
|
299
299
|
return ""
|
|
300
300
|
|
|
301
301
|
|
|
302
|
-
def write_registered_plugins(
|
|
302
|
+
def write_registered_plugins(
|
|
303
|
+
pkg_dir: str | Path, plugins: dict, save_type: str
|
|
304
|
+
) -> None:
|
|
303
305
|
"""Write dictionary of all plugins available from installed packages.
|
|
304
306
|
|
|
305
307
|
Parameters
|
|
306
308
|
----------
|
|
307
|
-
pkg_dir: str
|
|
309
|
+
pkg_dir : str
|
|
308
310
|
Path in which to write registered_plugins
|
|
309
|
-
plugins: dict
|
|
311
|
+
plugins : dict
|
|
310
312
|
A dictionary object of all installed package plugins
|
|
311
|
-
save_type: str
|
|
313
|
+
save_type : str
|
|
312
314
|
The file format to save to [json, yaml]
|
|
313
|
-
|
|
314
|
-
Returns
|
|
315
|
-
-------
|
|
316
|
-
No returns, file written to `pkg_dir`
|
|
317
315
|
"""
|
|
318
316
|
if save_type == "yaml":
|
|
319
317
|
reg_plug_abspath = os_join(pkg_dir, "registered_plugins.yaml")
|
|
@@ -323,21 +321,32 @@ def write_registered_plugins(pkg_dir, plugins, save_type):
|
|
|
323
321
|
# Write to a temporary file and move it into place, so that other processes
|
|
324
322
|
# reading the registry (for example parallel test workers) never see a
|
|
325
323
|
# partially written file.
|
|
326
|
-
tmp_abspath =
|
|
324
|
+
tmp_abspath = None
|
|
327
325
|
try:
|
|
328
|
-
with
|
|
326
|
+
with tempfile.NamedTemporaryFile(
|
|
327
|
+
mode="w",
|
|
328
|
+
dir=dirname(reg_plug_abspath),
|
|
329
|
+
suffix=".tmp",
|
|
330
|
+
delete=False,
|
|
331
|
+
) as plugin_registry:
|
|
332
|
+
tmp_abspath = plugin_registry.name
|
|
329
333
|
if save_type == "yaml":
|
|
330
334
|
yaml.safe_dump(plugins, plugin_registry, default_flow_style=False)
|
|
331
335
|
else:
|
|
332
336
|
json.dump(plugins, plugin_registry, indent=4)
|
|
337
|
+
|
|
333
338
|
replace(tmp_abspath, reg_plug_abspath)
|
|
334
|
-
|
|
335
|
-
if
|
|
336
|
-
|
|
337
|
-
|
|
339
|
+
finally:
|
|
340
|
+
if tmp_abspath is not None:
|
|
341
|
+
try:
|
|
342
|
+
remove(tmp_abspath)
|
|
343
|
+
except FileNotFoundError:
|
|
344
|
+
pass
|
|
338
345
|
|
|
339
346
|
|
|
340
|
-
def create_plugin_registries(
|
|
347
|
+
def create_plugin_registries(
|
|
348
|
+
plugin_packages: list, save_type: str, namespace: str
|
|
349
|
+
) -> None:
|
|
341
350
|
"""Generate all plugin paths associated with every installed packages.
|
|
342
351
|
|
|
343
352
|
These paths include text plugins, class_based plugins
|
|
@@ -347,13 +356,13 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
347
356
|
|
|
348
357
|
Parameters
|
|
349
358
|
----------
|
|
350
|
-
plugin_packages: list EntryPoints
|
|
359
|
+
plugin_packages : list EntryPoints
|
|
351
360
|
A list of EntryPoints pointing to each installed package --> ie.
|
|
352
361
|
[EntryPoint(name='pluginify', value='pluginify',
|
|
353
362
|
group='pluginify.plugin_packages'), ...]
|
|
354
|
-
save_type: str
|
|
363
|
+
save_type : str
|
|
355
364
|
The file format to save to [json, yaml]
|
|
356
|
-
namespace: str
|
|
365
|
+
namespace : str
|
|
357
366
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
358
367
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
359
368
|
namespaces if developing interfaces outside of pluginify.
|
|
@@ -361,8 +370,8 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
361
370
|
# It appears when there is *.egg-info directory, it picks that package up
|
|
362
371
|
# twice in the list. If the same package path exists twice, only keep one
|
|
363
372
|
# of them. This appears to be a bug with Python 3.9 entry points.
|
|
364
|
-
pkg_dirs = []
|
|
365
|
-
unique_package_entry_points = []
|
|
373
|
+
pkg_dirs: list = []
|
|
374
|
+
unique_package_entry_points: list = []
|
|
366
375
|
# Note we only use ep.value (resources.files finds the actual plugins),
|
|
367
376
|
# so we do not need to worry about saving the "wrong" package here.
|
|
368
377
|
# We are actually looping through all the files in each package, so
|
|
@@ -381,7 +390,7 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
381
390
|
for pkg in unique_package_entry_points:
|
|
382
391
|
# This is passed by reference and populated with each call to parse
|
|
383
392
|
# plugin packages.
|
|
384
|
-
plugins = {
|
|
393
|
+
plugins: dict = {
|
|
385
394
|
"text_based": {},
|
|
386
395
|
"yaml_based": {},
|
|
387
396
|
"class_based": {},
|
|
@@ -398,7 +407,7 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
398
407
|
# that holds all plugins, and the package_schema entry point directly
|
|
399
408
|
# to the directory that holds all scheam, so the subdirectories are
|
|
400
409
|
# not hard coded here in the create_plugin_registries code.
|
|
401
|
-
pkg_plugin_path = resources.files(package) / "plugins"
|
|
410
|
+
pkg_plugin_path = Path(str(resources.files(package) / "plugins"))
|
|
402
411
|
pkg_dir = str(resources.files(package))
|
|
403
412
|
# Generate a path to the parent directory where we should write the plugin
|
|
404
413
|
# registry file for this package to.
|
|
@@ -455,7 +464,13 @@ def create_plugin_registries(plugin_packages, save_type, namespace):
|
|
|
455
464
|
registry_sanity_check(unique_package_entry_points, save_type, namespace)
|
|
456
465
|
|
|
457
466
|
|
|
458
|
-
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:
|
|
459
474
|
"""Parse the plugin_paths provided from the current installed package.
|
|
460
475
|
|
|
461
476
|
Then, add them to the plugins dictionary based on the path of the plugin.
|
|
@@ -464,22 +479,22 @@ def parse_plugin_paths(plugin_paths, package, package_dir, plugins, namespace):
|
|
|
464
479
|
|
|
465
480
|
Parameters
|
|
466
481
|
----------
|
|
467
|
-
plugin_paths: dict
|
|
482
|
+
plugin_paths : dict
|
|
468
483
|
A dictionary of filepaths, with keys referring to the type of plugin
|
|
469
|
-
package: str
|
|
484
|
+
package : str
|
|
470
485
|
The current package being parsed
|
|
471
|
-
package_dir: str
|
|
486
|
+
package_dir : str
|
|
472
487
|
The path to the current package (for determining relative paths)
|
|
473
|
-
plugins: dict
|
|
488
|
+
plugins : dict
|
|
474
489
|
A dictionary object of all installed package plugins
|
|
475
|
-
namespace: str
|
|
490
|
+
namespace : str
|
|
476
491
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
477
492
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
478
493
|
namespaces if developing interfaces outside of pluginify.
|
|
479
494
|
|
|
480
495
|
Returns
|
|
481
496
|
-------
|
|
482
|
-
error_message: str
|
|
497
|
+
error_message : str
|
|
483
498
|
String containing informative error messages from any plugins that
|
|
484
499
|
were improperly formatted. An exception will be raised at the
|
|
485
500
|
very end if error_message is not the empty string - this allows
|
|
@@ -531,27 +546,29 @@ def parse_plugin_paths(plugin_paths, package, package_dir, plugins, namespace):
|
|
|
531
546
|
return error_message
|
|
532
547
|
|
|
533
548
|
|
|
534
|
-
def add_yaml_plugin(
|
|
549
|
+
def add_yaml_plugin(
|
|
550
|
+
filepath: str, relpath: str, package: str, plugins: dict, namespace: str
|
|
551
|
+
) -> str:
|
|
535
552
|
"""Add the yaml plugin associated with the filepaths and package to plugins.
|
|
536
553
|
|
|
537
554
|
Parameters
|
|
538
555
|
----------
|
|
539
|
-
filepath: str
|
|
556
|
+
filepath : str
|
|
540
557
|
The path of the plugin derived from resouces.files(package) / plugin
|
|
541
|
-
relpath: str
|
|
558
|
+
relpath : str
|
|
542
559
|
The relative path to the filepath provided
|
|
543
|
-
package: str
|
|
560
|
+
package : str
|
|
544
561
|
The current package being parsed
|
|
545
|
-
plugins: dict
|
|
562
|
+
plugins : dict
|
|
546
563
|
A dictionary object of all installed package plugins
|
|
547
|
-
namespace: str
|
|
564
|
+
namespace : str
|
|
548
565
|
Namespace that your plugin packages fall under. The argument parser defaults
|
|
549
566
|
this value to 'pluginify.plugin_packages', but a user can create separate
|
|
550
567
|
namespaces if developing interfaces outside of pluginify.
|
|
551
568
|
|
|
552
569
|
Returns
|
|
553
570
|
-------
|
|
554
|
-
error_message: str
|
|
571
|
+
error_message : str
|
|
555
572
|
String containing informative error messages from any plugins that
|
|
556
573
|
were improperly formatted. An exception will be raised at the
|
|
557
574
|
very end if error_message is not the empty string - this allows
|
|
@@ -686,7 +703,7 @@ def add_yaml_plugin(filepath, relpath, package, plugins, namespace):
|
|
|
686
703
|
# if not family:
|
|
687
704
|
# family = plugins["product_defaults"][pd]["family"]
|
|
688
705
|
plugins[interface_name][subplg_source][subplg_product] = {
|
|
689
|
-
"docstring": str(format_docstring(docstring)),
|
|
706
|
+
"docstring": str(format_docstring(docstring or "")),
|
|
690
707
|
"family": family,
|
|
691
708
|
"interface": interface_module.name,
|
|
692
709
|
"package": plugin["package"],
|
|
@@ -727,21 +744,21 @@ def add_yaml_plugin(filepath, relpath, package, plugins, namespace):
|
|
|
727
744
|
return error_message
|
|
728
745
|
|
|
729
746
|
|
|
730
|
-
def add_text_plugin(package, relpath, plugins):
|
|
747
|
+
def add_text_plugin(package: str, relpath: str, plugins: dict) -> str:
|
|
731
748
|
"""Add all text plugins into plugin registries.
|
|
732
749
|
|
|
733
750
|
Parameters
|
|
734
751
|
----------
|
|
735
|
-
package: str
|
|
752
|
+
package : str
|
|
736
753
|
The current package being parsed
|
|
737
|
-
relpath: str
|
|
754
|
+
relpath : str
|
|
738
755
|
The relpath path to the text plugin
|
|
739
|
-
plugins: dict
|
|
756
|
+
plugins : dict
|
|
740
757
|
A dictionary object of all installed package plugins
|
|
741
758
|
|
|
742
759
|
Returns
|
|
743
760
|
-------
|
|
744
|
-
error_message: str
|
|
761
|
+
error_message : str
|
|
745
762
|
String containing informative error messages from any plugins that
|
|
746
763
|
were improperly formatted. An exception will be raised at the
|
|
747
764
|
very end if error_message is not the empty string - this allows
|
|
@@ -770,8 +787,12 @@ def add_text_plugin(package, relpath, plugins):
|
|
|
770
787
|
|
|
771
788
|
|
|
772
789
|
def collect_module_plugin_metadata(
|
|
773
|
-
module
|
|
774
|
-
|
|
790
|
+
module: Any,
|
|
791
|
+
module_name: str,
|
|
792
|
+
package: str,
|
|
793
|
+
relpath: str,
|
|
794
|
+
error_message: str,
|
|
795
|
+
) -> dict | str:
|
|
775
796
|
"""Collect metadata for the module-based plugin for the plugin registry.
|
|
776
797
|
|
|
777
798
|
Metadata necessary to collect here includes the interface of the plugin, the
|
|
@@ -783,27 +804,27 @@ def collect_module_plugin_metadata(
|
|
|
783
804
|
|
|
784
805
|
Parameters
|
|
785
806
|
----------
|
|
786
|
-
module: ModuleType
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
module_name: str
|
|
790
|
-
|
|
791
|
-
package: str
|
|
792
|
-
|
|
793
|
-
relpath: str
|
|
794
|
-
|
|
795
|
-
error_message: str
|
|
796
|
-
|
|
797
|
-
|
|
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.
|
|
798
819
|
|
|
799
820
|
Returns
|
|
800
821
|
-------
|
|
801
|
-
metadata: dict
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
error_message: str
|
|
805
|
-
|
|
806
|
-
|
|
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.
|
|
807
828
|
"""
|
|
808
829
|
# Try to get "interface" variable from the module. This is required
|
|
809
830
|
# on ALL files within the python module based plugins directory, to
|
|
@@ -874,7 +895,7 @@ def collect_module_plugin_metadata(
|
|
|
874
895
|
return metadata
|
|
875
896
|
|
|
876
897
|
|
|
877
|
-
def collect_class_plugin_metadata(plugin_class):
|
|
898
|
+
def collect_class_plugin_metadata(plugin_class, package, relpath, error_message):
|
|
878
899
|
"""Collect metadata linked to the class plugin in 'module' for the plugin registry.
|
|
879
900
|
|
|
880
901
|
Metadata necessary to collect here includes the interface of the plugin, the
|
|
@@ -883,24 +904,60 @@ def collect_class_plugin_metadata(plugin_class):
|
|
|
883
904
|
|
|
884
905
|
Parameters
|
|
885
906
|
----------
|
|
886
|
-
plugin_class: Object, default=None
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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.
|
|
890
911
|
|
|
891
912
|
Returns
|
|
892
913
|
-------
|
|
893
|
-
metadata: dict
|
|
894
|
-
|
|
895
|
-
|
|
914
|
+
metadata : dict
|
|
915
|
+
A dictionary of metadata for the provided plugin class to store in the plugin
|
|
916
|
+
registry.
|
|
896
917
|
"""
|
|
897
|
-
|
|
898
|
-
|
|
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
|
+
|
|
899
956
|
metadata = {
|
|
900
957
|
"docstring": format_docstring(plugin_class.__doc__),
|
|
901
|
-
"name":
|
|
902
|
-
"family":
|
|
903
|
-
"interface":
|
|
958
|
+
"name": name,
|
|
959
|
+
"family": family,
|
|
960
|
+
"interface": interface,
|
|
904
961
|
"plugin_type": "class_based",
|
|
905
962
|
"signature": str(signature(plugin_class.call)),
|
|
906
963
|
"is_derived_from_module": False,
|
|
@@ -909,7 +966,7 @@ def collect_class_plugin_metadata(plugin_class):
|
|
|
909
966
|
return metadata
|
|
910
967
|
|
|
911
968
|
|
|
912
|
-
def add_class_plugin(package, relpath, plugins):
|
|
969
|
+
def add_class_plugin(package: str, relpath: str, plugins: dict) -> str:
|
|
913
970
|
"""Add the class-based plugin associated with the filepaths and package to plugins.
|
|
914
971
|
|
|
915
972
|
NOTE: This function will work for 'legacy' module-based plugins that are dynamically
|
|
@@ -920,16 +977,16 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
920
977
|
|
|
921
978
|
Parameters
|
|
922
979
|
----------
|
|
923
|
-
package: str
|
|
980
|
+
package : str
|
|
924
981
|
The current package being parsed
|
|
925
|
-
relpath: str
|
|
982
|
+
relpath : str
|
|
926
983
|
The relpath path to the class-based plugin
|
|
927
|
-
plugins: dict
|
|
984
|
+
plugins : dict
|
|
928
985
|
A dictionary object of all installed package plugins
|
|
929
986
|
|
|
930
987
|
Returns
|
|
931
988
|
-------
|
|
932
|
-
error_message: str
|
|
989
|
+
error_message : str
|
|
933
990
|
String containing informative error messages from any plugins that
|
|
934
991
|
were improperly formatted. An exception will be raised at the
|
|
935
992
|
very end if error_message is not the empty string - this allows
|
|
@@ -960,7 +1017,11 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
960
1017
|
module_path = f"{package}.{module_path}"
|
|
961
1018
|
abspath = resources.files(package) / relpath
|
|
962
1019
|
|
|
963
|
-
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
|
+
)
|
|
964
1025
|
module = util.module_from_spec(spec)
|
|
965
1026
|
# Attempting importing module, catch ImportError
|
|
966
1027
|
# We have to fix these to be able to import the module to
|
|
@@ -1012,7 +1073,14 @@ def add_class_plugin(package, relpath, plugins):
|
|
|
1012
1073
|
error_message += str(plugin_class_error_message)
|
|
1013
1074
|
return error_message
|
|
1014
1075
|
|
|
1015
|
-
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
|
+
|
|
1016
1084
|
interface_name = plugin_metadata["interface"]
|
|
1017
1085
|
name = plugin_metadata.pop("name")
|
|
1018
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
|