npe2 0.7.9rc0__tar.gz → 0.8.0rc0__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.
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/ci.yml +22 -25
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/test_all_plugins.yml +3 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/test_conversion.yml +6 -6
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/update_changelog.yml +3 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.gitignore +2 -0
- npe2-0.8.0rc0/.pre-commit-config.yaml +32 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/PKG-INFO +13 -7
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/README.md +0 -1
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_plugin/some_module.py +5 -4
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/render.py +4 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_contributions.md.jinja +5 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_readers_guide.md.jinja +41 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/pyproject.toml +11 -11
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_command_registry.py +6 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_dynamic_plugin.py +25 -27
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_compile.py +9 -8
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_fetch.py +18 -30
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_from_npe1.py +26 -32
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_setuputils.py +14 -14
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_visitors.py +26 -21
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_plugin_manager.py +45 -57
- npe2-0.8.0rc0/src/npe2/_pydantic_util.py +53 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_pytest_plugin.py +3 -4
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_setuptools_plugin.py +9 -9
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/cli.py +25 -21
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/implements.py +13 -10
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/implements.pyi +3 -2
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/io_utils.py +40 -44
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_bases.py +15 -14
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_npe1_adapter.py +3 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_package_metadata.py +40 -47
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_commands.py +16 -14
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_configuration.py +22 -20
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_contributions.py +13 -14
- npe2-0.8.0rc0/src/npe2/manifest/contributions/_icon.py +6 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_json_schema.py +86 -89
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_keybindings.py +5 -6
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_menus.py +11 -9
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_readers.py +10 -8
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_sample_data.py +16 -15
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_submenu.py +2 -4
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_themes.py +18 -22
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_widgets.py +6 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_writers.py +22 -18
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/schema.py +82 -70
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/utils.py +24 -28
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/plugin_manager.py +17 -14
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/types.py +16 -19
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/conftest.py +19 -10
- npe2-0.8.0rc0/tests/fixtures/my-compiled-plugin/README.md +1 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/_a.py +3 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/_with_decorators.py +5 -4
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin/__init__.py +5 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test__io_utils.py +5 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_cli.py +5 -3
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_compile.py +4 -2
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_config_contribution.py +1 -1
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_contributions.py +1 -1
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_conversion.py +4 -4
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_fetch.py +3 -9
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_implements.py +7 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_manifest.py +5 -5
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_npe1_adapter.py +7 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_setuptools_plugin.py +13 -10
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_utils.py +2 -2
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_validations.py +10 -12
- npe2-0.7.9rc0/.pre-commit-config.yaml +0 -33
- npe2-0.7.9rc0/docs/requirements.txt +0 -5
- npe2-0.7.9rc0/src/npe2/_pydantic_compat.py +0 -54
- npe2-0.7.9rc0/src/npe2/manifest/contributions/_icon.py +0 -8
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/ISSUE_TEMPLATE.md +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/dependabot.yml +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github_changelog_generator +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/CHANGELOG.md +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/LICENSE +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/Makefile +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_manifest.yaml +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_plugin/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_manifest.md.jinja +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_menus_guide.md.jinja +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_sample_data_guide.md.jinja +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_widgets_guide.md.jinja +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_writers_guide.md.jinja +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/codecov.yml +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/docs/_config.yml +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/docs/index.md +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/__main__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_validators.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/menus.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/package_metadata.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/py.typed +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/_b.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/setup.cfg +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/METADATA +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/entry_points.txt +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/top_level.txt +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1_module/__init__.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/setup.cfg +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin/napari.yaml +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/METADATA +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/entry_points.txt +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/top_level.txt +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_all_plugins.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_docs.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_package_meta.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_plugin_manager.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_pm_module.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_pytest_plugin.py +0 -0
- {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_tmp_plugin.py +0 -0
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
name: Check Manifest
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
21
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
22
22
|
- name: Check
|
|
23
23
|
run: pipx run check-manifest
|
|
24
24
|
|
|
@@ -28,22 +28,20 @@ jobs:
|
|
|
28
28
|
strategy:
|
|
29
29
|
fail-fast: false
|
|
30
30
|
matrix:
|
|
31
|
-
python-version: [3.
|
|
31
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
32
32
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
|
33
|
-
pydantic: ["pydantic<2", "pydantic>2"]
|
|
34
33
|
|
|
35
34
|
steps:
|
|
36
|
-
- uses: actions/checkout@
|
|
35
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
37
36
|
|
|
38
37
|
- name: Set up Python ${{ matrix.python-version }}
|
|
39
|
-
uses: actions/setup-python@
|
|
38
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
40
39
|
with:
|
|
41
40
|
python-version: ${{ matrix.python-version }}
|
|
42
41
|
|
|
43
42
|
- name: Install dependencies
|
|
44
43
|
run: |
|
|
45
44
|
python -m pip install --upgrade pip
|
|
46
|
-
pip install "${{ matrix.pydantic }}"
|
|
47
45
|
pip install -e .[json,docs,testing]
|
|
48
46
|
|
|
49
47
|
- name: Test Main docs build
|
|
@@ -53,9 +51,9 @@ jobs:
|
|
|
53
51
|
coverage run --source=npe2 -m pytest --color yes
|
|
54
52
|
|
|
55
53
|
- name: Upload coverage as artifact
|
|
56
|
-
uses: actions/upload-artifact@
|
|
54
|
+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
|
57
55
|
with:
|
|
58
|
-
name: coverage reports ${{ matrix.platform }} py ${{ matrix.python-version }}
|
|
56
|
+
name: coverage reports ${{ matrix.platform }} py ${{ matrix.python-version }}
|
|
59
57
|
path: |
|
|
60
58
|
./.coverage.*
|
|
61
59
|
include-hidden-files: true
|
|
@@ -64,14 +62,14 @@ jobs:
|
|
|
64
62
|
name: napari tests
|
|
65
63
|
runs-on: ubuntu-latest
|
|
66
64
|
steps:
|
|
67
|
-
- uses: actions/checkout@
|
|
68
|
-
- uses: actions/checkout@
|
|
65
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
66
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
69
67
|
with:
|
|
70
68
|
repository: napari/napari
|
|
71
69
|
path: napari-from-github
|
|
72
70
|
fetch-depth: 0
|
|
73
71
|
|
|
74
|
-
- uses: actions/setup-python@
|
|
72
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
75
73
|
with:
|
|
76
74
|
python-version: "3.10"
|
|
77
75
|
- name: Install
|
|
@@ -90,10 +88,10 @@ jobs:
|
|
|
90
88
|
name: docs render
|
|
91
89
|
runs-on: ubuntu-latest
|
|
92
90
|
steps:
|
|
93
|
-
- uses: actions/checkout@
|
|
94
|
-
- uses: actions/setup-python@
|
|
91
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
92
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
95
93
|
with:
|
|
96
|
-
python-version: "3.
|
|
94
|
+
python-version: "3.14"
|
|
97
95
|
- name: Build schema
|
|
98
96
|
run: |
|
|
99
97
|
python -m pip install --upgrade pip
|
|
@@ -105,15 +103,14 @@ jobs:
|
|
|
105
103
|
env:
|
|
106
104
|
NPE2_SCHEMA: "_schema.json"
|
|
107
105
|
- name: Build jupyter book
|
|
108
|
-
#
|
|
106
|
+
# generate toc, then build
|
|
109
107
|
run: |
|
|
110
|
-
pip install jupyter-book sphinx-tabs furo
|
|
111
108
|
cd docs
|
|
112
109
|
jupyter-book toc from-project . -f jb-book > _toc.yml
|
|
113
110
|
jupyter-book build .
|
|
114
111
|
# Upload the book's HTML as an artifact
|
|
115
112
|
- name: Upload artifact
|
|
116
|
-
uses: actions/upload-pages-artifact@
|
|
113
|
+
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
|
|
117
114
|
with:
|
|
118
115
|
path: "docs/_build/html"
|
|
119
116
|
|
|
@@ -123,11 +120,11 @@ jobs:
|
|
|
123
120
|
if: always()
|
|
124
121
|
runs-on: ubuntu-latest
|
|
125
122
|
steps:
|
|
126
|
-
- uses: actions/checkout@
|
|
123
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
127
124
|
|
|
128
|
-
- uses: actions/setup-python@
|
|
125
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
129
126
|
with:
|
|
130
|
-
python-version: "3.
|
|
127
|
+
python-version: "3.14"
|
|
131
128
|
cache-dependency-path: setup.cfg
|
|
132
129
|
cache: 'pip'
|
|
133
130
|
|
|
@@ -137,7 +134,7 @@ jobs:
|
|
|
137
134
|
pip install codecov
|
|
138
135
|
|
|
139
136
|
- name: Download coverage data
|
|
140
|
-
uses: actions/download-artifact@
|
|
137
|
+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
|
141
138
|
with:
|
|
142
139
|
pattern: coverage reports*
|
|
143
140
|
path: coverage
|
|
@@ -152,7 +149,7 @@ jobs:
|
|
|
152
149
|
python -Im coverage report --format=markdown --skip-empty --skip-covered >> $GITHUB_STEP_SUMMARY
|
|
153
150
|
|
|
154
151
|
- name: Upload coverage data
|
|
155
|
-
uses: codecov/codecov-action@v5
|
|
152
|
+
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
|
156
153
|
with:
|
|
157
154
|
fail_ci_if_error: true
|
|
158
155
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -166,10 +163,10 @@ jobs:
|
|
|
166
163
|
contents: write
|
|
167
164
|
id-token: write
|
|
168
165
|
steps:
|
|
169
|
-
- uses: actions/checkout@
|
|
166
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
170
167
|
|
|
171
168
|
- name: Set up Python
|
|
172
|
-
uses: actions/setup-python@
|
|
169
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
173
170
|
with:
|
|
174
171
|
python-version: "3.x"
|
|
175
172
|
|
|
@@ -194,7 +191,7 @@ jobs:
|
|
|
194
191
|
TWINE_USERNAME: __token__
|
|
195
192
|
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
|
|
196
193
|
|
|
197
|
-
- uses: softprops/action-gh-release@v2
|
|
194
|
+
- uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
198
195
|
if: startsWith(github.ref, 'refs/tags/')
|
|
199
196
|
with:
|
|
200
197
|
generate_release_notes: true
|
|
@@ -39,11 +39,11 @@ jobs:
|
|
|
39
39
|
shell: bash -l {0}
|
|
40
40
|
|
|
41
41
|
steps:
|
|
42
|
-
- uses: actions/checkout@
|
|
42
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
43
43
|
|
|
44
|
-
- uses: tlambert03/setup-qt-libs@v1
|
|
44
|
+
- uses: tlambert03/setup-qt-libs@19e4ef2d781d81f5f067182e228b54ec90d23b76 # v1.8
|
|
45
45
|
|
|
46
|
-
- uses: conda-incubator/setup-miniconda@v3
|
|
46
|
+
- uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3.3.0
|
|
47
47
|
with:
|
|
48
48
|
python-version: '3.10'
|
|
49
49
|
miniforge-variant: Miniforge3
|
|
@@ -29,8 +29,8 @@ jobs:
|
|
|
29
29
|
plugin: ${{ fromJson(needs.get-plugins.outputs.plugins) }}
|
|
30
30
|
|
|
31
31
|
steps:
|
|
32
|
-
- uses: tlambert03/setup-qt-libs@v1
|
|
33
|
-
- uses: actions/setup-python@
|
|
32
|
+
- uses: tlambert03/setup-qt-libs@19e4ef2d781d81f5f067182e228b54ec90d23b76 # v1.8
|
|
33
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
34
34
|
with:
|
|
35
35
|
python-version: 3.9
|
|
36
36
|
|
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
echo "plugin_repo=$URL" >> $GITHUB_ENV
|
|
51
51
|
|
|
52
52
|
- name: Checkout plugin repo
|
|
53
|
-
uses: actions/checkout@
|
|
53
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
54
54
|
with:
|
|
55
55
|
repository: ${{ env.plugin_repo }}
|
|
56
56
|
path: 'plugin_repo'
|
|
@@ -61,7 +61,7 @@ jobs:
|
|
|
61
61
|
|
|
62
62
|
- name: Test Conversion
|
|
63
63
|
id: test-without-napari
|
|
64
|
-
uses: aganders3/headless-gui@v2
|
|
64
|
+
uses: aganders3/headless-gui@f85dd6316993505dfc5f21839d520ae440c84816 # v2.2
|
|
65
65
|
continue-on-error: true
|
|
66
66
|
with:
|
|
67
67
|
run: npe2 convert ./plugin_repo
|
|
@@ -73,13 +73,13 @@ jobs:
|
|
|
73
73
|
- name: Test Conversion again with napari
|
|
74
74
|
id: test-with-napari
|
|
75
75
|
if: ${{ steps.test-without-napari.outcome == 'failure' }}
|
|
76
|
-
uses: aganders3/headless-gui@v2
|
|
76
|
+
uses: aganders3/headless-gui@f85dd6316993505dfc5f21839d520ae440c84816 # v2.2
|
|
77
77
|
with:
|
|
78
78
|
run: npe2 convert ./plugin_repo
|
|
79
79
|
|
|
80
80
|
- name: Test Conversion again with napari
|
|
81
81
|
if: ${{ steps.test-without-napari.outcome == 'failure' && steps.test-with-napari.outcome == 'failure' }}
|
|
82
|
-
uses: aganders3/headless-gui@v2
|
|
82
|
+
uses: aganders3/headless-gui@f85dd6316993505dfc5f21839d520ae440c84816 # v2.2
|
|
83
83
|
with:
|
|
84
84
|
# try without modifying directory
|
|
85
85
|
run: npe2 convert -n ${{ matrix.plugin }}
|
|
@@ -11,15 +11,15 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-20.04
|
|
12
12
|
steps:
|
|
13
13
|
- name: Checkout
|
|
14
|
-
uses: actions/checkout@
|
|
14
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
15
15
|
- name: "✏️ Generate release changelog"
|
|
16
|
-
uses: heinrichreimer/github-changelog-generator-action@v2.4
|
|
16
|
+
uses: heinrichreimer/github-changelog-generator-action@e60b5a2bd9fcd88dadf6345ff8327863fb8b490f # v2.4
|
|
17
17
|
with:
|
|
18
18
|
futureRelease: ${{ github.event.inputs.next_tag }}
|
|
19
19
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
20
20
|
repo: napari/npe2
|
|
21
21
|
- name: Create Pull Request
|
|
22
|
-
uses: peter-evans/create-pull-request@
|
|
22
|
+
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
|
23
23
|
with:
|
|
24
24
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
25
25
|
commit-message: Automatic changelog update
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
ci:
|
|
2
|
+
autoupdate_schedule: monthly
|
|
3
|
+
autofix_commit_msg: "style: [pre-commit.ci] auto fixes [...]"
|
|
4
|
+
autoupdate_commit_msg: "ci: [pre-commit.ci] autoupdate"
|
|
5
|
+
|
|
6
|
+
exclude: _docs/example_plugin/some_module.py
|
|
7
|
+
|
|
8
|
+
repos:
|
|
9
|
+
|
|
10
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
11
|
+
rev: v0.14.14
|
|
12
|
+
hooks:
|
|
13
|
+
- id: ruff-check
|
|
14
|
+
- id: ruff-format
|
|
15
|
+
|
|
16
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
17
|
+
rev: v6.0.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: check-docstring-first
|
|
20
|
+
- id: end-of-file-fixer
|
|
21
|
+
- id: trailing-whitespace
|
|
22
|
+
- id: check-yaml
|
|
23
|
+
- id: check-toml
|
|
24
|
+
|
|
25
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
26
|
+
rev: v1.19.1
|
|
27
|
+
hooks:
|
|
28
|
+
- id: mypy
|
|
29
|
+
additional_dependencies:
|
|
30
|
+
- types-toml
|
|
31
|
+
- types-PyYAML
|
|
32
|
+
exclude: npe2/implements.pyi|_docs/render.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: npe2
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0rc0
|
|
4
4
|
Summary: napari plugin engine v2
|
|
5
5
|
Project-URL: homepage, https://github.com/napari/npe2
|
|
6
6
|
Project-URL: repository, https://github.com/napari/npe2
|
|
@@ -13,38 +13,45 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.10
|
|
22
23
|
Requires-Dist: build>=1
|
|
23
24
|
Requires-Dist: platformdirs
|
|
24
25
|
Requires-Dist: psygnal>=0.3.0
|
|
25
|
-
Requires-Dist: pydantic
|
|
26
|
+
Requires-Dist: pydantic-extra-types
|
|
27
|
+
Requires-Dist: pydantic>1
|
|
26
28
|
Requires-Dist: pyyaml
|
|
27
29
|
Requires-Dist: rich
|
|
28
30
|
Requires-Dist: tomli-w
|
|
29
31
|
Requires-Dist: tomli; python_version < '3.11'
|
|
30
32
|
Requires-Dist: typer
|
|
31
33
|
Provides-Extra: dev
|
|
32
|
-
Requires-Dist: black; extra == 'dev'
|
|
33
34
|
Requires-Dist: ipython; extra == 'dev'
|
|
34
35
|
Requires-Dist: isort; extra == 'dev'
|
|
35
36
|
Requires-Dist: mypy; extra == 'dev'
|
|
36
37
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
38
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
37
39
|
Provides-Extra: docs
|
|
40
|
+
Requires-Dist: furo; extra == 'docs'
|
|
38
41
|
Requires-Dist: jinja2; extra == 'docs'
|
|
42
|
+
Requires-Dist: jupyter-book<2; extra == 'docs'
|
|
39
43
|
Requires-Dist: magicgui>=0.3.3; extra == 'docs'
|
|
44
|
+
Requires-Dist: sphinx-tabs; extra == 'docs'
|
|
40
45
|
Provides-Extra: json
|
|
41
46
|
Requires-Dist: jsonschema; extra == 'json'
|
|
42
47
|
Provides-Extra: testing
|
|
48
|
+
Requires-Dist: build; extra == 'testing'
|
|
43
49
|
Requires-Dist: jsonschema; extra == 'testing'
|
|
44
50
|
Requires-Dist: magicgui; extra == 'testing'
|
|
45
51
|
Requires-Dist: napari-plugin-engine; extra == 'testing'
|
|
46
52
|
Requires-Dist: napari-svg==0.1.5; extra == 'testing'
|
|
47
53
|
Requires-Dist: numpy; extra == 'testing'
|
|
54
|
+
Requires-Dist: pip; extra == 'testing'
|
|
48
55
|
Requires-Dist: pytest; extra == 'testing'
|
|
49
56
|
Requires-Dist: pytest-cov; extra == 'testing'
|
|
50
57
|
Requires-Dist: pytest-pretty; extra == 'testing'
|
|
@@ -72,7 +79,6 @@ offers comprehensive information for **plugin users** and for **plugin developer
|
|
|
72
79
|
### Plugin users
|
|
73
80
|
|
|
74
81
|
For plugin users, the docs include information about:
|
|
75
|
-
- [Starting to use plugins](https://napari.org/stable/plugins/start_using_plugins/index.html#plugins-getting-started)
|
|
76
82
|
- [Finding and installing plugins](https://napari.org/stable/plugins/start_using_plugins/finding_and_installing_plugins.html#find-and-install-plugins)
|
|
77
83
|
|
|
78
84
|
### Plugin developers
|
|
@@ -20,7 +20,6 @@ offers comprehensive information for **plugin users** and for **plugin developer
|
|
|
20
20
|
### Plugin users
|
|
21
21
|
|
|
22
22
|
For plugin users, the docs include information about:
|
|
23
|
-
- [Starting to use plugins](https://napari.org/stable/plugins/start_using_plugins/index.html#plugins-getting-started)
|
|
24
23
|
- [Finding and installing plugins](https://napari.org/stable/plugins/start_using_plugins/finding_and_installing_plugins.html#find-and-install-plugins)
|
|
25
24
|
|
|
26
25
|
### Plugin developers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# python_name: example_plugin._data:fractal
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, Any,
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
4
4
|
|
|
5
5
|
from magicgui import magic_factory
|
|
6
6
|
from qtpy.QtWidgets import QWidget
|
|
@@ -8,10 +8,11 @@ from qtpy.QtWidgets import QWidget
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
import napari.types
|
|
10
10
|
import napari.viewer
|
|
11
|
+
|
|
11
12
|
from npe2.types import LayerData, PathOrPaths, ReaderFunction
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def write_points(path: str, layer_data: Any, attributes:
|
|
15
|
+
def write_points(path: str, layer_data: Any, attributes: dict[str, Any]) -> list[str]:
|
|
15
16
|
with open(path, "w"):
|
|
16
17
|
... # save layer_data and attributes to file
|
|
17
18
|
|
|
@@ -27,7 +28,7 @@ def get_reader(path: "PathOrPaths") -> Optional["ReaderFunction"]:
|
|
|
27
28
|
return None
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
def xyz_file_reader(path: "PathOrPaths") ->
|
|
31
|
+
def xyz_file_reader(path: "PathOrPaths") -> list["LayerData"]:
|
|
31
32
|
data = ... # somehow read data from path
|
|
32
33
|
layer_attributes = {"name": "etc..."}
|
|
33
34
|
return [(data, layer_attributes)]
|
|
@@ -63,7 +64,7 @@ def threshold(
|
|
|
63
64
|
return (image > threshold).astype(int)
|
|
64
65
|
|
|
65
66
|
|
|
66
|
-
def create_fractal() ->
|
|
67
|
+
def create_fractal() -> list["LayerData"]:
|
|
67
68
|
"""An example of a Sample Data Function.
|
|
68
69
|
|
|
69
70
|
Note: Sample Data with URIs don't need python code.
|
|
@@ -10,7 +10,6 @@ from functools import lru_cache, partial
|
|
|
10
10
|
from inspect import getsource
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from types import FunctionType
|
|
13
|
-
from typing import Dict, Optional, Set
|
|
14
13
|
from urllib.request import urlopen
|
|
15
14
|
|
|
16
15
|
import yaml
|
|
@@ -46,7 +45,7 @@ def _mocked_qtwidgets():
|
|
|
46
45
|
|
|
47
46
|
|
|
48
47
|
@lru_cache
|
|
49
|
-
def type_strings() ->
|
|
48
|
+
def type_strings() -> dict[str, str]:
|
|
50
49
|
"""Return map of type name to source code for all types in types.py"""
|
|
51
50
|
from npe2 import types as _t
|
|
52
51
|
|
|
@@ -76,7 +75,7 @@ def type_strings() -> Dict[str, str]:
|
|
|
76
75
|
return type_strings
|
|
77
76
|
|
|
78
77
|
|
|
79
|
-
def _get_needed_types(source: str, so_far:
|
|
78
|
+
def _get_needed_types(source: str, so_far: set[str] | None = None) -> set[str]:
|
|
80
79
|
"""Return the names of types in the npe2.types.py that are used in `source`"""
|
|
81
80
|
so_far = so_far or set()
|
|
82
81
|
for name, string in type_strings().items():
|
|
@@ -150,7 +149,7 @@ def example_contribution(
|
|
|
150
149
|
if not ex.commands:
|
|
151
150
|
ex.commands = []
|
|
152
151
|
ex.commands.append(associated_command)
|
|
153
|
-
output = {"contributions": json.loads(ex.
|
|
152
|
+
output = {"contributions": json.loads(ex.model_dump_json(exclude_unset=True))}
|
|
154
153
|
if format == "yaml":
|
|
155
154
|
return yaml.safe_dump(output, sort_keys=False)
|
|
156
155
|
if format == "toml":
|
|
@@ -192,7 +191,7 @@ def main(dest: Path = _BUILD):
|
|
|
192
191
|
with urlopen(SCHEMA_URL) as response:
|
|
193
192
|
schema = json.load(response)
|
|
194
193
|
|
|
195
|
-
contributions = schema["
|
|
194
|
+
contributions = schema["$defs"]["ContributionPoints"]["properties"]
|
|
196
195
|
context = {
|
|
197
196
|
"schema": schema,
|
|
198
197
|
"contributions": contributions,
|
|
@@ -24,19 +24,19 @@ is being discussed.
|
|
|
24
24
|
{%- if contrib.type == 'object' and contrib.additionalProperties is defined %}
|
|
25
25
|
{# Handle object types like menus #}
|
|
26
26
|
{%- if contrib['additionalProperties']['items']['anyOf'] is defined %}
|
|
27
|
-
{%- set type_names = contrib['additionalProperties']['items']['anyOf']|map(attribute='$ref')|map("replace", "
|
|
27
|
+
{%- set type_names = contrib['additionalProperties']['items']['anyOf']|map(attribute='$ref')|map("replace", "#/$defs/", "")|list %}
|
|
28
28
|
{%- set union = True %}
|
|
29
29
|
{%- else %}
|
|
30
30
|
{%- set type_names = [contrib['additionalProperties']['items']['$ref']|replace("#/definitions/", "")] %}
|
|
31
31
|
{%- set union = False %}
|
|
32
32
|
{%- endif -%}
|
|
33
|
-
{%- elif contrib['
|
|
33
|
+
{%- elif contrib['anyOf'] is defined %}
|
|
34
34
|
{# Handle array types with union #}
|
|
35
|
-
{%- set type_names = contrib['
|
|
35
|
+
{%- set type_names = contrib['anyOf']|map(attribute='items')|selectattr('$ref')|map(attribute='$ref')|map("replace", "#/$defs/", "")|list %}
|
|
36
36
|
{%- set union = True %}
|
|
37
37
|
{%- else %}
|
|
38
38
|
{# Handle array types with single type #}
|
|
39
|
-
{%- set type_names = [contrib['items']['$ref']|replace("
|
|
39
|
+
{%- set type_names = [contrib['items']['$ref']|replace("#/$defs/", "")] %}
|
|
40
40
|
{%- set union = False %}
|
|
41
41
|
{%- endif -%}
|
|
42
42
|
{%- if union %}
|
|
@@ -51,7 +51,7 @@ This contribution accepts {{ type_names|length }} schema types
|
|
|
51
51
|
{%- endif %}
|
|
52
52
|
|
|
53
53
|
{%- for tname in type_names -%}
|
|
54
|
-
{% set type = schema['
|
|
54
|
+
{% set type = schema['$defs'][tname] %}
|
|
55
55
|
{% if union %}##### {{loop.index}}. {{type.title}}{% endif %}
|
|
56
56
|
{%- if contrib.type != 'object' or contrib.additionalProperties is not defined %}
|
|
57
57
|
{{ type.description }}
|
|
@@ -6,14 +6,52 @@ They are invoked whenever `viewer.open('some/path')` is used on the
|
|
|
6
6
|
command line, or when a user opens a file in the graphical user interface by
|
|
7
7
|
dropping a file into the canvas, or using `File -> Open...`
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Introduction to the `reader` contribution
|
|
10
|
+
|
|
11
|
+
`napari`'s reading process is motivated by the idea that a plugin should
|
|
12
|
+
**only** attempt to read a file once we are fairly confident that this process
|
|
13
|
+
will not fail. The determination of whether a plugin **can** read a file is
|
|
14
|
+
based on two checks:
|
|
15
|
+
|
|
16
|
+
- The `filename_patterns` field of the manifest contribution. If the given
|
|
17
|
+
path does not match any of the specified `filename_patterns`, the plugin
|
|
18
|
+
will never be given the file
|
|
19
|
+
- The `command` provided by the reader contribution. The plugin developer
|
|
20
|
+
should use this function, sometimes called `napari_get_reader` or `get_reader`,
|
|
21
|
+
to check various properties and attributes of
|
|
22
|
+
the file at the given path to determine whether it can be read
|
|
23
|
+
|
|
24
|
+
The `get_reader` `command` provided by a reader contribution is expected to be a function
|
|
10
25
|
that accepts a path (`str`) or a list of paths and:
|
|
11
26
|
* returns `None` (if it does not want to accept the given path)
|
|
12
|
-
* returns a *new function*
|
|
27
|
+
* returns a *new function* that is capable of doing the reading.
|
|
28
|
+
|
|
29
|
+
```{admonition} Why do we need two functions?
|
|
30
|
+
The `get_reader` command should make as many checks as possible
|
|
31
|
+
(without loading the full file) to determine if it can read the path. For example,
|
|
32
|
+
you might check for the presence of specific pointer files (like a `zarr.json`), or
|
|
33
|
+
call a file format validating function to ensure the file is well-formed
|
|
34
|
+
before reading, or inspect a few bytes at the beginning of the file to
|
|
35
|
+
make sure it's the right file format.
|
|
36
|
+
|
|
37
|
+
Another benefit of the `get_reader` function is that it allows the plugin
|
|
38
|
+
developer to define multiple different reading functions depending on
|
|
39
|
+
the file format or its properties, and return the appropriate one for the given path.
|
|
40
|
+
|
|
41
|
+
This function should not raise exceptions, as napari has its own handlers
|
|
42
|
+
that check for available compatible readers, and surface this information
|
|
43
|
+
to the user. The `ReaderFunction` (described below), **can** raise errors,
|
|
44
|
+
and napari will surface any raised errors to the user.
|
|
45
|
+
```
|
|
13
46
|
|
|
14
47
|
The `ReaderFunction` will be passed the same path (or list of paths) and
|
|
15
48
|
is expected to return a list containing {ref}`LayerData tuples <layer-data-tuples>` or
|
|
16
|
-
a fully instantiated napari `Layer` objects like `Image` or `Labels`.
|
|
49
|
+
a fully instantiated napari `Layer` objects like `Image` or `Labels`. Formally, the
|
|
50
|
+
`ReaderFunction` type is specified as:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
ReaderFunction = Callable[[PathOrPaths], List[LayerData]]
|
|
54
|
+
```
|
|
17
55
|
|
|
18
56
|
In the rare case that a reader plugin would like to "claim" a file, but *not*
|
|
19
57
|
actually add any data to the viewer, the `ReaderFunction` may return
|
|
@@ -12,7 +12,7 @@ name = "npe2"
|
|
|
12
12
|
dynamic = ["version"]
|
|
13
13
|
description = "napari plugin engine v2"
|
|
14
14
|
readme = "README.md"
|
|
15
|
-
requires-python = ">=3.
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
16
|
license = { text = "BSD-3-Clause" }
|
|
17
17
|
authors = [
|
|
18
18
|
{ name = "Talley Lambert", email = "talley.lambert@gmail.com" },
|
|
@@ -24,10 +24,11 @@ classifiers = [
|
|
|
24
24
|
"Natural Language :: English",
|
|
25
25
|
"Programming Language :: Python :: 3",
|
|
26
26
|
"Programming Language :: Python :: 3 :: Only",
|
|
27
|
-
"Programming Language :: Python :: 3.8",
|
|
28
|
-
"Programming Language :: Python :: 3.9",
|
|
29
27
|
"Programming Language :: Python :: 3.10",
|
|
30
28
|
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
31
32
|
"Typing :: Typed",
|
|
32
33
|
]
|
|
33
34
|
dependencies = [
|
|
@@ -35,7 +36,8 @@ dependencies = [
|
|
|
35
36
|
"platformdirs",
|
|
36
37
|
"build>=1",
|
|
37
38
|
"psygnal>=0.3.0",
|
|
38
|
-
"pydantic",
|
|
39
|
+
"pydantic>1",
|
|
40
|
+
"pydantic_extra_types",
|
|
39
41
|
"tomli-w",
|
|
40
42
|
"tomli; python_version < '3.11'",
|
|
41
43
|
"rich",
|
|
@@ -57,9 +59,11 @@ testing = [
|
|
|
57
59
|
"pytest-cov",
|
|
58
60
|
"jsonschema",
|
|
59
61
|
"pytest-pretty",
|
|
62
|
+
"build",
|
|
63
|
+
"pip",
|
|
60
64
|
]
|
|
61
|
-
dev = ["
|
|
62
|
-
docs = ["Jinja2", "magicgui>=0.3.3"]
|
|
65
|
+
dev = ["ruff", "ipython", "isort", "mypy", "pre-commit"]
|
|
66
|
+
docs = ["Jinja2", "magicgui>=0.3.3", "furo", "jupyter-book<2", "sphinx-tabs"]
|
|
63
67
|
json = ["jsonschema"]
|
|
64
68
|
|
|
65
69
|
# Entry points
|
|
@@ -88,14 +92,10 @@ markers = [
|
|
|
88
92
|
"github_main_only: Test to run only on github main (verify it does not break latest napari docs build)",
|
|
89
93
|
]
|
|
90
94
|
|
|
91
|
-
[tool.black]
|
|
92
|
-
target-version = ['py38', 'py39', 'py310']
|
|
93
|
-
line-length = 88
|
|
94
|
-
|
|
95
95
|
# https://github.com/charliermarsh/ruff
|
|
96
96
|
[tool.ruff]
|
|
97
97
|
line-length = 88
|
|
98
|
-
target-version = "
|
|
98
|
+
target-version = "py310"
|
|
99
99
|
fix = true
|
|
100
100
|
src = ["src/npe2", "tests"]
|
|
101
101
|
lint.select = [
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Callable
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from functools import partial
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
6
7
|
|
|
7
8
|
from psygnal import Signal
|
|
8
9
|
|
|
@@ -19,8 +20,8 @@ if TYPE_CHECKING:
|
|
|
19
20
|
@dataclass
|
|
20
21
|
class CommandHandler:
|
|
21
22
|
id: str
|
|
22
|
-
function:
|
|
23
|
-
python_name:
|
|
23
|
+
function: Callable | None = None
|
|
24
|
+
python_name: PythonName | None = None
|
|
24
25
|
|
|
25
26
|
def resolve(self) -> Callable:
|
|
26
27
|
if self.function is not None:
|
|
@@ -50,9 +51,9 @@ class CommandRegistry:
|
|
|
50
51
|
command_unregistered = Signal(str)
|
|
51
52
|
|
|
52
53
|
def __init__(self) -> None:
|
|
53
|
-
self._commands:
|
|
54
|
+
self._commands: dict[str, CommandHandler] = {}
|
|
54
55
|
|
|
55
|
-
def register(self, id: str, command:
|
|
56
|
+
def register(self, id: str, command: Callable | str) -> PDisposable:
|
|
56
57
|
"""Register a command under `id`.
|
|
57
58
|
|
|
58
59
|
Parameters
|