spacr.crashreport¶
Create an attachable diagnostic bundle with spacr-crashreport.
This module produces one .zip with the logs, settings, versions and
diagnostics a bug report needs, for attaching to a GitHub issue, and it is
assembled out of the pieces spaCR already keeps rather than collected a second
time:
spacr.doctorruns 17 checks over the installation — the running checkout, duplicate installs, the GPU, the Cellpose version, the project database, the settings — and every non-PASSrow already carries the fix.spacr.runctxwrites a per-run JSONL log keyed by run id, so “show me everything from the run that produced this” has an answer. The bundle carries that run’s log verbatim, not a filtered summary of it.spacr.errorsstamps each run’s status onto the artifact it wrote, so the report can say whether the last run finished — which is a different question from whether it crashed just now, and the more useful one when a user reports numbers rather than a traceback.
Design rules:
Collectors do not raise. Every collector runs through _collect(),
which turns a failure into a manifest entry naming what could not be gathered
and why. Catching Exception is acceptable here because the failure is
recorded in the output: the manifest is part of the bundle, so a missing
section is visible to the report recipient instead of silently absent.
Bounded size. spacr.log rotates but a single run can still write
hundreds of megabytes. The bundle takes the tail of it, capped by
MAX_LOG_BYTES, and the manifest records how much was dropped.
Nothing secret. Environment variables are included because SPACR_*,
CUDA_* and PATH explain a large fraction of “works here, not there” —
but a value whose name looks like a credential is replaced with
"<redacted>" and listed by name in the manifest, so you can see what was
withheld and the report recipient can see that something was. Absolute paths are
kept: they name the checkout, the plate and the database, and a report with
them stripped cannot be acted on.
Deterministic layout. File names and ordering remain stable so repeated reports are quick to inspect.
Anonymous reports¶
The bundle above stays on this computer. What may leave it is a separate,
much smaller thing: public_summary(), an allowlisted record of the
versions, the operating-system family, the GPU class, the module, the
exception type, a scrubbed one-line message and the call stack as file
basenames, function names and line numbers. No log, setting, environment
variable or path is in it. submit_public_summary() posts it to spaCR’s
relay (REPORT_RELAY_URL), which files it as a public issue on
EinarOlafsson/spacr, so a user needs no GitHub account. The switch is
anonymous_reports_enabled(), on unless the user turns it off; with it
off, or with NO_NETWORK_ENV set, nothing is sent. A report that
cannot be sent waits in a small queue (QUEUE_LIMIT reports,
QUEUE_RETENTION_S seconds) and is tried again later.
diagnose_remotely() sends the same summary to the relay’s hosted model
for advice.
Usage¶
python -m spacr.crashreport # last run, current folder
python -m spacr.crashreport --run-id 3f9c1a2b # one particular run
python -m spacr.crashreport --db plate1/measurements/measurements.db \
--settings measure.csv --app measure -o ~/spacr-bug.zip
from spacr.crashreport import report_exception, install_excepthook
install_excepthook() # any unhandled crash writes a bundle
try:
measure_crop(settings)
except Exception as exc: # noqa: BLE001 - reporting, then re-raising
print(report_exception(exc, settings=settings))
raise
Exceptions¶
The relay did not answer a hosted diagnosis. |
Classes¶
Collected report sections and manifest, before serialisation. |
Functions¶
|
The fixed prompt that asks the user's own AI to summarise a report. |
|
Whether the one-time notice about anonymised reports is still to show. |
|
Whether any failure is sent as an anonymised report. |
|
Return the |
|
Return whether a failure is a fault or a user error. |
|
Collect the crash-report sections into a |
|
Ask the relay's hosted model about an error, with no account. |
|
Return the "AI Error report" choice, a key of |
|
Build a summary from a made-up error, to show what a report holds. |
|
|
|
The error's signature, computed as the relay computes it. |
|
Ask the relay how much of today's free allowance this install has left. |
|
Return the id of the most recent run this machine logged. |
|
Try to send every waiting report, oldest first. |
|
Write a crash report on any unhandled exception, then behave as before. |
|
Return this profile's random install id, creating it on first use. |
|
Command-line entry point. |
Record that the notice about anonymised reports has been shown. |
|
|
Whether a choice adds each spaCR frame's source line to a report. |
|
Whether a choice has the AI explain every failure in the console. |
|
Whether a choice files a failure of |
|
Whether a report or a hosted diagnosis may use the network at all. |
|
Return the next 00:00 UTC, when the relay's daily allowances reset. |
|
A likely-cause key as the relay keeps it: kebab-case, 5 words, 40 chars. |
|
A question as both caches compare it. |
|
The computer and libraries a report was made on, without any name. |
|
What the run's source folder holds, without any name in it. |
|
The console's last lines before a failure, scrubbed and cut. |
|
Scrub settings problems into short lines that may be published. |
|
The settings a run used, with nothing personal left in them. |
|
Build the anonymised summary of an error that may be published. |
|
The key a question's saved answer is kept under, or |
|
Return the reports waiting to be sent, oldest first. |
|
Return the relay address reports are posted to, without a trailing slash. |
|
Write a crash report about |
|
Keep an explanation on this computer for the next identical error. |
|
The explanation saved on this computer for an error, if still good. |
|
Scrub a text like |
|
Remove anything personal from a short text that will be published. |
|
Turn reports on or off, as the first-run notice's switch does. |
|
Save the "AI Error report" choice. |
|
Take the |
|
Send an anonymised summary to the relay, or queue it for later. |
Queue a summary now and send it on a daemon thread. |
|
|
Gather a report and write it as one zip. |
Module Contents¶
- exception spacr.crashreport.RelayRefused(reason: str, detail: str = '', scope: str = '', resets_at: datetime.datetime | None = None)[source]¶
Bases:
RuntimeErrorThe relay did not answer a hosted diagnosis.
- Parameters:
reason –
"quota"when a free daily limit is used up,"network"when the relay could not be reached or the network is forbidden, and"error"for any other refusal.detail – what the relay or the network said, for the log.
scope – for a quota refusal, whose limit it was:
"install"(this install’s questions),"global"(spaCR’s shared free capacity) or"ip".resets_at – when the limit resets, as an aware UTC datetime; defaults to the next 00:00 UTC.
The values are kept on the exception as
reason,scopeandresets_at.Keep the reason, scope and reset time, and the detail as the message.
- class spacr.crashreport.CrashReport[source]¶
Collected report sections and manifest, before serialisation.
Kept as data rather than written straight to a zip so that a caller — a test, the Qt layer, a support script — can inspect or re-render it without a temporary file, and so that
write_crash_report()has nothing in it but serialisation.- Parameters:
created_utc – when the report was made, ISO-8601.
run_id – the run it is about, empty when none could be identified.
sections – file name to text, exactly as it will appear in the zip.
manifest – what was collected, what was not, and why. Every entry that failed carries the exception text; every entry that was truncated carries the byte counts.
- summary() str[source]¶
One short block naming the run, the versions and what is missing.
This is also
summary.txt, the first file in the bundle, because a report recipient opening a zip should not have to guess which file to read first.- Returns:
the summary text.
- property omitted: List[str][source]¶
Names of the sections that had nothing to collect.
No settings file was named, the project has no database, the run wrote no warnings. Each is a fact about the invocation rather than a failure, and each is still listed, because the report recipient must be able to tell “there were no warnings” from “the warnings were not gathered”.
- Returns:
section names, in collection order.
- spacr.crashreport.ai_summary_prompt(summary: Mapping[str, Any]) str[source]¶
The fixed prompt that asks the user’s own AI to summarise a report.
The AI sees only the anonymised summary, never the traceback, log or settings, and what it writes is scrubbed again before it is sent.
- Parameters:
summary – a summary from
public_summary().- Returns:
the prompt.
- spacr.crashreport.anonymous_report_notice_due() bool[source]¶
Whether the one-time notice about anonymised reports is still to show.
- Returns:
Trueuntilmark_anonymous_report_notice_shown().
- spacr.crashreport.anonymous_reports_enabled() bool[source]¶
Whether any failure is sent as an anonymised report.
- Returns:
Falsefor the choices that send nothing.
- spacr.crashreport.build_parser() argparse.ArgumentParser[source]¶
Return the
spacr-crashreportargument parser.- Returns:
the parser, built separately so a test can exercise the argument surface without running a collection.
- spacr.crashreport.classify_failure(exception: BaseException | None = None, traceback_text: str = '') str[source]¶
Return whether a failure is a fault or a user error.
A
spacr.errors.ConfigurationError, or any of its subclasses, is a user error: a setting, a validation or a missing input the user can fix. Anything else is a fault.- Parameters:
exception – the exception, when at hand.
traceback_text – a formatted traceback otherwise.
- Returns:
KIND_USER_ERRORorKIND_FAULT.
- spacr.crashreport.collect(run_id: str | None = None, *, db: os.PathLike | None = None, settings: os.PathLike | None = None, settings_values: Mapping[str, Any] | None = None, checkout: os.PathLike | None = None, app: str = '', exception: BaseException | None = None, note: str = '', probe_gpu: bool = False) CrashReport[source]¶
Collect the crash-report sections into a
CrashReport.Composed from
spacr.doctor,spacr.runctx,spacr.errorsandspacr.logging_utilrather than collected again, so the report cannot disagree with what those tools say when run directly.- Parameters:
run_id – the run to include. Defaults to
find_last_run_id(), which prefers an activerun_contextover the newest log on disk.db – project database, e.g.
plate1/measurements/measurements.db. Adds the doctor’s database checks and the run-status stamps.settings – settings csv/json that was run.
settings_values – the settings as a mapping, when the caller has them in memory. Takes precedence over
settings.checkout – directory the user believes they are editing; defaults to the current one; the report states whether the running spaCR is imported from it.
app – app key for the settings file (
mask,measure, …).exception – the exception being reported, if any. Its traceback goes in verbatim.
note – free text from the user — what they were doing.
probe_gpu – let the doctor allocate on the GPU to prove it works. Off by default: a probe that fails would add a failure that is not the one being reported.
- Returns:
the assembled
CrashReport. Never raises; a section that could not be gathered is named inCrashReport.problems.
- spacr.crashreport.diagnose_remotely(summary: Mapping[str, Any], question: str = '', *, timeout: float = DIAGNOSE_TIMEOUT_S) Dict[str, str][source]¶
Ask the relay’s hosted model about an error, with no account.
Sends the anonymised summary and the scrubbed question, cut to
MAX_QUESTION_CHARS, to/diagnose. The answer is generated advice, not a checked fix. Blocks, so call it off the GUI thread.- Parameters:
summary – a summary from
public_summary().question – what the user asks; may be empty.
timeout – seconds the model may take.
- Returns:
{"advice": text, "model": model name}.- Raises:
RelayRefused – with
reason"quota"when the free daily limit is used up,"network"when the relay cannot be reached or the network is forbidden, and"error"otherwise.
- spacr.crashreport.error_report_mode() str[source]¶
Return the “AI Error report” choice, a key of
ERROR_REPORT_MODES.A profile that only ever turned the older on/off switch off reads as
"off".- Returns:
the saved key, or
DEFAULT_ERROR_REPORT_MODE.
- spacr.crashreport.example_public_summary() Dict[str, Any][source]¶
Build a summary from a made-up error, to show what a report holds.
The error’s message names a plate file under the home folder, an e-mail address and a token, so the example shows each one removed.
- Returns:
a summary built exactly as a real one is.
- spacr.crashreport.explanation_key(traceback_text: str, module: str = '', kind: str = '', message: str = '') str[source]¶
explanation_signature()of a failure, without the environment.Reads only the traceback, so it is cheap enough for the GUI thread and gives what the full
public_summary()of the same failure gives.- Parameters:
traceback_text – the formatted traceback.
module – the spaCR module key.
kind –
KIND_FAULTorKIND_USER_ERROR.message – the message sent in place of the exception’s own.
- Returns:
the signature.
- spacr.crashreport.explanation_signature(summary: Mapping[str, Any]) str[source]¶
The error’s signature, computed as the relay computes it.
The exception type, the module and the innermost five frames (
file:function); a user error adds its normalised problem list. The relay keys its saved explanations, and its issues, by the same value.- Parameters:
summary – a summary from
public_summary().- Returns:
the SHA-256 hex digest.
- spacr.crashreport.fetch_allowance(timeout: float = SEND_TIMEOUT_S) Dict[str, Any] | None[source]¶
Ask the relay how much of today’s free allowance this install has left.
A GET of
/allowance?install_id=...; it uses up nothing. Blocks, so call it off the GUI thread.- Parameters:
timeout – seconds the request may take.
- Returns:
{"ai": {"best_left", "light_left"}, "reports_left", "pool", "resets_at"}withpoolone ofbest,lightorexhaustedandresets_atan ISO-8601 UTC time;Nonewhen the network is forbidden or the relay gives no answer.
- spacr.crashreport.find_last_run_id(problems: List[str] | None = None) str[source]¶
Return the id of the most recent run this machine logged.
The run ids live one JSONL file per run under
spacr.runctx.runs_log_dir(), so “the last run” is the newest file there. An active run wins over it: inside arun_contextthe crash being reported is this run, not the one before it.- Parameters:
problems – optional list that anything which went wrong on the way to the answer is appended to. It distinguishes “no run was found” from “the run directory could not be read”.
collect()passes one and puts it in the manifest.- Returns:
the run id, or
''when none could be identified. Never raises – a missing log directory is an ordinary answer here.
- spacr.crashreport.flush_report_queue(timeout: float = SEND_TIMEOUT_S) Dict[str, int][source]¶
Try to send every waiting report, oldest first.
Stops at the first report that has to wait again (no network, a server error or the relay’s quota), so an offline computer makes one attempt, not twenty. Does nothing while another send is running.
- Parameters:
timeout – seconds each post may take.
- Returns:
{"sent": n, "dropped": n, "kept": n}.
- spacr.crashreport.install_excepthook(destination: os.PathLike | None = None, **kwargs: Any) Callable[source]¶
Write a crash report on any unhandled exception, then behave as before.
Chains rather than replaces: the previous
sys.excepthookstill runs, so the traceback the user is used to seeing still appears, with one line after it saying where the bundle is. With anonymised reports on (anonymous_reports_enabled()), the crash’spublic_summary()is also queued and sent on a background thread; the bundle is never sent.- Parameters:
destination – as
write_crash_report().kwargs – passed to
collect().
- Returns:
the hook that was installed, so a caller can restore
sys.excepthookto what it was.
- spacr.crashreport.install_id() str[source]¶
Return this profile’s random install id, creating it on first use.
The id is a random UUID, not derived from the computer, the user or the network. The relay uses it only to share its free allowance fairly.
- Returns:
the id as a string.
- spacr.crashreport.main(argv: Sequence[str] | None = None) int[source]¶
Command-line entry point.
python -m spacr.crashreport.- Parameters:
argv – arguments, defaulting to
sys.argv[1:].- Returns:
0when the bundle was written,1when it could not be — and never anything else; a section that could not be gathered does not change the exit code.
- spacr.crashreport.mark_anonymous_report_notice_shown() None[source]¶
Record that the notice about anonymised reports has been shown.
- spacr.crashreport.mode_code_context(mode: str | None = None) bool[source]¶
Whether a choice adds each spaCR frame’s source line to a report.
- Parameters:
mode – a key of
ERROR_REPORT_MODES; defaults to the saved one.- Returns:
Trueonly for"all_code".
- spacr.crashreport.mode_explains(mode: str | None = None) bool[source]¶
Whether a choice has the AI explain every failure in the console.
- Parameters:
mode – a key of
ERROR_REPORT_MODES; defaults to the saved one.- Returns:
Truefor the four “Summarize” choices.
- spacr.crashreport.mode_sends(kind: str, mode: str | None = None) bool[source]¶
Whether a choice files a failure of
kindas an issue.- Parameters:
kind –
KIND_FAULTorKIND_USER_ERROR.mode – a key of
ERROR_REPORT_MODES; defaults to the saved one.
- Returns:
whether it is sent.
- spacr.crashreport.network_allowed() bool[source]¶
Whether a report or a hosted diagnosis may use the network at all.
- Returns:
FalsewhenNO_NETWORK_ENVis set or spaCR runs in safe mode,Trueotherwise.
- spacr.crashreport.next_reset_utc(now: datetime.datetime | None = None) datetime.datetime[source]¶
Return the next 00:00 UTC, when the relay’s daily allowances reset.
- Parameters:
now – the moment to count from; defaults to now.
- Returns:
an aware UTC datetime.
- spacr.crashreport.normalise_cause(raw: Any) str[source]¶
A likely-cause key as the relay keeps it: kebab-case, 5 words, 40 chars.
- Parameters:
raw – the key as the AI wrote it.
- Returns:
the slug, or
"".
- spacr.crashreport.normalise_question(text: Any) str[source]¶
A question as both caches compare it.
Lower case, placeholders such as
<PATH>removed, and every run of whitespace or punctuation folded into one space, the way the relay’snormaliseQuestiondoes it.- Parameters:
text – the question.
- Returns:
the normalised question.
- spacr.crashreport.public_environment() Dict[str, Any][source]¶
The computer and libraries a report was made on, without any name.
OS version, CPU architecture and cores, memory rounded to whole GB, the GPU’s model and memory when torch already knows it, CUDA or MPS, the versions of spaCR’s main libraries and how spaCR was installed.
- Returns:
the facts that could be read.
- spacr.crashreport.public_input_facts(settings: Mapping[str, Any] | None, limit: int = 5000) Dict[str, Any][source]¶
What the run’s source folder holds, without any name in it.
Counts the image files directly in the folder (at most
limitentries are looked at), the extensions seen, and the shape, dtype and likely channel count of the first image, read from its header.- Parameters:
settings – the run’s settings.
limit – the most folder entries looked at.
- Returns:
{"images", "extensions", "shape", "dtype", "channels"}, only the keys that could be found;{}without a readable folder.
- spacr.crashreport.public_log_tail(text: str, lines: int = MAX_LOG_TAIL_LINES, limit: int = MAX_LOG_TAIL_CHARS) str[source]¶
The console’s last lines before a failure, scrubbed and cut.
- Parameters:
text – the console text.
lines – the most lines kept, from the end.
limit – the most characters kept, from the end.
- Returns:
the scrubbed excerpt.
- spacr.crashreport.public_problem_lines(problems: Sequence[Any], limit: int = MAX_USER_ERROR_MESSAGE_CHARS) List[str][source]¶
Scrub settings problems into short lines that may be published.
Paths and file names are taken out rather than replaced, so “src does not exist: /data/plate1” is sent as “src does not exist”.
- Parameters:
problems –
spacr.validate.Problemobjects, or texts.limit – the longest line, in characters.
- Returns:
one line per problem,
"setting: message"when the problem names a setting; duplicates and empty lines are dropped.
- spacr.crashreport.public_settings(settings: Mapping[str, Any] | None) Dict[str, Any][source]¶
The settings a run used, with nothing personal left in them.
Setting names are kept as they are; values go through
_public_setting_value(), sosrcreads<path: exists=no>when the folder is missing, without the folder’s name. At mostMAX_SETTINGSsettings, in name order.- Parameters:
settings – the run’s settings.
- Returns:
{name: value}.
- spacr.crashreport.public_summary(exception: BaseException | None = None, *, traceback_text: str = '', module: str = '', user_note: str = '', dry_run: bool = False, kind: str = '', code_context: bool = False, screen: str = '', error_text: str = '', message: str = '', settings: Mapping[str, Any] | None = None, log_text: str = '', run: Mapping[str, Any] | None = None, diagnostics: bool = False) Dict[str, Any][source]¶
Build the anonymised summary of an error that may be published.
Only the keys in
PUBLIC_SUMMARY_KEYSare present. The message is passed throughscrub_public_text()and cut toMAX_MESSAGE_CHARS; the frames are at mostMAX_FRAMES{file, function, line}records with the file as a basename, or<user-code>for a file outside spaCR, Python and its libraries. No log, setting, environment variable or path is read into it.- Parameters:
exception – the exception, with its traceback.
traceback_text – a formatted traceback, used when no exception object is at hand (a failed pipeline run reports one as text).
module – the spaCR module key the error happened in, such as
maskormeasure.user_note – what the user adds, scrubbed and cut to
MAX_NOTE_CHARS; left out when empty.dry_run – ask the relay to validate without filing; left out when false.
kind –
KIND_FAULTorKIND_USER_ERROR; worked out byclassify_failure()when empty.code_context – add each spaCR frame’s source line as
code. Frames of any other file never carry code.screen – the screen’s title as the user sees it, such as
Mask; at mostMAX_SCREEN_CHARScharacters.error_text – the error as the console showed it, scrubbed with its line breaks kept and cut to
MAX_ERROR_TEXT_CHARS.message – the message to send in place of the exception’s own, such as every problem a refused run found; a user error keeps its line breaks and may be
MAX_USER_ERROR_MESSAGE_CHARSlong.settings – the run’s settings; their names and public values (
public_settings()) and the source folder’s input facts (public_input_facts()) are added.log_text – the console text before the failure; its last lines are added, scrubbed (
public_log_tail()).run –
{"stage", "elapsed_s", "dry_run"}of the failed run.diagnostics – add the environment block (
public_environment()) and the exception chain.
- Returns:
the summary, ready for
submit_public_summary().
- spacr.crashreport.question_key(question: str, place: str = '', context_chars: int = 0) str[source]¶
The key a question’s saved answer is kept under, or
"".SHA-256 of the scrubbed, normalised question, the place it was asked (screen or module) and spaCR’s
major.minor, prefixedq:. A question with more thanQUESTION_CACHE_CONTEXT_MAXcharacters of question and pasted context is never saved.- Parameters:
question – what the user asked.
place – the screen or module it was asked on.
context_chars – characters of console context, code or traceback pasted with it.
- Returns:
the key, or
""when the answer must not be saved.
- spacr.crashreport.queued_summaries() List[Dict[str, Any]][source]¶
Return the reports waiting to be sent, oldest first.
Expired and surplus reports are deleted first.
- Returns:
the summaries.
- spacr.crashreport.relay_url() str[source]¶
Return the relay address reports are posted to, without a trailing slash.
REPORT_RELAY_ENVoverridesREPORT_RELAY_URLwhen it holds anhttps://address, or anhttp://one on this computer.- Returns:
the base address.
- spacr.crashreport.report_exception(exception: BaseException, destination: os.PathLike | None = None, **kwargs: Any) str[source]¶
Write a crash report about
exceptionand return where it went.The call to make from an
exceptblock that is about to re-raise.- Parameters:
exception – the exception being reported.
destination – as
write_crash_report().kwargs – passed to
collect().
- Returns:
the path written, or
''when even writing the file failed – which is reported on stderr rather than raised, because this is called from a failure path and must never replace the user’s exception with one of its own.
- spacr.crashreport.save_explanation(signature: str, text: str, tier: str = '', model: str = '', now: float | None = None, cause: str = '') None[source]¶
Keep an explanation on this computer for the next identical error.
At most
MAX_SAVED_EXPLANATIONSare kept, oldest dropped first, and expired ones are dropped on every write. A failed write is ignored.- Parameters:
signature – from
explanation_signature().text – the scrubbed explanation.
tier –
"best","light"or"".model – the model that wrote it, when known.
now – the time, in seconds since the epoch; defaults to now.
cause – the likely-cause key the AI gave, if any.
- spacr.crashreport.saved_explanation(signature: str, now: float | None = None) Dict[str, Any] | None[source]¶
The explanation saved on this computer for an error, if still good.
Good means written for this spaCR
major.minorand less thanSAVED_EXPLANATION_DAYSdays ago.- Parameters:
signature – from
explanation_signature().now – the time, in seconds since the epoch; defaults to now.
- Returns:
{"text", "tier", "model"}, orNone.
- spacr.crashreport.scrub_public_lines(text: Any, limit: int = MAX_AI_SUMMARY_CHARS, keep_docs_urls: bool = False) str[source]¶
Scrub a text like
scrub_public_text(), keeping its line breaks.Each line is scrubbed on its own, so a Markdown answer keeps its headings and numbered steps on lines of their own; runs of blank lines become one.
- Parameters:
text – the text; anything else is turned into text first.
limit – the longest result, in characters; a longer one is cut and ends in
….keep_docs_urls – as for
scrub_public_text().
- Returns:
the scrubbed text, lines joined by
\n.
- spacr.crashreport.scrub_public_text(text: Any, limit: int = MAX_MESSAGE_CHARS, keep_docs_urls: bool = False) str[source]¶
Remove anything personal from a short text that will be published.
Absolute paths (POSIX, Windows and network shares) become
<PATH>, file names with a data or code extension<FILE>, e-mail addresses<EMAIL>, web addresses<URL>, IP addresses<IP>, host names<HOST>, this computer’s login name<USER>, and anything shaped like a key, token or long hexadecimal or base64 string<TOKEN>. The redaction helpers ofspacr.qt.ai.issue_reportrun first, and these stricter rules after them.- Parameters:
text – the text; anything else is turned into text first.
limit – the longest result, in characters; a longer one is cut and ends in
….keep_docs_urls – keep the public spaCR addresses in
PUBLIC_DOCS_URLS, for an AI answer that links the documentation; every other address still becomes<URL>.
- Returns:
the scrubbed text on one line.
- spacr.crashreport.set_anonymous_reports_enabled(enabled: bool) None[source]¶
Turn reports on or off, as the first-run notice’s switch does.
On restores the default choice when the current one sends nothing; off is
"off".- Parameters:
enabled – whether reports may be sent.
- spacr.crashreport.set_error_report_mode(mode: str) None[source]¶
Save the “AI Error report” choice.
A choice that sends nothing also deletes every report still waiting, so nothing queued before the change is sent after it.
- Parameters:
mode – a key of
ERROR_REPORT_MODES.- Raises:
ValueError – for any other value.
- spacr.crashreport.split_cause(text: str) Tuple[str, str][source]¶
Take the
CAUSE: <slug>line off an AI answer.- Parameters:
text – the answer.
- Returns:
(answer without the line, slug or "").
- spacr.crashreport.submit_public_summary(summary: Mapping[str, Any], *, timeout: float = SEND_TIMEOUT_S) Dict[str, Any][source]¶
Send an anonymised summary to the relay, or queue it for later.
Blocks for up to
timeoutseconds, so call it off the GUI thread, or usesubmit_public_summary_in_background(). With reports off, or the network forbidden, nothing is sent and nothing is stored.- Parameters:
summary – a summary from
public_summary().timeout – seconds the post may take.
- Returns:
{"status": ...}withREPORT_SENT(andissue_number,issue_url,deduplicated),REPORT_QUEUED(kept to try again),REPORT_DROPPED(rejected, not retried) orREPORT_DISABLED.
- spacr.crashreport.submit_public_summary_in_background(summary: Mapping[str, Any], on_done: Callable[[Dict[str, Any]], None] | None = None) threading.Thread | None[source]¶
Queue a summary now and send it on a daemon thread.
The summary is on disk before this returns, so a process that ends at once still sends it the next time spaCR starts.
- Parameters:
summary – a summary from
public_summary().on_done – called with the outcome
submit_public_summary()describes, on the sending thread.
- Returns:
the thread, or
Nonewhen reports are off and nothing was queued.
- spacr.crashreport.write_crash_report(destination: os.PathLike | None = None, **kwargs: Any) str[source]¶
Gather a report and write it as one zip.
- Parameters:
destination – where to write. A directory gets
spacr-crashreport-<run id or timestamp>.zipinside it; anything else is used as the file name. Defaults to<log dir>/spacr-crashreport-<...>.zip, the spaCR log directory.kwargs – passed straight to
collect().
- Returns:
the absolute path of the file written.
- Raises:
OSError – only when the destination itself cannot be written, This failure is raised rather than recorded, so the caller is never given the path of a report that is not on disk.
Example
from spacr.crashreport import write_crash_report path = write_crash_report(db='plate1/measurements/measurements.db') print(f'attach {path} to the issue')
Nested helpers¶
- _doctor_sections.gather_json() str¶
Return retained Doctor rows as JSON, or raise if collection failed.
spacr/crashreport.py:509
- _doctor_sections.gather_text() str¶
Run Doctor, retain its rows and summary, and return formatted text.
spacr/crashreport.py:491
- _main_log_section.gather() str | None¶
Return the capped main-log tail and record its path and size facts.
spacr/crashreport.py:624
- _run_log_section.gather() str | None¶
Return the capped run-log tail and record its path and size facts.
spacr/crashreport.py:524
- _run_status_section.gather() str | None¶
Return project run-status rows as JSON when a database provides them.
spacr/crashreport.py:604
- _run_summary_section.gather() str | None¶
Return WARNING-and-higher run records as readable text, if any.
spacr/crashreport.py:548
- _settings_section.gather() str | None¶
Return explicit or recorded settings as JSON, or
Noneif absent.spacr/crashreport.py:574
- collect.environment() str¶
Return redacted environment JSON and record every redacted name.
spacr/crashreport.py:726
- install_excepthook.hook(kind, value, tb) None¶
Report, then delegate to the hook that was installed before.
spacr/crashreport.py:844