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 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
- "read_get_reader",
23
+ "__version__",
24
+ "fetch_manifest",
25
+ "get_manifest_from_wheel",
27
26
  "read",
28
- "write_get_writer",
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
@@ -7,7 +7,7 @@ from ._visitors import (
7
7
 
8
8
  __all__ = [
9
9
  "NPE2PluginModuleVisitor",
10
- "find_npe2_module_contributions",
11
- "find_npe1_module_contributions",
12
10
  "compile",
11
+ "find_npe1_module_contributions",
12
+ "find_npe2_module_contributions",
13
13
  ]
@@ -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, parse, request
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
- "get_pypi_plugins",
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:
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 urllib.parse.urlparse(path).scheme:
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
- str, Dict[str, Dict[str, List[MenuCommand]]]
241
- ] = defaultdict(dict)
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
- "ValidationError",
44
- "root_validator",
45
- "validator",
45
+ "GenericModel",
46
+ "ModelMetaclass",
46
47
  "PrivateAttr",
48
+ "ValidationError",
47
49
  "color",
48
50
  "conlist",
49
51
  "constr",
50
- "ModelMetaclass",
51
- "ErrorWrapper",
52
- "GenericModel",
53
- "SHAPE_LIST",
52
+ "root_validator",
53
+ "validator",
54
54
  )
@@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [tool.npe2]
8
8
  """
9
+
9
10
  from __future__ import annotations
10
11
 
11
12
  import os
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
@@ -24,7 +24,7 @@ def writer(
24
24
  id: str,
25
25
  title: str,
26
26
  layer_types: list[str],
27
- filename_extensions: list[str] = [], # noqa: B006
27
+ filename_extensions: list[str] = [],
28
28
  display_name: str = "",
29
29
  ensure_args_valid: bool = False,
30
30
  ) -> Callable[[T], T]:
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
- cast("napari.layers.Layer", x).as_layer_data_tuple()
304
- if hasattr(x, "as_layer_data_tuple")
305
- else x
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
@@ -1,4 +1,4 @@
1
1
  from ._package_metadata import PackageMetadata
2
2
  from .schema import ENTRY_POINT, PluginManifest
3
3
 
4
- __all__ = ["PackageMetadata", "PluginManifest", "ENTRY_POINT"]
4
+ __all__ = ["ENTRY_POINT", "PackageMetadata", "PluginManifest"]
@@ -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 # noqa: E501
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 # noqa
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.properties is not None
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) # draft 6+
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(self, *, kwargs):
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.0"
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
- assert (match := entry_point.pattern.match(entry_point.value))
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
- assert (match := entry_point.pattern.match(entry_point.value))
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
- (d.metadata["Name"] for d in distributions()), key=len, reverse=True
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 = Dict
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
+ [![CI](https://github.com/napari/npe2/actions/workflows/ci.yml/badge.svg)](https://github.com/napari/npe2/actions/workflows/ci.yml)
56
+ [![codecov](https://codecov.io/gh/napari/npe2/branch/main/graph/badge.svg?token=FTH635x542)](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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.25.0
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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
- [![CI](https://github.com/napari/npe2/actions/workflows/ci.yml/badge.svg)](https://github.com/napari/npe2/actions/workflows/ci.yml)
56
- [![codecov](https://codecov.io/gh/napari/npe2/branch/main/graph/badge.svg?token=FTH635x542)](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
- ```
@@ -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,,