napari-plugin-manager 0.1.6__py3-none-any.whl → 0.1.8__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.
@@ -10,17 +10,17 @@ from napari_plugin_manager.qt_package_installer import CondaInstallerTool
10
10
  @pytest.fixture(autouse=True)
11
11
  def _block_message_box(monkeypatch, request):
12
12
  def raise_on_call(*_, **__):
13
- raise RuntimeError("exec_ call") # pragma: no cover
14
-
15
- monkeypatch.setattr(QMessageBox, "exec_", raise_on_call)
16
- monkeypatch.setattr(QMessageBox, "critical", raise_on_call)
17
- monkeypatch.setattr(QMessageBox, "information", raise_on_call)
18
- monkeypatch.setattr(QMessageBox, "question", raise_on_call)
19
- monkeypatch.setattr(QMessageBox, "warning", raise_on_call)
20
- monkeypatch.setattr(QInputDialog, "getText", raise_on_call)
13
+ raise RuntimeError('exec_ call') # pragma: no cover
14
+
15
+ monkeypatch.setattr(QMessageBox, 'exec_', raise_on_call)
16
+ monkeypatch.setattr(QMessageBox, 'critical', raise_on_call)
17
+ monkeypatch.setattr(QMessageBox, 'information', raise_on_call)
18
+ monkeypatch.setattr(QMessageBox, 'question', raise_on_call)
19
+ monkeypatch.setattr(QMessageBox, 'warning', raise_on_call)
20
+ monkeypatch.setattr(QInputDialog, 'getText', raise_on_call)
21
21
  # QDialogs can be allowed via a marker; only raise if not decorated
22
- if "enabledialog" not in request.keywords:
23
- monkeypatch.setattr(QDialog, "exec_", raise_on_call)
22
+ if 'enabledialog' not in request.keywords:
23
+ monkeypatch.setattr(QDialog, 'exec_', raise_on_call)
24
24
 
25
25
 
26
26
  if TYPE_CHECKING:
@@ -19,6 +19,7 @@ from napari_plugin_manager.qt_package_installer import (
19
19
  NapariCondaInstallerTool,
20
20
  NapariInstallerQueue,
21
21
  NapariPipInstallerTool,
22
+ NapariUvInstallerTool,
22
23
  )
23
24
 
24
25
  if TYPE_CHECKING:
@@ -34,7 +35,7 @@ def _assert_exit_code_not_zero(
34
35
  if error is not None:
35
36
  errors.append("- 'error' should have been None!")
36
37
  if errors:
37
- raise AssertionError("\n".join(errors))
38
+ raise AssertionError('\n'.join(errors))
38
39
  return self._on_process_done_original(exit_code, exit_status, error)
39
40
 
40
41
 
@@ -45,13 +46,13 @@ def _assert_error_used(self, exit_code=None, exit_status=None, error=None):
45
46
  if exit_code is not None:
46
47
  errors.append("- 'exit_code' should not have been populated!")
47
48
  if errors:
48
- raise AssertionError("\n".join(errors))
49
+ raise AssertionError('\n'.join(errors))
49
50
  return self._on_process_done_original(exit_code, exit_status, error)
50
51
 
51
52
 
52
53
  class _NonExistingTool(AbstractInstallerTool):
53
54
  def executable(self):
54
- return f"this-tool-does-not-exist-{hash(time.time())}"
55
+ return f'this-tool-does-not-exist-{hash(time.time())}'
55
56
 
56
57
  def arguments(self):
57
58
  return ()
@@ -60,32 +61,52 @@ class _NonExistingTool(AbstractInstallerTool):
60
61
  return QProcessEnvironment.systemEnvironment()
61
62
 
62
63
 
64
+ @pytest.fixture
65
+ def patch_tool_executable(request):
66
+ mp = request.getfixturevalue('monkeypatch')
67
+ venv = request.getfixturevalue('tmp_virtualenv')
68
+ tool = request.getfixturevalue('tool')
69
+ mp.setattr(
70
+ tool,
71
+ 'executable'
72
+ if tool == NapariPipInstallerTool
73
+ else '_python_executable',
74
+ lambda *a: venv.creator.exe,
75
+ )
76
+
77
+
78
+ @pytest.mark.parametrize(
79
+ 'tool', [NapariPipInstallerTool, NapariUvInstallerTool]
80
+ )
63
81
  def test_pip_installer_tasks(
64
- qtbot, tmp_virtualenv: 'Session', monkeypatch, caplog
82
+ qtbot,
83
+ tool,
84
+ tmp_virtualenv: 'Session',
85
+ monkeypatch,
86
+ caplog,
87
+ patch_tool_executable,
65
88
  ):
66
89
  caplog.set_level(logging.DEBUG, logger=bqpi.__name__)
67
- installer = NapariInstallerQueue()
68
90
  monkeypatch.setattr(
69
- NapariPipInstallerTool,
70
- "executable",
71
- lambda *a: tmp_virtualenv.creator.exe,
91
+ NapariInstallerQueue, 'PYPI_INSTALLER_TOOL_CLASS', tool
72
92
  )
93
+ installer = NapariInstallerQueue()
73
94
  monkeypatch.setattr(
74
- NapariPipInstallerTool,
75
- "origins",
76
- ("https://pypi.org/simple",),
95
+ tool,
96
+ 'origins',
97
+ ('https://pypi.org/simple',),
77
98
  )
78
- with qtbot.waitSignal(installer.allFinished, timeout=20000):
99
+ with qtbot.waitSignal(installer.allFinished, timeout=30_000):
79
100
  installer.install(
80
- tool=InstallerTools.PIP,
101
+ tool=InstallerTools.PYPI,
81
102
  pkgs=['pip-install-test'],
82
103
  )
83
104
  installer.install(
84
- tool=InstallerTools.PIP,
105
+ tool=InstallerTools.PYPI,
85
106
  pkgs=['typing-extensions'],
86
107
  )
87
108
  job_id = installer.install(
88
- tool=InstallerTools.PIP,
109
+ tool=InstallerTools.PYPI,
89
110
  pkgs=['requests'],
90
111
  )
91
112
  assert isinstance(job_id, int)
@@ -104,74 +125,82 @@ def test_pip_installer_tasks(
104
125
 
105
126
  assert pkgs >= 2, 'package was not installed'
106
127
 
107
- with qtbot.waitSignal(installer.allFinished, timeout=10000):
128
+ with qtbot.waitSignal(installer.allFinished, timeout=30_000):
108
129
  job_id = installer.uninstall(
109
- tool=InstallerTools.PIP,
130
+ tool=InstallerTools.PYPI,
110
131
  pkgs=['pip-install-test'],
111
132
  )
112
133
 
113
134
  for pth in tmp_virtualenv.creator.libs:
114
- assert not (
115
- pth / 'pip_install_test'
116
- ).exists(), 'pip_install_test still installed'
135
+ assert not (pth / 'pip_install_test').exists(), (
136
+ 'pip_install_test still installed'
137
+ )
117
138
  assert not installer.hasJobs()
118
139
 
119
140
  # Test new signals
120
- with qtbot.waitSignal(installer.processFinished, timeout=20000) as blocker:
141
+ with qtbot.waitSignal(
142
+ installer.processFinished, timeout=30_000
143
+ ) as blocker:
121
144
  installer.install(
122
- tool=InstallerTools.PIP,
145
+ tool=InstallerTools.PYPI,
123
146
  pkgs=['pydantic'],
124
147
  )
125
148
  process_finished_data = blocker.args[0]
126
149
  assert process_finished_data['action'] == InstallerActions.INSTALL
127
- assert process_finished_data['pkgs'] == ["pydantic"]
150
+ assert process_finished_data['pkgs'] == ('pydantic',)
128
151
 
129
152
  # Test upgrade
130
- with qtbot.waitSignal(installer.allFinished, timeout=20000):
153
+ with qtbot.waitSignal(installer.allFinished, timeout=30_000):
131
154
  installer.install(
132
- tool=InstallerTools.PIP,
155
+ tool=InstallerTools.PYPI,
133
156
  pkgs=['requests==2.30.0'],
134
157
  )
135
158
  installer.upgrade(
136
- tool=InstallerTools.PIP,
159
+ tool=InstallerTools.PYPI,
137
160
  pkgs=['requests'],
138
161
  )
139
162
 
140
163
 
141
- def test_pip_installer_invalid_action(tmp_virtualenv: 'Session', monkeypatch):
142
- installer = NapariInstallerQueue()
164
+ @pytest.mark.parametrize(
165
+ 'tool', [NapariPipInstallerTool, NapariUvInstallerTool]
166
+ )
167
+ def test_pip_installer_invalid_action(
168
+ tool, tmp_virtualenv: 'Session', monkeypatch, patch_tool_executable
169
+ ):
143
170
  monkeypatch.setattr(
144
- NapariPipInstallerTool,
145
- "executable",
146
- lambda *a: tmp_virtualenv.creator.exe,
171
+ NapariInstallerQueue, 'PYPI_INSTALLER_TOOL_CLASS', tool
147
172
  )
173
+ installer = NapariInstallerQueue()
148
174
  invalid_action = 'Invalid Action'
175
+ item = installer._build_queue_item(
176
+ tool=InstallerTools.PYPI,
177
+ action=invalid_action,
178
+ pkgs=['pip-install-test'],
179
+ prefix=None,
180
+ origins=(),
181
+ process=installer._create_process(),
182
+ )
149
183
  with pytest.raises(
150
184
  ValueError, match=f"Action '{invalid_action}' not supported!"
151
185
  ):
152
- item = installer._build_queue_item(
153
- tool=InstallerTools.PIP,
154
- action=invalid_action,
155
- pkgs=['pip-install-test'],
156
- prefix=None,
157
- origins=(),
158
- process=installer._create_process(),
159
- )
160
186
  installer._queue_item(item)
161
187
 
162
188
 
163
- def test_installer_failures(qtbot, tmp_virtualenv: 'Session', monkeypatch):
164
- installer = NapariInstallerQueue()
189
+ @pytest.mark.parametrize(
190
+ 'tool', [NapariPipInstallerTool, NapariUvInstallerTool]
191
+ )
192
+ def test_installer_failures(
193
+ tool, qtbot, tmp_virtualenv: 'Session', monkeypatch, patch_tool_executable
194
+ ):
165
195
  monkeypatch.setattr(
166
- NapariPipInstallerTool,
167
- "executable",
168
- lambda *a: tmp_virtualenv.creator.exe,
196
+ NapariInstallerQueue, 'PYPI_INSTALLER_TOOL_CLASS', tool
169
197
  )
198
+ installer = NapariInstallerQueue()
170
199
 
171
200
  # CHECK 1) Errors should trigger finished and allFinished too
172
- with qtbot.waitSignal(installer.allFinished, timeout=10000):
201
+ with qtbot.waitSignal(installer.allFinished, timeout=10_000):
173
202
  installer.install(
174
- tool=InstallerTools.PIP,
203
+ tool=InstallerTools.PYPI,
175
204
  pkgs=[f'this-package-does-not-exist-{hash(time.time())}'],
176
205
  )
177
206
 
@@ -181,51 +210,57 @@ def test_installer_failures(qtbot, tmp_virtualenv: 'Session', monkeypatch):
181
210
  # CHECK 2) Non-existing packages should return non-zero
182
211
  monkeypatch.setattr(
183
212
  installer,
184
- "_on_process_done",
213
+ '_on_process_done',
185
214
  MethodType(_assert_exit_code_not_zero, installer),
186
215
  )
187
- with qtbot.waitSignal(installer.allFinished, timeout=10000):
216
+ with qtbot.waitSignal(installer.allFinished, timeout=10_000):
188
217
  installer.install(
189
- tool=InstallerTools.PIP,
218
+ tool=InstallerTools.PYPI,
190
219
  pkgs=[f'this-package-does-not-exist-{hash(time.time())}'],
191
220
  )
192
221
 
193
222
  # CHECK 3) Non-existing tools should fail to start
194
223
  monkeypatch.setattr(
195
224
  installer,
196
- "_on_process_done",
225
+ '_on_process_done',
197
226
  MethodType(_assert_error_used, installer),
198
227
  )
199
- monkeypatch.setattr(installer, "_get_tool", lambda *a: _NonExistingTool)
200
- with qtbot.waitSignal(installer.allFinished, timeout=10000):
228
+ monkeypatch.setattr(installer, '_get_tool', lambda *a: _NonExistingTool)
229
+ with qtbot.waitSignal(installer.allFinished, timeout=10_000):
201
230
  installer.install(
202
231
  tool=_NonExistingTool,
203
232
  pkgs=[f'this-package-does-not-exist-{hash(time.time())}'],
204
233
  )
205
234
 
206
235
 
207
- def test_cancel_incorrect_job_id(qtbot, tmp_virtualenv: 'Session'):
236
+ @pytest.mark.parametrize(
237
+ 'tool', [NapariPipInstallerTool, NapariUvInstallerTool]
238
+ )
239
+ def test_cancel_incorrect_job_id(tool, qtbot, monkeypatch):
240
+ monkeypatch.setattr(
241
+ NapariInstallerQueue, 'PYPI_INSTALLER_TOOL_CLASS', tool
242
+ )
208
243
  installer = NapariInstallerQueue()
209
- with qtbot.waitSignal(installer.allFinished, timeout=20000):
244
+ with qtbot.waitSignal(installer.allFinished, timeout=30_000):
210
245
  job_id = installer.install(
211
- tool=InstallerTools.PIP,
246
+ tool=InstallerTools.PYPI,
212
247
  pkgs=['requests'],
213
248
  )
214
- with pytest.raises(ValueError):
249
+ with pytest.raises(ValueError, match=f'No job with id {job_id + 1}.'):
215
250
  installer.cancel(job_id + 1)
216
251
 
217
252
 
218
253
  @pytest.mark.skipif(
219
- not NapariCondaInstallerTool.available(), reason="Conda is not available."
254
+ not NapariCondaInstallerTool.available(), reason='Conda is not available.'
220
255
  )
221
256
  def test_conda_installer(qtbot, caplog, monkeypatch, tmp_conda_env: Path):
222
- if sys.platform == "darwin":
257
+ if sys.platform == 'darwin':
223
258
  # check handled for `PYTHONEXECUTABLE` env definition on macOS
224
- monkeypatch.setenv("PYTHONEXECUTABLE", sys.executable)
259
+ monkeypatch.setenv('PYTHONEXECUTABLE', sys.executable)
225
260
  caplog.set_level(logging.DEBUG, logger=bqpi.__name__)
226
- conda_meta = tmp_conda_env / "conda-meta"
227
- glob_pat = "typing-extensions-*.json"
228
- glob_pat_2 = "pyzenhub-*.json"
261
+ conda_meta = tmp_conda_env / 'conda-meta'
262
+ glob_pat = 'typing-extensions-*.json'
263
+ glob_pat_2 = 'packaging-*.json'
229
264
  installer = NapariInstallerQueue()
230
265
 
231
266
  with qtbot.waitSignal(installer.allFinished, timeout=600_000):
@@ -257,7 +292,7 @@ def test_conda_installer(qtbot, caplog, monkeypatch, tmp_conda_env: Path):
257
292
  )
258
293
  installer.install(
259
294
  tool=InstallerTools.CONDA,
260
- pkgs=['pyzenhub'],
295
+ pkgs=['packaging'],
261
296
  prefix=tmp_conda_env,
262
297
  )
263
298
  assert installer.currentJobs() == 2
@@ -276,7 +311,7 @@ def test_conda_installer(qtbot, caplog, monkeypatch, tmp_conda_env: Path):
276
311
  )
277
312
  job_id_2 = installer.install(
278
313
  tool=InstallerTools.CONDA,
279
- pkgs=['pyzenhub'],
314
+ pkgs=['packaging'],
280
315
  prefix=tmp_conda_env,
281
316
  )
282
317
  assert installer.currentJobs() == 2
@@ -294,7 +329,7 @@ def test_conda_installer(qtbot, caplog, monkeypatch, tmp_conda_env: Path):
294
329
  )
295
330
  job_id_2 = installer.install(
296
331
  tool=InstallerTools.CONDA,
297
- pkgs=['pyzenhub'],
332
+ pkgs=['packaging'],
298
333
  prefix=tmp_conda_env,
299
334
  )
300
335
  assert installer.currentJobs() == 2
@@ -304,22 +339,24 @@ def test_conda_installer(qtbot, caplog, monkeypatch, tmp_conda_env: Path):
304
339
  assert not installer.hasJobs()
305
340
 
306
341
 
307
- def test_installer_error(qtbot, tmp_virtualenv: 'Session', monkeypatch):
308
- installer = NapariInstallerQueue()
342
+ @pytest.mark.parametrize(
343
+ 'tool', [NapariPipInstallerTool, NapariUvInstallerTool]
344
+ )
345
+ def test_installer_error(qtbot, tool, monkeypatch):
309
346
  monkeypatch.setattr(
310
- NapariPipInstallerTool,
311
- "executable",
312
- lambda *a: 'not-a-real-executable',
347
+ NapariInstallerQueue, 'PYPI_INSTALLER_TOOL_CLASS', tool
313
348
  )
349
+ installer = NapariInstallerQueue()
350
+ monkeypatch.setattr(tool, 'executable', lambda *a: 'not-a-real-executable')
314
351
  with qtbot.waitSignal(installer.allFinished, timeout=600_000):
315
352
  installer.install(
316
- tool=InstallerTools.PIP,
353
+ tool=InstallerTools.PYPI,
317
354
  pkgs=['some-package-that-does-not-exist'],
318
355
  )
319
356
 
320
357
 
321
358
  @pytest.mark.skipif(
322
- not NapariCondaInstallerTool.available(), reason="Conda is not available."
359
+ not NapariCondaInstallerTool.available(), reason='Conda is not available.'
323
360
  )
324
361
  def test_conda_installer_wait_for_finished(qtbot, tmp_conda_env: Path):
325
362
  installer = NapariInstallerQueue()
@@ -332,37 +369,41 @@ def test_conda_installer_wait_for_finished(qtbot, tmp_conda_env: Path):
332
369
  )
333
370
  installer.install(
334
371
  tool=InstallerTools.CONDA,
335
- pkgs=['pyzenhub'],
372
+ pkgs=['packaging'],
336
373
  prefix=tmp_conda_env,
337
374
  )
338
- installer.waitForFinished(20000)
375
+ installer.waitForFinished(30_000)
339
376
 
340
377
 
341
378
  def test_constraints_are_in_sync():
342
379
  conda_constraints = sorted(NapariCondaInstallerTool.constraints())
343
- pip_constraints = sorted(NapariPipInstallerTool.constraints())
380
+ pypi_constraints = sorted(NapariPipInstallerTool.constraints())
344
381
 
345
- assert len(conda_constraints) == len(pip_constraints)
382
+ assert len(conda_constraints) == len(pypi_constraints)
346
383
 
347
- name_re = re.compile(r"([a-z0-9_\-]+).*")
348
- for conda_constraint, pip_constraint in zip(
349
- conda_constraints, pip_constraints, strict=False
384
+ name_re = re.compile(r'([a-z0-9_\-]+).*')
385
+ for conda_constraint, pypi_constraint in zip(
386
+ conda_constraints, pypi_constraints, strict=False
350
387
  ):
351
388
  conda_name = name_re.match(conda_constraint).group(1)
352
- pip_name = name_re.match(pip_constraint).group(1)
353
- assert conda_name == pip_name
389
+ pypi_name = name_re.match(pypi_constraint).group(1)
390
+ assert conda_name == pypi_name
354
391
 
355
392
 
356
393
  def test_executables():
357
394
  assert NapariCondaInstallerTool.executable()
358
395
  assert NapariPipInstallerTool.executable()
396
+ assert NapariUvInstallerTool.executable()
359
397
 
360
398
 
361
399
  def test_available():
362
400
  assert str(NapariCondaInstallerTool.available())
363
401
  assert NapariPipInstallerTool.available()
402
+ assert NapariUvInstallerTool.available()
364
403
 
365
404
 
366
405
  def test_unrecognized_tool():
367
- with pytest.raises(ValueError):
406
+ with pytest.raises(
407
+ ValueError, match='InstallerTool shrug not recognized!'
408
+ ):
368
409
  NapariInstallerQueue().install(tool='shrug', pkgs=[])
@@ -18,16 +18,17 @@ def test_user_agent():
18
18
  @flaky(max_runs=4, min_passes=2)
19
19
  def test_plugin_summaries():
20
20
  keys = [
21
- "name",
22
- "version",
23
- "display_name",
24
- "summary",
25
- "author",
26
- "license",
27
- "home_page",
28
- "pypi_versions",
29
- "conda_versions",
30
- "project_url",
21
+ 'name',
22
+ 'normalized_name',
23
+ 'version',
24
+ 'display_name',
25
+ 'summary',
26
+ 'author',
27
+ 'license',
28
+ 'home_page',
29
+ 'pypi_versions',
30
+ 'conda_versions',
31
+ 'project_url',
31
32
  ]
32
33
  try:
33
34
  data = plugin_summaries()
@@ -42,7 +43,7 @@ def test_plugin_summaries():
42
43
 
43
44
 
44
45
  def test_conda_map():
45
- pkgs = ["napari-svg"]
46
+ pkgs = ['napari-svg']
46
47
  try:
47
48
  data = conda_map()
48
49
  for pkg in pkgs: