pluginify 0.0.0.post1.dev0__tar.gz → 0.1.0.post11.dev0__tar.gz
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-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/package-and-publish.yaml +2 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/PKG-INFO +6 -4
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/README.md +2 -2
- pluginify-0.1.0.post11.dev0/docs/source/releases/latest/ignore-dot-files-and-dirs.yaml +17 -0
- pluginify-0.1.0.post11.dev0/docs/source/releases/latest/obp-plugin-registry-updates.yaml +18 -0
- pluginify-0.1.0.post11.dev0/docs/source/releases/latest/pluginify-updates-jeremy-comments.yaml +27 -0
- pluginify-0.1.0.post11.dev0/docs/source/releases/latest/pypi-publish-bug-fix.yaml +15 -0
- pluginify-0.1.0.post11.dev0/docs/source/releases/latest/version-update.yaml +15 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/_version.py +2 -2
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/commandline_typer.py +94 -10
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/create_plugin_registries.py +8 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/plugin_registry.py +77 -9
- pluginify-0.1.0.post11.dev0/pluginify/utils/validators.py +183 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pyproject.toml +2 -2
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/plugin_registries/test_plugin_registries.py +7 -175
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/generate-badges.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/lint-and-test.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/status-check.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/LICENSE +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/ci-fix-perms.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/copy-geoips-functionality.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/initial-commit.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/remove-all-geoips-references.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/replace-geoips-functionality.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/docs/source/releases/latest/update-black-formatting.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/__init__.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/config.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/errors.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/interfaces/__init__.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/interfaces/base.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/interfaces/class_based/data_modifiers.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/interfaces/class_based_plugin.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/interfaces/yaml_based/configs.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/plugins/classes/data_modifiers/cuboid.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/plugins/yaml/configs/stucco.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/pydantic_models/v1/configs.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/utils/__init__.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/utils/context_managers.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/conftest.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/config/test_config.py +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/invalid_interfaces.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/missing_lowest_keys.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/missing_plugin_types.yaml +0 -0
- {pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/plugin_registries/files/good/pluginify.yaml +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pluginify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.0.post11.dev0
|
|
4
4
|
Summary: Pluginify package
|
|
5
5
|
License: LICENSE
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Author: Evan Rose
|
|
8
|
-
Requires-Python: >=3.11.0
|
|
8
|
+
Requires-Python: >=3.11.0
|
|
9
9
|
Classifier: License :: Other/Proprietary License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
15
|
Provides-Extra: doc
|
|
14
16
|
Requires-Dist: docstring_parser
|
|
15
17
|
Requires-Dist: jsonschema (>4.18.0)
|
|
@@ -49,9 +51,9 @@ git clone https://github.com/NRLMMD-GEOIPS/pluginify.git
|
|
|
49
51
|
# cd to pluginify's top level dir
|
|
50
52
|
pip install -e .
|
|
51
53
|
```
|
|
52
|
-
|
|
54
|
+
OR
|
|
53
55
|
```bash
|
|
54
|
-
|
|
56
|
+
pip install pluginify
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
Use pluginify
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
enhancement:
|
|
2
|
+
- title: 'Omit dot files and directories from registry.'
|
|
3
|
+
description: |
|
|
4
|
+
Updated plugin registry code to ignore any instance of dot files or directories
|
|
5
|
+
from the plugin registry. This is a minor update to prevent .ipynb files and other
|
|
6
|
+
irrelevant files from sometimes leaking into the registry and causing problems
|
|
7
|
+
(such as duplicate keys).
|
|
8
|
+
files:
|
|
9
|
+
modified:
|
|
10
|
+
- pluginify/create_plugin_registries.py
|
|
11
|
+
related-issue:
|
|
12
|
+
number: null
|
|
13
|
+
repo_url: ''
|
|
14
|
+
date:
|
|
15
|
+
start: null
|
|
16
|
+
finish: null
|
|
17
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
enhancement:
|
|
2
|
+
- title: 'Migrate OBP "expand" functionality to pluginify.'
|
|
3
|
+
description: |
|
|
4
|
+
Direct copy over of changes made in a GeoIPS PR to the plugin_registry.py file.
|
|
5
|
+
|
|
6
|
+
Changes made added support for pluginify to dynamically 'expand' a workflow if a
|
|
7
|
+
product, product_default, or workflow was found within a workflow plugin. This is
|
|
8
|
+
only done if the '_expand' argument is set to True, which is done via the
|
|
9
|
+
'geoips expand <workflow_name>' command.
|
|
10
|
+
files:
|
|
11
|
+
modified:
|
|
12
|
+
- pluginify/plugin_registry.py
|
|
13
|
+
related-issue:
|
|
14
|
+
number: null
|
|
15
|
+
repo_url: ''
|
|
16
|
+
date:
|
|
17
|
+
start: null
|
|
18
|
+
finish: null
|
pluginify-0.1.0.post11.dev0/docs/source/releases/latest/pluginify-updates-jeremy-comments.yaml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
documentation:
|
|
2
|
+
- title: 'Minor updates to pluginify CLI and unit tests'
|
|
3
|
+
description: |
|
|
4
|
+
Updates to CLI help information and refactoring of plugin registry unit test code.
|
|
5
|
+
|
|
6
|
+
Moved PluginRegistryValidator class to a utility module so packages outside of
|
|
7
|
+
pluginify could make use of that code.
|
|
8
|
+
|
|
9
|
+
Updating CLI commands help information by providing examples and more context.
|
|
10
|
+
|
|
11
|
+
Removed <3.13 restriction for python.
|
|
12
|
+
files:
|
|
13
|
+
added:
|
|
14
|
+
- pluginify/utils/validators.py
|
|
15
|
+
modified:
|
|
16
|
+
- docs/source/concepts/command_line/index.rst
|
|
17
|
+
- docs/source/concepts/functionality/index.rst
|
|
18
|
+
- pluginify/commandline_typer.py
|
|
19
|
+
- tests/unit_tests/plugin_registries/test_plugin_registries.py
|
|
20
|
+
- README.md
|
|
21
|
+
- pyproject.toml
|
|
22
|
+
related-issue:
|
|
23
|
+
number: null
|
|
24
|
+
repo_url: ''
|
|
25
|
+
date:
|
|
26
|
+
start: null
|
|
27
|
+
finish: null
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
continuous integration:
|
|
2
|
+
- title: 'PyPi Publish bug fix'
|
|
3
|
+
description: |
|
|
4
|
+
Updated package-and-publish.yaml workflow to recognize dynamic versioning via git
|
|
5
|
+
tags. Hopefully this will work.
|
|
6
|
+
files:
|
|
7
|
+
modified:
|
|
8
|
+
- .github/workflows/package-and-publish.yaml
|
|
9
|
+
- pyproject.toml
|
|
10
|
+
related-issue:
|
|
11
|
+
number: null
|
|
12
|
+
repo_url: ''
|
|
13
|
+
date:
|
|
14
|
+
start: null
|
|
15
|
+
finish: null
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
continuous integration:
|
|
2
|
+
- title: 'Pluginify Version Update'
|
|
3
|
+
description: |
|
|
4
|
+
Updated package version from v0.0.0 to v1.0.0. Needed to push most up to date code
|
|
5
|
+
to PyPi.
|
|
6
|
+
files:
|
|
7
|
+
modified:
|
|
8
|
+
- pluginify/_version.py
|
|
9
|
+
- pyproject.toml
|
|
10
|
+
related-issue:
|
|
11
|
+
number: null
|
|
12
|
+
repo_url: ''
|
|
13
|
+
date:
|
|
14
|
+
start: null
|
|
15
|
+
finish: null
|
|
@@ -56,8 +56,8 @@ def configure_logging(level=logging.INFO):
|
|
|
56
56
|
root = logging.getLogger()
|
|
57
57
|
|
|
58
58
|
# Avoid duplicate handlers if CLI is called multiple times
|
|
59
|
-
if root.handlers:
|
|
60
|
-
|
|
59
|
+
# if root.handlers:
|
|
60
|
+
# return
|
|
61
61
|
|
|
62
62
|
root.setLevel(level)
|
|
63
63
|
|
|
@@ -131,6 +131,10 @@ class DocstringTyper(typer.Typer):
|
|
|
131
131
|
if doc.long_description:
|
|
132
132
|
new_doc += "\n\n" + doc.long_description
|
|
133
133
|
|
|
134
|
+
# Keep examples portion in command's help output if specified
|
|
135
|
+
if doc.examples:
|
|
136
|
+
new_doc += "\n\nExamples\n--------\n" + doc.examples[0].description
|
|
137
|
+
|
|
134
138
|
func.__doc__ = new_doc
|
|
135
139
|
|
|
136
140
|
new_params = []
|
|
@@ -165,10 +169,32 @@ class DocstringTyper(typer.Typer):
|
|
|
165
169
|
return wrapper
|
|
166
170
|
|
|
167
171
|
|
|
168
|
-
app = DocstringTyper(
|
|
172
|
+
app = DocstringTyper(
|
|
173
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
174
|
+
help="pluginify command line interface.",
|
|
175
|
+
)
|
|
169
176
|
config_app = DocstringTyper()
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
config_set_app = DocstringTyper()
|
|
178
|
+
|
|
179
|
+
config_variable_help = (
|
|
180
|
+
"NAMESPACE specifies the default namespace in which pluginify will manage "
|
|
181
|
+
"registries.\n"
|
|
182
|
+
"REGISTRY_DIRECTORY specifies the base directory where registries and their "
|
|
183
|
+
"supporting directory structure will be written.\n"
|
|
184
|
+
"REBUILD_REGISTRIES informs pluginify whether or not to rebuild registries at "
|
|
185
|
+
"runtime if a plugin cannot be located."
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
app.add_typer(
|
|
189
|
+
config_app,
|
|
190
|
+
name="config",
|
|
191
|
+
help=f"Configure pluginify.\n\n{config_variable_help}",
|
|
192
|
+
)
|
|
193
|
+
config_app.add_typer(
|
|
194
|
+
config_set_app,
|
|
195
|
+
name="set",
|
|
196
|
+
help=f"Set pluginify configuration variables.\n\n{config_variable_help}",
|
|
197
|
+
)
|
|
172
198
|
|
|
173
199
|
|
|
174
200
|
@app.command()
|
|
@@ -177,7 +203,37 @@ def create(
|
|
|
177
203
|
packages: Optional[List[str]] = None,
|
|
178
204
|
save_type: Literal["json", "yaml"] = "json",
|
|
179
205
|
):
|
|
180
|
-
"""Create plugin
|
|
206
|
+
"""Create plugin registries.
|
|
207
|
+
|
|
208
|
+
Plugin registries are low-level files which contain dictionaries of metadata for all
|
|
209
|
+
plugins implemented in one or more packages registered under a common namespace.
|
|
210
|
+
|
|
211
|
+
Pluginify creates and makes use of these registry files to properly load and
|
|
212
|
+
instantiate your plugins.
|
|
213
|
+
|
|
214
|
+
Examples
|
|
215
|
+
--------
|
|
216
|
+
Namespace: packageX.plugin_packages
|
|
217
|
+
Directory structure:
|
|
218
|
+
|
|
219
|
+
packageX/
|
|
220
|
+
plugins/
|
|
221
|
+
interfaces/
|
|
222
|
+
utils/
|
|
223
|
+
registered_plugins.json [will create]
|
|
224
|
+
packageY/
|
|
225
|
+
plugins/
|
|
226
|
+
registered_plugins.json [will create]
|
|
227
|
+
|
|
228
|
+
JSON registry files are faster to load and are used by default. YAML
|
|
229
|
+
extensions are also supported for easier viewing.
|
|
230
|
+
|
|
231
|
+
CLI examples
|
|
232
|
+
------------
|
|
233
|
+
pluginify create
|
|
234
|
+
pluginify create -n packageX.plugin_packages
|
|
235
|
+
pluginify create -s yaml
|
|
236
|
+
pluginify create -n packageX.plugin_packages -p packageY
|
|
181
237
|
|
|
182
238
|
Parameters
|
|
183
239
|
----------
|
|
@@ -199,7 +255,35 @@ def delete(
|
|
|
199
255
|
namespace: str = NAMESPACE,
|
|
200
256
|
packages: Optional[List[str]] = None,
|
|
201
257
|
):
|
|
202
|
-
"""Delete plugin
|
|
258
|
+
"""Delete plugin registries.
|
|
259
|
+
|
|
260
|
+
Plugin registries are low-level files which contain dictionaries of metadata for all
|
|
261
|
+
plugins implemented in one or more packages registered under a common namespace.
|
|
262
|
+
|
|
263
|
+
By default, this command will delete every .json and .yaml instance of registry
|
|
264
|
+
files for every package registered under a given namespace. If you only want to
|
|
265
|
+
delete registry files from a subset of packages, use the '-p' flag to specify those
|
|
266
|
+
packages.
|
|
267
|
+
|
|
268
|
+
Examples
|
|
269
|
+
--------
|
|
270
|
+
Namespace: packageX.plugin_packages
|
|
271
|
+
Directory structure:
|
|
272
|
+
|
|
273
|
+
packageX/
|
|
274
|
+
plugins/
|
|
275
|
+
interfaces/
|
|
276
|
+
utils/
|
|
277
|
+
registered_plugins.json [will delete]
|
|
278
|
+
packageY/
|
|
279
|
+
plugins/
|
|
280
|
+
registered_plugins.json [will delete]
|
|
281
|
+
|
|
282
|
+
CLI examples
|
|
283
|
+
------------
|
|
284
|
+
pluginify delete
|
|
285
|
+
pluginify delete -n packageX.plugin_packages
|
|
286
|
+
pluginify delete -n packageX.plugin_packages -p packageY
|
|
203
287
|
|
|
204
288
|
Parameters
|
|
205
289
|
----------
|
|
@@ -213,7 +297,7 @@ def delete(
|
|
|
213
297
|
plugin_registry.delete_registries(packages=packages)
|
|
214
298
|
|
|
215
299
|
|
|
216
|
-
@
|
|
300
|
+
@config_set_app.command("rebuild-registries")
|
|
217
301
|
def set_rebuild_registries(rebuild_registries: bool):
|
|
218
302
|
"""Set pluginify's REBUILD_REGISTRIES config variable.
|
|
219
303
|
|
|
@@ -226,7 +310,7 @@ def set_rebuild_registries(rebuild_registries: bool):
|
|
|
226
310
|
update_existing_fields({"REBUILD_REGISTRIES": rebuild_registries})
|
|
227
311
|
|
|
228
312
|
|
|
229
|
-
@
|
|
313
|
+
@config_set_app.command("namespace")
|
|
230
314
|
def set_namespace(namespace: str):
|
|
231
315
|
"""Set pluginify's NAMESPACE config variable.
|
|
232
316
|
|
|
@@ -239,7 +323,7 @@ def set_namespace(namespace: str):
|
|
|
239
323
|
update_existing_fields({"NAMESPACE": namespace})
|
|
240
324
|
|
|
241
325
|
|
|
242
|
-
@
|
|
326
|
+
@config_set_app.command("registry-directory")
|
|
243
327
|
def set_registry_directory(registry_directory: Path):
|
|
244
328
|
"""Set pluginify's REGISTRY_DIRECTORY config variable.
|
|
245
329
|
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/create_plugin_registries.py
RENAMED
|
@@ -27,6 +27,7 @@ from os.path import (
|
|
|
27
27
|
split,
|
|
28
28
|
splitext,
|
|
29
29
|
)
|
|
30
|
+
from pathlib import Path
|
|
30
31
|
import re
|
|
31
32
|
import warnings
|
|
32
33
|
|
|
@@ -481,6 +482,13 @@ def parse_plugin_paths(plugin_paths, package, package_dir, plugins, namespace):
|
|
|
481
482
|
for plugin_type in plugin_paths:
|
|
482
483
|
# Loop through each file of the current plugin type.
|
|
483
484
|
for filepath in plugin_paths[plugin_type]:
|
|
485
|
+
# If any 'part' of the full filepath starts with a '.' (dot) directory or
|
|
486
|
+
# file, do not use this filepath. Just continue to the next filepath
|
|
487
|
+
# provided. Resolving path to prevent false-positives on "." or ".."
|
|
488
|
+
# paths for relative paths as an edge case.
|
|
489
|
+
if any(part.startswith(".") for part in Path(filepath).resolve().parts):
|
|
490
|
+
continue
|
|
491
|
+
|
|
484
492
|
filepath = str(filepath)
|
|
485
493
|
# Path relative to the package directory
|
|
486
494
|
relpath = os_relpath(filepath, start=package_dir)
|
|
@@ -36,6 +36,34 @@ from pluginify.utils import merge_nested_dicts
|
|
|
36
36
|
LOG = logging.getLogger(__name__)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def env_constructor(loader, node):
|
|
40
|
+
"""YAML constructor for the ``!ENV`` tag that expands environment variables.
|
|
41
|
+
|
|
42
|
+
This function allows YAML files to include environment variables using
|
|
43
|
+
the ``!ENV`` tag. The scalar value associated with the tag is read and
|
|
44
|
+
any environment variables within the string (e.g. ``${VAR_NAME}``) are
|
|
45
|
+
expanded using :func:`os.path.expandvars`.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
loader : yaml.Loader
|
|
50
|
+
The YAML loader instance currently parsing the document.
|
|
51
|
+
node : yaml.Node
|
|
52
|
+
The YAML node containing the scalar value associated with the ``!ENV`` tag.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
str
|
|
57
|
+
The scalar value with any environment variables expanded using the
|
|
58
|
+
current process environment.
|
|
59
|
+
"""
|
|
60
|
+
value = loader.construct_scalar(node)
|
|
61
|
+
return os.path.expandvars(value)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
yaml.SafeLoader.add_constructor("!ENV", env_constructor)
|
|
65
|
+
|
|
66
|
+
|
|
39
67
|
class PluginRegistry:
|
|
40
68
|
"""Plugin Registry class definition.
|
|
41
69
|
|
|
@@ -372,7 +400,7 @@ class PluginRegistry:
|
|
|
372
400
|
|
|
373
401
|
return metadata
|
|
374
402
|
|
|
375
|
-
def load_plugin(self, data: dict) -> BaseModel:
|
|
403
|
+
def load_plugin(self, data: dict, _expand: bool = False) -> BaseModel:
|
|
376
404
|
"""
|
|
377
405
|
Dynamically load and validate pydantic models based on apiVersion and interface.
|
|
378
406
|
|
|
@@ -387,6 +415,10 @@ class PluginRegistry:
|
|
|
387
415
|
Dictionary representing a plugin definition. Must include the `interface`
|
|
388
416
|
field. May optionally include `apiVersion`. If not present, "pluginify/v1"
|
|
389
417
|
is assumed.
|
|
418
|
+
_expand : private bool (default=False)
|
|
419
|
+
If true, fully expand the workflow plugin in place. Otherwise, load as is
|
|
420
|
+
done usually. This should only be used for the
|
|
421
|
+
'geoips expand <workflow>' command.
|
|
390
422
|
|
|
391
423
|
Returns
|
|
392
424
|
-------
|
|
@@ -400,7 +432,9 @@ class PluginRegistry:
|
|
|
400
432
|
ImportError
|
|
401
433
|
If the specified module for the given model version cannot be imported.
|
|
402
434
|
"""
|
|
403
|
-
|
|
435
|
+
# Make this GeoIPS for now, change later once apiVersion is set on all yaml
|
|
436
|
+
# plugins.
|
|
437
|
+
api_version = data.get("apiVersion", "geoips/v1")
|
|
404
438
|
|
|
405
439
|
# Split "package_name/model_version"
|
|
406
440
|
# Use package_name to select the appropriate package to search for the api.
|
|
@@ -418,9 +452,15 @@ class PluginRegistry:
|
|
|
418
452
|
|
|
419
453
|
# Construct module path and import
|
|
420
454
|
try:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
455
|
+
if interface == "product_defaults":
|
|
456
|
+
# product_defaults model defined in products pydantic module
|
|
457
|
+
module = import_module(
|
|
458
|
+
f"{package_name}.pydantic_models.{model_version}.products"
|
|
459
|
+
)
|
|
460
|
+
else:
|
|
461
|
+
module = import_module(
|
|
462
|
+
f"{package_name}.pydantic_models.{model_version}.{interface}"
|
|
463
|
+
)
|
|
424
464
|
except ImportError as e:
|
|
425
465
|
raise ImportError(
|
|
426
466
|
f"Could not import models from '{api_version}': {e}"
|
|
@@ -431,15 +471,20 @@ class PluginRegistry:
|
|
|
431
471
|
|
|
432
472
|
try:
|
|
433
473
|
model_class = getattr(module, model_name)
|
|
434
|
-
print("model class \t", model_class)
|
|
435
474
|
except AttributeError as e:
|
|
436
475
|
raise ValueError(
|
|
437
476
|
f"Model '{model_name}' not found in '{api_version}'"
|
|
438
477
|
) from e
|
|
439
478
|
|
|
440
|
-
|
|
479
|
+
if _expand:
|
|
480
|
+
# Only applies to workflow plugins
|
|
481
|
+
return model_class.model_validate(data, context={"expand": True})
|
|
441
482
|
|
|
442
|
-
|
|
483
|
+
return model_class(**data)
|
|
484
|
+
|
|
485
|
+
def get_yaml_plugin(
|
|
486
|
+
self, interface_obj, name, rebuild_registries=None, _expand=False
|
|
487
|
+
):
|
|
443
488
|
"""Get a YAML plugin by its name.
|
|
444
489
|
|
|
445
490
|
Parameters
|
|
@@ -456,7 +501,16 @@ class PluginRegistry:
|
|
|
456
501
|
is true and get_plugin fails, rebuild the plugin registry, call then call
|
|
457
502
|
get_plugin once more with rebuild_registries toggled off, so it only gets
|
|
458
503
|
rebuilt once.
|
|
504
|
+
_expand: private bool (default=False)
|
|
505
|
+
- If true, fully expand the workflow plugin in place. Otherwise, load as is
|
|
506
|
+
done usually. This should only be used for the
|
|
507
|
+
'geoips expand <workflow>' command.
|
|
459
508
|
"""
|
|
509
|
+
if _expand and interface_obj.name != "workflows":
|
|
510
|
+
raise AssertionError(
|
|
511
|
+
"Error: you cannot set argument 'expand' to true unless you are "
|
|
512
|
+
"requesting a workflow plugin."
|
|
513
|
+
)
|
|
460
514
|
try:
|
|
461
515
|
registered_yaml_plugins = self.registered_plugins["yaml_based"]
|
|
462
516
|
except KeyError:
|
|
@@ -562,7 +616,21 @@ class PluginRegistry:
|
|
|
562
616
|
plugin["relpath"] = relpath
|
|
563
617
|
|
|
564
618
|
if getattr(interface_obj, "use_pydantic", False):
|
|
565
|
-
|
|
619
|
+
|
|
620
|
+
def remove_none(d: dict) -> dict:
|
|
621
|
+
"""Recursively remove all keys with value None from a dictionary."""
|
|
622
|
+
if not isinstance(d, dict):
|
|
623
|
+
return d
|
|
624
|
+
return {k: remove_none(v) for k, v in d.items() if v is not None}
|
|
625
|
+
|
|
626
|
+
validated = self.load_plugin(plugin, _expand).model_dump()
|
|
627
|
+
validated = remove_none(validated)
|
|
628
|
+
|
|
629
|
+
if "package" not in validated or "relpath" not in validated:
|
|
630
|
+
validated["package"] = package
|
|
631
|
+
validated["relpath"] = relpath
|
|
632
|
+
|
|
633
|
+
return interface_obj._plugin_yaml_to_obj(name, validated)
|
|
566
634
|
else:
|
|
567
635
|
validated = interface_obj.validator.validate(plugin)
|
|
568
636
|
return interface_obj._plugin_yaml_to_obj(name, validated)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Pluginify validators module.
|
|
2
|
+
|
|
3
|
+
Currently only implements a PluginRegistryValidator class.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from importlib import metadata, import_module
|
|
7
|
+
import json
|
|
8
|
+
from pprint import pformat
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from pluginify.errors import PluginRegistryError
|
|
13
|
+
from pluginify.plugin_registry import PluginRegistry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PluginRegistryValidator(PluginRegistry):
|
|
17
|
+
"""Subclass of PluginRegistry which adds functionality for unit testing."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, namespace, fpaths=None):
|
|
20
|
+
"""Initialize TestPluginRegistry Class."""
|
|
21
|
+
self.namespace = namespace
|
|
22
|
+
super().__init__(self.namespace, _test_registry_files=fpaths)
|
|
23
|
+
|
|
24
|
+
def validate_plugin_types_exist(self, reg_dict, reg_path):
|
|
25
|
+
"""Test that all top level plugin types exist in each registry file."""
|
|
26
|
+
expected_plugin_types = ["yaml_based", "class_based", "text_based"]
|
|
27
|
+
for p_type in expected_plugin_types:
|
|
28
|
+
if p_type not in reg_dict:
|
|
29
|
+
error_str = f"Expected plugin type '{p_type}' to be in the registry but"
|
|
30
|
+
error_str += f" wasn't. This was in file '{reg_path}'."
|
|
31
|
+
raise PluginRegistryError(error_str)
|
|
32
|
+
|
|
33
|
+
def validate_all_registries(self):
|
|
34
|
+
"""Validate all registries in the current installation.
|
|
35
|
+
|
|
36
|
+
This should be run during testing, but not at runtime.
|
|
37
|
+
Ensure we do not fail catastrophically for a single bad plugin
|
|
38
|
+
at runtime, so test up front to test validity.
|
|
39
|
+
"""
|
|
40
|
+
for reg_path in self.registry_files:
|
|
41
|
+
pkg_plugins = json.load(open(reg_path, "r"))
|
|
42
|
+
self.validate_registry(pkg_plugins, reg_path)
|
|
43
|
+
|
|
44
|
+
def validate_registry(self, current_registry, fpath):
|
|
45
|
+
"""Test all plugins found in registered plugins for their validity."""
|
|
46
|
+
try:
|
|
47
|
+
self.validate_plugin_types_exist(current_registry, fpath)
|
|
48
|
+
except PluginRegistryError as e:
|
|
49
|
+
# xfail if this is a test, otherwise just raise PluginRegistryError
|
|
50
|
+
if self._is_test:
|
|
51
|
+
pytest.xfail(str(e))
|
|
52
|
+
else:
|
|
53
|
+
raise PluginRegistryError(e)
|
|
54
|
+
try:
|
|
55
|
+
self.validate_registry_interfaces(current_registry)
|
|
56
|
+
except PluginRegistryError as e:
|
|
57
|
+
# xfail if this is a test, otherwise just raise PluginRegistryError
|
|
58
|
+
if self._is_test:
|
|
59
|
+
pytest.xfail(str(e))
|
|
60
|
+
else:
|
|
61
|
+
raise PluginRegistryError(e)
|
|
62
|
+
for plugin_type in current_registry:
|
|
63
|
+
for interface in current_registry[plugin_type]:
|
|
64
|
+
for plugin in current_registry[plugin_type][interface]:
|
|
65
|
+
try:
|
|
66
|
+
if interface == "products":
|
|
67
|
+
for subplg in current_registry[plugin_type][interface][
|
|
68
|
+
plugin
|
|
69
|
+
]:
|
|
70
|
+
self.validate_plugin_attrs(
|
|
71
|
+
plugin_type,
|
|
72
|
+
interface,
|
|
73
|
+
(plugin, subplg),
|
|
74
|
+
current_registry[plugin_type][interface][plugin][
|
|
75
|
+
subplg
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
elif plugin_type != "text_based":
|
|
79
|
+
self.validate_plugin_attrs(
|
|
80
|
+
plugin_type,
|
|
81
|
+
interface,
|
|
82
|
+
plugin,
|
|
83
|
+
current_registry[plugin_type][interface][plugin],
|
|
84
|
+
)
|
|
85
|
+
except PluginRegistryError as e:
|
|
86
|
+
# xfail if this is a test,
|
|
87
|
+
# otherwise just raise PluginRegistryError
|
|
88
|
+
if self._is_test:
|
|
89
|
+
pytest.xfail(str(e))
|
|
90
|
+
else:
|
|
91
|
+
raise PluginRegistryError(e)
|
|
92
|
+
|
|
93
|
+
def validate_plugin_attrs(self, plugin_type, interface, name, plugin):
|
|
94
|
+
"""Test non-product plugin for all required attributes."""
|
|
95
|
+
missing = []
|
|
96
|
+
if plugin_type == "yaml_based" and interface != "products":
|
|
97
|
+
attrs = [
|
|
98
|
+
"docstring",
|
|
99
|
+
"family",
|
|
100
|
+
"interface",
|
|
101
|
+
"package",
|
|
102
|
+
"plugin_type",
|
|
103
|
+
"relpath",
|
|
104
|
+
]
|
|
105
|
+
elif plugin_type == "yaml_based":
|
|
106
|
+
attrs = [
|
|
107
|
+
"docstring",
|
|
108
|
+
"family",
|
|
109
|
+
"interface",
|
|
110
|
+
"package",
|
|
111
|
+
"plugin_type",
|
|
112
|
+
"product_defaults",
|
|
113
|
+
"source_names",
|
|
114
|
+
"relpath",
|
|
115
|
+
]
|
|
116
|
+
else:
|
|
117
|
+
attrs = [
|
|
118
|
+
"docstring",
|
|
119
|
+
"family",
|
|
120
|
+
"interface",
|
|
121
|
+
"package",
|
|
122
|
+
"plugin_type",
|
|
123
|
+
"signature",
|
|
124
|
+
"relpath",
|
|
125
|
+
]
|
|
126
|
+
for attr in attrs:
|
|
127
|
+
try:
|
|
128
|
+
plugin[attr]
|
|
129
|
+
except KeyError:
|
|
130
|
+
missing.append(attr)
|
|
131
|
+
if missing:
|
|
132
|
+
raise PluginRegistryError(
|
|
133
|
+
f"Plugin '{name}' is missing the following required "
|
|
134
|
+
f"top-level properties: '{missing}'"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def validate_registry_interfaces(self, current_registry):
|
|
138
|
+
"""Test Plugin Registry interfaces validity."""
|
|
139
|
+
yaml_interfaces = []
|
|
140
|
+
class_interfaces = []
|
|
141
|
+
|
|
142
|
+
# NOTE: all <pkg>/interfaces/__init__.py files MUST
|
|
143
|
+
# contain a "class_based_interfaces" list and a
|
|
144
|
+
# "yaml_based_interfaces" list - this is how we
|
|
145
|
+
# identify the valid interface names.
|
|
146
|
+
# We must avoid actually importing the interfaces within
|
|
147
|
+
# the pluginify repo - or we will end up with a circular import
|
|
148
|
+
# due to BaseInterface.
|
|
149
|
+
for pkg in metadata.entry_points(group=self.namespace):
|
|
150
|
+
try:
|
|
151
|
+
mod = import_module(f"{pkg.value}.interfaces")
|
|
152
|
+
except ModuleNotFoundError as resp:
|
|
153
|
+
if f"No module named '{pkg.value}.interfaces'" in str(resp):
|
|
154
|
+
continue
|
|
155
|
+
else:
|
|
156
|
+
raise ModuleNotFoundError(resp)
|
|
157
|
+
class_interfaces += mod.class_based_interfaces
|
|
158
|
+
yaml_interfaces += mod.yaml_based_interfaces
|
|
159
|
+
|
|
160
|
+
bad_interfaces = []
|
|
161
|
+
for plugin_type in ["class_based", "yaml_based"]:
|
|
162
|
+
for interface in current_registry[plugin_type]:
|
|
163
|
+
if (
|
|
164
|
+
interface not in class_interfaces
|
|
165
|
+
and interface not in yaml_interfaces
|
|
166
|
+
):
|
|
167
|
+
error_str = f"\nPlugin type '{plugin_type}' does not allow "
|
|
168
|
+
error_str += f"interface '{interface}'.\n"
|
|
169
|
+
error_str += "\nValid interfaces: "
|
|
170
|
+
if plugin_type == "class_based":
|
|
171
|
+
interface_list = class_interfaces
|
|
172
|
+
else:
|
|
173
|
+
interface_list = yaml_interfaces
|
|
174
|
+
error_str += f"\n{interface_list}\n"
|
|
175
|
+
error_str += "\nPlease update the following plugins "
|
|
176
|
+
error_str += "to use a valid interface:\n"
|
|
177
|
+
error_str += pformat(current_registry[plugin_type][interface])
|
|
178
|
+
bad_interfaces.append(error_str)
|
|
179
|
+
if bad_interfaces:
|
|
180
|
+
error_str = "The following interfaces were not valid:\n"
|
|
181
|
+
for error in bad_interfaces:
|
|
182
|
+
error_str += error
|
|
183
|
+
raise PluginRegistryError(error_str)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pluginify" # package available at the root of the project
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.1.0.post11.dev0"
|
|
4
4
|
description = "Pluginify package"
|
|
5
5
|
authors = ["Evan Rose", "evrose54@colostate.edu"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -38,7 +38,7 @@ requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
|
38
38
|
build-backend = "poetry_dynamic_versioning.backend" # object used to perform the build process
|
|
39
39
|
|
|
40
40
|
[tool.poetry.dependencies] # Include any python dependencies here
|
|
41
|
-
python = ">=3.11.0
|
|
41
|
+
python = ">=3.11.0" # mandatory to declare the required python version
|
|
42
42
|
pydantic = ">=2.10.0" # Data validation using Python type hints
|
|
43
43
|
pyyaml = "*" # Base requirement
|
|
44
44
|
jsonschema = ">4.18.0" # Provides validation for YAML plugins
|
|
@@ -4,19 +4,16 @@
|
|
|
4
4
|
"""TestPluginRegistry Class used for Unit Testing the Plugin Registries."""
|
|
5
5
|
|
|
6
6
|
from glob import glob
|
|
7
|
-
from importlib import import_module, metadata
|
|
8
7
|
import logging
|
|
9
8
|
from os.path import basename, splitext
|
|
10
|
-
from pprint import pformat
|
|
11
9
|
|
|
12
10
|
import pytest
|
|
13
|
-
import json
|
|
14
11
|
import yaml
|
|
15
12
|
|
|
16
13
|
from pluginify.errors import PluginError, PluginRegistryError
|
|
17
14
|
from pluginify.interfaces import configs
|
|
18
15
|
from pluginify.interfaces import data_modifiers
|
|
19
|
-
from pluginify.
|
|
16
|
+
from pluginify.utils.validators import PluginRegistryValidator
|
|
20
17
|
|
|
21
18
|
LOG = logging.getLogger(__name__)
|
|
22
19
|
|
|
@@ -28,175 +25,6 @@ class FakeInterface:
|
|
|
28
25
|
name = "fake_interface"
|
|
29
26
|
|
|
30
27
|
|
|
31
|
-
class PluginRegistryValidator(PluginRegistry):
|
|
32
|
-
"""Subclass of PluginRegistry which adds functionality for unit testing."""
|
|
33
|
-
|
|
34
|
-
def __init__(self, fpaths=None):
|
|
35
|
-
"""Initialize TestPluginRegistry Class."""
|
|
36
|
-
super().__init__("pluginify.plugin_packages", _test_registry_files=fpaths)
|
|
37
|
-
|
|
38
|
-
def validate_plugin_types_exist(self, reg_dict, reg_path):
|
|
39
|
-
"""Test that all top level plugin types exist in each registry file."""
|
|
40
|
-
expected_plugin_types = ["yaml_based", "class_based", "text_based"]
|
|
41
|
-
for p_type in expected_plugin_types:
|
|
42
|
-
if p_type not in reg_dict:
|
|
43
|
-
error_str = f"Expected plugin type '{p_type}' to be in the registry but"
|
|
44
|
-
error_str += f" wasn't. This was in file '{reg_path}'."
|
|
45
|
-
raise PluginRegistryError(error_str)
|
|
46
|
-
|
|
47
|
-
def validate_all_registries(self):
|
|
48
|
-
"""Validate all registries in the current installation.
|
|
49
|
-
|
|
50
|
-
This should be run during testing, but not at runtime.
|
|
51
|
-
Ensure we do not fail catastrophically for a single bad plugin
|
|
52
|
-
at runtime, so test up front to test validity.
|
|
53
|
-
"""
|
|
54
|
-
for reg_path in self.registry_files:
|
|
55
|
-
pkg_plugins = json.load(open(reg_path, "r"))
|
|
56
|
-
self.validate_registry(pkg_plugins, reg_path)
|
|
57
|
-
|
|
58
|
-
def validate_registry(self, current_registry, fpath):
|
|
59
|
-
"""Test all plugins found in registered plugins for their validity."""
|
|
60
|
-
try:
|
|
61
|
-
self.validate_plugin_types_exist(current_registry, fpath)
|
|
62
|
-
except PluginRegistryError as e:
|
|
63
|
-
# xfail if this is a test, otherwise just raise PluginRegistryError
|
|
64
|
-
if self._is_test:
|
|
65
|
-
pytest.xfail(str(e))
|
|
66
|
-
else:
|
|
67
|
-
raise PluginRegistryError(e)
|
|
68
|
-
try:
|
|
69
|
-
self.validate_registry_interfaces(current_registry)
|
|
70
|
-
except PluginRegistryError as e:
|
|
71
|
-
# xfail if this is a test, otherwise just raise PluginRegistryError
|
|
72
|
-
if self._is_test:
|
|
73
|
-
pytest.xfail(str(e))
|
|
74
|
-
else:
|
|
75
|
-
raise PluginRegistryError(e)
|
|
76
|
-
for plugin_type in current_registry:
|
|
77
|
-
for interface in current_registry[plugin_type]:
|
|
78
|
-
for plugin in current_registry[plugin_type][interface]:
|
|
79
|
-
try:
|
|
80
|
-
if interface == "products":
|
|
81
|
-
for subplg in current_registry[plugin_type][interface][
|
|
82
|
-
plugin
|
|
83
|
-
]:
|
|
84
|
-
self.validate_plugin_attrs(
|
|
85
|
-
plugin_type,
|
|
86
|
-
interface,
|
|
87
|
-
(plugin, subplg),
|
|
88
|
-
current_registry[plugin_type][interface][plugin][
|
|
89
|
-
subplg
|
|
90
|
-
],
|
|
91
|
-
)
|
|
92
|
-
elif plugin_type != "text_based":
|
|
93
|
-
self.validate_plugin_attrs(
|
|
94
|
-
plugin_type,
|
|
95
|
-
interface,
|
|
96
|
-
plugin,
|
|
97
|
-
current_registry[plugin_type][interface][plugin],
|
|
98
|
-
)
|
|
99
|
-
except PluginRegistryError as e:
|
|
100
|
-
# xfail if this is a test,
|
|
101
|
-
# otherwise just raise PluginRegistryError
|
|
102
|
-
if self._is_test:
|
|
103
|
-
pytest.xfail(str(e))
|
|
104
|
-
else:
|
|
105
|
-
raise PluginRegistryError(e)
|
|
106
|
-
|
|
107
|
-
def validate_plugin_attrs(self, plugin_type, interface, name, plugin):
|
|
108
|
-
"""Test non-product plugin for all required attributes."""
|
|
109
|
-
missing = []
|
|
110
|
-
if plugin_type == "yaml_based" and interface != "products":
|
|
111
|
-
attrs = [
|
|
112
|
-
"docstring",
|
|
113
|
-
"family",
|
|
114
|
-
"interface",
|
|
115
|
-
"package",
|
|
116
|
-
"plugin_type",
|
|
117
|
-
"relpath",
|
|
118
|
-
]
|
|
119
|
-
elif plugin_type == "yaml_based":
|
|
120
|
-
attrs = [
|
|
121
|
-
"docstring",
|
|
122
|
-
"family",
|
|
123
|
-
"interface",
|
|
124
|
-
"package",
|
|
125
|
-
"plugin_type",
|
|
126
|
-
"product_defaults",
|
|
127
|
-
"source_names",
|
|
128
|
-
"relpath",
|
|
129
|
-
]
|
|
130
|
-
else:
|
|
131
|
-
attrs = [
|
|
132
|
-
"docstring",
|
|
133
|
-
"family",
|
|
134
|
-
"interface",
|
|
135
|
-
"package",
|
|
136
|
-
"plugin_type",
|
|
137
|
-
"signature",
|
|
138
|
-
"relpath",
|
|
139
|
-
]
|
|
140
|
-
for attr in attrs:
|
|
141
|
-
try:
|
|
142
|
-
plugin[attr]
|
|
143
|
-
except KeyError:
|
|
144
|
-
missing.append(attr)
|
|
145
|
-
if missing:
|
|
146
|
-
raise PluginRegistryError(
|
|
147
|
-
f"Plugin '{name}' is missing the following required "
|
|
148
|
-
f"top-level properties: '{missing}'"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
def validate_registry_interfaces(self, current_registry):
|
|
152
|
-
"""Test Plugin Registry interfaces validity."""
|
|
153
|
-
yaml_interfaces = []
|
|
154
|
-
class_interfaces = []
|
|
155
|
-
|
|
156
|
-
# NOTE: all <pkg>/interfaces/__init__.py files MUST
|
|
157
|
-
# contain a "class_based_interfaces" list and a
|
|
158
|
-
# "yaml_based_interfaces" list - this is how we
|
|
159
|
-
# identify the valid interface names.
|
|
160
|
-
# We must avoid actually importing the interfaces within
|
|
161
|
-
# the pluginify repo - or we will end up with a circular import
|
|
162
|
-
# due to BaseInterface.
|
|
163
|
-
for pkg in metadata.entry_points(group="pluginify.plugin_packages"):
|
|
164
|
-
try:
|
|
165
|
-
mod = import_module(f"{pkg.value}.interfaces")
|
|
166
|
-
except ModuleNotFoundError as resp:
|
|
167
|
-
if f"No module named '{pkg.value}.interfaces'" in str(resp):
|
|
168
|
-
continue
|
|
169
|
-
else:
|
|
170
|
-
raise ModuleNotFoundError(resp)
|
|
171
|
-
class_interfaces += mod.class_based_interfaces
|
|
172
|
-
yaml_interfaces += mod.yaml_based_interfaces
|
|
173
|
-
|
|
174
|
-
bad_interfaces = []
|
|
175
|
-
for plugin_type in ["class_based", "yaml_based"]:
|
|
176
|
-
for interface in current_registry[plugin_type]:
|
|
177
|
-
if (
|
|
178
|
-
interface not in class_interfaces
|
|
179
|
-
and interface not in yaml_interfaces
|
|
180
|
-
):
|
|
181
|
-
error_str = f"\nPlugin type '{plugin_type}' does not allow "
|
|
182
|
-
error_str += f"interface '{interface}'.\n"
|
|
183
|
-
error_str += "\nValid interfaces: "
|
|
184
|
-
if plugin_type == "class_based":
|
|
185
|
-
interface_list = class_interfaces
|
|
186
|
-
else:
|
|
187
|
-
interface_list = yaml_interfaces
|
|
188
|
-
error_str += f"\n{interface_list}\n"
|
|
189
|
-
error_str += "\nPlease update the following plugins "
|
|
190
|
-
error_str += "to use a valid interface:\n"
|
|
191
|
-
error_str += pformat(current_registry[plugin_type][interface])
|
|
192
|
-
bad_interfaces.append(error_str)
|
|
193
|
-
if bad_interfaces:
|
|
194
|
-
error_str = "The following interfaces were not valid:\n"
|
|
195
|
-
for error in bad_interfaces:
|
|
196
|
-
error_str += error
|
|
197
|
-
raise PluginRegistryError(error_str)
|
|
198
|
-
|
|
199
|
-
|
|
200
28
|
class TestPluginRegistry:
|
|
201
29
|
"""
|
|
202
30
|
Pytest-based Unit Test for the PluginRegistry Class.
|
|
@@ -212,12 +40,16 @@ class TestPluginRegistry:
|
|
|
212
40
|
)
|
|
213
41
|
|
|
214
42
|
# pr_validator uses test registry files for most tests
|
|
215
|
-
pr_validator = PluginRegistryValidator(
|
|
43
|
+
pr_validator = PluginRegistryValidator(
|
|
44
|
+
namespace="pluginify.plugin_packages", fpaths=default_fpaths
|
|
45
|
+
)
|
|
216
46
|
# real reg validator is responsible for testing deleting and building of registries
|
|
217
47
|
# based on whether or not we want that to occur:
|
|
218
48
|
# I.e. whether or not config.REBUILD_REGISTRIES is set to True or
|
|
219
49
|
# False
|
|
220
|
-
real_reg_validator = PluginRegistryValidator(
|
|
50
|
+
real_reg_validator = PluginRegistryValidator(
|
|
51
|
+
namespace="pluginify.plugin_packages", fpaths=None
|
|
52
|
+
)
|
|
221
53
|
|
|
222
54
|
# Couldn't implement this class via inheritance because PyTest Raised this error:
|
|
223
55
|
# PytestCollectionWarning: cannot collect test class 'TestPluginRegistry' because
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/generate-badges.yaml
RENAMED
|
File without changes
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/lint-and-test.yaml
RENAMED
|
File without changes
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/.github/workflows/status-check.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/pydantic_models/v1/configs.py
RENAMED
|
File without changes
|
|
File without changes
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/pluginify/utils/context_managers.py
RENAMED
|
File without changes
|
|
File without changes
|
{pluginify-0.0.0.post1.dev0 → pluginify-0.1.0.post11.dev0}/tests/unit_tests/config/test_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|