spacr.doctor¶
spacr-doctor — check a spaCR installation and print a fix for each problem found.
One command, one line per check, and for every line that is not PASS a
concrete command the user can copy and run. Result carries fix
as a field of its own, and format_report() prints it for every
non-PASS row.
The checks cover failures that have occurred in practice:
A stale editable install. This repository is checked out more than once on the same machine, and an editable install points at exactly one of them. Edits to checkout A have no effect while
import spacrresolves to checkout B, and nothing reports it untilspacr.__file__is printed.check_running_checkout()detects this case.A broken GUI dependency install. PySide6 ships with the core package, but a missing or incomplete wheel can still make the
spacrcommand fail on import.spacr.qtreports that case with an install message;check_qt_extra()reuses its logic so the two cannot drift.A GPU that is present but unusable. A CPU-only torch build on a machine with an NVIDIA card, or a driver older than the CUDA runtime torch was built against, both present as “cuda not available” and have entirely different fixes.
Cellpose version drift. spaCR migrated to the Cellpose 4.x / SAM API. Cellpose 3 lingering in an environment breaks at the first
CellposeModelcall, deep inside a run that has already spent an hour on masks.A project database that is corrupt, locked, or from a newer spaCR.
Settings whose combination cannot work, as reported by
spacr.validate.validate_settings().
Design rules:
Every check is an independent module-level function taking a
Contextand returning aResult(or a sequence of them), so each one is callable and assertable on its own.run_checks()wraps every call, so a check that raises becomes anERRORrow rather than ending the report.Nothing heavy is imported at module scope, so
spacr-doctor --helpdoes not import torch.
Attributes¶
The CPUs setup.py installs wgpu on by default. |
|
The first Python version setup.py installs wgpu on by default. |
Classes¶
Functions¶
|
Return the |
|
Report what can draw the True 3D view: wgpu, OpenGL and the CPU. |
|
Check that the installed Cellpose provides the 4.x / SAM API spaCR calls. |
|
Check that the |
|
Check that exactly one installed metadata directory claims to be spaCR. |
|
Check that every installed |
|
Check that every core dependency imports. |
|
Check that a project database is readable, intact, unlocked and of a supported schema. |
|
Check that a checkout's environment.yaml does not contradict its setup.py. |
|
Check whether the environment provides a display for the GUI. |
|
Check that exactly one importable |
|
Check that CUDA is usable, not only reported as available. |
|
Report the accelerator choice recorded by the desktop installer. |
|
Check that no optional extra is partly installed. |
|
Check that the running Python version is one spaCR supports. |
|
Check that the GUI dependencies import in this interpreter. |
|
Check that the checkout containing |
|
Check that a settings file describes a combination of settings that can run. |
|
Check that |
|
Check that torch imports and report whether it was built with CUDA. |
|
|
|
Render the rows as the text the command prints. |
|
Run every check, print the report, and return a shell exit code. |
|
Run the selected checks and return their rows. |
|
Count rows by verdict, always returning every key. |
Module Contents¶
- class spacr.doctor.Context[source]¶
Invocation inputs passed to every check.
- Parameters:
checkout – the directory the user believes they are editing. Defaults to the current working directory, because the checkout check compares the code that runs with the code in that directory.
db – optional project database to inspect.
settings – optional settings file (csv or json) to validate.
app – app key the settings file is for (
mask,measure, …).probe_gpu – allocate a tensor on the GPU to confirm it can be used. A driver/runtime mismatch is not detected until something is allocated.
- class spacr.doctor.Result[source]¶
One check’s verdict.
- Parameters:
check – short label shown in the left column of the report.
status – one of
PASS,WARN,FAIL,ERROR,SKIP.message – what was found, in the user’s terms.
fix – a command or action the user can carry out. Expected for every non-
PASSrow;format_report()prints it verbatim, including newlines.details – supporting facts worth showing but not worth a verdict.
- spacr.doctor.build_parser() argparse.ArgumentParser[source]¶
Return the
spacr-doctorargument parser.
- spacr.doctor.check_3d_backends(ctx: Context) Result[source]¶
Report what can draw the True 3D view: wgpu, OpenGL and the CPU.
wgpu (Vulkan, Metal, Direct3D 12) is a default dependency, but a computer without it is a supported install: the view then draws with OpenGL (VisPy) or the CPU. A missing wgpu is therefore a
PASS; only a wgpu that is installed and cannot be imported is aWARN.- Parameters:
ctx –
probe_gpu=Truelets wgpu list its adapters, which loads the graphics drivers but draws nothing; it is skipped whenSPACR_NO_GPU_RENDERis set orCUDA_VISIBLE_DEVICESis empty, as the True 3D view skips the GPU then.
- spacr.doctor.check_cellpose(ctx: Context) Result[source]¶
Check that the installed Cellpose provides the 4.x / SAM API spaCR calls.
- Parameters:
ctx – not read. The version bound comes from spaCR’s own installed metadata, falling back to
FALLBACK_CELLPOSE_SPECIFIERwhen that cannot be read, and the verdict does not rest on the version string alone: the API itself is probed formodels.CellposeModel, for the absence of the 3.xmodels.Cellposewrapper, and forcpsaminMODEL_NAMES. A version string too odd to compare downgrades a finding to WARN rather than guessing.
- spacr.doctor.check_command_on_path(ctx: Context) Result[source]¶
Check that the
spacrcommand onPATHbelongs to the running Python environment.A second environment earlier on
PATHis the other half of “which spacr am I actually running”: the import can be right while the command is not.- Parameters:
ctx – not read. The comparison is between
PATHandsys.prefixas this process sees them, so the row describes the shell that launched it: activate a different environment and the answer legitimately changes. A command found outsidesys.prefixis a FAIL; nospacrcommand anywhere onPATHis only a WARN, sincepython -m spacr.doctorstill works.
- spacr.doctor.check_conflicting_distributions(ctx: Context) Result[source]¶
Check that exactly one installed metadata directory claims to be spaCR.
Two shapes of the same problem.
spacrandspacr-nightlyinstalled together share one package directory and overwrite each other’s files. A leftoverspacr.egg-infoinside a checkout shadows the real install whenever the checkout is onsys.path, so version, dependency list and console-script table are all read from stale metadata.- Parameters:
ctx – not read. Every installed metadata directory on
sys.pathis enumerated, so — likecheck_duplicate_installs()— the row can change with the directory the process was launched from and not withctx.checkout. Two distinct distribution names (SPACR_DISTRIBUTION_NAMES) are a FAIL; two copies of the same name, typically a stalespacr.egg-info, are only a WARN.
- spacr.doctor.check_console_scripts(ctx: Context) Result[source]¶
Check that every installed
spacr-*command points at a module that exists.sim=spacr.app_sim:gui_simoutlived the file it named, so the installedsimcommand died with ImportError. A partially upgraded install reproduces that for any command.- Parameters:
ctx – not read. The entry points come from the installed distribution’s metadata rather than from
ctx.checkout’s setup.py, because it is the installed table that pip turned into scripts on PATH. Only the module half ofmodule:functionis resolved, so a command whose target function was renamed still passes here and fails when run.
- spacr.doctor.check_core_dependencies(ctx: Context) Result[source]¶
Check that every core dependency imports.
- Parameters:
ctx – not read. The list is the fixed
CORE_MODULEStable, and each entry is genuinely imported rather than merely looked up in the installed metadata — a distribution present but broken (a torch whose shared libraries will not load, say) has to fail here rather than at hour one of a run. That makes this the slowest of the dependency checks.
- spacr.doctor.check_database(ctx: Context) Result | List[Result][source]¶
Check that a project database is readable, intact, unlocked and of a supported schema.
- Parameters:
ctx – only
ctx.dbis read.None— the usual case — makes the whole check one SKIP row rather than a failure, since most invocations have no project to point at. It must name the SQLite file itself, which spaCR writes to<src>/measurements/measurements.db, not the plate directory; a path that is not a file is a FAIL. The integrity and schema rows open it read-only, but the locking row briefly takes a real write lock, so pointing this at a database a run or an open GUI is currently writing to reports that lock — which is the intended answer, not a false alarm.
- spacr.doctor.check_declared_pins(ctx: Context) Result[source]¶
Check that a checkout’s environment.yaml does not contradict its setup.py.
Only meaningful inside a source checkout, and it is there that it matters:
conda env create -f environment.yamlis how a new user builds an environment, so a pin in that file that setup.py forbids produces an install that is broken before anyone runs anything.- Parameters:
ctx – only
ctx.checkoutis read, and only to walk up to the enclosing checkout root the waycheck_running_checkout()does. Everything after that is read from files on disk —setup.pyandenvironment.yamlin that root — never from the installed environment, so this row says nothing about a wheel install and is a SKIP whenever the pair is absent. Pointctx.checkoutat another clone to cross-check that clone instead of the running one.
- spacr.doctor.check_display(ctx: Context) Result[source]¶
Check whether the environment provides a display for the GUI.
- Parameters:
ctx – not read. The inputs are
sys.platformand this process’sQT_QPA_PLATFORM,DISPLAYandWAYLAND_DISPLAYenvironment variables, so the row follows the environment rather than anything the caller passes. Only Linux is examined; every other platform passes outright, as does Linux withQT_QPA_PLATFORMset to a deliberately headless value. A missing display is a WARN, never a FAIL, because the pipelines run headless.
- spacr.doctor.check_duplicate_installs(ctx: Context) Result[source]¶
Check that exactly one importable
spacrpackage directory exists.- Parameters:
ctx – not read, and deliberately so: the candidates come from the import machinery and
sys.pathof the running process, which is what an actualimport spacrconsults. That makes the row depend on the directory this process was launched from — an emptysys.pathentry means the current directory — rather than onctx.checkout.
- spacr.doctor.check_gpu(ctx: Context) Result[source]¶
Check that CUDA is usable, not only reported as available.
- Parameters:
ctx –
probe_gpu=Truepermits explicit CUDA initialization and an 8x8 tensor allocation/multiplication probe. False reports metadata without initialization, device-name or dtype-allocation probes; it cannot prove that a reported device would accept a tensor. Explicitly hidden CUDA devices are reported as skipped, while other backends remain eligible. A forced CPU selection is also reported as skipped.
- spacr.doctor.check_installer_backend(ctx: Context) Result[source]¶
Report the accelerator choice recorded by the desktop installer.
- spacr.doctor.check_optional_extras(ctx: Context) Result[source]¶
Check that no optional extra is partly installed.
A missing extra is fine and expected. An extra with some of its distributions present and others not is a resolve that went wrong, and it fails at the moment the feature is used rather than at install time.
- Parameters:
ctx – not read; the extras and the distributions in each come from
OPTIONAL_EXTRAS. Presence is decided from installed metadata rather than by importing, which keeps the check cheap but means an extra that is installed and broken still counts as present — the opposite trade-off fromcheck_core_dependencies().
- spacr.doctor.check_python(ctx: Context) Result[source]¶
Check that the running Python version is one spaCR supports.
- Parameters:
ctx – not read. The verdict comes from
sys.version_infoand theRequires-Pythonfield of the installed metadata, falling back toFALLBACK_REQUIRES_PYTHONwhen spaCR is not installed at all. Accepted only so every check shares one signature, so anyContext— including a default-constructed one — gives the same row.
- spacr.doctor.check_qt_extra(ctx: Context) Result[source]¶
Check that the GUI dependencies import in this interpreter.
Reuses
spacr.qt’s own diagnosis —_missing_qt_extraand_QT_MISSING_MESSAGE— rather than restating which distributions the GUI needs. The legacyqt extrareport key stays stable for consumers.- Parameters:
ctx – not read. The check imports the real GUI entry point, which means it is the one check that pays for importing PySide6, and it reports what that import did in this interpreter. It says nothing about whether a window can be opened — see
check_display()for that half.
- spacr.doctor.check_running_checkout(ctx: Context) Result[source]¶
Check that the checkout containing
ctx.checkoutis the oneimport spacrloads.This detects two clones of spaCR on one machine with an editable install pointing at the first while the second is being edited: tests pass and edits have no effect, and no other output reports it.
- Parameters:
ctx – only
ctx.checkoutis read, and it need not be the checkout root: the check walks up from it for the first directory holding bothspacr/__init__.pyand a setup.py or pyproject.toml, so any subdirectory of a clone works. When no such directory is found above it — the usual case when spaCR is installed from a wheel — the row falls back to comparing the editable-install target recorded by pip with whatimport spacractually resolves to, and passes if there is nothing to contradict.
- spacr.doctor.check_settings(ctx: Context) Result | List[Result][source]¶
Check that a settings file describes a combination of settings that can run.
Delegates to
spacr.validate.validate_settings(), which checks the known invalid combinations —normalize=Truewithmeasure, acrop_modenaming an object with no mask dimension, a mask run with all four object channels unset.- Parameters:
ctx –
ctx.settingsandctx.appare both read, and both are needed.ctx.settingsis a.csvor.jsonsettings file;Noneis a SKIP and a path that is not a file is a FAIL.ctx.appselects which pipeline’s rules apply (mask,measure,classify, …); an empty string is a WARN instead of a guess, because the same file is valid for one app and invalid for another, and an app name thatspacr.validate.validate_settings()does not recognise runs only the generic checks. Unlike the other checks this one can return many rows — one per problem found.
- spacr.doctor.check_spacr_package(ctx: Context) Result[source]¶
Check that
import spacrworks and report its path and version.- Parameters:
ctx – not read. The row describes the interpreter this process is running in — it imports
spacrand reports__file__and__version__— so it answers “does spaCR import” and never “is it the copy inctx.checkout”; that second question belongs tocheck_running_checkout().
- spacr.doctor.check_torch(ctx: Context) Result[source]¶
Check that torch imports and report whether it was built with CUDA.
- Parameters:
ctx – not read — in particular
ctx.probe_gpubelongs tocheck_gpu(), not here. This row only imports torch and reportstorch.version.cuda, and a CPU-only build still passes: whether that build is wrong for this machine is a question about the driver, which is why it is answered one row later.
- spacr.doctor.exit_code(results: Iterable[Result], strict: bool = False) int[source]¶
0when the installation is healthy,1otherwise.- Parameters:
results – diagnostic rows to inspect.
FAILandERRORalways make the result non-zero; other statuses remain healthy unless strict warning handling applies.strict – also fail on
WARN, for CI that requires no warnings.
- spacr.doctor.format_report(results: Sequence[Result]) str[source]¶
Render the rows as the text the command prints.
- Parameters:
results – rows in the order they should appear — nothing here sorts or groups them, so the printed order is whatever produced the sequence, normally
run_checks()inCHECKSorder. It must be a real sequence and not a generator: it is traversed three times, once to size the check column to the widest label in this sequence, once for the lines, and once for the trailing counts.fixis printed only for non-PASSrows, so a fix attached to a passing row never reaches the report.
- spacr.doctor.main(argv: Sequence[str] | None = None) int[source]¶
Run every check, print the report, and return a shell exit code.
- Parameters:
argv – arguments without the program name, as
argparse.ArgumentParser.parse_args()takes them;Nonereadssys.argv[1:]. Anything argparse rejects — including--help— exits the process rather than returning, which is the one way a caller embedding this does not get its exit code back. Passing[]runs the full check list against the current working directory with no database and no settings file, which is what plainspacr-doctordoes.
- spacr.doctor.run_checks(ctx: Context, checks: Sequence[Callable[[Context], Any]] | None = None) List[Result][source]¶
Run the selected checks and return their rows.
A check that raises becomes an
ERRORrow and the run continues, so one failing probe does not discard the rows of the other checks.KeyboardInterruptis re-raised and stops the run.- Parameters:
ctx – invocation context passed unchanged to every selected check, including the checkout, optional project inputs and GPU-probe choice.
checks – the checks to run, in order;
Noneruns every check registered inCHECKS.
- spacr.doctor.summarize(results: Iterable[Result]) Dict[str, int][source]¶
Count rows by verdict, always returning every key.
- Parameters:
results – rows to tally, iterated exactly once, so a generator is safe here (it is not in
format_report()). The five standard verdicts are always present in the result even whenresultsis empty, so callers can index them withoutget; a row carrying some other status string adds a sixth key rather than being dropped.
Nested helpers¶
- _importable_spacr_dirs.add(path: Path) None¶
Resolve and append
pathonce, ignoring unresolvable paths.spacr/doctor.py:457
- _register.decorate(function: Callable) Callable¶
Label and register
function, then return it unchanged.spacr/doctor.py:145
- check_gpu._result(status, message, *, fix='', details=())¶
Append shared task evidence without changing the GPU diagnosis.
spacr/doctor.py:1355