Skip to content

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

Finding(level: str, code: str, where: str, message: str)

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

Report(findings: list = list(), strict: bool = False)

ok property

ok

No errors (and, with strict, no warnings).

lint

lint(obj, strict=False, max_combos=100000)

A System (or a Catalog) → Report. max_combos: the largest number of answer combinations tried per group of constraints that share questions.

silent_defaults

silent_defaults(func, names=None)

[(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

cmd_check(a)

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)).