npe2 0.7.7rc0__tar.gz → 0.7.8rc0__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.7rc0 → npe2-0.7.8rc0}/.github/workflows/ci.yml +7 -5
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github/workflows/test_all_plugins.yml +11 -3
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github/workflows/update_changelog.yml +2 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.pre-commit-config.yaml +4 -4
- npe2-0.7.8rc0/PKG-INFO +196 -0
- npe2-0.7.8rc0/README.md +144 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_readers_guide.md.jinja +2 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_widgets_guide.md.jinja +1 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/pyproject.toml +5 -4
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_dynamic_plugin.py +2 -4
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/_fetch.py +4 -75
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/_from_npe1.py +4 -4
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_plugin_manager.py +21 -17
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_setuptools_plugin.py +1 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/cli.py +0 -25
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/implements.pyi +1 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/io_utils.py +9 -11
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/__init__.py +1 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_configuration.py +1 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_json_schema.py +6 -7
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_readers.py +13 -3
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_sample_data.py +1 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/schema.py +7 -3
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/utils.py +7 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/plugin_manager.py +5 -1
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/types.py +6 -10
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/fixtures/my-compiled-plugin/my_module/_a.py +6 -8
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/fixtures/my-compiled-plugin/my_module/_b.py +3 -6
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/npe1_module/__init__.py +16 -16
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/_with_decorators.py +11 -18
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/my_plugin/__init__.py +4 -7
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_cli.py +0 -17
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_contributions.py +2 -4
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_conversion.py +27 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_fetch.py +6 -12
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_npe1_adapter.py +1 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_plugin_manager.py +23 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_tmp_plugin.py +7 -8
- npe2-0.7.7rc0/PKG-INFO +0 -93
- npe2-0.7.7rc0/README.md +0 -41
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github/ISSUE_TEMPLATE.md +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github/dependabot.yml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github/workflows/test_conversion.yml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.github_changelog_generator +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/.gitignore +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/CHANGELOG.md +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/LICENSE +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/Makefile +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/example_manifest.yaml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/example_plugin/__init__.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/example_plugin/some_module.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/render.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_contributions.md.jinja +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_manifest.md.jinja +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_sample_data_guide.md.jinja +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/_docs/templates/_npe2_writers_guide.md.jinja +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/codecov.yml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/docs/_config.yml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/docs/_toc.yml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/docs/index.md +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/docs/requirements.txt +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/__init__.py +5 -5
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/__main__.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_command_registry.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/__init__.py +2 -2
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/_compile.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/_setuputils.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_inspection/_visitors.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_pydantic_compat.py +7 -7
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/_pytest_plugin.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/implements.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/_bases.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/_npe1_adapter.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/_package_metadata.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/_validators.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/__init__.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_commands.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_contributions.py +3 -3
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_icon.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_keybindings.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_menus.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_submenu.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_themes.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_widgets.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/contributions/_writers.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/menus.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/manifest/package_metadata.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/src/npe2/py.typed +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/conftest.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/fixtures/my-compiled-plugin/my_module/__init__.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/fixtures/my-compiled-plugin/setup.cfg +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/METADATA +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/entry_points.txt +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/top_level.txt +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/npe1-plugin/setup.cfg +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/my_plugin/napari.yaml +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/my_plugin-1.2.3.dist-info/METADATA +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/my_plugin-1.2.3.dist-info/entry_points.txt +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/sample/my_plugin-1.2.3.dist-info/top_level.txt +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test__io_utils.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_all_plugins.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_compile.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_config_contribution.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_docs.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_implements.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_manifest.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_package_meta.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_pm_module.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_pytest_plugin.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_setuptools_plugin.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_utils.py +0 -0
- {npe2-0.7.7rc0 → npe2-0.7.8rc0}/tests/test_validations.py +0 -0
|
@@ -53,11 +53,12 @@ jobs:
|
|
|
53
53
|
coverage run --source=npe2 -m pytest --color yes
|
|
54
54
|
|
|
55
55
|
- name: Upload coverage as artifact
|
|
56
|
-
uses: actions/upload-artifact@
|
|
56
|
+
uses: actions/upload-artifact@v4
|
|
57
57
|
with:
|
|
58
|
-
name: coverage reports
|
|
58
|
+
name: coverage reports ${{ matrix.platform }} py ${{ matrix.python-version }} ${{ (matrix.pydantic == 'pydantic<2' && 'pydantic_lt_2') || 'pydantic_gt_2' }}
|
|
59
59
|
path: |
|
|
60
60
|
./.coverage.*
|
|
61
|
+
include-hidden-files: true
|
|
61
62
|
|
|
62
63
|
test_napari:
|
|
63
64
|
name: napari tests
|
|
@@ -124,10 +125,11 @@ jobs:
|
|
|
124
125
|
pip install codecov
|
|
125
126
|
|
|
126
127
|
- name: Download coverage data
|
|
127
|
-
uses: actions/download-artifact@
|
|
128
|
+
uses: actions/download-artifact@v4
|
|
128
129
|
with:
|
|
129
|
-
|
|
130
|
+
pattern: coverage reports*
|
|
130
131
|
path: coverage
|
|
132
|
+
merge-multiple: true
|
|
131
133
|
|
|
132
134
|
- name: combine coverage data
|
|
133
135
|
run: |
|
|
@@ -138,7 +140,7 @@ jobs:
|
|
|
138
140
|
python -Im coverage report --format=markdown --skip-empty --skip-covered >> $GITHUB_STEP_SUMMARY
|
|
139
141
|
|
|
140
142
|
- name: Upload coverage data
|
|
141
|
-
uses: codecov/codecov-action@
|
|
143
|
+
uses: codecov/codecov-action@v5
|
|
142
144
|
with:
|
|
143
145
|
fail_ci_if_error: true
|
|
144
146
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
name: Test all plugins
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
+
# To run this workflow, trigger it manually, or add the label 'test-all-plugins' to a pull request
|
|
4
5
|
pull_request:
|
|
5
6
|
types: [ labeled ]
|
|
6
7
|
workflow_dispatch:
|
|
@@ -15,7 +16,13 @@ jobs:
|
|
|
15
16
|
runs-on: ubuntu-latest
|
|
16
17
|
steps:
|
|
17
18
|
- id: plugin_names
|
|
18
|
-
|
|
19
|
+
# Query npe2api for index of all plugins, select the keys, turn them into an array, select 10 random
|
|
20
|
+
# names from that array, convert the "one per line" output back into a string array
|
|
21
|
+
# save the string array of 10 plugins into the output from this step
|
|
22
|
+
run: |
|
|
23
|
+
set -eux
|
|
24
|
+
DATA=$(echo $(curl -s https://npe2api.vercel.app/api/plugins | jq -c 'keys' | jq -r '.[]' | shuf -n 10 | jq -R -s 'split("\n") | map(select(. != ""))'))
|
|
25
|
+
echo "plugins=$DATA" >> "$GITHUB_OUTPUT"
|
|
19
26
|
outputs:
|
|
20
27
|
plugins: ${{ steps.plugin_names.outputs.plugins }}
|
|
21
28
|
|
|
@@ -38,8 +45,9 @@ jobs:
|
|
|
38
45
|
|
|
39
46
|
- uses: conda-incubator/setup-miniconda@v3
|
|
40
47
|
with:
|
|
41
|
-
python-version: 3.
|
|
42
|
-
miniforge-variant:
|
|
48
|
+
python-version: '3.10'
|
|
49
|
+
miniforge-variant: Miniforge3
|
|
50
|
+
conda-remove-defaults: "true"
|
|
43
51
|
miniforge-version: latest
|
|
44
52
|
use-mamba: true
|
|
45
53
|
|
|
@@ -13,13 +13,13 @@ jobs:
|
|
|
13
13
|
- name: Checkout
|
|
14
14
|
uses: actions/checkout@v4
|
|
15
15
|
- name: "✏️ Generate release changelog"
|
|
16
|
-
uses: heinrichreimer/github-changelog-generator-action@v2.
|
|
16
|
+
uses: heinrichreimer/github-changelog-generator-action@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@v7
|
|
23
23
|
with:
|
|
24
24
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
25
25
|
commit-message: Automatic changelog update
|
|
@@ -7,24 +7,24 @@ exclude: _docs/example_plugin/some_module.py
|
|
|
7
7
|
|
|
8
8
|
repos:
|
|
9
9
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
-
rev:
|
|
10
|
+
rev: v5.0.0
|
|
11
11
|
hooks:
|
|
12
12
|
- id: check-docstring-first
|
|
13
13
|
- id: end-of-file-fixer
|
|
14
14
|
- id: trailing-whitespace
|
|
15
15
|
|
|
16
16
|
- repo: https://github.com/psf/black
|
|
17
|
-
rev:
|
|
17
|
+
rev: 25.1.0
|
|
18
18
|
hooks:
|
|
19
19
|
- id: black
|
|
20
20
|
|
|
21
21
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
-
rev: v0.
|
|
22
|
+
rev: v0.9.4
|
|
23
23
|
hooks:
|
|
24
24
|
- id: ruff
|
|
25
25
|
|
|
26
26
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
27
|
-
rev: v1.
|
|
27
|
+
rev: v1.14.1
|
|
28
28
|
hooks:
|
|
29
29
|
- id: mypy
|
|
30
30
|
additional_dependencies:
|
npe2-0.7.8rc0/PKG-INFO
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: npe2
|
|
3
|
+
Version: 0.7.8rc0
|
|
4
|
+
Summary: napari plugin engine v2
|
|
5
|
+
Project-URL: homepage, https://github.com/napari/npe2
|
|
6
|
+
Project-URL: repository, https://github.com/napari/npe2
|
|
7
|
+
Author: Nathan Clack
|
|
8
|
+
Author-email: Talley Lambert <talley.lambert@gmail.com>
|
|
9
|
+
License: BSD-3-Clause
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Requires-Dist: appdirs
|
|
23
|
+
Requires-Dist: build>=1
|
|
24
|
+
Requires-Dist: psygnal>=0.3.0
|
|
25
|
+
Requires-Dist: pydantic
|
|
26
|
+
Requires-Dist: pyyaml
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Requires-Dist: tomli-w
|
|
29
|
+
Requires-Dist: tomli; python_version < '3.11'
|
|
30
|
+
Requires-Dist: typer
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: black; extra == 'dev'
|
|
33
|
+
Requires-Dist: ipython; extra == 'dev'
|
|
34
|
+
Requires-Dist: isort; extra == 'dev'
|
|
35
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
36
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
37
|
+
Provides-Extra: docs
|
|
38
|
+
Requires-Dist: jinja2; extra == 'docs'
|
|
39
|
+
Requires-Dist: magicgui>=0.3.3; extra == 'docs'
|
|
40
|
+
Provides-Extra: json
|
|
41
|
+
Requires-Dist: jsonschema; extra == 'json'
|
|
42
|
+
Provides-Extra: testing
|
|
43
|
+
Requires-Dist: jsonschema; extra == 'testing'
|
|
44
|
+
Requires-Dist: magicgui; extra == 'testing'
|
|
45
|
+
Requires-Dist: napari-plugin-engine; extra == 'testing'
|
|
46
|
+
Requires-Dist: napari-svg==0.1.5; extra == 'testing'
|
|
47
|
+
Requires-Dist: numpy; extra == 'testing'
|
|
48
|
+
Requires-Dist: pytest; extra == 'testing'
|
|
49
|
+
Requires-Dist: pytest-cov; extra == 'testing'
|
|
50
|
+
Requires-Dist: pytest-pretty; extra == 'testing'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# npe2 - napari plugin engine version 2
|
|
54
|
+
|
|
55
|
+
[](https://github.com/napari/npe2/actions/workflows/ci.yml)
|
|
56
|
+
[](https://codecov.io/gh/napari/npe2)
|
|
57
|
+
|
|
58
|
+
## Project description
|
|
59
|
+
|
|
60
|
+
The **napari plugin engine version 2**, **npe2** extends the functionality of
|
|
61
|
+
[napari's core](https://github.com/napari/napari).
|
|
62
|
+
The plugin ecosystem offers user additional functionality for napari as well
|
|
63
|
+
as specific support for different scientific domains.
|
|
64
|
+
|
|
65
|
+
This repo contains all source code and documentation required for defining, validating and managing plugins for napari.
|
|
66
|
+
|
|
67
|
+
## Getting started
|
|
68
|
+
|
|
69
|
+
The [napari plugin docs landing page](https://napari.org/stable/plugins/index.html)
|
|
70
|
+
offers comprehensive information for **plugin users** and for **plugin developers**.
|
|
71
|
+
|
|
72
|
+
### Plugin users
|
|
73
|
+
|
|
74
|
+
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
|
+
- [Finding and installing plugins](https://napari.org/stable/plugins/start_using_plugins/finding_and_installing_plugins.html#find-and-install-plugins)
|
|
77
|
+
|
|
78
|
+
### Plugin developers
|
|
79
|
+
|
|
80
|
+
For plugin developers, the docs cover topics like:
|
|
81
|
+
- [Building a plugin](https://napari.org/stable/plugins/building_a_plugin/index.html)
|
|
82
|
+
- [Guides to different plugin contributions](https://napari.org/stable/plugins/building_a_plugin/guides.html)
|
|
83
|
+
- [Technical references such as the plugin manifest](https://napari.org/stable/plugins/technical_references/manifest.html)
|
|
84
|
+
|
|
85
|
+
Try the [**napari plugin template**](https://github.com/napari/napari-plugin-template)
|
|
86
|
+
to streamline development of a new plugin.
|
|
87
|
+
|
|
88
|
+
## Installation
|
|
89
|
+
|
|
90
|
+
The `npe2` command line tool can be installed with `pip` or `conda`, but will already be installed as a dependency if you have napari installed.
|
|
91
|
+
|
|
92
|
+
### Using pip
|
|
93
|
+
|
|
94
|
+
1. Create and activate a virtual environment.
|
|
95
|
+
|
|
96
|
+
*If you are new to using virtual environments, visit our [virtual environments guide](https://napari.org/stable/plugins/virtual_environment_docs/1-virtual-environments.html)*.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
python3 -m venv .venv
|
|
100
|
+
source .venv/bin/activate
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
2. Install npe2.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install npe2
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
3. Test your installation.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npe2 --help
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Using conda
|
|
116
|
+
|
|
117
|
+
1. Create and activate a virtual environment.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
conda create -n npe-test -c conda-forge python=3.12
|
|
121
|
+
conda activate npe-test
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
2. Install npe2.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
conda install npe2
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
3. Test your installation.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npe2 --help
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Usage
|
|
137
|
+
|
|
138
|
+
The command line tool `npe2` offers the following commands:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
cache Cache utils
|
|
142
|
+
compile Compile @npe2.implements contributions to generate a manifest.
|
|
143
|
+
convert Convert first generation napari plugin to new (manifest) format.
|
|
144
|
+
fetch Fetch manifest from remote package.
|
|
145
|
+
list List currently installed plugins.
|
|
146
|
+
parse Show parsed manifest as yaml.
|
|
147
|
+
validate Validate manifest for a distribution name or manifest filepath.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Examples
|
|
151
|
+
|
|
152
|
+
List currently installed plugins:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npe2 list
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Compile a source directory to create a plugin manifest:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npe2 compile PATH_TO_SOURCE_DIRECTORY
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Convert current directory to an npe2-ready plugin
|
|
165
|
+
(note: the repo must also be installed and importable in the current environment.):
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npe2 convert .
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Validate a plugin package. For example, a plugin named `your-plugin-package`:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npe2 validate your-plugin-package
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Show a parsed manifest of your plugin:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npe2 parse your-plugin-package
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
npe2 uses the [BSD License](./LICENSE).
|
|
186
|
+
|
|
187
|
+
## History
|
|
188
|
+
|
|
189
|
+
This repo replaces the initial napari plugin engine v1.
|
|
190
|
+
See also https://github.com/napari/napari/issues/3115 for
|
|
191
|
+
motivation and technical discussion about the creation of v2.
|
|
192
|
+
|
|
193
|
+
## Contact us
|
|
194
|
+
|
|
195
|
+
Visit [our community documentation](https://napari.org/stable/community/index.html)
|
|
196
|
+
or [open a new issue on this repo](https://github.com/napari/npe2/issues/new).
|
npe2-0.7.8rc0/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# npe2 - napari plugin engine version 2
|
|
2
|
+
|
|
3
|
+
[](https://github.com/napari/npe2/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/napari/npe2)
|
|
5
|
+
|
|
6
|
+
## Project description
|
|
7
|
+
|
|
8
|
+
The **napari plugin engine version 2**, **npe2** extends the functionality of
|
|
9
|
+
[napari's core](https://github.com/napari/napari).
|
|
10
|
+
The plugin ecosystem offers user additional functionality for napari as well
|
|
11
|
+
as specific support for different scientific domains.
|
|
12
|
+
|
|
13
|
+
This repo contains all source code and documentation required for defining, validating and managing plugins for napari.
|
|
14
|
+
|
|
15
|
+
## Getting started
|
|
16
|
+
|
|
17
|
+
The [napari plugin docs landing page](https://napari.org/stable/plugins/index.html)
|
|
18
|
+
offers comprehensive information for **plugin users** and for **plugin developers**.
|
|
19
|
+
|
|
20
|
+
### Plugin users
|
|
21
|
+
|
|
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
|
+
- [Finding and installing plugins](https://napari.org/stable/plugins/start_using_plugins/finding_and_installing_plugins.html#find-and-install-plugins)
|
|
25
|
+
|
|
26
|
+
### Plugin developers
|
|
27
|
+
|
|
28
|
+
For plugin developers, the docs cover topics like:
|
|
29
|
+
- [Building a plugin](https://napari.org/stable/plugins/building_a_plugin/index.html)
|
|
30
|
+
- [Guides to different plugin contributions](https://napari.org/stable/plugins/building_a_plugin/guides.html)
|
|
31
|
+
- [Technical references such as the plugin manifest](https://napari.org/stable/plugins/technical_references/manifest.html)
|
|
32
|
+
|
|
33
|
+
Try the [**napari plugin template**](https://github.com/napari/napari-plugin-template)
|
|
34
|
+
to streamline development of a new plugin.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
The `npe2` command line tool can be installed with `pip` or `conda`, but will already be installed as a dependency if you have napari installed.
|
|
39
|
+
|
|
40
|
+
### Using pip
|
|
41
|
+
|
|
42
|
+
1. Create and activate a virtual environment.
|
|
43
|
+
|
|
44
|
+
*If you are new to using virtual environments, visit our [virtual environments guide](https://napari.org/stable/plugins/virtual_environment_docs/1-virtual-environments.html)*.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3 -m venv .venv
|
|
48
|
+
source .venv/bin/activate
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Install npe2.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install npe2
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
3. Test your installation.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npe2 --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Using conda
|
|
64
|
+
|
|
65
|
+
1. Create and activate a virtual environment.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
conda create -n npe-test -c conda-forge python=3.12
|
|
69
|
+
conda activate npe-test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2. Install npe2.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
conda install npe2
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
3. Test your installation.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npe2 --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
The command line tool `npe2` offers the following commands:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cache Cache utils
|
|
90
|
+
compile Compile @npe2.implements contributions to generate a manifest.
|
|
91
|
+
convert Convert first generation napari plugin to new (manifest) format.
|
|
92
|
+
fetch Fetch manifest from remote package.
|
|
93
|
+
list List currently installed plugins.
|
|
94
|
+
parse Show parsed manifest as yaml.
|
|
95
|
+
validate Validate manifest for a distribution name or manifest filepath.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Examples
|
|
99
|
+
|
|
100
|
+
List currently installed plugins:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npe2 list
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Compile a source directory to create a plugin manifest:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npe2 compile PATH_TO_SOURCE_DIRECTORY
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Convert current directory to an npe2-ready plugin
|
|
113
|
+
(note: the repo must also be installed and importable in the current environment.):
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npe2 convert .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Validate a plugin package. For example, a plugin named `your-plugin-package`:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npe2 validate your-plugin-package
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Show a parsed manifest of your plugin:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npe2 parse your-plugin-package
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
npe2 uses the [BSD License](./LICENSE).
|
|
134
|
+
|
|
135
|
+
## History
|
|
136
|
+
|
|
137
|
+
This repo replaces the initial napari plugin engine v1.
|
|
138
|
+
See also https://github.com/napari/napari/issues/3115 for
|
|
139
|
+
motivation and technical discussion about the creation of v2.
|
|
140
|
+
|
|
141
|
+
## Contact us
|
|
142
|
+
|
|
143
|
+
Visit [our community documentation](https://napari.org/stable/community/index.html)
|
|
144
|
+
or [open a new issue on this repo](https://github.com/napari/npe2/issues/new).
|
|
@@ -12,7 +12,8 @@ that accepts a path (`str`) or a list of paths and:
|
|
|
12
12
|
* returns a *new function* (a `ReaderFunction`) that is capable of doing the reading.
|
|
13
13
|
|
|
14
14
|
The `ReaderFunction` will be passed the same path (or list of paths) and
|
|
15
|
-
is expected to return a list
|
|
15
|
+
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`.
|
|
16
17
|
|
|
17
18
|
In the rare case that a reader plugin would like to "claim" a file, but *not*
|
|
18
19
|
actually add any data to the viewer, the `ReaderFunction` may return
|
|
@@ -64,7 +64,7 @@ specification:
|
|
|
64
64
|
For more examples see [](creating-widgets) and
|
|
65
65
|
[GUI gallery examples](https://napari.org/stable/_tags/gui.html) (only a subset
|
|
66
66
|
involve widgets). Additionally,
|
|
67
|
-
[
|
|
67
|
+
[napari-plugin-template](https://github.com/napari/napari-plugin-template)
|
|
68
68
|
has more robust widget examples that you can adapt to your needs.
|
|
69
69
|
|
|
70
70
|
```{note}
|
|
@@ -98,7 +98,7 @@ line-length = 88
|
|
|
98
98
|
target-version = "py38"
|
|
99
99
|
fix = true
|
|
100
100
|
src = ["src/npe2", "tests"]
|
|
101
|
-
select = [
|
|
101
|
+
lint.select = [
|
|
102
102
|
"E",
|
|
103
103
|
"F",
|
|
104
104
|
"W", #flake8
|
|
@@ -110,14 +110,15 @@ select = [
|
|
|
110
110
|
"RUF", # ruff-specific rules
|
|
111
111
|
]
|
|
112
112
|
|
|
113
|
-
[tool.ruff.per-file-ignores]
|
|
113
|
+
[tool.ruff.lint.per-file-ignores]
|
|
114
114
|
"src/npe2/cli.py" = ["B008", "A00"]
|
|
115
|
+
"**/test_*.py" = ["RUF018"]
|
|
115
116
|
|
|
116
|
-
[tool.ruff.pyupgrade]
|
|
117
|
+
[tool.ruff.lint.pyupgrade]
|
|
117
118
|
# Preserve types, even if a file imports `from __future__ import annotations`.
|
|
118
119
|
keep-runtime-typing = true
|
|
119
120
|
|
|
120
|
-
[tool.ruff.isort]
|
|
121
|
+
[tool.ruff.lint.isort]
|
|
121
122
|
known-first-party = ['npe2']
|
|
122
123
|
|
|
123
124
|
# https://mypy.readthedocs.io/en/stable/config_file.html
|
|
@@ -203,14 +203,12 @@ class ContributionDecorator(Generic[C]):
|
|
|
203
203
|
self._contrib_name = CONTRIB_NAMES[self.contrib_type]
|
|
204
204
|
|
|
205
205
|
@overload
|
|
206
|
-
def __call__(self, func: T, **kwargs) -> T:
|
|
207
|
-
...
|
|
206
|
+
def __call__(self, func: T, **kwargs) -> T: ...
|
|
208
207
|
|
|
209
208
|
@overload
|
|
210
209
|
def __call__(
|
|
211
210
|
self, func: Optional[Literal[None]] = None, **kwargs
|
|
212
|
-
) -> Callable[[T], T]:
|
|
213
|
-
...
|
|
211
|
+
) -> Callable[[T], T]: ...
|
|
214
212
|
|
|
215
213
|
def __call__(
|
|
216
214
|
self, func: Optional[T] = None, **kwargs
|
|
@@ -3,10 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
|
-
import re
|
|
7
6
|
import subprocess
|
|
8
7
|
import tempfile
|
|
9
|
-
from concurrent.futures import ProcessPoolExecutor
|
|
10
8
|
from contextlib import contextmanager
|
|
11
9
|
from functools import lru_cache
|
|
12
10
|
from importlib import metadata
|
|
@@ -20,11 +18,10 @@ from typing import (
|
|
|
20
18
|
Iterator,
|
|
21
19
|
List,
|
|
22
20
|
Optional,
|
|
23
|
-
Tuple,
|
|
24
21
|
Union,
|
|
25
22
|
)
|
|
26
23
|
from unittest.mock import patch
|
|
27
|
-
from urllib import error,
|
|
24
|
+
from urllib import error, request
|
|
28
25
|
from zipfile import ZipFile
|
|
29
26
|
|
|
30
27
|
from npe2.manifest import PackageMetadata
|
|
@@ -39,9 +36,8 @@ NPE1_ENTRY_POINT = "napari.plugin"
|
|
|
39
36
|
NPE2_ENTRY_POINT = "napari.manifest"
|
|
40
37
|
__all__ = [
|
|
41
38
|
"fetch_manifest",
|
|
42
|
-
"get_pypi_url",
|
|
43
39
|
"get_hub_plugin",
|
|
44
|
-
"
|
|
40
|
+
"get_pypi_url",
|
|
45
41
|
]
|
|
46
42
|
|
|
47
43
|
|
|
@@ -59,7 +55,7 @@ def _manifest_from_npe2_dist(
|
|
|
59
55
|
module: str = match.groupdict()["module"]
|
|
60
56
|
attr: str = match.groupdict()["attr"]
|
|
61
57
|
|
|
62
|
-
mf_file = Path(dist.locate_file(Path(module.replace(".", os.sep)) / attr))
|
|
58
|
+
mf_file = Path(dist.locate_file(Path(module.replace(".", os.sep)) / attr)) # type: ignore[arg-type]
|
|
63
59
|
if not mf_file.exists():
|
|
64
60
|
raise ValueError( # pragma: no cover
|
|
65
61
|
f"manifest {mf_file.name!r} does not exist in distribution "
|
|
@@ -264,7 +260,7 @@ def fetch_manifest(
|
|
|
264
260
|
return _manifest_from_extracted_wheel(td)
|
|
265
261
|
except metadata.PackageNotFoundError:
|
|
266
262
|
return _manifest_from_pypi_sdist(package_or_url, version)
|
|
267
|
-
except error.HTTPError:
|
|
263
|
+
except error.HTTPError: # pragma: no cover
|
|
268
264
|
pass # pragma: no cover
|
|
269
265
|
raise ValueError( # pragma: no cover
|
|
270
266
|
f"Could not interpret {package_or_url!r} as a PYPI package name or URL to a "
|
|
@@ -377,75 +373,8 @@ def _tmp_pypi_sdist_download(
|
|
|
377
373
|
return _tmp_targz_download(url)
|
|
378
374
|
|
|
379
375
|
|
|
380
|
-
@lru_cache
|
|
381
|
-
def _get_packages_by_classifier(classifier: str) -> Dict[str, str]:
|
|
382
|
-
"""Search for packages declaring ``classifier`` on PyPI.
|
|
383
|
-
|
|
384
|
-
Returns
|
|
385
|
-
-------
|
|
386
|
-
packages : List[str]
|
|
387
|
-
name of all packages at pypi that declare ``classifier``
|
|
388
|
-
"""
|
|
389
|
-
PACKAGE_NAME_PATTERN = re.compile('class="package-snippet__name">(.+)</span>')
|
|
390
|
-
PACKAGE_VERSION_PATTERN = re.compile('class="package-snippet__version">(.+)</span>')
|
|
391
|
-
|
|
392
|
-
packages = {}
|
|
393
|
-
page = 1
|
|
394
|
-
url = f"https://pypi.org/search/?c={parse.quote_plus(classifier)}&page="
|
|
395
|
-
while True:
|
|
396
|
-
try:
|
|
397
|
-
with request.urlopen(f"{url}{page}") as response:
|
|
398
|
-
html = response.read().decode()
|
|
399
|
-
names = PACKAGE_NAME_PATTERN.findall(html)
|
|
400
|
-
versions = PACKAGE_VERSION_PATTERN.findall(html)
|
|
401
|
-
packages.update(dict(zip(names, versions)))
|
|
402
|
-
page += 1
|
|
403
|
-
except error.HTTPError:
|
|
404
|
-
break
|
|
405
|
-
|
|
406
|
-
return dict(sorted(packages.items()))
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
def get_pypi_plugins() -> Dict[str, str]:
|
|
410
|
-
"""Return {name: latest_version} for all plugins found on pypi."""
|
|
411
|
-
NAPARI_CLASSIFIER = "Framework :: napari"
|
|
412
|
-
return _get_packages_by_classifier(NAPARI_CLASSIFIER)
|
|
413
|
-
|
|
414
|
-
|
|
415
376
|
@lru_cache
|
|
416
377
|
def get_hub_plugin(plugin_name: str) -> Dict[str, Any]:
|
|
417
378
|
"""Return hub information for a specific plugin."""
|
|
418
379
|
with request.urlopen(f"https://api.napari-hub.org/plugins/{plugin_name}") as r:
|
|
419
380
|
return json.load(r)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
def _try_fetch_and_write_manifest(args: Tuple[str, str, Path, int]):
|
|
423
|
-
name, version, dest, indent = args
|
|
424
|
-
FORMAT = "json"
|
|
425
|
-
|
|
426
|
-
try: # pragma: no cover
|
|
427
|
-
mf = fetch_manifest(name, version=version)
|
|
428
|
-
manifest_string = getattr(mf, FORMAT)(exclude=set(), indent=indent)
|
|
429
|
-
|
|
430
|
-
(dest / f"{name}.{FORMAT}").write_text(manifest_string)
|
|
431
|
-
print(f"✅ {name}")
|
|
432
|
-
except Exception as e:
|
|
433
|
-
print(f"❌ {name}")
|
|
434
|
-
return name, {"version": version, "error": str(e)}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
def fetch_all_manifests(dest: str = "manifests", indent: int = 2) -> None:
|
|
438
|
-
"""Fetch all manifests for plugins on PyPI and write to ``dest`` directory."""
|
|
439
|
-
_dest = Path(dest)
|
|
440
|
-
_dest.mkdir(exist_ok=True, parents=True)
|
|
441
|
-
|
|
442
|
-
args = [
|
|
443
|
-
(name, ver, _dest, indent) for name, ver in sorted(get_pypi_plugins().items())
|
|
444
|
-
]
|
|
445
|
-
|
|
446
|
-
# use processes instead of threads, because many of the subroutines in build
|
|
447
|
-
# and setuptools use `os.chdir()`, which is not thread-safe
|
|
448
|
-
with ProcessPoolExecutor() as executor:
|
|
449
|
-
errors = list(executor.map(_try_fetch_and_write_manifest, args))
|
|
450
|
-
_errors = {tup[0]: tup[1] for tup in errors if tup}
|
|
451
|
-
(_dest / "errors.json").write_text(json.dumps(_errors, indent=indent))
|
|
@@ -404,11 +404,11 @@ class HookImplParser:
|
|
|
404
404
|
)
|
|
405
405
|
|
|
406
406
|
def add_command(self, impl: HookImplementation, py_name: str = "") -> str:
|
|
407
|
-
name = impl.specname.replace("napari_", "")
|
|
408
|
-
id = f"{self.package}.{name}"
|
|
409
|
-
title = " ".join(name.split("_")).title()
|
|
410
407
|
if not py_name:
|
|
411
408
|
py_name = _python_name(impl.function)
|
|
409
|
+
name = impl.function.__name__
|
|
410
|
+
id = f"{self.package}.{name}"
|
|
411
|
+
title = " ".join(name.split("_")).title()
|
|
412
412
|
c = CommandContribution(id=id, python_name=py_name, title=title)
|
|
413
413
|
self.contributions["commands"].append(c)
|
|
414
414
|
return id
|
|
@@ -516,7 +516,7 @@ def get_top_module_path(package_name, top_module: Optional[str] = None) -> Path:
|
|
|
516
516
|
)
|
|
517
517
|
top_module = top_mods[0]
|
|
518
518
|
|
|
519
|
-
path = Path(dist.locate_file(top_module))
|
|
519
|
+
path = Path(dist.locate_file(top_module)) # type: ignore[arg-type]
|
|
520
520
|
if not path.is_dir() and dist.files:
|
|
521
521
|
for f_path in dist.files:
|
|
522
522
|
if "__editable__" in f_path.name:
|