npe2 0.7.8rc0__py3-none-any.whl → 0.7.9rc0__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.
@@ -160,9 +160,47 @@ def _get_manifest_from_zip_url(url: str) -> PluginManifest:
160
160
  --------
161
161
  $ npe2 fetch https://github.com/org/project/archive/refs/heads/master.zip
162
162
  """
163
+ from npe2.manifest import PluginManifest
164
+
165
+ def find_manifest_file(root: Path) -> Optional[Path]:
166
+ """Recursively find a napari manifest file."""
167
+ # Check current directory for manifest files
168
+ for filename in ["napari.yaml", "napari.yml"]:
169
+ manifest_path = root / filename
170
+ if manifest_path.exists():
171
+ return manifest_path
172
+
173
+ # Check for pyproject.toml with napari config
174
+ pyproject_path = root / "pyproject.toml"
175
+ if pyproject_path.exists():
176
+ try:
177
+ import tomllib
178
+ except ImportError:
179
+ import tomli as tomllib # type: ignore
180
+
181
+ with open(pyproject_path, "rb") as f:
182
+ data = tomllib.load(f)
183
+ if "tool" in data and "napari" in data["tool"]:
184
+ return pyproject_path
185
+
186
+ # Recursively search subdirectories for the manifest file
187
+ for item in root.iterdir():
188
+ if item.is_dir() and not item.name.startswith("."):
189
+ result = find_manifest_file(item)
190
+ if result:
191
+ return result
192
+ return None
193
+
163
194
  with _tmp_zip_download(url) as zip_path:
164
- src_dir = next(Path(zip_path).iterdir()) # find first directory
165
- return _build_src_and_extract_manifest(src_dir)
195
+ # In a zip file, we do not need to build a wheel. We can extract
196
+ # the manifest directly from the extracted files.
197
+ manifest_file = find_manifest_file(Path(zip_path))
198
+ if manifest_file:
199
+ return PluginManifest.from_file(manifest_file)
200
+ else:
201
+ # Keep original behavior to try to build a wheel as a fallback
202
+ src_dir = next(Path(zip_path).iterdir())
203
+ return _build_src_and_extract_manifest(src_dir)
166
204
 
167
205
 
168
206
  def _get_manifest_from_wheel_url(url: str) -> PluginManifest:
npe2/cli.py CHANGED
@@ -11,7 +11,11 @@ from npe2 import PluginManager, PluginManifest, __version__
11
11
  if TYPE_CHECKING:
12
12
  from rich.console import RenderableType
13
13
 
14
- app = typer.Typer(no_args_is_help=True)
14
+ app = typer.Typer(
15
+ no_args_is_help=True,
16
+ context_settings={"help_option_names": ["-h", "--help"]},
17
+ rich_markup_mode="rich",
18
+ )
15
19
 
16
20
 
17
21
  def _show_version_and_exit(value: bool) -> None:
@@ -498,4 +502,10 @@ def compile(
498
502
 
499
503
 
500
504
  def main():
505
+ import sys
506
+
507
+ # If no arguments provided, show help without error box
508
+ if len(sys.argv) == 1:
509
+ sys.argv.append("--help")
510
+
501
511
  app()
npe2/io_utils.py CHANGED
@@ -319,5 +319,5 @@ def _write(
319
319
 
320
320
  # napari_get_writer-style writers don't always return a list
321
321
  # though strictly speaking they should?
322
- result = [res] if isinstance(res, str) else res or [] # type: ignore
322
+ result = [res] if isinstance(res, str) else res or []
323
323
  return (result, writer) if return_writer else result
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
  from shutil import rmtree
9
9
  from typing import List, Sequence
10
10
 
11
- from appdirs import user_cache_dir
11
+ from platformdirs import user_cache_dir
12
12
 
13
13
  from npe2._inspection._from_npe1 import manifest_from_npe1
14
14
  from npe2.manifest import PackageMetadata
@@ -36,8 +36,15 @@ class ContributionPoints(BaseModel):
36
36
  widgets: Optional[List[WidgetContribution]]
37
37
  sample_data: Optional[List[SampleDataContribution]]
38
38
  themes: Optional[List[ThemeContribution]]
39
- menus: Dict[str, List[MenuItem]] = Field(default_factory=dict, hide_docs=True)
40
- submenus: Optional[List[SubmenuContribution]] = Field(None, hide_docs=True)
39
+ menus: Dict[str, List[MenuItem]] = Field(
40
+ default_factory=dict,
41
+ description="Add menu items to existing napari menus."
42
+ "A menu item can be a command, such as open a widget, or a submenu."
43
+ "Using menu items, nested hierarchies can be created within napari menus."
44
+ "This allows you to organize your plugin's contributions within"
45
+ "napari's menu structure.",
46
+ )
47
+ submenus: Optional[List[SubmenuContribution]]
41
48
  keybindings: Optional[List[KeyBindingContribution]] = Field(None, hide_docs=True)
42
49
 
43
50
  configuration: List[ConfigurationContribution] = Field(
@@ -32,7 +32,9 @@ class SampleDataGenerator(_SampleDataContribution, Executable[List[LayerData]]):
32
32
  """Contribute a callable command that creates data on demand."""
33
33
 
34
34
  command: str = Field(
35
- ..., description="Identifier of a command that returns layer data tuple."
35
+ ...,
36
+ description="Identifier of a command that returns layer data tuple. "
37
+ "Note that this command cannot return `[(None,)]`.",
36
38
  )
37
39
 
38
40
  def open(
@@ -6,6 +6,13 @@ from ._icon import Icon
6
6
 
7
7
 
8
8
  class SubmenuContribution(BaseModel):
9
+ """Contributes a submenu that can contain menu items or other submenus.
10
+
11
+ Submenus allow you to organize menu items into hierarchical structures.
12
+ Each submenu defines an id, label, and optional icon that can be
13
+ referenced by menu items to create nested menu structures.
14
+ """
15
+
9
16
  id: str = Field(description="Identifier of the menu to display as a submenu.")
10
17
  label: str = Field(
11
18
  description="The label of the menu item which leads to this submenu."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: npe2
3
- Version: 0.7.8rc0
3
+ Version: 0.7.9rc0
4
4
  Summary: napari plugin engine v2
5
5
  Project-URL: homepage, https://github.com/napari/npe2
6
6
  Project-URL: repository, https://github.com/napari/npe2
@@ -19,8 +19,8 @@ Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.8
22
- Requires-Dist: appdirs
23
22
  Requires-Dist: build>=1
23
+ Requires-Dist: platformdirs
24
24
  Requires-Dist: psygnal>=0.3.0
25
25
  Requires-Dist: pydantic
26
26
  Requires-Dist: pyyaml
@@ -6,22 +6,22 @@ npe2/_plugin_manager.py,sha256=0sPujjzJqdx03e-BuXlnMv652OtO18-xyPuJAlva320,30694
6
6
  npe2/_pydantic_compat.py,sha256=S7snBijrnYsIovWmlmiZ91bbQNmbLuftr8CUcDnek20,1152
7
7
  npe2/_pytest_plugin.py,sha256=Dw73_eHFoBT_mivmFzr73ZGwBe1wwoSb790Mk1QlKn8,3610
8
8
  npe2/_setuptools_plugin.py,sha256=VefF1gMcnRxA0yp8tSYkcoB4mkefaf7NST5f0PWHm-M,6786
9
- npe2/cli.py,sha256=hPsDOjOInm9Fr5wd2GYHAnRWA-DpxqaLj8D9-ucL1S0,15401
9
+ npe2/cli.py,sha256=Q3SsDydPHzxxpGAm23JxBHQnp2yo4ZVYR-ug3hHU3dk,15637
10
10
  npe2/implements.py,sha256=RmzTS42dCNPLfCKK29rXlLBQ0Gj7gz7A_ewbxKR_2X0,4023
11
11
  npe2/implements.pyi,sha256=KcATek_BMxZGGlpd1yBgwBSFkeeJoJhX8S80GuprYRw,1392
12
- npe2/io_utils.py,sha256=H2EtIPJX5NCstA5zrmArqHwAHPdZy36h_ZVXr3nKDNE,10058
12
+ npe2/io_utils.py,sha256=0jnCHWjHSpVeKEXDmsvBXywchxdx2r8JEWIImGB6wBc,10042
13
13
  npe2/plugin_manager.py,sha256=e6egPx3H4tlyJ1fMJhske9uKCt1BdupAR6S-EBx1Afg,4466
14
14
  npe2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  npe2/types.py,sha256=3CCIQ84yTwUbc8rHBfvpMGgv7Fg968MFX-brXbbhk1I,2216
16
16
  npe2/_inspection/__init__.py,sha256=Z23w_mB3aHP1AZlLYDB4OMHjgXQMuBAPzOIYEd1HuFk,295
17
17
  npe2/_inspection/_compile.py,sha256=sIjcpyfgaETHrdrAB_jPyLHd2HxOuWdnKkDotXuOTk8,3941
18
- npe2/_inspection/_fetch.py,sha256=cR0DJBIq1JYRVkQI297yqO_G3qitaCdRvG6-QbQDW24,12792
18
+ npe2/_inspection/_fetch.py,sha256=WA1kp8KrUThxXpI4GcKwC8l7jR-1X7bJzIalbB20Vgk,14298
19
19
  npe2/_inspection/_from_npe1.py,sha256=WP4mvMouGxRr9KlDmcQNukzS6ScT3izF0WlAN1g91o8,23607
20
20
  npe2/_inspection/_setuputils.py,sha256=EUnNq7rAJ4nVnMyaGztCufbT1JDxcJK3G9o3omqmd5U,4575
21
21
  npe2/_inspection/_visitors.py,sha256=XqjCR2VqgOlFKBw2SvJJfWhUCEqofSuIxZ8ecLyi7k0,19738
22
22
  npe2/manifest/__init__.py,sha256=sfgHfeC2AKoewwRgnAqX84X0U-ThDHxmt0Olidnmalo,159
23
23
  npe2/manifest/_bases.py,sha256=Fs_juL_8sS3vR6_FdQ_O5RESiTEUER7BQI0C8ZnKOC0,3649
24
- npe2/manifest/_npe1_adapter.py,sha256=siztkuFQ4QhS6LkLv7_7-a3B6hS2KuAa_JhK4lfAbyw,5212
24
+ npe2/manifest/_npe1_adapter.py,sha256=6ZLGSSy-U6oEQekXeI2l-GXTJC3DuWTlFownbI5e8mo,5217
25
25
  npe2/manifest/_package_metadata.py,sha256=NQpEoq50vPK1ULVZnGQVJ5Ns9TUZEwtctQV0s_kpp78,9241
26
26
  npe2/manifest/_validators.py,sha256=v_GaW2DE1A7x3J-VA6mIWMLXO-AijLvZgf4-kcpQfWI,2875
27
27
  npe2/manifest/menus.py,sha256=SMJyp1wI45ub8hdZUKqQi9SUZ3GpFlarHsOJ1ZwAMbs,181
@@ -31,19 +31,19 @@ npe2/manifest/utils.py,sha256=fuHCy573ZyJH4GFi_KdOUnJA0xRDY0qiN1dldP_aTTE,12355
31
31
  npe2/manifest/contributions/__init__.py,sha256=b5eNqUL5tskS6gVwh_zalBCsPxX6HO1R1tNvG9A3m68,1003
32
32
  npe2/manifest/contributions/_commands.py,sha256=f7x1yDoHS2GBAjQaFGqIcsqNC1avqtr6o4w_RZcHexs,4329
33
33
  npe2/manifest/contributions/_configuration.py,sha256=i8DPBUfpJZ4Y_Hug1E1FGCsLivd6SpZZmGZ_80WdFeg,6516
34
- npe2/manifest/contributions/_contributions.py,sha256=fmNSbzZ6VEjVdIAwfmsowRp_EFbKXfd7KGLM7muZkcc,2175
34
+ npe2/manifest/contributions/_contributions.py,sha256=gC9-zMnmLQrObu8YTL_4NF4szD11jsN5ms9nf7K8YD8,2476
35
35
  npe2/manifest/contributions/_icon.py,sha256=NXeAPy1qoPLcFBHpQK_7j4pqxHGYbZ0gb3yCXr47CLM,161
36
36
  npe2/manifest/contributions/_json_schema.py,sha256=rIERaRMzVJzx5YPkdnGUyQDrfK3PN-eLM-gOOumFyHA,10953
37
37
  npe2/manifest/contributions/_keybindings.py,sha256=E_mrSzngXEGuyIfytPLOc0WL7Bb1Zebazdt1Nw6ZhD0,962
38
38
  npe2/manifest/contributions/_menus.py,sha256=FAkXdpSGHdDqjA4yr2Oxb62x7POXJY7ETAcs23AJTFY,2233
39
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
40
+ npe2/manifest/contributions/_sample_data.py,sha256=KRZ2vEDhd0Qmv9HOa6e_Db9sTzLrPc2VsTsFeWQbmvw,2400
41
+ npe2/manifest/contributions/_submenu.py,sha256=jRUFdKB1iIgUfADeaHJV9rZ3_u3SfYjwfGgXelhEfXs,960
42
42
  npe2/manifest/contributions/_themes.py,sha256=FeHBeqayZOIN4ccyik1xPZ30cE-_3Xr7aozilyoamiY,2683
43
43
  npe2/manifest/contributions/_widgets.py,sha256=ung4t6R4EB6Rz9Xmlb4Z46ilOHHmkoWkEMuHQC4kABw,2217
44
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,,
45
+ npe2-0.7.9rc0.dist-info/METADATA,sha256=PSwtqFzgHbvC4UIYP33c4JrMK4-K_0dFL17K9X6l-sc,5973
46
+ npe2-0.7.9rc0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
+ npe2-0.7.9rc0.dist-info/entry_points.txt,sha256=5zoegMudO8Hkn6589GlAXIW49Ar5HSWZjTRc0TkJu3s,250
48
+ npe2-0.7.9rc0.dist-info/licenses/LICENSE,sha256=uyX2Y2Q7D-WzOUc6EKZVDuadkWenBWSYTR--BsewGpg,1514
49
+ npe2-0.7.9rc0.dist-info/RECORD,,