pluginify 0.1.0.post11.dev0__tar.gz → 1.0.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.
Files changed (57) hide show
  1. pluginify-1.0.0.post11.dev0/.github/workflows/deploy-docs.yaml +32 -0
  2. pluginify-1.0.0.post11.dev0/.github/workflows/integ_test.yaml +25 -0
  3. pluginify-1.0.0.post11.dev0/.github/workflows/mypy.yaml +39 -0
  4. pluginify-1.0.0.post11.dev0/.github/workflows/package-and-publish.yaml +34 -0
  5. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/.github/workflows/status-check.yaml +3 -1
  6. pluginify-1.0.0.post11.dev0/.github/workflows/tag-and-release.yaml +27 -0
  7. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/PKG-INFO +14 -9
  8. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/README.md +12 -8
  9. pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0/1.0.0-no-bug-fixes-required-on-this-release.yaml +17 -0
  10. pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0/1.19.0-internal-release.yaml +12 -0
  11. pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0/1.19.0-open-source-upload.yaml +7 -0
  12. pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0/1.19.0-patch-internal-use-vars-runner.yaml +17 -0
  13. pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0/module-to-class-updates.yaml +12 -0
  14. pluginify-1.0.0.post11.dev0/docs/source/releases/latest/atomic-registry-writes.yaml +22 -0
  15. pluginify-1.0.0.post11.dev0/docs/source/releases/latest/ignore-files-in-utils-directories.yaml +17 -0
  16. pluginify-1.0.0.post11.dev0/docs/source/releases/latest/use-geoips-ci.yaml +32 -0
  17. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/_version.py +2 -2
  18. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/commandline_typer.py +4 -2
  19. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/config.py +3 -1
  20. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/create_plugin_registries.py +38 -7
  21. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/interfaces/__init__.py +5 -2
  22. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/interfaces/base.py +2 -2
  23. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/interfaces/class_based/data_modifiers.py +1 -1
  24. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/interfaces/class_based_plugin.py +10 -6
  25. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/plugin_registry.py +4 -5
  26. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/plugins/classes/data_modifiers/cuboid.py +3 -0
  27. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/pydantic_models/v1/configs.py +3 -0
  28. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/utils/validators.py +3 -0
  29. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pyproject.toml +19 -1
  30. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/conftest.py +13 -6
  31. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/config/test_config.py +3 -0
  32. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/plugin_registries/test_plugin_registries.py +29 -0
  33. pluginify-0.1.0.post11.dev0/.github/workflows/generate-badges.yaml +0 -240
  34. pluginify-0.1.0.post11.dev0/.github/workflows/lint-and-test.yaml +0 -119
  35. pluginify-0.1.0.post11.dev0/.github/workflows/package-and-publish.yaml +0 -106
  36. pluginify-0.1.0.post11.dev0/docs/source/releases/latest/initial-commit.yaml +0 -92
  37. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/LICENSE +0 -0
  38. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/ci-fix-perms.yaml +0 -0
  39. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/copy-geoips-functionality.yaml +0 -0
  40. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/ignore-dot-files-and-dirs.yaml +0 -0
  41. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/obp-plugin-registry-updates.yaml +0 -0
  42. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/pluginify-updates-jeremy-comments.yaml +0 -0
  43. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/pypi-publish-bug-fix.yaml +0 -0
  44. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/remove-all-geoips-references.yaml +0 -0
  45. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/replace-geoips-functionality.yaml +0 -0
  46. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/update-black-formatting.yaml +0 -0
  47. {pluginify-0.1.0.post11.dev0/docs/source/releases/latest → pluginify-1.0.0.post11.dev0/docs/source/releases/1.0.0}/version-update.yaml +0 -0
  48. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/__init__.py +0 -0
  49. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/errors.py +0 -0
  50. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/interfaces/yaml_based/configs.py +0 -0
  51. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/plugins/yaml/configs/stucco.yaml +0 -0
  52. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/utils/__init__.py +0 -0
  53. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/pluginify/utils/context_managers.py +0 -0
  54. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/invalid_interfaces.yaml +0 -0
  55. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/missing_lowest_keys.yaml +0 -0
  56. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/plugin_registries/files/bad/missing_plugin_types.yaml +0 -0
  57. {pluginify-0.1.0.post11.dev0 → pluginify-1.0.0.post11.dev0}/tests/unit_tests/plugin_registries/files/good/pluginify.yaml +0 -0
@@ -0,0 +1,32 @@
1
+ name: Deploy docs
2
+
3
+ # 1. Tag and Release - on merge of v*-version-release
4
+ # * Tag current version
5
+ # * Release just tagged version
6
+ # 2. Package and Publish - on published release (from #1)
7
+ # * Build wheel
8
+ # * Publish to pypi
9
+ # 3. Deploy docs - on published release (from #1)
10
+ # * pip install geoips
11
+ # * pip install plugin repo
12
+ # * build docs with geoips/docs/build_docs.sh
13
+ # * deploy docs with geoips/docs/deploy_pages.sh
14
+
15
+ on:
16
+ # Triggers the workflow on published release
17
+ release:
18
+ types:
19
+ - published
20
+ # Allows run of this workflow manually from the Actions tab
21
+ # Must be merged to default before it will be available to manually run.
22
+ workflow_dispatch:
23
+
24
+ jobs:
25
+ deploy-docs:
26
+ name: Deploy docs
27
+ # You do not appear to be able to use variables in the "uses" field.
28
+ uses: NRLMMD-GEOIPS/geoips_ci/.github/workflows/reusable-deploy-docs.yaml@main
29
+ permissions:
30
+ contents: write
31
+ secrets:
32
+ token: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,25 @@
1
+ name: Integration tests
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, ready_for_review]
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: integ-test-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ integ-test:
14
+ if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
15
+ uses: NRLMMD-GEOIPS/geoips_ci/.github/workflows/reusable-ci.yaml
16
+ # reusable-ci's job requests these; a called workflow cannot exceed the caller.
17
+ permissions:
18
+ contents: read
19
+ packages: write
20
+ with:
21
+ geoips_image: "ghcr.io/nrlmmd-geoips/geoips:latest"
22
+ run_core_tests: true
23
+ secrets:
24
+ token: ${{ secrets.GITHUB_TOKEN }}
25
+ docker_token: ${{ secrets.DOCKER_TOKEN }}
@@ -0,0 +1,39 @@
1
+ name: mypy
2
+
3
+ # Lint, unit tests and the geoips unit tests are in integ_test.yaml (geoips_ci's
4
+ # reusable-ci). It does not type check, so mypy stays here.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ branches: [main]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: ${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ env:
20
+ UV_SYSTEM_PYTHON: true
21
+
22
+ jobs:
23
+ mypy:
24
+ continue-on-error: true
25
+ runs-on: ${{ vars.RUNNER }}
26
+ steps:
27
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
28
+
29
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
30
+ with:
31
+ python-version: "3.13"
32
+
33
+ - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
34
+
35
+ - name: Install package
36
+ run: uv pip install -e ".[doc]" mypy
37
+
38
+ - name: Run mypy
39
+ run: mypy ./pluginify
@@ -0,0 +1,34 @@
1
+ name: Package/publish
2
+
3
+ # 1. Tag and Release - on merge of v*-version-release
4
+ # * Tag current version
5
+ # * Release just tagged version
6
+ # 2. Package and Publish - on published release (from #1)
7
+ # * Build wheel
8
+ # * Publish to pypi
9
+ # 3. Deploy docs - on published release (from #1)
10
+ # * pip install geoips
11
+ # * pip install plugin repo
12
+ # * build docs with geoips/docs/build_docs.sh
13
+ # * deploy docs with geoips/docs/deploy_pages.sh
14
+
15
+ on:
16
+ # triggers the workflow on published release
17
+ release:
18
+ types:
19
+ - published
20
+ # allows run of this workflow manually from the actions tab
21
+ # must be merged to default before it will be available to manually run.
22
+ workflow_dispatch:
23
+
24
+ jobs:
25
+ package-publish:
26
+ name: Package/publish
27
+ # You do not appear to be able to use variables in the "uses" field.
28
+ uses: NRLMMD-GEOIPS/geoips_ci/.github/workflows/reusable-package-and-publish.yaml@main
29
+ permissions:
30
+ contents: read
31
+ secrets:
32
+ token: ${{ secrets.GITHUB_TOKEN }}
33
+ pypi_user: ${{ secrets.PYPI_USER }}
34
+ pypi_password: ${{ secrets.PYPI_PASSWORD }}
@@ -2,6 +2,7 @@ name: Require up-to-date branch
2
2
 
3
3
  on:
4
4
  pull_request:
5
+ types: [opened, synchronize, reopened, ready_for_review]
5
6
  push:
6
7
  branches:
7
8
  - main
@@ -11,7 +12,8 @@ permissions:
11
12
 
12
13
  jobs:
13
14
  status-check:
14
- runs-on: ubuntu-latest
15
+ if: github.event_name == 'push' || github.event.pull_request.draft == false
16
+ runs-on: ${{ vars.RUNNER }}
15
17
  steps:
16
18
  - name: No-op status check
17
19
  run: echo "No-op Actions workflow to allow enforcing up to date branches before merge"
@@ -0,0 +1,27 @@
1
+ name: Tag/release
2
+
3
+ on:
4
+ # Triggers the workflow when pull request closed (either closed
5
+ # as non-planned, or actually approved and merged.)
6
+ pull_request:
7
+ types:
8
+ - closed
9
+ # You do not appear to be able to use variables in triggers
10
+ branches:
11
+ - main
12
+ # Allows run of this workflow manually from the Actions tab
13
+ # Must be merged to default branch before it will be available
14
+ # to manually run.
15
+ workflow_dispatch:
16
+
17
+ jobs:
18
+ tag-release:
19
+ name: Tag/release
20
+ # You do not appear to be able to use variables in the "uses" field.
21
+ uses: NRLMMD-GEOIPS/geoips_ci/.github/workflows/reusable-tag-and-release.yaml@main
22
+ # Only run this if the pull request was merged, not just closed.
23
+ if: github.event.pull_request.merged == true
24
+ permissions:
25
+ contents: write
26
+ secrets:
27
+ token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pluginify
3
- Version: 0.1.0.post11.dev0
3
+ Version: 1.0.0.post11.dev0
4
4
  Summary: Pluginify package
5
5
  License: LICENSE
6
6
  License-File: LICENSE
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
15
16
  Provides-Extra: doc
16
17
  Requires-Dist: docstring_parser
17
18
  Requires-Dist: jsonschema (>4.18.0)
@@ -24,11 +25,12 @@ Requires-Dist: typer
24
25
  Project-URL: Repository, https://github.com/NRLMMD-GEOIPS/pluginify
25
26
  Description-Content-Type: text/markdown
26
27
 
27
- # # # This source code is subject to the license referenced at
28
- # # # https://github.com/NRLMMD-GEOIPS.
28
+ ```
29
+ # # # This source code is subject to the license referenced at
30
+ # # # https://github.com/NRLMMD-GEOIPS.
31
+ ```
29
32
 
30
- Pluginify
31
- =========
33
+ # Pluginify
32
34
 
33
35
  This repository contains everything necessary to fully register YAML and python classes
34
36
  and/or modules as valid python plugin objects. A YAML-based plugin object essentially
@@ -43,21 +45,24 @@ PluginRegistry class. This package can and is used alongside other packages such
43
45
  [GeoIPS](https://github.com/NRLMMD-GEOIPS/geoips) to handle their plugin-based
44
46
  infrastructure.
45
47
 
46
- Install pluginify package
47
- -------------------------
48
+ ## Install pluginify package
49
+
48
50
  Current status:
51
+
49
52
  ```bash
50
53
  git clone https://github.com/NRLMMD-GEOIPS/pluginify.git
51
54
  # cd to pluginify's top level dir
52
55
  pip install -e .
53
56
  ```
57
+
54
58
  OR
59
+
55
60
  ```bash
56
61
  pip install pluginify
57
62
  ```
58
63
 
59
- Use pluginify
60
- -------------
64
+ ## Use pluginify
65
+
61
66
  ```bash
62
67
  pluginify -h
63
68
  # Top level commands without additional args
@@ -1,8 +1,9 @@
1
- # # # This source code is subject to the license referenced at
2
- # # # https://github.com/NRLMMD-GEOIPS.
1
+ ```
2
+ # # # This source code is subject to the license referenced at
3
+ # # # https://github.com/NRLMMD-GEOIPS.
4
+ ```
3
5
 
4
- Pluginify
5
- =========
6
+ # Pluginify
6
7
 
7
8
  This repository contains everything necessary to fully register YAML and python classes
8
9
  and/or modules as valid python plugin objects. A YAML-based plugin object essentially
@@ -17,21 +18,24 @@ PluginRegistry class. This package can and is used alongside other packages such
17
18
  [GeoIPS](https://github.com/NRLMMD-GEOIPS/geoips) to handle their plugin-based
18
19
  infrastructure.
19
20
 
20
- Install pluginify package
21
- -------------------------
21
+ ## Install pluginify package
22
+
22
23
  Current status:
24
+
23
25
  ```bash
24
26
  git clone https://github.com/NRLMMD-GEOIPS/pluginify.git
25
27
  # cd to pluginify's top level dir
26
28
  pip install -e .
27
29
  ```
30
+
28
31
  OR
32
+
29
33
  ```bash
30
34
  pip install pluginify
31
35
  ```
32
36
 
33
- Use pluginify
34
- -------------
37
+ ## Use pluginify
38
+
35
39
  ```bash
36
40
  pluginify -h
37
41
  # Top level commands without additional args
@@ -0,0 +1,17 @@
1
+ documentation:
2
+ - title: Add release note for version 1.19.0
3
+ description: |
4
+ Release note on release branch to force tag/release, package/publish,
5
+ and deploy docs.
6
+
7
+ Additionally add any updated files from geoips_ci/sync
8
+ files:
9
+ added:
10
+ - docs/source/releases/latest/1.19.0-release-note.yaml
11
+ modified:
12
+ - geoips_ci/sync updated files
13
+ date:
14
+ start: 2026-06-05
15
+ finish: 2026-06-05
16
+ related-issue:
17
+ internal: geoips#1067 - 1.19.0 updates
@@ -0,0 +1,12 @@
1
+ Release Process:
2
+ - title: Add 1.19.0 release note
3
+ description: |
4
+ Currently tagged internal version 1.18.2.
5
+ files:
6
+ added:
7
+ - docs/source/releases/1.19.0/1.19.0-internal-release.yaml
8
+ related-issue:
9
+ internal: pluginify#1067 - 1.19.0 updates*
10
+ date:
11
+ start: 2026-05-26
12
+ finish: 2026-05-26
@@ -0,0 +1,7 @@
1
+ Release Updates - Open Source Upload:
2
+ - title: Open source upload
3
+ description: "This repo updated with current geoips version release"
4
+ files:
5
+ modified:
6
+ - standard sync files
7
+ - release notes
@@ -0,0 +1,17 @@
1
+ continuous integration:
2
+ - title: 'Use vars.RUNNER for runners'
3
+ description: |
4
+ Ensure we are using standard vars.RUNNER for the new pluginify-specific workflows.
5
+ Eventually we will want to consolidate this CI with the standard actions in
6
+ geoips_ci, but for now at least use vars.RUNNER.
7
+ files:
8
+ added: []
9
+ modified:
10
+ - '.github/workflows/generate-badges.yaml'
11
+ - '.github/workflows/lint-and-test.yaml'
12
+ deleted: []
13
+ moved: []
14
+ date:
15
+ start: 2026-05-26
16
+ finish: 2026-05-26
17
+
@@ -0,0 +1,12 @@
1
+ bug fix:
2
+ - title: 'Fix bug where non-plugin modules are treated like plugins during registry initialization'
3
+ description: |
4
+ In GeoIPS 1342 we converted nearly all of the module-based plugins to true
5
+ class-based plugins. In doing so I discovered a bug where non-plugin modules living
6
+ in the geoips/plugins/classes/.../ directory caused errors when trying to build
7
+ the plugin registry. To fix this, I've added a check to see if a module has an
8
+ 'interface' attribute set to None. If that condition is met, then just ignore the
9
+ module and continue to the next potential plugin.
10
+ files:
11
+ modified:
12
+ - pluginify/create_plugin_registries.py
@@ -0,0 +1,22 @@
1
+ bug fix:
2
+ - title: 'Write plugin registries atomically'
3
+ description: |
4
+ Plugin registries were rewritten in place, so another process reading a
5
+ registry at the same time (for example parallel pytest-xdist workers, one of
6
+ them rebuilding the registries) could read an empty or partial file and fail
7
+ with ``JSONDecodeError``. Registries are now written to a temporary file in
8
+ the same directory and moved into place with ``os.replace``.
9
+ files:
10
+ deleted:
11
+ - ''
12
+ modified:
13
+ - 'pluginify/create_plugin_registries.py'
14
+ - 'tests/unit_tests/plugin_registries/test_plugin_registries.py'
15
+ added:
16
+ - ''
17
+ related-issue:
18
+ number: null
19
+ repo_url: 'https://github.com/NRLMMD-GEOIPS/pluginify'
20
+ date:
21
+ start: 2026-10-01
22
+ finish: 2026-10-01
@@ -0,0 +1,17 @@
1
+ bug fix:
2
+ - title: 'Force ignore files in directories named */utils/'
3
+ description: 'Force ignore files in directories named */utils/'
4
+ files:
5
+ deleted:
6
+ - ''
7
+ modified:
8
+ - 'pluginify/create_plugin_registries.py'
9
+ - 'tests/unit_tests/plugin_registries/test_plugin_registries.py'
10
+ added:
11
+ - ''
12
+ related-issue:
13
+ number: null
14
+ repo_url: ''
15
+ date:
16
+ start: 2026-07-12
17
+ finish: 2026-07-12
@@ -0,0 +1,32 @@
1
+ continuous integration:
2
+ - title: 'Use the standard GeoIPS CI from geoips_ci'
3
+ description: |
4
+ Run geoips_ci's reusable-ci on pull requests, like geoips and the plugin
5
+ repositories: geoips' lint (flake8, black, bandit), pluginify's unit tests, and
6
+ geoips' unit tests against the pluginify branch, in the geoips:latest image.
7
+ Remove the code-quality and nightly badge workflows and the unused badges; mypy
8
+ keeps its own workflow, since reusable-ci does not type check. Skip draft pull
9
+ requests in the status check, like geoips.
10
+ files:
11
+ deleted:
12
+ - '.github/badges/coverage-badge.svg'
13
+ - '.github/badges/cspell-badge.svg'
14
+ - '.github/badges/install-badge.svg'
15
+ - '.github/badges/mypy-badge.svg'
16
+ - '.github/badges/ruff-badge.svg'
17
+ - '.github/badges/tests-badge.svg'
18
+ - '.github/workflows/code-quality.yaml'
19
+ - '.github/workflows/generate-badges.yaml'
20
+ modified:
21
+ - '.github/workflows/status-check.yaml'
22
+ - 'docs/source/releases/latest/ignore-files-in-utils-directories.yaml'
23
+ added:
24
+ - '.github/workflows/integ_test.yaml'
25
+ - '.github/workflows/mypy.yaml'
26
+ - 'docs/source/releases/latest/use-geoips-ci.yaml'
27
+ related-issue:
28
+ number: null
29
+ repo_url: ''
30
+ date:
31
+ start: 2026-10-20
32
+ finish: 2026-10-20
@@ -3,5 +3,5 @@
3
3
 
4
4
  # DO NOT EDIT
5
5
  # managed by poetry-dynamic-versioning
6
- __version__ = "0.1.0.post11.dev0"
7
- __version_tuple__ = (0, 1, 0, "post11", "dev0")
6
+ __version__ = "1.0.0.post11.dev0"
7
+ __version_tuple__ = (1, 0, 0, "post11", "dev0")
@@ -1,3 +1,6 @@
1
+ # # # This source code is subject to the license referenced at
2
+ # # # https://github.com/NRLMMD-GEOIPS.
3
+
1
4
  """Commandline module for Pluginify.
2
5
 
3
6
  Supports two main commands, `pluginify create` and `pluginify delete` as well as
@@ -140,7 +143,6 @@ class DocstringTyper(typer.Typer):
140
143
  new_params = []
141
144
 
142
145
  for name, param in sig.parameters.items():
143
-
144
146
  default = param.default
145
147
  help_text = param_help.get(name)
146
148
 
@@ -155,7 +157,7 @@ class DocstringTyper(typer.Typer):
155
157
 
156
158
  # Optional arguments
157
159
  short = f"-{name[0]}"
158
- long = f"--{name.replace('_','-')}"
160
+ long = f"--{name.replace('_', '-')}"
159
161
 
160
162
  option = typer.Option(default, short, long, help=help_text)
161
163
 
@@ -1,3 +1,6 @@
1
+ # # # This source code is subject to the license referenced at
2
+ # # # https://github.com/NRLMMD-GEOIPS.
3
+
1
4
  """Module containing configuration variables needed for pluginify to run."""
2
5
 
3
6
 
@@ -58,7 +61,6 @@ def _env_to_variable(name, default):
58
61
  env_val = getenv(name)
59
62
 
60
63
  if env_val:
61
-
62
64
  if name == "PLUGINIFY_REGISTRY_DIRECTORY":
63
65
  return Path(env_val)
64
66
  elif name == "PLUGINIFY_REBUILD_REGISTRIES":
@@ -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 makedirs, remove
20
+ from os import getpid, makedirs, remove, replace
21
21
  from os.path import (
22
22
  basename,
23
23
  dirname,
@@ -317,14 +317,24 @@ def write_registered_plugins(pkg_dir, plugins, save_type):
317
317
  """
318
318
  if save_type == "yaml":
319
319
  reg_plug_abspath = os_join(pkg_dir, "registered_plugins.yaml")
320
- with open(reg_plug_abspath, "w") as plugin_registry:
321
- LOG.info("Writing %s", reg_plug_abspath)
322
- yaml.safe_dump(plugins, plugin_registry, default_flow_style=False)
323
320
  else:
324
321
  reg_plug_abspath = os_join(pkg_dir, "registered_plugins.json")
325
- with open(reg_plug_abspath, "w") as plugin_registry:
326
- LOG.info("Writing %s", reg_plug_abspath)
327
- json.dump(plugins, plugin_registry, indent=4)
322
+ LOG.info("Writing %s", reg_plug_abspath)
323
+ # Write to a temporary file and move it into place, so that other processes
324
+ # reading the registry (for example parallel test workers) never see a
325
+ # partially written file.
326
+ tmp_abspath = f"{reg_plug_abspath}.{getpid()}.tmp"
327
+ try:
328
+ with open(tmp_abspath, "w") as plugin_registry:
329
+ if save_type == "yaml":
330
+ yaml.safe_dump(plugins, plugin_registry, default_flow_style=False)
331
+ else:
332
+ json.dump(plugins, plugin_registry, indent=4)
333
+ replace(tmp_abspath, reg_plug_abspath)
334
+ except BaseException:
335
+ if exists(tmp_abspath):
336
+ remove(tmp_abspath)
337
+ raise
328
338
 
329
339
 
330
340
  def create_plugin_registries(plugin_packages, save_type, namespace):
@@ -934,6 +944,15 @@ def add_class_plugin(package, relpath, plugins):
934
944
  # __version.py, etc.
935
945
  return error_message
936
946
 
947
+ if "utils" in Path(relpath).parts:
948
+ # Ignore helper modules that live under plugin trees. These files are not
949
+ # plugins and should not be imported or validated for PLUGIN_CLASS.
950
+ LOG.info(
951
+ f"Skipping module at relpath {relpath} from {package}, "
952
+ "found under a utils directory"
953
+ )
954
+ return error_message
955
+
937
956
  module_name = splitext(basename(relpath))[0]
938
957
  # We need the full path to the module in order
939
958
  # for relative imports to work within modules.
@@ -957,6 +976,18 @@ def add_class_plugin(package, relpath, plugins):
957
976
  at relpath '{relpath}'\n"""
958
977
  return error_message
959
978
 
979
+ # If interface is None, then legitimately skip the module.
980
+ # We want to skip this first, before we test anything else.
981
+ # If it is not a plugin, we don't care if there are other
982
+ # errors in it at this stage (ie, avoid unnecessary unrelated
983
+ # catastrophic failures)
984
+ if hasattr(module, "interface") and module.interface is None:
985
+ LOG.info(
986
+ f"Skipping module '{module_name}' from '{package}', "
987
+ "interface_name is 'None'"
988
+ )
989
+ return error_message
990
+
960
991
  # We've encountered a truly 'class-based' plugin. I.e. we don't need to generate
961
992
  # the plugin object from the module itself. Collect metadata from this plugin
962
993
  # in a different fashion to what we've done previously.
@@ -1,7 +1,10 @@
1
+ # # # This source code is subject to the license referenced at
2
+ # # # https://github.com/NRLMMD-GEOIPS.
3
+
1
4
  """Initialization module for pluginify interfaces."""
2
5
 
3
- from pluginify.interfaces.class_based.data_modifiers import data_modifiers
4
- from pluginify.interfaces.yaml_based.configs import configs
6
+ from pluginify.interfaces.class_based.data_modifiers import data_modifiers # noqa: F401
7
+ from pluginify.interfaces.yaml_based.configs import configs # noqa: F401
5
8
 
6
9
  class_based_interfaces = ["data_modifiers"]
7
10
  yaml_based_interfaces = ["configs"]
@@ -35,7 +35,7 @@ class BaseInterface(abc.ABC):
35
35
  import pluginify.plugin_registry as plugin_registry_module
36
36
 
37
37
  name = "BaseInterface"
38
- interface_type = None # This is set by child classes
38
+ interface_type: str = "" # This is set by child classes
39
39
  rebuild_registries = REBUILD_REGISTRIES
40
40
  # Setting this attribute at the top level so it can be used by all methods.
41
41
  # This can be overridden by setting them in child interface classes
@@ -375,7 +375,7 @@ class BaseClassInterface(BaseInterface):
375
375
 
376
376
  interface_type = "class_based"
377
377
  name = "BaseClassInterface"
378
- required_args = {}
378
+ required_args: dict[str, list[str]] = {}
379
379
 
380
380
  def __repr__(self):
381
381
  """Plugin interface repr method."""
@@ -32,7 +32,7 @@ class DataModifiersInterface(BaseClassInterface):
32
32
  "standard": ["data", "config"],
33
33
  }
34
34
 
35
- required_kwargs = {"standard": []}
35
+ required_kwargs: dict[str, list[str]] = {"standard": []}
36
36
 
37
37
 
38
38
  data_modifiers = DataModifiersInterface()
@@ -1,3 +1,6 @@
1
+ # # # This source code is subject to the license referenced at
2
+ # # # https://github.com/NRLMMD-GEOIPS.
3
+
1
4
  """Implements a base class for class-based plugins.
2
5
 
3
6
  The base class implemented here would expose the call signature of the child plugin
@@ -72,6 +75,7 @@ class BaseClassPlugin(ABC):
72
75
  # If no family is provided, just assume it works with DT
73
76
  data_tree = False
74
77
  required_attributes = ["interface", "family", "name"]
78
+ __plugin_abstract__: bool = False
75
79
 
76
80
  def _check_interface_attribute(cls):
77
81
  """Check the validity of the 'interface' attribute.
@@ -196,15 +200,15 @@ class BaseClassPlugin(ABC):
196
200
  if "__call__" in cls.__dict__:
197
201
  raise TypeError(f"{cls.__name__} cannot override __call__")
198
202
 
199
- try:
200
- call_method = cls.__dict__.get("call")
201
- except AttributeError:
203
+ if "call" not in cls.__dict__:
202
204
  raise TypeError(f"{cls.__name__} must implement call()")
205
+ call_method = cls.__dict__["call"]
203
206
 
204
- @functools.wraps(call_method)
207
+ @functools.wraps(call_method) # type: ignore[arg-type]
205
208
  def _call(self, data=None, *args, **kwargs):
206
209
  return cls._invoke(self, data, *args, **kwargs)
207
210
 
208
- _call.__signature__ = inspect.signature(call_method) # mirror only call()
211
+ call_sig = inspect.signature(call_method)
212
+ _call.__signature__ = call_sig # type: ignore[attr-defined]
209
213
  _call.__annotations__ = getattr(call_method, "__annotations__", {})
210
- cls.__call__ = _call
214
+ cls.__call__ = _call # type: ignore[method-assign]