pluginify 0.0.0.post1.dev0__py3-none-any.whl → 0.1.0.post11.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/_version.py CHANGED
@@ -3,5 +3,5 @@
3
3
 
4
4
  # DO NOT EDIT
5
5
  # managed by poetry-dynamic-versioning
6
- __version__ = "0.0.0.post1.dev0"
7
- __version_tuple__ = (0, 0, 0, "post1", "dev0")
6
+ __version__ = "0.1.0.post11.dev0"
7
+ __version_tuple__ = (0, 1, 0, "post11", "dev0")
@@ -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
- return
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(context_settings={"help_option_names": ["-h", "--help"]})
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
- app.add_typer(config_app, name="config", help="Configuration commands for pluginify.")
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 registry files for one or more packages under a given namespace.
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 registry files for one or more packages under a given namespace.
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
- @config_app.command("set-rebuild-registries")
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
- @config_app.command("set-namespace")
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
- @config_app.command("set-registry-directory")
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
 
@@ -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
- api_version = data.get("apiVersion", "pluginify/v1")
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
- module = import_module(
422
- f"{package_name}.pydantic_models.{model_version}.{interface}"
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
- return model_class.model_validate(data)
479
+ if _expand:
480
+ # Only applies to workflow plugins
481
+ return model_class.model_validate(data, context={"expand": True})
441
482
 
442
- def get_yaml_plugin(self, interface_obj, name, rebuild_registries=None):
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
- return self.load_plugin(plugin).model_dump()
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,15 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pluginify
3
- Version: 0.0.0.post1.dev0
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,<3.13.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
- In the future:
54
+ OR
53
55
  ```bash
54
- pip install pluginify
56
+ pip install pluginify
55
57
  ```
56
58
 
57
59
  Use pluginify
@@ -1,22 +1,23 @@
1
1
  pluginify/__init__.py,sha256=NvdicJtfy-dZZ8tWjR74lDBHGV6rlaH8pAJ7UHyTTog,391
2
- pluginify/_version.py,sha256=I_xg_zrKOMl6Y8jLXjH6TifTHdty9HEaI8c-DJniO18,237
3
- pluginify/commandline_typer.py,sha256=v9r7oBnD39pFLBqhoUNefGEwyRFCzDKpiPa3cvXXzCk,8676
2
+ pluginify/_version.py,sha256=n4Mesoqrw8DK8LkjhySdwEOZeCLfBv_cuSDOZyMBxiU,239
3
+ pluginify/commandline_typer.py,sha256=g_pT1V8xwAF9KK-ndE-ydIYk2ivZYoO24PBW80CjsoM,11257
4
4
  pluginify/config.py,sha256=co4HkXsOrA68M0PlRaUAhz5KMp5pa60ssb7DwLvUuxk,4894
5
- pluginify/create_plugin_registries.py,sha256=9NGFRzyZIaF7Mj1BMbNLTbyRVtnZWjmwSnCL0qnra_0,48494
5
+ pluginify/create_plugin_registries.py,sha256=rZmZV_iIBc3FHfhhCW4axMpoBaT1dq_P3OPS7jVrQlQ,48934
6
6
  pluginify/errors.py,sha256=kkdipLfxvj8f-Pp53SD3RPnD9QS3samBLlQBjBskZP8,373
7
7
  pluginify/interfaces/__init__.py,sha256=XcPl52V8oMihP7NoRs_NgbiIvqoL3A4YeyfTOp3NqBA,329
8
8
  pluginify/interfaces/base.py,sha256=ubimAsOgAXCyhKhn40-a1HIfrjMPqHtKr4rBnwm645M,26724
9
9
  pluginify/interfaces/class_based/data_modifiers.py,sha256=p-d80oywjo9Rr6txowr1BIlg08qeOm3dAMm_4JQ0iH8,1072
10
10
  pluginify/interfaces/class_based_plugin.py,sha256=TMuJ2INaaFaUlxPX05L5r2gH3ISu1W_2qWllGA63oq4,8209
11
11
  pluginify/interfaces/yaml_based/configs.py,sha256=3U_nKCm2mIVfA_cRl-VUmu8JrQQQ3u24eshH46a9GI0,808
12
- pluginify/plugin_registry.py,sha256=yN8ooSHB7HfM31AQoeDUB7oJc87K7ZJHR_XllVr-bCM,40546
12
+ pluginify/plugin_registry.py,sha256=CLcR8xXPPaB3fd2KOODUZRiOSg3hhMyKiyKyAv9KpVE,43291
13
13
  pluginify/plugins/classes/data_modifiers/cuboid.py,sha256=WyvhPrRptpSHtPqeOdF1oiHm6zFwHahsLwNvsMdmQEE,1107
14
14
  pluginify/plugins/yaml/configs/stucco.yaml,sha256=OXLWzmmzxSAWs-MzSlmEa-OLGJzVJzrVQZwgGPL2jqE,376
15
15
  pluginify/pydantic_models/v1/configs.py,sha256=As15amNtdFwAobufh0d6mDlF3mMS23ThwTKQy9q25bc,2122
16
16
  pluginify/utils/__init__.py,sha256=gdy4E4yEI9lrHbWPeBn0Bspj7iz4z6r8BAjbmHf_hpM,2088
17
17
  pluginify/utils/context_managers.py,sha256=1-bREbijNNc5PrqMIihKqxlxNaK-YZ3hj5XGpHiYmfw,926
18
- pluginify-0.0.0.post1.dev0.dist-info/METADATA,sha256=YoQUcPZOkt58ozii6zhO33T9-N11vPIxl-uxXl1aWsU,2086
19
- pluginify-0.0.0.post1.dev0.dist-info/WHEEL,sha256=Vz2fHgx6HFtSwhs8KvkHLqH5Ea4w1_rner5uNVGCeIE,88
20
- pluginify-0.0.0.post1.dev0.dist-info/entry_points.txt,sha256=kCm7HOEEzSnPyDpy98W-LYYmlLYyTnRqOj40UqxX6-w,111
21
- pluginify-0.0.0.post1.dev0.dist-info/licenses/LICENSE,sha256=sIBqTiU78gw5bC9MZMySNxzCE7DIlOcDqUWdmflkDsY,7266
22
- pluginify-0.0.0.post1.dev0.dist-info/RECORD,,
18
+ pluginify/utils/validators.py,sha256=gCESprY4mZgE0KXigjsgHOipjZAg8OUF5n3-SGQ7PG4,7577
19
+ pluginify-0.1.0.post11.dev0.dist-info/METADATA,sha256=aY4LQhrTUPYsmTcEh9QyW6SkMiqlDM-dgX-HW7DmIZs,2165
20
+ pluginify-0.1.0.post11.dev0.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
21
+ pluginify-0.1.0.post11.dev0.dist-info/entry_points.txt,sha256=kCm7HOEEzSnPyDpy98W-LYYmlLYyTnRqOj40UqxX6-w,111
22
+ pluginify-0.1.0.post11.dev0.dist-info/licenses/LICENSE,sha256=sIBqTiU78gw5bC9MZMySNxzCE7DIlOcDqUWdmflkDsY,7266
23
+ pluginify-0.1.0.post11.dev0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.2
2
+ Generator: poetry-core 2.4.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any