solvi.check¶
solvi check: catalog lint.
solvi check: catalog lint — mistakes a catalog can carry for a long time before a decision shows them.
from solvi.check import lint
rep = lint(system) # a System (or a Catalog: the question checks are skipped)
print(rep); rep.ok # errors fail; warnings fail with strict=True (solvi check --strict)
solvi check myapp.decisions:system [--strict] [--json] # exit 0: no errors; 1: errors; 2: usage errors
Errors (a decision is, or can be, wrong or impossible):
then_not_in_flow a hard check sets then= for a question whose flow never runs it (the question's rule does not
read it, through any fact, and the question does not list it in requires): when the check
fails, the question is answered as if it had passed
then_unknown / then_bad_answer then= names no question / an answer that is not one of the question's options
cycle facts that need each other: none of them can be computed unless one is given (facts derived
from each other, each with a producer outside the loop, are a cycle for the deterministic
strategist only; with System(strategist=...) they are the note mutual_producers)
unanswerable a question no input can answer (its flow needs a fact nothing can compute; a span / rank /
estimate question without a rule — a learned head cannot answer it; a missing required part)
type_conflict a producer's return type that its consumer cannot read; a System(input_model=...) field that a typed
reader cannot read
constraint_never_holds / constraints_conflict constraints between answers that no combination of answers satisfies
(brute force over the answers' finite domains)
constraint_unknown_question a constraint that reads a name that is not a question: it never applies
validate_reads_unknown a producer's validate requires an argument no producer of its fact takes as an input:
it cannot run, so every output of that producer is rejected (a System refuses such a catalog)
hard_check_untyped a hard check without -> bool with a return that is plainly not True or False (return 0,
return None, a bare return, a string): on that path the check is rejected and every
question it governs abstains — return a bool and declare it -> bool
rule_returns_non_option a rule with return <literal> (also in a if c else b) that is not one of its question's
options (return "aprove"): on that path the question abstains
Warnings:
unused_part a part no question's flow uses (a question without a rule, fit or uses uses everything computable:
those are the candidate features of the head it will be fitted with)
then_on_soft_check then= on a check that is not hard: it is ignored
question_as_fact a rule reads a question's name: answers are not facts, so it must be given in the input
reader_types_differ typed readers of a given fact, or alternative producers of a fact, that no value can satisfy both
dead_option an option the constraints rule out whatever the other answers are
constraint_raises a constraint that raises on some combination of answers (it counts as broken)
unused_rule a rule registered for a question that is not among the System's questions: it never runs
input_not_declared with System(input_model=Model): a part or a rule reads a name that is neither computed by a part nor
a field of the model (amout for amount) — it can only arrive as an extra key of the input;
when the model forbids extra keys it can never arrive
uses_unknown with System(input_model=Model): a question's uses hint names something that is neither a part nor
a field of the model. (Without an input model a uses name that is not a part is taken for a
given fact — a learned head may read given facts directly — so a typo there cannot be told;
a misspelled requires entry is an error: unanswerable)
silent_default in a function that reads the input (a given fact): x or <literal> or .get(k, <literal>) on
that input (amount or 0, order.get("total", 0); not a lookup in a constant table or a
default on a computed value), which turns a missing, empty or null input into a value nobody
gave — say so explicitly (check
for None and abstain, or declare the default in System(input_model=...)); # solvi: ok on the line
accepts it
Notes (never fail):
mutual_producers facts derived from each other (net from gross and gross from net), each with a producer
outside the loop: the system's strategist plans around it
no_rule a question without a rule answers only after fit (it abstains until then)
The flows are planned with every given fact present (solvi.strategist.given_facts), as solvi serve does, by the
system's own strategist (System(strategist=)) — the deterministic one when none is set.
Finding
dataclass
¶
One problem solvi check found: its level (error, warning or note), code (see the module docs), where (a part,
a question, a constraint or file:line) and the message.
Report
dataclass
¶
lint ¶
A System (or a Catalog) → Report. max_combos: the largest number of answer combinations tried per group of
constraints that share questions.
silent_defaults ¶
[(line, snippet, why)] in a function's source: x or <literal> and .get(k, <literal>) (the literal not None),
except on lines marked # solvi: ok. [] when the source is not available. names: the function's arguments that are
given inputs — then only a default on one of them counts (amount or 0, order.get("total", 0), order["x"] or
0), not one on a constant table ({...}.get(kind, 1)), a computed value (math.sqrt(v) or 1e-9) or an
exception's attribute (e.lineno or 0).
cmd_check ¶
solvi check (see solvi.cli) → 0: no errors (with --strict: no warnings either); 1: problems. The target is
module:attr / file.py:attr, or — as for solvi test — a task file or a directory holding task.py (its System:
task.system(), else System(task.cat, task.QUESTIONS)).