npe2 0.7.7rc0__py3-none-any.whl → 0.7.8rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- npe2/__init__.py +5 -5
- npe2/_dynamic_plugin.py +2 -4
- npe2/_inspection/__init__.py +2 -2
- npe2/_inspection/_fetch.py +4 -75
- npe2/_inspection/_from_npe1.py +4 -4
- npe2/_plugin_manager.py +21 -17
- npe2/_pydantic_compat.py +7 -7
- npe2/_setuptools_plugin.py +1 -0
- npe2/cli.py +0 -25
- npe2/implements.pyi +1 -1
- npe2/io_utils.py +9 -11
- npe2/manifest/__init__.py +1 -1
- npe2/manifest/contributions/_configuration.py +1 -1
- npe2/manifest/contributions/_contributions.py +3 -3
- npe2/manifest/contributions/_json_schema.py +6 -7
- npe2/manifest/contributions/_readers.py +13 -3
- npe2/manifest/contributions/_sample_data.py +1 -2
- npe2/manifest/schema.py +7 -3
- npe2/manifest/utils.py +7 -1
- npe2/plugin_manager.py +5 -1
- npe2/types.py +6 -10
- npe2-0.7.8rc0.dist-info/METADATA +196 -0
- npe2-0.7.8rc0.dist-info/RECORD +49 -0
- {npe2-0.7.7rc0.dist-info → npe2-0.7.8rc0.dist-info}/WHEEL +1 -1
- npe2-0.7.7rc0.dist-info/METADATA +0 -93
- npe2-0.7.7rc0.dist-info/RECORD +0 -49
- {npe2-0.7.7rc0.dist-info → npe2-0.7.8rc0.dist-info}/entry_points.txt +0 -0
- {npe2-0.7.7rc0.dist-info → npe2-0.7.8rc0.dist-info}/licenses/LICENSE +0 -0
npe2/__init__.py
CHANGED
|
@@ -15,16 +15,16 @@ from .io_utils import read, read_get_reader, write, write_get_writer
|
|
|
15
15
|
from .manifest import PackageMetadata, PluginManifest
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
18
|
-
"__version__",
|
|
19
18
|
"DynamicPlugin",
|
|
20
|
-
"fetch_manifest",
|
|
21
|
-
"get_manifest_from_wheel",
|
|
22
19
|
"PackageMetadata",
|
|
23
20
|
"PluginContext",
|
|
24
21
|
"PluginManager",
|
|
25
22
|
"PluginManifest",
|
|
26
|
-
"
|
|
23
|
+
"__version__",
|
|
24
|
+
"fetch_manifest",
|
|
25
|
+
"get_manifest_from_wheel",
|
|
27
26
|
"read",
|
|
28
|
-
"
|
|
27
|
+
"read_get_reader",
|
|
29
28
|
"write",
|
|
29
|
+
"write_get_writer",
|
|
30
30
|
]
|
npe2/_dynamic_plugin.py
CHANGED
|
@@ -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
|
npe2/_inspection/__init__.py
CHANGED
npe2/_inspection/_fetch.py
CHANGED
|
@@ -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))
|
npe2/_inspection/_from_npe1.py
CHANGED
|
@@ -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:
|
npe2/_plugin_manager.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import os
|
|
5
|
-
import urllib
|
|
6
5
|
import warnings
|
|
7
6
|
from collections import Counter, defaultdict
|
|
8
7
|
from fnmatch import fnmatch
|
|
@@ -26,6 +25,7 @@ from typing import (
|
|
|
26
25
|
Tuple,
|
|
27
26
|
Union,
|
|
28
27
|
)
|
|
28
|
+
from urllib import parse
|
|
29
29
|
|
|
30
30
|
from psygnal import Signal, SignalGroup
|
|
31
31
|
|
|
@@ -144,7 +144,7 @@ class _ContributionsIndex:
|
|
|
144
144
|
yield from (r for pattern, r in self._readers if pattern == "")
|
|
145
145
|
else:
|
|
146
146
|
# ensure not a URI
|
|
147
|
-
if not
|
|
147
|
+
if not parse.urlparse(path).scheme:
|
|
148
148
|
# lower case the extension for checking manifest pattern
|
|
149
149
|
base = os.path.splitext(Path(path).stem)[0]
|
|
150
150
|
ext = "".join(Path(path).suffixes)
|
|
@@ -236,9 +236,17 @@ class PluginManager:
|
|
|
236
236
|
self._manifests: Dict[PluginName, PluginManifest] = {}
|
|
237
237
|
self.events = PluginManagerEvents(self)
|
|
238
238
|
self._npe1_adapters: List[NPE1Adapter] = []
|
|
239
|
-
self._command_menu_map: Dict[
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
self._command_menu_map: Dict[str, Dict[str, Dict[str, List[MenuCommand]]]] = (
|
|
240
|
+
# for each manifest, maps command IDs to menu IDs to list of MenuCommands
|
|
241
|
+
# belonging to that menu
|
|
242
|
+
# i.e. manifest -> command -> menu_id -> list[MenuCommand]
|
|
243
|
+
# we keep a list of MenuCommands even though we've already indexed by
|
|
244
|
+
# command ID **and** menu ID because it's possible to declare the
|
|
245
|
+
# same command in the same menu with different `when` and `group`
|
|
246
|
+
# fields. This seems an unlikely use case, but it is possible in
|
|
247
|
+
# the current schema.
|
|
248
|
+
defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
|
|
249
|
+
)
|
|
242
250
|
|
|
243
251
|
# up to napari 0.4.15, discovery happened in the init here
|
|
244
252
|
# so if we're running on an older version of napari, we need to discover
|
|
@@ -248,11 +256,8 @@ class PluginManager:
|
|
|
248
256
|
pass
|
|
249
257
|
else: # pragma: no cover
|
|
250
258
|
vsplit = nv.split(".")[:4]
|
|
251
|
-
if (
|
|
252
|
-
"dev" in nv
|
|
253
|
-
and vsplit < ["0", "4", "16", "dev4"]
|
|
254
|
-
or "dev" not in nv
|
|
255
|
-
and vsplit < ["0", "4", "16"]
|
|
259
|
+
if ("dev" in nv and vsplit < ["0", "4", "16", "dev4"]) or (
|
|
260
|
+
"dev" not in nv and vsplit < ["0", "4", "16"]
|
|
256
261
|
):
|
|
257
262
|
self.discover()
|
|
258
263
|
|
|
@@ -367,7 +372,6 @@ class PluginManager:
|
|
|
367
372
|
|
|
368
373
|
def _populate_command_menu_map(self, manifest: PluginManifest):
|
|
369
374
|
# map of manifest -> command -> menu_id -> list[items]
|
|
370
|
-
self._command_menu_map[manifest.name] = defaultdict(lambda: defaultdict(list))
|
|
371
375
|
menu_map = self._command_menu_map[manifest.name] # just for conciseness below
|
|
372
376
|
for menu_id, menu_items in manifest.contributions.menus.items() or ():
|
|
373
377
|
# command IDs are keys in map
|
|
@@ -698,12 +702,8 @@ class PluginManager:
|
|
|
698
702
|
|
|
699
703
|
for writer in self.iter_compatible_writers(layer_types):
|
|
700
704
|
if not plugin_name or writer.command.startswith(plugin_name):
|
|
701
|
-
if (
|
|
702
|
-
ext
|
|
703
|
-
and ext in writer.filename_extensions
|
|
704
|
-
or not ext
|
|
705
|
-
and len(layer_types) != 1
|
|
706
|
-
and not writer.filename_extensions
|
|
705
|
+
if (ext and ext in writer.filename_extensions) or (
|
|
706
|
+
not ext and len(layer_types) != 1 and not writer.filename_extensions
|
|
707
707
|
):
|
|
708
708
|
return writer, path
|
|
709
709
|
elif not ext and len(layer_types) == 1: # No extension, single layer.
|
|
@@ -715,6 +715,10 @@ class PluginManager:
|
|
|
715
715
|
# Nothing got found
|
|
716
716
|
return None, path
|
|
717
717
|
|
|
718
|
+
def get_shimmed_plugins(self) -> List[str]:
|
|
719
|
+
"""Return a list of all shimmed plugin names."""
|
|
720
|
+
return [mf.name for mf in self.iter_manifests() if mf.npe1_shim]
|
|
721
|
+
|
|
718
722
|
|
|
719
723
|
class PluginContext:
|
|
720
724
|
"""An object that can contain information for a plugin over its lifetime."""
|
npe2/_pydantic_compat.py
CHANGED
|
@@ -37,18 +37,18 @@ except ImportError:
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
__all__ = (
|
|
40
|
+
"SHAPE_LIST",
|
|
40
41
|
"BaseModel",
|
|
42
|
+
"ErrorWrapper",
|
|
41
43
|
"Extra",
|
|
42
44
|
"Field",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"validator",
|
|
45
|
+
"GenericModel",
|
|
46
|
+
"ModelMetaclass",
|
|
46
47
|
"PrivateAttr",
|
|
48
|
+
"ValidationError",
|
|
47
49
|
"color",
|
|
48
50
|
"conlist",
|
|
49
51
|
"constr",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"GenericModel",
|
|
53
|
-
"SHAPE_LIST",
|
|
52
|
+
"root_validator",
|
|
53
|
+
"validator",
|
|
54
54
|
)
|
npe2/_setuptools_plugin.py
CHANGED
npe2/cli.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import builtins
|
|
2
|
-
import sys
|
|
3
2
|
import warnings
|
|
4
3
|
from enum import Enum
|
|
5
4
|
from pathlib import Path
|
|
@@ -316,23 +315,6 @@ def list(
|
|
|
316
315
|
typer.echo(template.format(**r, ncontrib=ncontrib))
|
|
317
316
|
|
|
318
317
|
|
|
319
|
-
def _fetch_all_manifests(doit: bool):
|
|
320
|
-
"""Fetch all manifests and dump to "manifests" folder."""
|
|
321
|
-
if not doit:
|
|
322
|
-
return
|
|
323
|
-
|
|
324
|
-
from npe2._inspection import _fetch
|
|
325
|
-
|
|
326
|
-
dest = "manifests"
|
|
327
|
-
if "-o" in sys.argv:
|
|
328
|
-
dest = sys.argv[sys.argv.index("-o") + 1]
|
|
329
|
-
elif "--output" in sys.argv: # pragma: no cover
|
|
330
|
-
dest = sys.argv[sys.argv.index("--output") + 1]
|
|
331
|
-
|
|
332
|
-
_fetch.fetch_all_manifests(dest)
|
|
333
|
-
raise typer.Exit(0)
|
|
334
|
-
|
|
335
|
-
|
|
336
318
|
@app.command()
|
|
337
319
|
def fetch(
|
|
338
320
|
name: List[str],
|
|
@@ -361,13 +343,6 @@ def fetch(
|
|
|
361
343
|
help="If provided, will write manifest to filepath (must end with .yaml, "
|
|
362
344
|
".json, or .toml). Otherwise, will print to stdout.",
|
|
363
345
|
),
|
|
364
|
-
all: Optional[bool] = typer.Option(
|
|
365
|
-
None,
|
|
366
|
-
"--all",
|
|
367
|
-
help="Fetch manifests for ALL known plugins (will be SLOW)",
|
|
368
|
-
callback=_fetch_all_manifests,
|
|
369
|
-
is_eager=True,
|
|
370
|
-
),
|
|
371
346
|
):
|
|
372
347
|
"""Fetch manifest from remote package.
|
|
373
348
|
|
npe2/implements.pyi
CHANGED
npe2/io_utils.py
CHANGED
|
@@ -126,8 +126,7 @@ def _read(
|
|
|
126
126
|
plugin_name: Optional[str] = None,
|
|
127
127
|
return_reader: Literal[False] = False,
|
|
128
128
|
_pm=None,
|
|
129
|
-
) -> List[LayerData]:
|
|
130
|
-
...
|
|
129
|
+
) -> List[LayerData]: ...
|
|
131
130
|
|
|
132
131
|
|
|
133
132
|
@overload
|
|
@@ -138,8 +137,7 @@ def _read(
|
|
|
138
137
|
plugin_name: Optional[str] = None,
|
|
139
138
|
return_reader: Literal[True],
|
|
140
139
|
_pm=None,
|
|
141
|
-
) -> Tuple[List[LayerData], ReaderContribution]:
|
|
142
|
-
...
|
|
140
|
+
) -> Tuple[List[LayerData], ReaderContribution]: ...
|
|
143
141
|
|
|
144
142
|
|
|
145
143
|
def _read(
|
|
@@ -270,8 +268,7 @@ def _write(
|
|
|
270
268
|
plugin_name: Optional[str] = None,
|
|
271
269
|
return_writer: Literal[False] = False,
|
|
272
270
|
_pm: Optional[PluginManager] = None,
|
|
273
|
-
) -> List[str]:
|
|
274
|
-
...
|
|
271
|
+
) -> List[str]: ...
|
|
275
272
|
|
|
276
273
|
|
|
277
274
|
@overload
|
|
@@ -282,8 +279,7 @@ def _write(
|
|
|
282
279
|
plugin_name: Optional[str] = None,
|
|
283
280
|
return_writer: Literal[True],
|
|
284
281
|
_pm: Optional[PluginManager] = None,
|
|
285
|
-
) -> Tuple[List[str], WriterContribution]:
|
|
286
|
-
...
|
|
282
|
+
) -> Tuple[List[str], WriterContribution]: ...
|
|
287
283
|
|
|
288
284
|
|
|
289
285
|
def _write(
|
|
@@ -300,9 +296,11 @@ def _write(
|
|
|
300
296
|
_pm = PluginManager.instance()
|
|
301
297
|
|
|
302
298
|
_layer_tuples: List[FullLayerData] = [
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
(
|
|
300
|
+
cast("napari.layers.Layer", x).as_layer_data_tuple()
|
|
301
|
+
if hasattr(x, "as_layer_data_tuple")
|
|
302
|
+
else x
|
|
303
|
+
)
|
|
306
304
|
for x in layer_data
|
|
307
305
|
]
|
|
308
306
|
layer_types = [x[2] for x in _layer_tuples]
|
npe2/manifest/__init__.py
CHANGED
|
@@ -45,7 +45,7 @@ class ConfigurationProperty(Draft07JsonSchema):
|
|
|
45
45
|
"plain text, set this value to `plain`.",
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
enum: Optional[conlist(Any, min_items=1, unique_items=True)] = Field( # type: ignore
|
|
48
|
+
enum: Optional[conlist(Any, min_items=1, unique_items=True)] = Field( # type: ignore
|
|
49
49
|
None,
|
|
50
50
|
description="A list of valid options for this field. If you provide this field,"
|
|
51
51
|
"the settings UI will render a dropdown menu.",
|
|
@@ -14,18 +14,18 @@ from ._widgets import WidgetContribution
|
|
|
14
14
|
from ._writers import WriterContribution
|
|
15
15
|
|
|
16
16
|
__all__ = [
|
|
17
|
-
"ContributionPoints",
|
|
18
17
|
"CommandContribution",
|
|
18
|
+
"ContributionPoints",
|
|
19
19
|
"KeyBindingContribution",
|
|
20
20
|
"MenuItem",
|
|
21
21
|
"ReaderContribution",
|
|
22
22
|
"SampleDataContribution",
|
|
23
|
+
"SampleDataGenerator",
|
|
24
|
+
"SampleDataURI",
|
|
23
25
|
"SubmenuContribution",
|
|
24
26
|
"ThemeContribution",
|
|
25
27
|
"WidgetContribution",
|
|
26
28
|
"WriterContribution",
|
|
27
|
-
"SampleDataGenerator",
|
|
28
|
-
"SampleDataURI",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
|
|
@@ -21,10 +21,10 @@ else:
|
|
|
21
21
|
ValidationError = Exception
|
|
22
22
|
|
|
23
23
|
__all__ = [
|
|
24
|
-
"ValidationError",
|
|
25
24
|
"Draft04JsonSchema",
|
|
26
25
|
"Draft06JsonSchema",
|
|
27
26
|
"Draft07JsonSchema",
|
|
27
|
+
"ValidationError",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
JsonType = Literal["array", "boolean", "integer", "null", "number", "object", "string"]
|
|
@@ -110,7 +110,7 @@ class _JsonSchemaBase(BaseModel):
|
|
|
110
110
|
unique_items: bool = Field(False)
|
|
111
111
|
max_properties: Optional[int] = Field(None, ge=0)
|
|
112
112
|
min_properties: Optional[int] = Field(0, ge=0)
|
|
113
|
-
enum: Optional[conlist(Any, min_items=1, unique_items=True)] = Field(None) # type: ignore
|
|
113
|
+
enum: Optional[conlist(Any, min_items=1, unique_items=True)] = Field(None) # type: ignore
|
|
114
114
|
type: Union[JsonType, JsonTypeArray] = Field(None) # type: ignore
|
|
115
115
|
format: Optional[str] = Field(None)
|
|
116
116
|
|
|
@@ -169,14 +169,13 @@ class _JsonSchemaBase(BaseModel):
|
|
|
169
169
|
@property
|
|
170
170
|
def is_object(self) -> bool:
|
|
171
171
|
"""Return True if this schema is an object schema."""
|
|
172
|
-
return (
|
|
173
|
-
self.
|
|
174
|
-
or self.type == "object"
|
|
172
|
+
return self.properties is not None or (
|
|
173
|
+
self.type == "object"
|
|
175
174
|
and not self.all_of
|
|
176
175
|
and not self.one_of
|
|
177
176
|
and not self.any_of
|
|
178
|
-
and not getattr(self, "ref", False)
|
|
179
|
-
)
|
|
177
|
+
and not getattr(self, "ref", False)
|
|
178
|
+
) # draft 6+
|
|
180
179
|
|
|
181
180
|
@property
|
|
182
181
|
def json_validator(self) -> Type[Validator]:
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from functools import wraps
|
|
2
|
-
from typing import List, Optional
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
3
3
|
|
|
4
4
|
from npe2._pydantic_compat import Extra, Field
|
|
5
5
|
from npe2.manifest.utils import Executable, v2_to_v1
|
|
6
6
|
from npe2.types import ReaderFunction
|
|
7
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from npe2._command_registry import CommandRegistry
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class ReaderContribution(Executable[Optional[ReaderFunction]]):
|
|
10
13
|
"""Contribute a file reader.
|
|
@@ -36,7 +39,12 @@ class ReaderContribution(Executable[Optional[ReaderFunction]]):
|
|
|
36
39
|
(self.command, tuple(self.filename_patterns), self.accepts_directories)
|
|
37
40
|
)
|
|
38
41
|
|
|
39
|
-
def exec(
|
|
42
|
+
def exec(
|
|
43
|
+
self,
|
|
44
|
+
args: tuple = (),
|
|
45
|
+
kwargs: Optional[dict] = None,
|
|
46
|
+
_registry: Optional["CommandRegistry"] = None,
|
|
47
|
+
):
|
|
40
48
|
"""
|
|
41
49
|
We are trying to simplify internal npe2 logic to always deal with a
|
|
42
50
|
(list[str], bool) pair instead of Union[PathLike, Seq[Pathlike]]. We
|
|
@@ -44,11 +52,13 @@ class ReaderContribution(Executable[Optional[ReaderFunction]]):
|
|
|
44
52
|
on we could add a "if manifest.version == 2" or similar to not have this
|
|
45
53
|
backward-compatibility logic for new plugins.
|
|
46
54
|
"""
|
|
55
|
+
if kwargs is None: # pragma: no cover
|
|
56
|
+
kwargs = {}
|
|
47
57
|
kwargs = kwargs.copy()
|
|
48
58
|
stack = kwargs.pop("stack", None)
|
|
49
59
|
assert stack is not None
|
|
50
60
|
kwargs["path"] = v2_to_v1(kwargs["path"], stack)
|
|
51
|
-
callable_ = super().exec(kwargs=kwargs)
|
|
61
|
+
callable_ = super().exec(args=args, kwargs=kwargs, _registry=_registry)
|
|
52
62
|
|
|
53
63
|
if callable_ is None: # pragma: no cover
|
|
54
64
|
return None
|
|
@@ -25,8 +25,7 @@ class _SampleDataContribution(GenericModel, ABC):
|
|
|
25
25
|
@abstractmethod
|
|
26
26
|
def open(
|
|
27
27
|
self, *args, _registry: Optional["CommandRegistry"] = None, **kwargs
|
|
28
|
-
) -> List[LayerData]:
|
|
29
|
-
...
|
|
28
|
+
) -> List[LayerData]: ...
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class SampleDataGenerator(_SampleDataContribution, Executable[List[LayerData]]):
|
npe2/manifest/schema.py
CHANGED
|
@@ -29,7 +29,7 @@ from .utils import Executable, Version
|
|
|
29
29
|
logger = getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
SCHEMA_VERSION = "0.2.
|
|
32
|
+
SCHEMA_VERSION = "0.2.1"
|
|
33
33
|
ENTRY_POINT = "napari.manifest"
|
|
34
34
|
NPE1_ENTRY_POINT = "napari.plugin"
|
|
35
35
|
|
|
@@ -55,6 +55,8 @@ class Category(str, Enum):
|
|
|
55
55
|
# Tools that extract measurements (i.e. into tabular, graph, or other data formats),
|
|
56
56
|
# such as region properties, etc...
|
|
57
57
|
Measurement = "Measurement"
|
|
58
|
+
# tools that allow registration/alignment between different layers/datasets
|
|
59
|
+
Registration = "Registration"
|
|
58
60
|
# tools that identify objects and/or boundaries in datasets
|
|
59
61
|
# (including, but not limited to, images)
|
|
60
62
|
Segmentation = "Segmentation"
|
|
@@ -385,7 +387,8 @@ class PluginManifest(ImportExportModel):
|
|
|
385
387
|
entry_point: metadata.EntryPoint,
|
|
386
388
|
distribution: Optional[metadata.Distribution] = None,
|
|
387
389
|
) -> PluginManifest:
|
|
388
|
-
|
|
390
|
+
match = entry_point.pattern.match(entry_point.value)
|
|
391
|
+
assert match is not None
|
|
389
392
|
module = match.group("module")
|
|
390
393
|
|
|
391
394
|
spec = util.find_spec(module or "")
|
|
@@ -395,7 +398,8 @@ class PluginManifest(ImportExportModel):
|
|
|
395
398
|
f"entrypoint: {entry_point.value!r}"
|
|
396
399
|
)
|
|
397
400
|
|
|
398
|
-
|
|
401
|
+
match = entry_point.pattern.match(entry_point.value)
|
|
402
|
+
assert match is not None
|
|
399
403
|
fname = match.group("attr")
|
|
400
404
|
|
|
401
405
|
for loc in spec.submodule_search_locations or []:
|
npe2/manifest/utils.py
CHANGED
|
@@ -81,7 +81,13 @@ class Executable(GenericModel, Generic[R]):
|
|
|
81
81
|
|
|
82
82
|
# look for a package name in the command id
|
|
83
83
|
dists = sorted(
|
|
84
|
-
(
|
|
84
|
+
(
|
|
85
|
+
d.metadata["Name"]
|
|
86
|
+
for d in distributions()
|
|
87
|
+
if d.metadata["Name"] is not None
|
|
88
|
+
),
|
|
89
|
+
key=len,
|
|
90
|
+
reverse=True,
|
|
85
91
|
)
|
|
86
92
|
for name in dists:
|
|
87
93
|
if self.command.startswith(name): # pragma: no cover
|
npe2/plugin_manager.py
CHANGED
|
@@ -102,7 +102,7 @@ def iter_themes() -> Iterator[contributions.ThemeContribution]:
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def iter_compatible_readers(
|
|
105
|
-
path: Union[PathLike, Sequence[str]]
|
|
105
|
+
path: Union[PathLike, Sequence[str]],
|
|
106
106
|
) -> Iterator[contributions.ReaderContribution]:
|
|
107
107
|
"""Iterate over ReaderContributions compatible with `path`."""
|
|
108
108
|
|
|
@@ -129,6 +129,10 @@ def get_writer(
|
|
|
129
129
|
"""Get Writer contribution appropriate for `path`, and `layer_types`."""
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
def get_shimmed_plugins() -> List[str]:
|
|
133
|
+
"""Return a list of all shimmed plugin names."""
|
|
134
|
+
|
|
135
|
+
|
|
132
136
|
def _populate_module():
|
|
133
137
|
"""Convert all functions in this module into global plugin manager methods."""
|
|
134
138
|
import functools
|
npe2/types.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
1
2
|
from typing import (
|
|
2
3
|
TYPE_CHECKING,
|
|
3
4
|
Callable,
|
|
4
|
-
Dict,
|
|
5
5
|
List,
|
|
6
6
|
Literal,
|
|
7
7
|
NewType,
|
|
@@ -30,25 +30,21 @@ PythonName = NewType("PythonName", str)
|
|
|
30
30
|
|
|
31
31
|
class ArrayLike(Protocol):
|
|
32
32
|
@property
|
|
33
|
-
def shape(self) -> Tuple[int, ...]:
|
|
34
|
-
...
|
|
33
|
+
def shape(self) -> Tuple[int, ...]: ...
|
|
35
34
|
|
|
36
35
|
@property
|
|
37
|
-
def ndim(self) -> int:
|
|
38
|
-
...
|
|
36
|
+
def ndim(self) -> int: ...
|
|
39
37
|
|
|
40
38
|
@property
|
|
41
|
-
def dtype(self) -> "np.dtype":
|
|
42
|
-
...
|
|
39
|
+
def dtype(self) -> "np.dtype": ...
|
|
43
40
|
|
|
44
|
-
def __array__(self) -> "np.ndarray":
|
|
45
|
-
... # pragma: no cover
|
|
41
|
+
def __array__(self) -> "np.ndarray": ... # pragma: no cover
|
|
46
42
|
|
|
47
43
|
|
|
48
44
|
LayerName = Literal[
|
|
49
45
|
"graph", "image", "labels", "points", "shapes", "surface", "tracks", "vectors"
|
|
50
46
|
]
|
|
51
|
-
Metadata =
|
|
47
|
+
Metadata = Mapping
|
|
52
48
|
DataType = Union[ArrayLike, Sequence[ArrayLike]]
|
|
53
49
|
FullLayerData = Tuple[DataType, Metadata, LayerName]
|
|
54
50
|
LayerData = Union[Tuple[DataType], Tuple[DataType, Metadata], FullLayerData]
|
|
@@ -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).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
npe2/__init__.py,sha256=IE86nFLHzFypLagLE3lFmieFQINGq-5pDH7IAe39lBU,812
|
|
2
|
+
npe2/__main__.py,sha256=NwbyYIU4bE1YPADH6zxLF3i7_51oELpK1UXz6141iwE,65
|
|
3
|
+
npe2/_command_registry.py,sha256=-L2QLeikqcFalIk-tZgqkgEGjHUCZpm5cIGP0TxzoN8,5056
|
|
4
|
+
npe2/_dynamic_plugin.py,sha256=d-5BcoycRIT7Cnf7ZDfQeiRBv33Ov9mBoH3U_bF8A8g,10024
|
|
5
|
+
npe2/_plugin_manager.py,sha256=0sPujjzJqdx03e-BuXlnMv652OtO18-xyPuJAlva320,30694
|
|
6
|
+
npe2/_pydantic_compat.py,sha256=S7snBijrnYsIovWmlmiZ91bbQNmbLuftr8CUcDnek20,1152
|
|
7
|
+
npe2/_pytest_plugin.py,sha256=Dw73_eHFoBT_mivmFzr73ZGwBe1wwoSb790Mk1QlKn8,3610
|
|
8
|
+
npe2/_setuptools_plugin.py,sha256=VefF1gMcnRxA0yp8tSYkcoB4mkefaf7NST5f0PWHm-M,6786
|
|
9
|
+
npe2/cli.py,sha256=hPsDOjOInm9Fr5wd2GYHAnRWA-DpxqaLj8D9-ucL1S0,15401
|
|
10
|
+
npe2/implements.py,sha256=RmzTS42dCNPLfCKK29rXlLBQ0Gj7gz7A_ewbxKR_2X0,4023
|
|
11
|
+
npe2/implements.pyi,sha256=KcATek_BMxZGGlpd1yBgwBSFkeeJoJhX8S80GuprYRw,1392
|
|
12
|
+
npe2/io_utils.py,sha256=H2EtIPJX5NCstA5zrmArqHwAHPdZy36h_ZVXr3nKDNE,10058
|
|
13
|
+
npe2/plugin_manager.py,sha256=e6egPx3H4tlyJ1fMJhske9uKCt1BdupAR6S-EBx1Afg,4466
|
|
14
|
+
npe2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
npe2/types.py,sha256=3CCIQ84yTwUbc8rHBfvpMGgv7Fg968MFX-brXbbhk1I,2216
|
|
16
|
+
npe2/_inspection/__init__.py,sha256=Z23w_mB3aHP1AZlLYDB4OMHjgXQMuBAPzOIYEd1HuFk,295
|
|
17
|
+
npe2/_inspection/_compile.py,sha256=sIjcpyfgaETHrdrAB_jPyLHd2HxOuWdnKkDotXuOTk8,3941
|
|
18
|
+
npe2/_inspection/_fetch.py,sha256=cR0DJBIq1JYRVkQI297yqO_G3qitaCdRvG6-QbQDW24,12792
|
|
19
|
+
npe2/_inspection/_from_npe1.py,sha256=WP4mvMouGxRr9KlDmcQNukzS6ScT3izF0WlAN1g91o8,23607
|
|
20
|
+
npe2/_inspection/_setuputils.py,sha256=EUnNq7rAJ4nVnMyaGztCufbT1JDxcJK3G9o3omqmd5U,4575
|
|
21
|
+
npe2/_inspection/_visitors.py,sha256=XqjCR2VqgOlFKBw2SvJJfWhUCEqofSuIxZ8ecLyi7k0,19738
|
|
22
|
+
npe2/manifest/__init__.py,sha256=sfgHfeC2AKoewwRgnAqX84X0U-ThDHxmt0Olidnmalo,159
|
|
23
|
+
npe2/manifest/_bases.py,sha256=Fs_juL_8sS3vR6_FdQ_O5RESiTEUER7BQI0C8ZnKOC0,3649
|
|
24
|
+
npe2/manifest/_npe1_adapter.py,sha256=siztkuFQ4QhS6LkLv7_7-a3B6hS2KuAa_JhK4lfAbyw,5212
|
|
25
|
+
npe2/manifest/_package_metadata.py,sha256=NQpEoq50vPK1ULVZnGQVJ5Ns9TUZEwtctQV0s_kpp78,9241
|
|
26
|
+
npe2/manifest/_validators.py,sha256=v_GaW2DE1A7x3J-VA6mIWMLXO-AijLvZgf4-kcpQfWI,2875
|
|
27
|
+
npe2/manifest/menus.py,sha256=SMJyp1wI45ub8hdZUKqQi9SUZ3GpFlarHsOJ1ZwAMbs,181
|
|
28
|
+
npe2/manifest/package_metadata.py,sha256=sRMPUhpfx-3nn5i3c0BKlbw7KR8AzgIo-ijKq0udXUU,191
|
|
29
|
+
npe2/manifest/schema.py,sha256=iUlnhfMI7JqgCzEVUqU5olIn7rgn2EI2Q30SGi9ug7A,20285
|
|
30
|
+
npe2/manifest/utils.py,sha256=fuHCy573ZyJH4GFi_KdOUnJA0xRDY0qiN1dldP_aTTE,12355
|
|
31
|
+
npe2/manifest/contributions/__init__.py,sha256=b5eNqUL5tskS6gVwh_zalBCsPxX6HO1R1tNvG9A3m68,1003
|
|
32
|
+
npe2/manifest/contributions/_commands.py,sha256=f7x1yDoHS2GBAjQaFGqIcsqNC1avqtr6o4w_RZcHexs,4329
|
|
33
|
+
npe2/manifest/contributions/_configuration.py,sha256=i8DPBUfpJZ4Y_Hug1E1FGCsLivd6SpZZmGZ_80WdFeg,6516
|
|
34
|
+
npe2/manifest/contributions/_contributions.py,sha256=fmNSbzZ6VEjVdIAwfmsowRp_EFbKXfd7KGLM7muZkcc,2175
|
|
35
|
+
npe2/manifest/contributions/_icon.py,sha256=NXeAPy1qoPLcFBHpQK_7j4pqxHGYbZ0gb3yCXr47CLM,161
|
|
36
|
+
npe2/manifest/contributions/_json_schema.py,sha256=rIERaRMzVJzx5YPkdnGUyQDrfK3PN-eLM-gOOumFyHA,10953
|
|
37
|
+
npe2/manifest/contributions/_keybindings.py,sha256=E_mrSzngXEGuyIfytPLOc0WL7Bb1Zebazdt1Nw6ZhD0,962
|
|
38
|
+
npe2/manifest/contributions/_menus.py,sha256=FAkXdpSGHdDqjA4yr2Oxb62x7POXJY7ETAcs23AJTFY,2233
|
|
39
|
+
npe2/manifest/contributions/_readers.py,sha256=XziSWpJrrbS_CJ6bvPhGH4vwKZq5in8kls07j1TAdXw,2513
|
|
40
|
+
npe2/manifest/contributions/_sample_data.py,sha256=4sm0QdChvJ8RsU6ClhsXJOhNZVPP5iYwMmZp8wrQ9Gg,2330
|
|
41
|
+
npe2/manifest/contributions/_submenu.py,sha256=pjHZbfn5iCQBwNaTIGMWHNNswtSPLlnmlWpOY67BFgw,666
|
|
42
|
+
npe2/manifest/contributions/_themes.py,sha256=FeHBeqayZOIN4ccyik1xPZ30cE-_3Xr7aozilyoamiY,2683
|
|
43
|
+
npe2/manifest/contributions/_widgets.py,sha256=ung4t6R4EB6Rz9Xmlb4Z46ilOHHmkoWkEMuHQC4kABw,2217
|
|
44
|
+
npe2/manifest/contributions/_writers.py,sha256=i1usy_tR_1XIf93c8HXu0APLJI3QrOJUc0-0nxLrLbI,7867
|
|
45
|
+
npe2-0.7.8rc0.dist-info/METADATA,sha256=NcMdnVwFIQ6IO3N58FftNLPTJuMPBzJIi_MmCjfAQ_8,5968
|
|
46
|
+
npe2-0.7.8rc0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
47
|
+
npe2-0.7.8rc0.dist-info/entry_points.txt,sha256=5zoegMudO8Hkn6589GlAXIW49Ar5HSWZjTRc0TkJu3s,250
|
|
48
|
+
npe2-0.7.8rc0.dist-info/licenses/LICENSE,sha256=uyX2Y2Q7D-WzOUc6EKZVDuadkWenBWSYTR--BsewGpg,1514
|
|
49
|
+
npe2-0.7.8rc0.dist-info/RECORD,,
|
npe2-0.7.7rc0.dist-info/METADATA
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: npe2
|
|
3
|
-
Version: 0.7.7rc0
|
|
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
|
|
54
|
-
|
|
55
|
-
[](https://github.com/napari/npe2/actions/workflows/ci.yml)
|
|
56
|
-
[](https://codecov.io/gh/napari/npe2)
|
|
57
|
-
|
|
58
|
-
napari plugin refactor
|
|
59
|
-
|
|
60
|
-
see also https://github.com/napari/napari/issues/3115
|
|
61
|
-
|
|
62
|
-
## Documentation
|
|
63
|
-
|
|
64
|
-
For documentation on authoring npe2 plugins, see the [napari plugin docs](https://napari.org/plugins/index.html). These include:
|
|
65
|
-
- the [manifest reference](https://napari.org/plugins/manifest.html)
|
|
66
|
-
- the [contribution guide](https://napari.org/plugins/contributions.html)
|
|
67
|
-
|
|
68
|
-
## Command line tool
|
|
69
|
-
|
|
70
|
-
Includes a command line tool `npe2` with the following commands:
|
|
71
|
-
```bash
|
|
72
|
-
Commands:
|
|
73
|
-
cache Cache utils
|
|
74
|
-
convert Convert first generation napari plugin to new (manifest) format.
|
|
75
|
-
parse Show parsed manifest as yaml
|
|
76
|
-
validate Validate manifest for a distribution name or manifest filepath.
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
examples:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
# convert current directory to an npe2-ready plugin
|
|
83
|
-
# (note: the repo must also be installed and importable in the current environment)
|
|
84
|
-
npe2 convert .
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
npe2 validate your-plugin-package
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
npe2 parse your-plugin-package
|
|
93
|
-
```
|
npe2-0.7.7rc0.dist-info/RECORD
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
npe2/__init__.py,sha256=2yMcClmts4aO-1qQR80J_XOcx_fiXXhPHxx6BYlvG3s,812
|
|
2
|
-
npe2/__main__.py,sha256=NwbyYIU4bE1YPADH6zxLF3i7_51oELpK1UXz6141iwE,65
|
|
3
|
-
npe2/_command_registry.py,sha256=-L2QLeikqcFalIk-tZgqkgEGjHUCZpm5cIGP0TxzoN8,5056
|
|
4
|
-
npe2/_dynamic_plugin.py,sha256=Y_kduq8YlyF9QrNVhGbMihlaBqeoww1HXvg456jpaww,10040
|
|
5
|
-
npe2/_plugin_manager.py,sha256=vhG_4988gXv2AVMWVw6_RDo15BYgjqau0izyJuZWdmI,30135
|
|
6
|
-
npe2/_pydantic_compat.py,sha256=RpUWS_pVOAx2pbNgZvdxsZtXxR5igZA2jsSQzTWtYNU,1152
|
|
7
|
-
npe2/_pytest_plugin.py,sha256=Dw73_eHFoBT_mivmFzr73ZGwBe1wwoSb790Mk1QlKn8,3610
|
|
8
|
-
npe2/_setuptools_plugin.py,sha256=85tFOTmGAMeMDcPYmT3HvadPsQSAKLrAt63BhYmJQxk,6785
|
|
9
|
-
npe2/cli.py,sha256=Hrivym4bfXGoGFTiEjQgkwCxzGkNZ_AJwwsbtjEcXww,16066
|
|
10
|
-
npe2/implements.py,sha256=RmzTS42dCNPLfCKK29rXlLBQ0Gj7gz7A_ewbxKR_2X0,4023
|
|
11
|
-
npe2/implements.pyi,sha256=05hd7ogvAJYPSWMirIT-C3vJWDjMxOpOEdZaHO775VY,1406
|
|
12
|
-
npe2/io_utils.py,sha256=pRxo7sBXnWH_di4QRr2TIuisBWyUj6eGErl-nr2HIYY,10042
|
|
13
|
-
npe2/plugin_manager.py,sha256=P62F2y5_4JeIW38hYdS036efRUEnuY4_vril95eRnVo,4370
|
|
14
|
-
npe2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
npe2/types.py,sha256=R674iPUyHHRkvIKX8spEM0QxZzBxpvNboTOTQ8PvdKg,2219
|
|
16
|
-
npe2/_inspection/__init__.py,sha256=UULM0fMqg2lJH1pNPWr6fKk6qDJgcH8AYy4eOZEQQSc,295
|
|
17
|
-
npe2/_inspection/_compile.py,sha256=sIjcpyfgaETHrdrAB_jPyLHd2HxOuWdnKkDotXuOTk8,3941
|
|
18
|
-
npe2/_inspection/_fetch.py,sha256=noDfdfmS6cTD8rtcuL2kV2pzZaXhtDONq78cZ984qfA,15265
|
|
19
|
-
npe2/_inspection/_from_npe1.py,sha256=743y8Uiv0U2AdOoz7kOONzsQ8hY_Syt_BU-dR1HBsh4,23595
|
|
20
|
-
npe2/_inspection/_setuputils.py,sha256=EUnNq7rAJ4nVnMyaGztCufbT1JDxcJK3G9o3omqmd5U,4575
|
|
21
|
-
npe2/_inspection/_visitors.py,sha256=XqjCR2VqgOlFKBw2SvJJfWhUCEqofSuIxZ8ecLyi7k0,19738
|
|
22
|
-
npe2/manifest/__init__.py,sha256=EhoTgJSOW44enLBVPq_d0dM7FgTsKICPhs7GnQZu1MM,159
|
|
23
|
-
npe2/manifest/_bases.py,sha256=Fs_juL_8sS3vR6_FdQ_O5RESiTEUER7BQI0C8ZnKOC0,3649
|
|
24
|
-
npe2/manifest/_npe1_adapter.py,sha256=siztkuFQ4QhS6LkLv7_7-a3B6hS2KuAa_JhK4lfAbyw,5212
|
|
25
|
-
npe2/manifest/_package_metadata.py,sha256=NQpEoq50vPK1ULVZnGQVJ5Ns9TUZEwtctQV0s_kpp78,9241
|
|
26
|
-
npe2/manifest/_validators.py,sha256=v_GaW2DE1A7x3J-VA6mIWMLXO-AijLvZgf4-kcpQfWI,2875
|
|
27
|
-
npe2/manifest/menus.py,sha256=SMJyp1wI45ub8hdZUKqQi9SUZ3GpFlarHsOJ1ZwAMbs,181
|
|
28
|
-
npe2/manifest/package_metadata.py,sha256=sRMPUhpfx-3nn5i3c0BKlbw7KR8AzgIo-ijKq0udXUU,191
|
|
29
|
-
npe2/manifest/schema.py,sha256=d__idMPHTe1zXbyZx65hNThMpR927d0SnIc7sp2c5EM,20125
|
|
30
|
-
npe2/manifest/utils.py,sha256=_vlw-gamtsu7LTTvneZ3CrNopC_GiCkgiFyi8-PFnmY,12210
|
|
31
|
-
npe2/manifest/contributions/__init__.py,sha256=b5eNqUL5tskS6gVwh_zalBCsPxX6HO1R1tNvG9A3m68,1003
|
|
32
|
-
npe2/manifest/contributions/_commands.py,sha256=f7x1yDoHS2GBAjQaFGqIcsqNC1avqtr6o4w_RZcHexs,4329
|
|
33
|
-
npe2/manifest/contributions/_configuration.py,sha256=eaVTf_P-kXMKSeAe8gNMU021sit5dXMzLjguolLGSIc,6529
|
|
34
|
-
npe2/manifest/contributions/_contributions.py,sha256=C0C_erA0vOPqm2gGSzP8mJx-GnW3Rgi8xvZObYf1Pwc,2175
|
|
35
|
-
npe2/manifest/contributions/_icon.py,sha256=NXeAPy1qoPLcFBHpQK_7j4pqxHGYbZ0gb3yCXr47CLM,161
|
|
36
|
-
npe2/manifest/contributions/_json_schema.py,sha256=yPaWRNaAl2bK-_5L7-cUSh2k900GtCvJfXfvyiqGmVQ,10973
|
|
37
|
-
npe2/manifest/contributions/_keybindings.py,sha256=E_mrSzngXEGuyIfytPLOc0WL7Bb1Zebazdt1Nw6ZhD0,962
|
|
38
|
-
npe2/manifest/contributions/_menus.py,sha256=FAkXdpSGHdDqjA4yr2Oxb62x7POXJY7ETAcs23AJTFY,2233
|
|
39
|
-
npe2/manifest/contributions/_readers.py,sha256=7B_hxWzCOm1TXX1lvmByf1Hzu5uCx6b-Ec1CGUbkXnw,2197
|
|
40
|
-
npe2/manifest/contributions/_sample_data.py,sha256=gGpqKZxLphJqqwCoVXfXuZw45QbULo3gGtaCqQCxIHk,2338
|
|
41
|
-
npe2/manifest/contributions/_submenu.py,sha256=pjHZbfn5iCQBwNaTIGMWHNNswtSPLlnmlWpOY67BFgw,666
|
|
42
|
-
npe2/manifest/contributions/_themes.py,sha256=FeHBeqayZOIN4ccyik1xPZ30cE-_3Xr7aozilyoamiY,2683
|
|
43
|
-
npe2/manifest/contributions/_widgets.py,sha256=ung4t6R4EB6Rz9Xmlb4Z46ilOHHmkoWkEMuHQC4kABw,2217
|
|
44
|
-
npe2/manifest/contributions/_writers.py,sha256=i1usy_tR_1XIf93c8HXu0APLJI3QrOJUc0-0nxLrLbI,7867
|
|
45
|
-
npe2-0.7.7rc0.dist-info/METADATA,sha256=rABTW9rv-FYrQyt8umBTNcb3x0tsH47JsSqcEsA0MTo,3098
|
|
46
|
-
npe2-0.7.7rc0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
47
|
-
npe2-0.7.7rc0.dist-info/entry_points.txt,sha256=5zoegMudO8Hkn6589GlAXIW49Ar5HSWZjTRc0TkJu3s,250
|
|
48
|
-
npe2-0.7.7rc0.dist-info/licenses/LICENSE,sha256=uyX2Y2Q7D-WzOUc6EKZVDuadkWenBWSYTR--BsewGpg,1514
|
|
49
|
-
npe2-0.7.7rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|