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.
Files changed (115) hide show
  1. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/ci.yml +22 -25
  2. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/test_all_plugins.yml +3 -3
  3. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/test_conversion.yml +6 -6
  4. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/workflows/update_changelog.yml +3 -3
  5. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.gitignore +2 -0
  6. npe2-0.8.0rc0/.pre-commit-config.yaml +32 -0
  7. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/PKG-INFO +13 -7
  8. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/README.md +0 -1
  9. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_plugin/some_module.py +5 -4
  10. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/render.py +4 -5
  11. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_contributions.md.jinja +5 -5
  12. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_readers_guide.md.jinja +41 -3
  13. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/pyproject.toml +11 -11
  14. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_command_registry.py +6 -5
  15. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_dynamic_plugin.py +25 -27
  16. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_compile.py +9 -8
  17. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_fetch.py +18 -30
  18. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_from_npe1.py +26 -32
  19. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_setuputils.py +14 -14
  20. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/_visitors.py +26 -21
  21. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_plugin_manager.py +45 -57
  22. npe2-0.8.0rc0/src/npe2/_pydantic_util.py +53 -0
  23. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_pytest_plugin.py +3 -4
  24. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_setuptools_plugin.py +9 -9
  25. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/cli.py +25 -21
  26. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/implements.py +13 -10
  27. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/implements.pyi +3 -2
  28. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/io_utils.py +40 -44
  29. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_bases.py +15 -14
  30. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_npe1_adapter.py +3 -3
  31. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_package_metadata.py +40 -47
  32. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_commands.py +16 -14
  33. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_configuration.py +22 -20
  34. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_contributions.py +13 -14
  35. npe2-0.8.0rc0/src/npe2/manifest/contributions/_icon.py +6 -0
  36. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_json_schema.py +86 -89
  37. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_keybindings.py +5 -6
  38. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_menus.py +11 -9
  39. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_readers.py +10 -8
  40. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_sample_data.py +16 -15
  41. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_submenu.py +2 -4
  42. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_themes.py +18 -22
  43. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_widgets.py +6 -5
  44. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/_writers.py +22 -18
  45. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/schema.py +82 -70
  46. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/utils.py +24 -28
  47. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/plugin_manager.py +17 -14
  48. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/types.py +16 -19
  49. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/conftest.py +19 -10
  50. npe2-0.8.0rc0/tests/fixtures/my-compiled-plugin/README.md +1 -0
  51. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/_a.py +3 -3
  52. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/_with_decorators.py +5 -4
  53. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin/__init__.py +5 -5
  54. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test__io_utils.py +5 -5
  55. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_cli.py +5 -3
  56. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_compile.py +4 -2
  57. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_config_contribution.py +1 -1
  58. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_contributions.py +1 -1
  59. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_conversion.py +4 -4
  60. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_fetch.py +3 -9
  61. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_implements.py +7 -5
  62. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_manifest.py +5 -5
  63. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_npe1_adapter.py +7 -0
  64. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_setuptools_plugin.py +13 -10
  65. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_utils.py +2 -2
  66. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_validations.py +10 -12
  67. npe2-0.7.9rc0/.pre-commit-config.yaml +0 -33
  68. npe2-0.7.9rc0/docs/requirements.txt +0 -5
  69. npe2-0.7.9rc0/src/npe2/_pydantic_compat.py +0 -54
  70. npe2-0.7.9rc0/src/npe2/manifest/contributions/_icon.py +0 -8
  71. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/ISSUE_TEMPLATE.md +0 -0
  72. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github/dependabot.yml +0 -0
  73. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/.github_changelog_generator +0 -0
  74. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/CHANGELOG.md +0 -0
  75. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/LICENSE +0 -0
  76. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/Makefile +0 -0
  77. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_manifest.yaml +0 -0
  78. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/example_plugin/__init__.py +0 -0
  79. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_manifest.md.jinja +0 -0
  80. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_menus_guide.md.jinja +0 -0
  81. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_sample_data_guide.md.jinja +0 -0
  82. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_widgets_guide.md.jinja +0 -0
  83. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/_docs/templates/_npe2_writers_guide.md.jinja +0 -0
  84. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/codecov.yml +0 -0
  85. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/docs/_config.yml +0 -0
  86. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/docs/index.md +0 -0
  87. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/__init__.py +0 -0
  88. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/__main__.py +0 -0
  89. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/_inspection/__init__.py +0 -0
  90. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/__init__.py +0 -0
  91. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/_validators.py +0 -0
  92. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/contributions/__init__.py +0 -0
  93. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/menus.py +0 -0
  94. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/manifest/package_metadata.py +0 -0
  95. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/src/npe2/py.typed +0 -0
  96. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/__init__.py +0 -0
  97. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/my_module/_b.py +0 -0
  98. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/fixtures/my-compiled-plugin/setup.cfg +0 -0
  99. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/METADATA +0 -0
  100. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD +0 -0
  101. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/entry_points.txt +0 -0
  102. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/top_level.txt +0 -0
  103. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/npe1_module/__init__.py +0 -0
  104. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/npe1-plugin/setup.cfg +0 -0
  105. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin/napari.yaml +0 -0
  106. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/METADATA +0 -0
  107. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/entry_points.txt +0 -0
  108. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/sample/my_plugin-1.2.3.dist-info/top_level.txt +0 -0
  109. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_all_plugins.py +0 -0
  110. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_docs.py +0 -0
  111. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_package_meta.py +0 -0
  112. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_plugin_manager.py +0 -0
  113. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_pm_module.py +0 -0
  114. {npe2-0.7.9rc0 → npe2-0.8.0rc0}/tests/test_pytest_plugin.py +0 -0
  115. {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@v4
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.8, 3.9, "3.10", "3.11"]
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@v4
35
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
37
36
 
38
37
  - name: Set up Python ${{ matrix.python-version }}
39
- uses: actions/setup-python@v5
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@v4
54
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
57
55
  with:
58
- name: coverage reports ${{ matrix.platform }} py ${{ matrix.python-version }} ${{ (matrix.pydantic == 'pydantic<2' && 'pydantic_lt_2') || 'pydantic_gt_2' }}
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@v4
68
- - uses: actions/checkout@v4
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@v5
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@v4
94
- - uses: actions/setup-python@v5
91
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
92
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
95
93
  with:
96
- python-version: "3.x"
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
- # install dependencies, generate toc, then build
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@v3
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@v4
123
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
127
124
 
128
- - uses: actions/setup-python@v5
125
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
129
126
  with:
130
- python-version: "3.x"
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@v4
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@v4
166
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
170
167
 
171
168
  - name: Set up Python
172
- uses: actions/setup-python@v5
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@v4
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@v5
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@v4
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@v4
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@v7
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
@@ -110,3 +110,5 @@ src/npe2/_version.py
110
110
  # ignore everything that gets rendered from _docs
111
111
  docs/plugins/*.md
112
112
  schema.json
113
+
114
+ uv.lock
@@ -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.7.9rc0
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.8
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, Dict, List, Optional
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: Dict[str, Any]) -> List[str]:
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") -> List["LayerData"]:
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() -> List["LayerData"]:
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() -> Dict[str, str]:
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: Optional[Set[str]] = None) -> Set[str]:
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.json(exclude_unset=True))}
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["definitions"]["ContributionPoints"]["properties"]
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", "#/definitions/", "")|list %}
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['items']['anyOf'] is defined %}
33
+ {%- elif contrib['anyOf'] is defined %}
34
34
  {# Handle array types with union #}
35
- {%- set type_names = contrib['items']['anyOf']|map(attribute='$ref')|map("replace", "#/definitions/", "")|list %}
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("#/definitions/", "")] %}
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['definitions'][tname] %}
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
- The `command` provided by a reader contribution is expected to be a function
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* (a `ReaderFunction`) that is capable of doing the reading.
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.8"
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 = ["black", "ipython", "isort", "mypy", "pre-commit"]
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 = "py38"
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, Callable, Dict, Optional, Union
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: Optional[Callable] = None
23
- python_name: Optional[PythonName] = None
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: Dict[str, CommandHandler] = {}
54
+ self._commands: dict[str, CommandHandler] = {}
54
55
 
55
- def register(self, id: str, command: Union[Callable, str]) -> PDisposable:
56
+ def register(self, id: str, command: Callable | str) -> PDisposable:
56
57
  """Register a command under `id`.
57
58
 
58
59
  Parameters