napari-plugin-manager 0.1.5rc0__tar.gz → 0.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/.github/workflows/test_and_deploy.yml +1 -1
  2. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/PKG-INFO +1 -1
  3. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/test_npe2api.py +1 -0
  4. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/test_qt_plugin_dialog.py +5 -0
  5. napari_plugin_manager-0.1.6/src/napari_plugin_manager/_tests/test_utils.py +58 -0
  6. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_version.py +2 -2
  7. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/base_qt_package_installer.py +3 -3
  8. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/base_qt_plugin_dialog.py +4 -3
  9. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/npe2api.py +8 -3
  10. napari_plugin_manager-0.1.6/src/napari_plugin_manager/utils.py +76 -0
  11. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager.egg-info/PKG-INFO +1 -1
  12. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/tox.ini +3 -0
  13. napari_plugin_manager-0.1.5rc0/src/napari_plugin_manager/_tests/test_utils.py +0 -27
  14. napari_plugin_manager-0.1.5rc0/src/napari_plugin_manager/utils.py +0 -21
  15. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/.github/dependabot.yml +0 -0
  16. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/.github/workflows/deploy_docs.yml +0 -0
  17. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/.gitignore +0 -0
  18. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/.pre-commit-config.yaml +0 -0
  19. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/LICENSE +0 -0
  20. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/Makefile +0 -0
  21. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/README.md +0 -0
  22. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/RELEASE.md +0 -0
  23. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/__init__.py +0 -0
  24. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_static/custom.css +0 -0
  25. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_static/favicon/logo-noborder-180.png +0 -0
  26. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_static/favicon/logo-silhouette-192.png +0 -0
  27. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_static/favicon/logo-silhouette-dark-light.svg +0 -0
  28. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_templates/navbar-project.html +0 -0
  29. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/_toc.yml +0 -0
  30. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/conf.py +0 -0
  31. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/developers/contributing.md +0 -0
  32. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/docs/index.md +0 -0
  33. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/description.png +0 -0
  34. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/direct-entry.png +0 -0
  35. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/filter.png +0 -0
  36. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/import-export.png +0 -0
  37. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/install.png +0 -0
  38. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/logo.png +0 -0
  39. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/status.png +0 -0
  40. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/uninstall.png +0 -0
  41. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/images/update.png +0 -0
  42. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/pyproject.toml +0 -0
  43. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/setup.cfg +0 -0
  44. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/__init__.py +0 -0
  45. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/__init__.py +0 -0
  46. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/conftest.py +0 -0
  47. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/test_base_installer_process.py +0 -0
  48. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/_tests/test_installer_process.py +0 -0
  49. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/qt_package_installer.py +0 -0
  50. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/qt_plugin_dialog.py +0 -0
  51. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/qt_warning_dialog.py +0 -0
  52. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/qt_widgets.py +0 -0
  53. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager/styles.qss +0 -0
  54. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager.egg-info/SOURCES.txt +0 -0
  55. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager.egg-info/dependency_links.txt +0 -0
  56. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager.egg-info/requires.txt +0 -0
  57. {napari_plugin_manager-0.1.5rc0 → napari_plugin_manager-0.1.6}/src/napari_plugin_manager.egg-info/top_level.txt +0 -0
@@ -173,7 +173,7 @@ jobs:
173
173
  uses: codecov/codecov-action@v5
174
174
  with:
175
175
  fail_ci_if_error: true
176
- use_oidc: true
176
+ use_oidc: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
177
177
 
178
178
 
179
179
  deploy:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-plugin-manager
3
- Version: 0.1.5rc0
3
+ Version: 0.1.6
4
4
  Summary: Install plugins for napari, in napari.
5
5
  Author-email: napari team <napari-steering-council@googlegroups.com>
6
6
  License: BSD 3-Clause License
@@ -27,6 +27,7 @@ def test_plugin_summaries():
27
27
  "home_page",
28
28
  "pypi_versions",
29
29
  "conda_versions",
30
+ "project_url",
30
31
  ]
31
32
  try:
32
33
  data = plugin_summaries()
@@ -467,6 +467,11 @@ def test_drop_event(plugin_dialog, tmp_path):
467
467
  assert plugin_dialog.direct_entry_edit.text() == str(path_1)
468
468
 
469
469
 
470
+ @pytest.mark.skipif(
471
+ 'napari_latest' in os.getenv('TOX_ENV_NAME')
472
+ and 'PySide2' in os.getenv('TOX_ENV_NAME'),
473
+ reason='PySide2 flaky with latest released napari',
474
+ )
470
475
  def test_installs(qtbot, tmp_virtualenv, plugin_dialog, request):
471
476
  if "[constructor]" in request.node.name:
472
477
  pytest.skip(
@@ -0,0 +1,58 @@
1
+ import sys
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ from napari_plugin_manager.utils import get_homepage_url, is_conda_package
7
+
8
+
9
+ @pytest.mark.parametrize(
10
+ "pkg_name,expected",
11
+ [
12
+ ("some-package", True),
13
+ ("some-other-package", False),
14
+ ("some-package-other", False),
15
+ ("other-some-package", False),
16
+ ("package", False),
17
+ ("some", False),
18
+ ],
19
+ )
20
+ def test_is_conda_package(pkg_name, expected, tmp_path):
21
+ mocked_conda_meta = tmp_path / 'conda-meta'
22
+ mocked_conda_meta.mkdir()
23
+ mocked_package = mocked_conda_meta / 'some-package-0.1.1-0.json'
24
+ mocked_package.touch()
25
+
26
+ with patch.object(sys, 'prefix', tmp_path):
27
+ assert is_conda_package(pkg_name) is expected
28
+
29
+
30
+ def test_get_homepage_url():
31
+ assert get_homepage_url({}) == ''
32
+
33
+ meta = {
34
+ 'Home-page': None,
35
+ }
36
+ assert get_homepage_url(meta) == ''
37
+
38
+ meta['Home-page'] = 'http://example.com'
39
+ assert get_homepage_url(meta) == 'http://example.com'
40
+
41
+ meta['Project-URL'] = ['Home Page, http://projurl.com']
42
+ assert get_homepage_url(meta) == 'http://example.com'
43
+
44
+ meta['home_page'] = meta.pop('Home-page')
45
+ meta['project_url'] = meta.pop('Project-URL')
46
+ assert get_homepage_url(meta) == 'http://example.com'
47
+
48
+ meta['home_page'] = None
49
+ assert get_homepage_url(meta) == 'http://projurl.com'
50
+
51
+ meta['project_url'] = ['Source Code, http://projurl.com']
52
+ assert get_homepage_url(meta) == 'http://projurl.com'
53
+
54
+ meta['project_url'] = ['Source, http://projurl.com']
55
+ assert get_homepage_url(meta) == 'http://projurl.com'
56
+
57
+ meta['project_url'] = None
58
+ assert get_homepage_url(meta) == ''
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.5rc0'
21
- __version_tuple__ = version_tuple = (0, 1, 5)
20
+ __version__ = version = '0.1.6'
21
+ __version_tuple__ = version_tuple = (0, 1, 6)
@@ -23,7 +23,7 @@ from logging import getLogger
23
23
  from pathlib import Path
24
24
  from subprocess import call
25
25
  from tempfile import gettempdir
26
- from typing import TypedDict
26
+ from typing import Optional, TypedDict
27
27
 
28
28
  from napari.plugins import plugin_manager
29
29
  from napari.plugins.npe2api import _user_agent
@@ -636,8 +636,8 @@ class InstallerQueue(QObject):
636
636
  def _on_process_done(
637
637
  self,
638
638
  exit_code: int | None = None,
639
- exit_status: QProcess.ExitStatus | None = None,
640
- error: QProcess.ProcessError | None = None,
639
+ exit_status: Optional[QProcess.ExitStatus] = None, # noqa
640
+ error: Optional[QProcess.ProcessError] = None, # noqa
641
641
  ):
642
642
  item = None
643
643
  with contextlib.suppress(IndexError):
@@ -53,7 +53,7 @@ from napari_plugin_manager.base_qt_package_installer import (
53
53
  )
54
54
  from napari_plugin_manager.qt_warning_dialog import RestartWarningDialog
55
55
  from napari_plugin_manager.qt_widgets import ClickableLabel
56
- from napari_plugin_manager.utils import is_conda_package
56
+ from napari_plugin_manager.utils import get_homepage_url, is_conda_package
57
57
 
58
58
  CONDA = 'Conda'
59
59
  PYPI = 'PyPI'
@@ -1225,7 +1225,8 @@ class BaseQtPluginDialog(QDialog):
1225
1225
  self.already_installed.add(norm_name)
1226
1226
  else:
1227
1227
  meta = {}
1228
-
1228
+ meta_dict = meta if isinstance(meta, dict) else meta.json
1229
+ home_page = get_homepage_url(meta_dict)
1229
1230
  self.installed_list.addItem(
1230
1231
  self.PROJECT_INFO_VERSION_CLASS(
1231
1232
  display_name=norm_name,
@@ -1236,7 +1237,7 @@ class BaseQtPluginDialog(QDialog):
1236
1237
  name=norm_name,
1237
1238
  version=meta.get('version', ''),
1238
1239
  summary=meta.get('summary', ''),
1239
- home_page=meta.get('Home-page', ''),
1240
+ home_page=home_page,
1240
1241
  author=meta.get('author', ''),
1241
1242
  license=meta.get('license', ''),
1242
1243
  ),
@@ -19,6 +19,8 @@ from napari.utils.notifications import show_warning
19
19
  from npe2 import PackageMetadata
20
20
  from typing_extensions import NotRequired
21
21
 
22
+ from napari_plugin_manager.utils import get_homepage_url
23
+
22
24
  PyPIname = str
23
25
 
24
26
 
@@ -98,12 +100,15 @@ def iter_napari_plugin_info() -> Iterator[tuple[PackageMetadata, bool, dict]]:
98
100
 
99
101
  conda_set = {normalized_name(x) for x in conda}
100
102
  for info in data_set:
101
- info_copy = dict(info)
103
+ info_copy: dict[str, str | list[str]] = dict(info)
102
104
  info_copy.pop('display_name', None)
103
105
  pypi_versions = info_copy.pop('pypi_versions')
104
106
  conda_versions = info_copy.pop('conda_versions')
105
107
  info_ = cast(_ShortSummaryDict, info_copy)
106
-
108
+ home_page = get_homepage_url(info_copy)
109
+ # this URL is used for the widget, so we have to replace the home_page
110
+ # link here as well as returning it in extra_info
111
+ info_['home_page'] = home_page
107
112
  # TODO: use this better.
108
113
  # this would require changing the api that qt_plugin_dialog expects to
109
114
  # receive
@@ -111,7 +116,7 @@ def iter_napari_plugin_info() -> Iterator[tuple[PackageMetadata, bool, dict]]:
111
116
  # TODO: once the new version of npe2 is out, this can be refactored
112
117
  # to all the metadata includes the conda and pypi versions.
113
118
  extra_info = {
114
- 'home_page': info_.get('home_page', ''),
119
+ 'home_page': home_page,
115
120
  'display_name': info.get('display_name', ''),
116
121
  'pypi_versions': pypi_versions,
117
122
  'conda_versions': conda_versions,
@@ -0,0 +1,76 @@
1
+ import re
2
+ import string
3
+ import sys
4
+ from pathlib import Path
5
+
6
+
7
+ def is_conda_package(pkg: str, prefix: str | None = None) -> bool:
8
+ """Determines if plugin was installed through conda.
9
+
10
+ Returns
11
+ -------
12
+ bool
13
+ ``True` if a conda package, ``False`` if not.
14
+ """
15
+ # Installed conda packages within a conda installation and environment can
16
+ # be identified as files with the template ``<package-name>-<version>-<build-string>.json``
17
+ # saved within a ``conda-meta`` folder within the given environment of interest.
18
+ conda_meta_dir = Path(prefix or sys.prefix) / 'conda-meta'
19
+ return any(
20
+ re.match(rf"{pkg}-[^-]+-[^-]+.json", p.name)
21
+ for p in conda_meta_dir.glob(f"{pkg}-*-*.json")
22
+ )
23
+
24
+
25
+ def normalize_label(label: str) -> str:
26
+ """Normalize project URL label.
27
+
28
+ Code reproduced from:
29
+ https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization
30
+ """
31
+ chars_to_remove = string.punctuation + string.whitespace
32
+ removal_map = str.maketrans("", "", chars_to_remove)
33
+ return label.translate(removal_map).lower()
34
+
35
+
36
+ def get_homepage_url(metadata: dict[str, str | list[str] | None]) -> str:
37
+ """Get URL for package homepage, if available.
38
+
39
+ Checks metadata first for `Home-page` field before
40
+ looking for `Project-URL` 'homepage' label and finally
41
+ 'source'/'sourcecode' label.
42
+
43
+ Parameters
44
+ ----------
45
+ metadata : dict[str, str | list[str] | None]
46
+ Package metadata information.
47
+
48
+ Returns
49
+ -------
50
+ str
51
+ Returns homepage URL if present, otherwise empty string.
52
+ """
53
+ if not len(metadata):
54
+ return ''
55
+
56
+ homepage = metadata.get('Home-page', '') or metadata.get('home_page', '')
57
+ if isinstance(homepage, str) and len(homepage):
58
+ return homepage
59
+
60
+ urls = {}
61
+ project_urls = metadata.get('Project-URL', []) or metadata.get(
62
+ 'project_url', []
63
+ )
64
+ if project_urls is None:
65
+ return ''
66
+
67
+ for url_info in project_urls:
68
+ label, url = url_info.split(', ')
69
+ urls[normalize_label(label)] = url
70
+
71
+ homepage = (
72
+ urls.get('homepage', '')
73
+ or urls.get('source', '')
74
+ or urls.get('sourcecode', '')
75
+ )
76
+ return homepage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-plugin-manager
3
- Version: 0.1.5rc0
3
+ Version: 0.1.6
4
4
  Summary: Install plugins for napari, in napari.
5
5
  Author-email: napari team <napari-steering-council@googlegroups.com>
6
6
  License: BSD 3-Clause License
@@ -30,6 +30,9 @@ passenv =
30
30
  NUMPY_EXPERIMENTAL_ARRAY_FUNCTION
31
31
  PYVISTA_OFF_SCREEN
32
32
 
33
+ setenv =
34
+ TOX_ENV_NAME = {envname}
35
+
33
36
  deps =
34
37
  napari_repo: git+https://github.com/napari/napari.git
35
38
  extras = testing
@@ -1,27 +0,0 @@
1
- import sys
2
- from unittest.mock import patch
3
-
4
- import pytest
5
-
6
- from napari_plugin_manager.utils import is_conda_package
7
-
8
-
9
- @pytest.mark.parametrize(
10
- "pkg_name,expected",
11
- [
12
- ("some-package", True),
13
- ("some-other-package", False),
14
- ("some-package-other", False),
15
- ("other-some-package", False),
16
- ("package", False),
17
- ("some", False),
18
- ],
19
- )
20
- def test_is_conda_package(pkg_name, expected, tmp_path):
21
- mocked_conda_meta = tmp_path / 'conda-meta'
22
- mocked_conda_meta.mkdir()
23
- mocked_package = mocked_conda_meta / 'some-package-0.1.1-0.json'
24
- mocked_package.touch()
25
-
26
- with patch.object(sys, 'prefix', tmp_path):
27
- assert is_conda_package(pkg_name) is expected
@@ -1,21 +0,0 @@
1
- import re
2
- import sys
3
- from pathlib import Path
4
-
5
-
6
- def is_conda_package(pkg: str, prefix: str | None = None) -> bool:
7
- """Determines if plugin was installed through conda.
8
-
9
- Returns
10
- -------
11
- bool
12
- ``True` if a conda package, ``False`` if not.
13
- """
14
- # Installed conda packages within a conda installation and environment can
15
- # be identified as files with the template ``<package-name>-<version>-<build-string>.json``
16
- # saved within a ``conda-meta`` folder within the given environment of interest.
17
- conda_meta_dir = Path(prefix or sys.prefix) / 'conda-meta'
18
- return any(
19
- re.match(rf"{pkg}-[^-]+-[^-]+.json", p.name)
20
- for p in conda_meta_dir.glob(f"{pkg}-*-*.json")
21
- )