Add GUI extra in auto-updater; support uv - #3346
Conversation
Add utilities to construct package specs and installer commands (supporting uv then pip) and import shutil/sys. Update MainWindow to run update commands in sequence: store a queue of backends, start each with _start_next_update_command, accumulate attempt outputs and backend names, and retry fallbacks when one backend fails. Improve error messages and logging, adjust progress/cleanup behavior, and surface a consolidated failure dialog if no backend succeeds.
Add unit tests for deeplabcut.gui.utils functions package_specs_for_update and build_update_commands. Tests verify package_specs_for_update adds the GUI extra to 'deeplabcut', preserves other packages, and strips surrounding whitespace. Tests for build_update_commands assert backend ordering and that 'uv' is used when available, the exact command arguments (including use of sys.executable), and that a pip fallback is always present; monkeypatching of shutil.which is used to simulate installer availability.
There was a problem hiding this comment.
Pull request overview
This PR refactors the GUI auto-updater to support multiple installer backends, preferring uv when available with a pip fallback, and ensures DeepLabCut updates from the GUI always include the [gui] extra so GUI dependencies stay in sync.
Changes:
- Added
package_specs_for_updateandbuild_update_commandsutilities to generate update package specs (includingdeeplabcut[gui]) and a prioritized installer command list. - Refactored the GUI update flow to sequentially try each backend and aggregate per-backend failure output for clearer diagnostics.
- Added unit tests validating package spec generation and backend command selection.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
deeplabcut/gui/utils.py |
Adds helpers to build package specs (injecting [gui]) and generate backend-specific update commands (uv → pip). |
deeplabcut/gui/window.py |
Updates GUI auto-update execution to try backends in order and improve logging/error reporting across attempts. |
tests/gui/test_auto_update.py |
Adds tests for package spec normalization and backend command prioritization/fallback behavior. |
Comments suppressed due to low confidence (1)
tests/gui/test_auto_update.py:38
- The parameterization includes two identical
{"uv": "/mock/bin/uv"}cases expecting the same backend order. This duplicates test work without increasing coverage; remove the redundant entry to keep the test intent clear.
({}, ["pip"]),
({"uv": "/mock/bin/uv"}, ["uv", "pip"]),
(
{"uv": "/mock/bin/uv"},
["uv", "pip"],
),
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Initialize _update_process_output to an empty list when tearing down the update process to avoid carrying over stale output after process deletion. Also make the GUI auto-update test skip when PySide6 is not installed by adding pytest.importorskip("PySide6") to tests/gui/test_auto_update.py, preventing failures in environments without the GUI dependency.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Add robust handling for the update QProcess to avoid races and leaks. Introduces _disconnect_update_process to safely disconnect signals, updates _drain_update_process_output to accept a process (and resolve sender()), and adds sender checks in error/finished handlers so only the current process is handled. Ensures signals are disconnected before deleteLater and prevents acting on stale or unrelated processes.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
deruyter92
left a comment
There was a problem hiding this comment.
Good fix! Just some minor comments and suggestions.
| if not self._start_next_update_command(): | ||
| self._cleanup_update_process() | ||
| QtWidgets.QMessageBox.warning( | ||
| self, | ||
| "Update failed", | ||
| "No available installer backend was found.", | ||
| ) | ||
|
|
There was a problem hiding this comment.
build_update_commands unconditionally appends the pip fallback so the list if never empty. This means that this code is unreachable, correct?
There was a problem hiding this comment.
Yes, right now it;s more of a future guard. I changed the message a bit, I think it's useful to have
Rename public update helper functions to private (_package_specs_for_update, _build_update_commands) to indicate internal API and update all imports/usages in deeplabcut/gui/window.py and tests. Adjust MainWindow process handling: add None checks when comparing sender/process, call deleteLater() on the local process variable, and remove a redundant progress_bar.hide() on successful update. Update tests to reference the new private function names.
Scope
uvinstead of justpip)Adds new utility functions for building update commands, refactors the update process to try preferred installers first with fallbacks, and adds tests for these.
Automated summary
Update backend improvements:
package_specs_for_updateandbuild_update_commandsutility functions indeeplabcut/gui/utils.pyto generate the correct package specifications (ensuringdeeplabcutis updated with[gui]extra) and to build a prioritized list of installer commands, preferringuvif available and always falling back topip([deeplabcut/gui/utils.pyR239-R292](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4279538071ed35262d378e598eb69f5d2e1aced94717f42138692f0d8c718e3cR239-R292)).deeplabcut/gui/window.pyto use the new backend selection logic, attempting each backend in order and providing more informative error messages and logs for each attempt ([[1]](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4dbcd4272a9f0fb80775a2e54ed8d52f229695085258e5b5d0172f05870a3772L344-R380),[[2]](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4dbcd4272a9f0fb80775a2e54ed8d52f229695085258e5b5d0172f05870a3772R336-R358),[[3]](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4dbcd4272a9f0fb80775a2e54ed8d52f229695085258e5b5d0172f05870a3772L408-L429),[[4]](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4dbcd4272a9f0fb80775a2e54ed8d52f229695085258e5b5d0172f05870a3772R418-R444),[[5]](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4dbcd4272a9f0fb80775a2e54ed8d52f229695085258e5b5d0172f05870a3772R404-R409)).Testing and reliability:
tests/gui/test_auto_update.pyto verify that package specs are built correctly and that the update command selection logic works as intended, including backend prioritization and fallback behavior ([tests/gui/test_auto_update.pyR1-R143](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-fe6347302c05d4e806136ae254f592f3a74c323a95a79724b435dd5c0d83c75eR1-R143)).Dependency management:
[gui]extra, keeping GUI dependencies in sync and reducing the risk of mismatched versions ([deeplabcut/gui/utils.pyR239-R292](https://github.com/DeepLabCut/DeepLabCut/pull/3346/files#diff-4279538071ed35262d378e598eb69f5d2e1aced94717f42138692f0d8c718e3cR239-R292)).