spacr.updater¶
Auto-updater — compare local spacr to PyPI + the nightly branch.
Exposes a small API the Qt GUI’s Help → “Check for updates” menu entry can call. Nothing runs automatically; users always trigger a check + confirm any upgrade.
The updater talks to two sources:
PyPI —
https://pypi.org/pypi/spacr/jsonfor the latest released version.GitHub — the nightly branch’s HEAD commit hash, so nightly users see how many commits they’re behind.
It also answers Home’s News panel, through fetch_release_notes():
the published releases, cached for a day, so a running copy can show a
release newer than the one bundled in its own wheel.
Both fetches use urllib from the stdlib to avoid pulling in an
extra HTTP dependency. Timeouts are short (3 s) so a slow / offline
network doesn’t block the UI. Errors are absorbed and surfaced as
“couldn’t check” — never a crash.
Public API:
from spacr.updater import check_for_updates, run_pip_upgrade
info = check_for_updates() # UpdateInfo
if info.upgrade_available:
run_pip_upgrade()
Classes¶
Represent a parsed |
|
What pressing Install on a greyed-out option should do. |
|
One line of a dry-run report: what a package is now, and would be. |
|
Result of a version check. |
Functions¶
|
PEP 503 normalisation, so |
|
Query PyPI + GitHub and return an |
|
Return the command that previews installation of |
|
Ask the packaging tool what installing |
|
Return the active spaCR checkout, or |
|
The repository's releases, newest first, or |
|
The |
|
Whether a plan may proceed, and what a second confirmation must say. |
|
Return the command that installs |
|
The version of |
|
Launch the updated Qt application after the old event loop has stopped. |
|
Where the fetched release list is remembered between launches. |
|
An offer that names another environment and runs nothing. |
|
An offer that says installing cannot help, and why. |
|
An offer that may run pip here, after a dry run and a confirmation. |
|
An offer for something that is already available. |
|
Is |
|
Run one packaging command, capturing everything it said. |
|
Upgrade |
|
Upgrade this interpreter, optionally to the exact version offered. |
Module Contents¶
- class spacr.updater.DryRun[source]¶
Represent a parsed
pip install --dry-runresult.okis False when the resolver refused, when the tool could not be run, or when it returned no machine-readable plan.- Parameters:
requirement – pip requirement string that was resolved.
ok – whether the packaging command succeeded and returned a readable machine plan.
changes – parsed resolver entries, including additions, version moves, and removals as
PackageChangerecords.error – resolver or launch failure detail when
okis false, otherwiseNone.raw – concatenated resolver stdout and stderr retained for diagnostics.
- property additions: Tuple[PackageChange, ...][source]¶
Packages that are not here at all today.
- property moves: Tuple[PackageChange, ...][source]¶
Packages already installed whose version would change.
- property protected_changes: Tuple[PackageChange, ...][source]¶
Protected packages whose version would change or be removed.
- property protected_moves: Tuple[PackageChange, ...][source]¶
The moves that land on
PROTECTED_PACKAGES.
- property removals: Tuple[PackageChange, ...][source]¶
Packages already installed that the resolver would remove.
- class spacr.updater.InstallOffer[source]¶
What pressing Install on a greyed-out option should do.
One shape, two callers – the regression backend picker (
spacr.regression_backends.backend_install_offer()) and the Image UMAP’s GPU acceleration (spacr.gpu_reduce.install_offer()) – so the panel that shows it does not have to know which asked.- Parameters:
action – offer state, normally one of
OFFER_ACTIONS; only"install"with a nonempty requirement can produce a command.title – short capability heading shown by the availability interface.
message – primary explanation shown with the offer.
requirement – pip requirement used to build a local install command for an install action, or
Nonewhen no local command is available.recipe – optional setup or external-environment instructions appended to the message.
runs_anything – informational local-install marker set by
offer_install();command, not this flag, controls execution.
- class spacr.updater.PackageChange[source]¶
One line of a dry-run report: what a package is now, and would be.
- Parameters:
name – distribution name as reported by the resolver; its spelling is retained.
current – installed version, or the version reported as removed by uv, or
Nonewhen the distribution is absent.proposed – version the resolver would install, or
Nonewhen it would remove the distribution.
- class spacr.updater.UpdateInfo[source]¶
Result of a version check.
- Parameters:
installed_version – locally installed spaCR version, or
"unknown"when neither distribution’s metadata is readable.latest_release – latest spaCR version returned by PyPI, or
Nonewhen it is missing or unavailable.nightly_sha – first seven characters of the nightly branch head returned by GitHub, or
Nonewhen unavailable.error – first PyPI or GitHub request failure, prefixed by service name, or
Nonewhen neither request failed.
- spacr.updater.canonical_package_name(name) str[source]¶
PEP 503 normalisation, so
scikit_learnandScikit-Learnmatch.- Parameters:
name – any spelling of a distribution name.
- Returns:
lower-case with runs of
-,_and.collapsed to a single-.
- spacr.updater.check_for_updates(timeout: float = 3.0) UpdateInfo[source]¶
Query PyPI + GitHub and return an
UpdateInfo.- Parameters:
timeout – per-request timeout in seconds.
- spacr.updater.dry_run_command(requirement) list[source]¶
Return the command that previews installation of
requirement.pip’s
--report -writes a JSON document to stdout and installs nothing;uv pip install --dry-runprints+ name==versionlines. Both are parsed bydry_run_install(), because the second is the only one available on the desktop installs whose venv has no pip.- Parameters:
requirement – pip requirement string whose installation to preview.
- spacr.updater.dry_run_install(requirement, timeout: float = 600.0, runner=None) DryRun[source]¶
Ask the packaging tool what installing
requirementwould change.This function does not install packages. It resolves and reports proposed additions and version changes so they can be reviewed before installation.
- Parameters:
requirement – a pip requirement string.
timeout – seconds before the resolver is given up on.
runner – injected for tests; defaults to
subprocess.run().
- Returns:
a
DryRun.
- spacr.updater.editable_install_location() str | None[source]¶
Return the active spaCR checkout, or
Nonefor a regular install.- Returns:
the absolute path of the working tree, when this interpreter is running spaCR out of one.
Detection first reads the PEP 610
direct_url.jsoneditable-install record. If that metadata is unavailable, it checks whether the imported package resides outsidesite-packagesand inside a directory containing.gitorpyproject.toml.
- spacr.updater.fetch_release_notes(timeout: float = 4.0, max_age: float = NEWS_MAX_AGE_S) list[source]¶
The repository’s releases, newest first, or
[].NEVER CALL THIS ON THE GUI THREAD. It opens a socket. Home’s News panel asks for it through the same
_UpdateWorkerthe manual update check runs on, after the page has been shown.Unauthenticated, because the alternative is a token the user does not have. That means the shared, per-address rate limit, which is why the answer is cached for a day and why every failure is silent: the bundled
spacr/resources/release_notes.jsonremains the offline source of truth and an empty list simply leaves it alone.- Parameters:
timeout – request timeout in seconds. Short on purpose.
max_age – how old a cached answer may be before it is asked again, in seconds.
- Returns:
release records shaped like the bundled resource’s, or an empty list when the answer is unavailable for any reason at all.
- spacr.updater.find_uv() str | None[source]¶
The
uvthe desktop installers bootstrap, if this is such an install.The native installers build their environment with
uv venv, which does not seedpip. On those installspython -m pipfails before it starts, so the updater has to use the same tool the installer did.uvis bootstrapped one level above the venv:<install root>/bootstrap/uv <install root>/venv/ <- sys.prefix
- Returns:
an executable path, or
Nonewhen this is an ordinary pip-managed environment.
- spacr.updater.install_decision(dry_run: DryRun) dict[source]¶
Whether a plan may proceed, and what a second confirmation must say.
An install that would move or remove NumPy, PyTorch, pandas, or scikit-learn is refused by default and needs a second confirmation naming what changes.
- Parameters:
dry_run – the result of
dry_run_install().- Returns:
{allowed, needs_second_confirmation, moves, headline, report}.allowedis False when the dry run did not answer – an install whose consequences are unknown is not offered.
- spacr.updater.install_requirement_command(requirement) list[source]¶
Return the command that installs
requirementin this environment.The same tool choice
upgrade_command()makes –uvwhen this is a desktop install whose venv has no pip,python -m pipotherwise.- Parameters:
requirement – a pip requirement string, e.g.
'cuml-cu12'.
- spacr.updater.installed_version(name) str | None[source]¶
The version of
nameinstalled here, orNoneif it is absent.- Parameters:
name – distribution name to query from installed package metadata.
- spacr.updater.launch_updated_app() int[source]¶
Launch the updated Qt application after the old event loop has stopped.
- Returns:
process identifier of the detached replacement application.
- Raises:
OSError – the replacement could not be started.
- spacr.updater.news_cache_path() pathlib.Path[source]¶
Where the fetched release list is remembered between launches.
- spacr.updater.offer_elsewhere(title: str, message: str, recipe: str) InstallOffer[source]¶
An offer that names another environment and runs nothing.
- Parameters:
title – short heading shown for the optional capability.
message – explanation of why installation must happen elsewhere.
recipe – instructions for preparing the external environment.
- spacr.updater.offer_impossible(title: str, message: str, recipe: str = '') InstallOffer[source]¶
An offer that says installing cannot help, and why.
- Parameters:
title – short heading shown for the unavailable capability.
message – explanation of why installation cannot satisfy it.
- spacr.updater.offer_install(title: str, message: str, requirement: str, recipe: str = '') InstallOffer[source]¶
An offer that may run pip here, after a dry run and a confirmation.
- Parameters:
title – short heading shown for the optional capability.
message – explanation shown with the install offer.
requirement – pip requirement string that can satisfy the feature.
- spacr.updater.offer_ready(title: str, message: str) InstallOffer[source]¶
An offer for something that is already available.
- Parameters:
title – short heading shown for the available capability.
message – explanation shown with the offer.
- spacr.updater.pip_available() bool[source]¶
Is
python -m pipusable in this interpreter?uv venvdoes not seed pip, which is the casefind_uv()exists for. It matters here because pip is the only one of the two that can produce a machine-readable--report.
- spacr.updater.run_install_command(args, timeout: float = 1800.0)[source]¶
Run one packaging command, capturing everything it said.
Install offers use the same capture behavior as
run_pip_upgrade(), including when the application was launched without a terminal.- Parameters:
args – the argv to run, from
upgrade_command()orinstall_requirement_command().timeout – seconds before the install is given up on.
- Returns:
(exit_code, output)with stdout and stderr combined.
- spacr.updater.run_pip_upgrade(pre_release: bool = False, *, target_version=None)[source]¶
Upgrade
spacrin place, capturing what the packaging tool said.- Parameters:
pre_release – pass
--preso pre-releases and.postNversions are considered.target_version – install this exact offered version and verify it in a fresh process before reporting success. None retains an unpinned upgrade.
- Returns:
(exit_code, output)with combined stdout and stderr. Captured output remains available to desktop installations launched without a terminal.