pluginify 0.0.0.post1.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 +11 -0
- pluginify/_version.py +7 -0
- pluginify/commandline_typer.py +258 -0
- pluginify/config.py +144 -0
- pluginify/create_plugin_registries.py +1037 -0
- pluginify/errors.py +16 -0
- pluginify/interfaces/__init__.py +9 -0
- pluginify/interfaces/base.py +665 -0
- pluginify/interfaces/class_based/data_modifiers.py +38 -0
- pluginify/interfaces/class_based_plugin.py +210 -0
- pluginify/interfaces/yaml_based/configs.py +27 -0
- pluginify/plugin_registry.py +925 -0
- pluginify/plugins/classes/data_modifiers/cuboid.py +41 -0
- pluginify/plugins/yaml/configs/stucco.yaml +16 -0
- pluginify/pydantic_models/v1/configs.py +58 -0
- pluginify/utils/__init__.py +62 -0
- pluginify/utils/context_managers.py +32 -0
- pluginify-0.0.0.post1.dev0.dist-info/METADATA +66 -0
- pluginify-0.0.0.post1.dev0.dist-info/RECORD +22 -0
- pluginify-0.0.0.post1.dev0.dist-info/WHEEL +4 -0
- pluginify-0.0.0.post1.dev0.dist-info/entry_points.txt +6 -0
- pluginify-0.0.0.post1.dev0.dist-info/licenses/LICENSE +128 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# # # This source code is subject to the license referenced at
|
|
2
|
+
# # # https://github.com/NRLMMD-GEOIPS.
|
|
3
|
+
|
|
4
|
+
"""Data modifiers interface class.
|
|
5
|
+
|
|
6
|
+
This is a 'dummy' interface which is strictly used for testing pluginify. All plugins
|
|
7
|
+
of this interface are also 'dummy' plugins strictly used for testing this package.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pluginify.interfaces.class_based_plugin import BaseClassPlugin
|
|
11
|
+
from pluginify.interfaces.base import BaseClassInterface
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseDataModifierPlugin(BaseClassPlugin, abstract=True):
|
|
15
|
+
"""Dummy base class for data_modifier plugins."""
|
|
16
|
+
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DataModifiersInterface(BaseClassInterface):
|
|
21
|
+
"""Data Modifiers interface class.
|
|
22
|
+
|
|
23
|
+
This is a 'dummy' interface which is strictly used for testing pluginify. All
|
|
24
|
+
plugins of this interface are also 'dummy' plugins strictly used for testing this
|
|
25
|
+
package.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
name = "data_modifiers"
|
|
29
|
+
plugin_class = BaseDataModifierPlugin
|
|
30
|
+
|
|
31
|
+
required_args = {
|
|
32
|
+
"standard": ["data", "config"],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
required_kwargs = {"standard": []}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
data_modifiers = DataModifiersInterface()
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Implements a base class for class-based plugins.
|
|
2
|
+
|
|
3
|
+
The base class implemented here would expose the call signature of the child plugin
|
|
4
|
+
class as `__call__()` while also providing hooks for pre- and post-processing.
|
|
5
|
+
|
|
6
|
+
The hooks are available as `_pre_call()` and `_post_call()`. They should be used to
|
|
7
|
+
implement common functionality that all plugins of this type should posess but which we
|
|
8
|
+
don't want developers to need to implement. They are intended to be overridden by the
|
|
9
|
+
child plugin-type class (e.g. BaseReaderPlugin). They should define what kwargs they
|
|
10
|
+
accept when defined on the plugin-type class but should accept their arguments from
|
|
11
|
+
`**kwargs` from `__call__()`.
|
|
12
|
+
|
|
13
|
+
The `call()` method should be overridden on the actual plugin class. It should provide
|
|
14
|
+
the data processing for the plugin. `__call__()`'s signature will be identical to that
|
|
15
|
+
of `call()` except that `call()` should not accept `**kwargs`. That should be consumed
|
|
16
|
+
by the hooks.
|
|
17
|
+
|
|
18
|
+
`__call__()` should not be overridden anywhere.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
from abc import ABC, abstractmethod
|
|
23
|
+
import functools
|
|
24
|
+
import inspect
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def valid_str_attr(cls, attr_name: str):
|
|
28
|
+
"""Check that the given attribute is a non-empty string."""
|
|
29
|
+
attr_value = getattr(cls, attr_name, None)
|
|
30
|
+
if not isinstance(attr_value, str):
|
|
31
|
+
raise TypeError(f"{cls.__name__}.{attr_name} must be a string")
|
|
32
|
+
if not attr_value:
|
|
33
|
+
raise ValueError(f"{cls.__name__}.{attr_name} cannot be empty")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BaseClassPlugin(ABC):
|
|
37
|
+
"""The base class for class-based plugins.
|
|
38
|
+
|
|
39
|
+
All plugins are required to carry the following class attributes:
|
|
40
|
+
|
|
41
|
+
- interface: The interface type the plugin belongs to
|
|
42
|
+
(e.g. 'configs', 'data_modifiers').
|
|
43
|
+
This is typically provided by the interface-level plugin class and not the
|
|
44
|
+
individual plugin class.
|
|
45
|
+
- family: The family name of the plugin. This should be defined by the plugin class.
|
|
46
|
+
- name: The specific name of the plugin. This should be defined by the plugin class.
|
|
47
|
+
|
|
48
|
+
Subclasses of this base class must also implement the following methods:
|
|
49
|
+
|
|
50
|
+
- call(): The main method that performs the plugin's functionality. This method
|
|
51
|
+
should be implemented by the plugin class.
|
|
52
|
+
- _pre_call(): A hook method that can be overridden to preprocess data before
|
|
53
|
+
calling the main call() method. This method should accept the same arguments as
|
|
54
|
+
call() via `*args` and `**kwargs` and should, typically, be implemented by the
|
|
55
|
+
interface-level plugin class.
|
|
56
|
+
- _post_call(): A hook method that can be overridden to post-process data after
|
|
57
|
+
calling the main call() method. This method should accept the same arguments as
|
|
58
|
+
call() via `*args` and `**kwargs` and should, typically, be implemented by the
|
|
59
|
+
interface-level plugin class.
|
|
60
|
+
|
|
61
|
+
The purpose of `_pre_call()` and `_post_call()` is to allow for common
|
|
62
|
+
functionality that all plugins of a certain type should possess, without requiring
|
|
63
|
+
developers to implement this functionality in every plugin class. Initially, this
|
|
64
|
+
will be used to convert inputs from DataTree to other formats and back to DataTree
|
|
65
|
+
after processing, but it could be used for other common tasks as well.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
# If set to True, we are in OBP. False means we are in a legacy procflow.
|
|
69
|
+
# Set up logic in this or interface classes to convert from DT to data type
|
|
70
|
+
# referenced in family
|
|
71
|
+
|
|
72
|
+
# If no family is provided, just assume it works with DT
|
|
73
|
+
data_tree = False
|
|
74
|
+
required_attributes = ["interface", "family", "name"]
|
|
75
|
+
|
|
76
|
+
def _check_interface_attribute(cls):
|
|
77
|
+
"""Check the validity of the 'interface' attribute.
|
|
78
|
+
|
|
79
|
+
Checks to be sure that the interface attribute is a non-empty string and that it
|
|
80
|
+
is a known plugin interface.
|
|
81
|
+
"""
|
|
82
|
+
valid_str_attr(cls, "interface")
|
|
83
|
+
|
|
84
|
+
def _check_family_attribute(cls):
|
|
85
|
+
"""Check the validity of the 'family' attribute."""
|
|
86
|
+
valid_str_attr(cls, "family")
|
|
87
|
+
|
|
88
|
+
def _check_name_attribute(cls):
|
|
89
|
+
"""Check the validity of the 'name' attribute."""
|
|
90
|
+
valid_str_attr(cls, "name")
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def call(self, *args, **kwargs):
|
|
94
|
+
"""Callable method to be implemented by the plugin class."""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
# hooks are intentionally loose; document their accepted kwargs
|
|
98
|
+
def _pre_call(self, data=None, *args, **kwargs):
|
|
99
|
+
"""Preprocess the data before calling the main plugin method.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
data : The input data for the plugin.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
The processed data.
|
|
108
|
+
"""
|
|
109
|
+
return data
|
|
110
|
+
|
|
111
|
+
def _post_call(self, data=None, *args, **kwargs):
|
|
112
|
+
"""Post-process the data after calling the main plugin method.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
data : R
|
|
117
|
+
The output data from the plugin.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
The processed data.
|
|
122
|
+
"""
|
|
123
|
+
return data
|
|
124
|
+
|
|
125
|
+
def _invoke(self, data=None, *args, **kwargs):
|
|
126
|
+
if data is None:
|
|
127
|
+
data = self.call(*args, **kwargs)
|
|
128
|
+
else:
|
|
129
|
+
data = self._pre_call(data, *args, **kwargs)
|
|
130
|
+
data = self.call(data, *args, **kwargs)
|
|
131
|
+
data = self._post_call(data, *args, **kwargs)
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
def __init__(self, module=None):
|
|
135
|
+
"""
|
|
136
|
+
Initialize the plugin object inheriting from BaseClasePlugin.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
module: ModuleType, default=None
|
|
141
|
+
The module from which the class-based plugin originated. This is used to
|
|
142
|
+
collect metadata from the module and attach it to the plugin object. The
|
|
143
|
+
metadata can then be used during validation to indicate where failing
|
|
144
|
+
plugins originated. If None, the 'testing' attributes will be set to a
|
|
145
|
+
string value that can also be used in tests.
|
|
146
|
+
"""
|
|
147
|
+
if module:
|
|
148
|
+
self.module_name = module.__name__
|
|
149
|
+
self.module_path = module.__file__
|
|
150
|
+
else:
|
|
151
|
+
self.module_name = "Unknown."
|
|
152
|
+
self.module_path = "Unknown."
|
|
153
|
+
|
|
154
|
+
def __init_subclass__(cls, *, abstract=False, **kwargs) -> None:
|
|
155
|
+
"""
|
|
156
|
+
Initialize a subclass of the plugin.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
abstract : bool, optional
|
|
161
|
+
If True, the subclass is treated as abstract. When treated as abstract,
|
|
162
|
+
validation is disabled in __init_subclass__. Defaults to False.
|
|
163
|
+
|
|
164
|
+
Raises
|
|
165
|
+
------
|
|
166
|
+
TypeError
|
|
167
|
+
If the subclass does not define the required attributes.
|
|
168
|
+
TypeError
|
|
169
|
+
If the subclass does not implement the call() method.
|
|
170
|
+
TypeError
|
|
171
|
+
If the subclass overrides the __call__() method.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
None
|
|
176
|
+
"""
|
|
177
|
+
super().__init_subclass__(**kwargs)
|
|
178
|
+
|
|
179
|
+
# Treat the root as abstract and honor explicit marker
|
|
180
|
+
if cls is BaseClassPlugin or abstract:
|
|
181
|
+
cls.__plugin_abstract__ = True
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
# Enforce required attributes and run attribute checkers if they exist
|
|
185
|
+
for attr in cls.required_attributes:
|
|
186
|
+
# Ensure required attributes are defined
|
|
187
|
+
if not hasattr(cls, attr):
|
|
188
|
+
raise TypeError(f"{cls.__name__} must define '{attr}' attribute")
|
|
189
|
+
|
|
190
|
+
# Run attribute checker for the current attribute if it exists
|
|
191
|
+
attribute_checker = getattr(cls, f"_check_{attr}_attribute", None)
|
|
192
|
+
if attribute_checker is not None:
|
|
193
|
+
attribute_checker(cls)
|
|
194
|
+
|
|
195
|
+
# Prevent overriding __call__ in a True class-based plugin
|
|
196
|
+
if "__call__" in cls.__dict__:
|
|
197
|
+
raise TypeError(f"{cls.__name__} cannot override __call__")
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
call_method = cls.__dict__.get("call")
|
|
201
|
+
except AttributeError:
|
|
202
|
+
raise TypeError(f"{cls.__name__} must implement call()")
|
|
203
|
+
|
|
204
|
+
@functools.wraps(call_method)
|
|
205
|
+
def _call(self, data=None, *args, **kwargs):
|
|
206
|
+
return cls._invoke(self, data, *args, **kwargs)
|
|
207
|
+
|
|
208
|
+
_call.__signature__ = inspect.signature(call_method) # mirror only call()
|
|
209
|
+
_call.__annotations__ = getattr(call_method, "__annotations__", {})
|
|
210
|
+
cls.__call__ = _call
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# # # This source code is subject to the license referenced at
|
|
2
|
+
# # # https://github.com/NRLMMD-GEOIPS.
|
|
3
|
+
|
|
4
|
+
"""Configs interface class.
|
|
5
|
+
|
|
6
|
+
This is a 'dummy' interface which is strictly used for testing pluginify. All plugins of
|
|
7
|
+
this interface are also 'dummy' plugins strictly used for testing this package.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pluginify.interfaces.base import BaseYamlInterface
|
|
11
|
+
from pluginify.pydantic_models.v1.configs import ConfigPluginModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigsInterface(BaseYamlInterface):
|
|
15
|
+
"""Configs interface class.
|
|
16
|
+
|
|
17
|
+
This is a 'dummy' interface which is strictly used for testing pluginify. All
|
|
18
|
+
plugins of this interface are also 'dummy' plugins strictly used for testing this
|
|
19
|
+
package.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
name = "configs"
|
|
23
|
+
use_pydantic = True
|
|
24
|
+
validator = ConfigPluginModel
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
configs = ConfigsInterface()
|