spacr.cli¶
spacr-run — run any spaCR module from a settings file, with no GUI.
The GUI entry points — the PySide6 app (spacr / spacr-qt) and the
classic Tk app (spacr-tk) — import Qt or Tk, which cannot run on a compute
node without a display.
This module runs pipelines without a GUI. Importing spacr.cli does not
import Qt, Tk, torch, cellpose, numpy or pandas. Everything
heavy is imported inside the command that needs it, so spacr-run --help and
spacr-run --list answer instantly on a login node, and --dry-run
validates a 40-plate settings file without touching a GPU.
Usage:
spacr-run <module> --settings settings.csv [--set key=value ...] [--dry-run]
spacr-run --list # every module that can run headless
spacr-run --describe measure # what it does, what it needs, what it writes
spacr-run validate --settings f --module mask # pre-flight only
The settings file is the one the GUI writes. Both spaCR CSV layouts are read —
Key,Value (what spacr.utils.save_settings() emits next to every run)
and setting_key,setting_value (the documented default of
spacr.utils.load_settings()) — plus the settings.json written into
each run-journal folder. So the round trip is: click through the GUI once on a
laptop, copy <src>/settings/gen_mask_settings.csv to the cluster, and
sbatch it unchanged.
Exit codes (a failed run never exits with 0, so a cluster scheduler sees the failure):
0 the module ran to completion, or the dry run / validation found no errors 1 the module raised 2 bad arguments, unreadable settings, or pre-flight found errors
A pipeline that raises SystemExit itself is the exception: its integer
code is passed through unchanged, so any value can reach the shell.
Matplotlib is forced to Agg when there is no display, and plt.show is
replaced by a close-the-figure shim for the duration of the run — the same
thing spacr.gui_utils.spacrFigShow() does inside the GUI — so a pipeline
that calls plt.show() neither blocks nor leaks figures.
Exceptions¶
Raised for an invalid settings file, override or module name. |
Classes¶
One headless-runnable spaCR pipeline. |
Functions¶
|
Apply |
|
Return the |
|
|
|
|
|
|
|
|
|
Coerce a |
|
Import and return the pipeline callable for |
|
Load a settings file written by the GUI, a pipeline run or a run journal. |
|
|
|
Return a fresh defaults dict for |
|
Render the |
|
Render the |
|
Render a resolved settings dict as an aligned, sorted table. |
|
Return the |
|
Build the settings dict that the pipeline receives. |
|
Configure timestamped logging to stdout for a batch run. |
|
Force matplotlib's Agg backend when there is no display. |
|
Write a settings dict to a CSV that the GUI and |
Module Contents¶
- exception spacr.cli.SettingsError[source]¶
Bases:
ExceptionRaised for an invalid settings file, override or module name.
Always maps to exit code 2: the run never started, so it is an argument problem rather than a runtime failure.
Initialize self. See help(type(self)) for accurate signature.
- class spacr.cli.Module[source]¶
One headless-runnable spaCR pipeline.
- Parameters:
key – name the user types, matching the GUI’s
app_key.summary – one line for
--list.entry –
"module:function"of the callable that does the work.defaults – name of the
spacr.settingshelper that fills the defaults, orNonewhen the pipeline has none there.validate_key – app key understood by
spacr.validate; empty when that module has no specific rules and only generic checks apply.requires – settings that must be supplied, phrased for a human.
writes – what lands on disk.
call_style –
"settings"forfn(settings_dict);"folder"forfn(settings["src"]). No built-in module uses"folder"; it reaches spaCR only through a plugin app that declares it.note – caveat worth printing in
--describe.defaults_entry –
"module:function"of a defaults helper that does not live inspacr.settings. Six built-in pipelines keep their own (foreign,external_masks,convert,illumination,barcode_qc,anndata_export), as does every plugin app, because their keys are theirs alone; without this the CLI would resolve an empty defaults dict for them, and--setwould then reject every one of their keys as a setting that does not exist.
- property func_name: str[source]¶
Attribute name of the callable inside
module_name.
- spacr.cli.apply_overrides(settings: Dict[str, Any], overrides: Sequence[str], module: Module | None = None) Dict[str, Any][source]¶
Apply
--set key=valueoverrides on top of a settings dict.An override naming a key spaCR does not know is an error, not a no-op: a typo’d override that quietly does nothing costs a whole run to discover, and the run looks like it succeeded.
So is an override naming a key spaCR does know but nothing reads. Those are worse, because they pass every “is this a real setting?” test there is:
remove_border_pathogensis typed, tooltipped and offered by the Pathogen category, andspacr-run mask --set remove_border_pathogens=Truewas accepted in silence and did nothing.spacr.settings.DEAD_SETTINGSnames every such key and the spelling that works instead.- Parameters:
settings – settings resolved from defaults plus file; mutated in place.
overrides – raw
key=valuestrings from the command line.module – the module being run, used only for the error message.
- Returns:
settings.- Raises:
SettingsError – on an unknown key, a key nothing reads, or an uncoercible value.
- spacr.cli.build_parser() argparse.ArgumentParser[source]¶
Return the
spacr-runargument parser.Building the parser imports nothing beyond the standard library, so
spacr-run --helpis instant even on a node with a cold NFS cache.- Returns:
the parser.
- spacr.cli.cmd_list(_args: argparse.Namespace) int[source]¶
--list— print every module that can run headless.
- spacr.cli.cmd_run(args: argparse.Namespace) int[source]¶
<module> --settings f— run the module, or print the plan with--dry-run.
- spacr.cli.cmd_validate(args: argparse.Namespace) int[source]¶
validate --settings f— pre-flight only, nothing is executed.
- spacr.cli.coerce_value(key: str, text: str, current: Any, expected_types: Mapping[str, Any], app: str = '') Any[source]¶
Coerce a
--set key=valuestring into the type the setting expects.The type comes from
spacr.settings.expected_typeswhen the key is declared there, otherwise from the type of the value the key already holds. A value that cannot be coerced is an error rather than a silently-stored string:cell_mask_dim='4'is exactly the bug the settings CSV round trip keeps producing, and measure_crop only notices it an hour in.- Parameters:
key – settings key.
text – the raw text after the first
=.current – the value
keyholds before the override.expected_types –
spacr.settings.expected_types.app – module key, so a key two pipelines share is read as the module being run means it (see
_APP_TYPE_OVERRIDES).
- Returns:
the coerced value.
- Raises:
SettingsError – when
textis not a legal value forkey.
- spacr.cli.import_entry(module: Module) Callable[..., Any][source]¶
Import and return the pipeline callable for
module.Deliberately late: this is where torch, cellpose and the rest of the heavy stack finally load, long after
--helpand--listhave answered.- Parameters:
module – module whose entry point is wanted.
- Returns:
the callable.
- Raises:
SettingsError – when the module or attribute cannot be imported.
- spacr.cli.load_settings_file(path: Any) Dict[str, Any][source]¶
Load a settings file written by the GUI, a pipeline run or a run journal.
- Parameters:
path – path to a
.csvor.jsonsettings file.- Returns:
the settings dict.
- Raises:
SettingsError – when the path is missing, unreadable or malformed. Never a traceback — a cluster job should fail with a sentence.
- spacr.cli.main(argv: Sequence[str] | None = None) int[source]¶
spacr-runentry point.- Parameters:
argv – argument list;
sys.argv[1:]when None.- Returns:
process exit code — 0 success, 1 the module raised, 2 bad arguments or settings. Any other integer comes from a pipeline that raised
SystemExititself;cmd_run()passes that code through unchanged.
- spacr.cli.module_defaults(module: Module) Dict[str, Any][source]¶
Return a fresh defaults dict for
module.Calls the same helper the pipeline itself uses to canonicalize its settings, so the resolved dict the CLI prints is the one the pipeline will see. That is usually a
spacr.settingsfunction (Module.defaults); for the pipelines that keep their own it isModule.defaults_entry, imported here rather than at module load so--liststays instant.- Parameters:
module – the module whose defaults are wanted.
- Returns:
dict of defaults; empty when the pipeline has no helper.
- Raises:
SettingsError – when the defaults module will not import. No empty
{}is returned in that case, becauseresolve_settings()uses this function on the run path: with empty defaults, a valid override such asspacr-run convert --set z_handling=maxwould be rejected as an unknown setting instead of reporting the missing dependency.--describehas its own guard around this call.
- spacr.cli.render_module_description(module: Module) str[source]¶
Render the
--describeblock for one module.- Parameters:
module – module to describe.
- Returns:
the description as one string, no trailing newline.
- spacr.cli.render_module_list() str[source]¶
Render the
--listtable of headless-runnable modules.- Returns:
the table as one string, no trailing newline.
- spacr.cli.render_settings(settings: Mapping[str, Any]) str[source]¶
Render a resolved settings dict as an aligned, sorted table.
- Parameters:
settings – the resolved settings.
- Returns:
the table as one string, no trailing newline.
- spacr.cli.resolve_module(name: Any) Module | None[source]¶
Return the
Modulefor a user-typed name, or None.- Parameters:
name – module key, alias, or the bare name of the pipeline function.
- Returns:
the matching
Module, or None when nothing matches.
- spacr.cli.resolve_settings(module: Module, settings_path: str | None, overrides: Sequence[str] = (), moved: List[Any] | None = None) Dict[str, Any][source]¶
Build the settings dict that the pipeline receives.
Layered lowest-to-highest: the module’s own defaults, the settings file, then the
--setoverrides. The file is read under today’s names first – see_under_todays_names(), without which a value the file set under an old name lost to the default already sitting under the new one. An object diameter of 0 or “auto” comes out as “auto”, the one form both are stored in (item 696).- Parameters:
module – module being run.
settings_path – path to the settings file, or None for defaults only.
overrides –
key=valuestrings.moved – list to receive the migration notices, passed straight to
_under_todays_names(); a caller that prints a pre-flight report hands these to_preflight()so the keys the fold consumed are still named there.
- Returns:
the fully-resolved settings dict.
- Raises:
SettingsError – on any unreadable file, unknown key or bad value.
- spacr.cli.setup_logging(verbose: bool = False) logging.Logger[source]¶
Configure timestamped logging to stdout for a batch run.
- Parameters:
verbose – raise the level from INFO to DEBUG.
- Returns:
the
spacr.clilogger.
- spacr.cli.use_agg_if_headless() bool[source]¶
Force matplotlib’s Agg backend when there is no display.
Called before the first spaCR import that could pull pyplot. An explicit
MPLBACKENDin the environment always wins, and interactive local use (a display is present) is left alone, so this applies only without a display.- Returns:
True when Agg was forced.
- spacr.cli.write_settings_file(settings: Mapping[str, Any], path: Any) List[str][source]¶
Write a settings dict to a CSV that the GUI and
spacr-runread back.The inverse of
load_settings_file(), and the file the GUI’s “Export settings…” button writes. The layout is the onespacr.utils.save_settings()writes next to every run: aKey,Valueheader, one row per setting in the dict’s order,Noneas an empty cell, booleans asTrue/False, and lists, tuples and dicts in Python literal form. The settings are written as given: unlikesave_settings,test_modeandplotare not turned off, and nothing is listed, scanned or opened except the files written.A
.jsoncopy is written beside the CSV, assave_settingswrites one, so the types survive exactly;spacr-run --settingsreads either.- Parameters:
settings – the settings to write.
path – the CSV to write;
~and$VARSare expanded and the folder is created.
- Returns:
the CSV and JSON paths written, in that order.
Example
from spacr.cli import load_settings_file, write_settings_file write_settings_file({"src": "/data/plate1", "channels": [0, 1]}, "measure_settings.csv") load_settings_file("measure_settings.csv")
Nested helpers¶
- _NoShow.__enter__._close_instead(*args: Any, **kwargs: Any) None¶
Close the captured current figure instead of displaying it.
- Parameters:
args – ignored positional arguments accepted for
showsignature compatibility.kwargs – ignored keyword arguments accepted for compatibility.
- Returns:
None. Backend and close errors are deliberately swallowed so optional visualization cleanup cannot fail a headless run.
spacr/cli.py:1680