Source code for matmelt.commands.update
"""The ``update`` command: upgrade MatMelt in place (5.2b).
The counterpart of ``install.sh``: the installer records what it installed
into ``<venv>/matmelt.spec`` (a PyPI name, a git URL, or a local
path), and this command re-runs ``pip install --upgrade <that spec>`` with
the interpreter it is running under -- an update always comes from the same
source the install did, never a guessed one.
A copy that was *not* installed by ``install.sh`` (a dev checkout, a plain
``pip install``, a system python) has no spec file, and upgrading it here
would be a guess about how the user manages that environment -- refused with
the right command for each case named instead.
Exit codes: 0 = updated, 1 = refused or pip failed.
"""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
from matmelt.ui.console import render_error, render_note
#: Written by install.sh next to the venv's bin/ -- the update contract.
SPEC_FILENAME = "matmelt.spec"
[docs]
def run_update() -> int:
"""Upgrade this installation from the spec its installer recorded.
Returns
-------
int
pip's own exit code on an attempted upgrade, or ``1`` when this
installation cannot be updated this way -- a system Python, or a venv
with no recorded spec, each refused with the command that *would* work.
Notes
-----
The new version is reported by a **fresh interpreter**: this process is
still running the old code, so asking it would print the version that was
just replaced.
"""
if sys.prefix == sys.base_prefix:
render_error(
"update: this MatMelt runs on a system Python, not an "
"install.sh venv -- update it the way it was installed "
"(e.g. pip install --upgrade matmelt)."
)
return 1
spec_file = Path(sys.prefix) / SPEC_FILENAME
if not spec_file.is_file():
render_error(
"update: this environment was not set up by install.sh (no "
f"{SPEC_FILENAME} in {sys.prefix}) -- update it the way it was "
"installed: 'pip install --upgrade matmelt' for a pip "
"install, 'git pull' + 'pip install -e .' for a dev checkout."
)
return 1
spec = spec_file.read_text(encoding="utf-8").strip()
if not spec:
render_error(f"update: {spec_file} is empty -- re-run install.sh to repair.")
return 1
render_note(f"Updating MatMelt from '{spec}' ...")
proc = subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", spec])
if proc.returncode != 0:
render_error(f"update: pip exited {proc.returncode} -- see its output above.")
return 1
# The new version, from a fresh interpreter -- this process imported the
# old code and cannot report it truthfully itself.
probe = subprocess.run(
[sys.executable, "-c", "import matmelt; print(matmelt.__version__)"],
capture_output=True,
text=True,
)
version = probe.stdout.strip() if probe.returncode == 0 and probe.stdout.strip() else "unknown"
render_note(f"MatMelt updated (version {version}).")
return 0