epythet.validation.model

The finding and report model shared by every level of epythet validate.

One in-memory model, several renderers: the human table, the JSON document and the JSONL stream are all views over the same Report, so they can never disagree. The vocabulary here (severities, levels, exit codes) is the one fixed in the epythet v2 decision record (discussion #15, decision D8).

Levels are named by what artifact they read, not by when they run:

level

name

reads

0

lint

the source text of each docstring (ruff, pydoclint)

0.5

parse

the docutils doctree of each docstring, in isolation

1

build

the Sphinx warning stream

2

render

the built output: XML, HTML and text pages

3

review

a review packet for an in-session agent (never gates)

The CLI exposes them as a tier index (--level 0 runs level 0, --level 1 runs levels 0 and 0.5, --level 2 adds the build), which is what TIERS and levels_for_tier() translate.

Module Attributes

SEVERITY_RANK

Lower rank is worse.

LEVELS

Level number -> level name, in run order.

TIERS

Run order of the levels; index into this list is the CLI --level tier.

IMPLEMENTED_TIERS

0 lint, 1 parse, 2 build, 3 render, 4 review.

IMPLEMENTED_LEVELS

The levels those tiers run.

REVIEW_LEVEL

The review level never gates unless the caller asks for it (decision D8).

REVIEW_PACKET_RULE

The one level-3 finding every packet run emits; "a packet was written" is not a defect.

EXIT_FOR_LEVEL

Level -> exit code when that level has findings at or above the threshold.

Functions

levels_for_tier(tier)

The levels a CLI tier runs: every level up to and including the tier's.

severity_at_or_above(severity, threshold)

True when severity is at least as serious as threshold.

sort_findings(findings)

Stable order for every renderer: severity, then rule id, then location.

Classes

Finding(rule, severity, level, message[, ...])

One problem found in one place.

Report(package, package_dir, levels_run[, ...])

Everything one validate run produced, plus enough context to reproduce it.

Timer(durations, key)

Records how long each level took, as report.durations[level_name].

epythet.validation.model.EXIT_FOR_LEVEL: dict[float, int] = {0: 10, 0.5: 11, 1: 12, 2: 13, 3: 14}

Level -> exit code when that level has findings at or above the threshold.

class epythet.validation.model.Finding(rule, severity, level, message, file=None, line=None, object=None, detector='', evidence='', fix='', autofixable=False, strategy='', tool='epythet', ledger_occurrences=None)[source]

Bases: object

One problem found in one place.

rule is a ledger rule id (DR001) for levels 0.5 and 1, or the upstream tool’s code (D102, DOC101) for level 0, in which case tool names the tool. line is 1-based and, for docstring findings, the line the docstring literal starts on.

property location: str

file:line for the table renderer, or - when unknown.

to_dict()[source]

JSON-ready dict; the JSON and JSONL renderers emit exactly this.

Return type:

dict[str, Any]

epythet.validation.model.IMPLEMENTED_LEVELS = (0, 0.5, 1, 2, 3)

The levels those tiers run.

epythet.validation.model.IMPLEMENTED_TIERS = (0, 1, 2, 3, 4)

0 lint, 1 parse, 2 build, 3 render, 4 review.

Type:

Every CLI tier is implemented

epythet.validation.model.LEVELS: dict[float, str] = {0: 'lint', 0.5: 'parse', 1: 'build', 2: 'render', 3: 'review'}

Level number -> level name, in run order.

epythet.validation.model.REVIEW_LEVEL = 3

The review level never gates unless the caller asks for it (decision D8).

epythet.validation.model.REVIEW_PACKET_RULE = 'REVIEW'

The one level-3 finding every packet run emits; “a packet was written” is not a defect.

class epythet.validation.model.Report(package, package_dir, levels_run, findings=<factory>, durations=<factory>, objects_checked=0, objects_undocumented=0, notes=<factory>, epythet_version=None, sphinx_version=None, docutils_version=None, ledger_sources=<factory>, schema_version='1')[source]

Bases: object

Everything one validate run produced, plus enough context to reproduce it.

counts_by_severity()[source]

{"error": n, "warning": n, "info": n} over all findings.

Return type:

dict[str, int]

exit_code(fail_on='error', *, fail_on_review=False)[source]

The process exit code: 0 when clean, else the code of the first failing level.

The first (lowest) failing level is reported because it is the first gate a CI pipeline would have stopped at.

Return type:

int

>>> r = Report("p", "/p", [0, 0.5])
>>> r.exit_code()
0
>>> r.findings.append(Finding("DR001", "error", 0.5, "leak"))
>>> r.exit_code(), r.exit_code("info")
(11, 11)
>>> r.findings.append(Finding("D102", "warning", 0, "missing"))
>>> r.exit_code(), r.exit_code("warning")
(11, 10)
>>> r = Report("p", "/p", [3], [Finding("REVIEW", "info", 3, "packet")])
>>> r.exit_code(fail_on_review=True)
0
>>> r.findings.append(Finding("DR001", "warning", 3, "reviewer said so"))
>>> r.exit_code(), r.exit_code(fail_on_review=True)
(0, 14)
failing_levels(fail_on='error', *, fail_on_review=False)[source]

Levels with at least one finding at or above fail_on, in run order.

Level 3 (review) never counts unless fail_on_review is set, and then any finding a reviewer’s reply produced counts whatever its severity (the “packet written” finding never does): a review proposes, it does not gate (decision D8).

Return type:

list[float]

summary()[source]

The summary block of the JSON document.

Return type:

dict[str, Any]

to_dict()[source]

The JSON document described in decision D8.

Return type:

dict[str, Any]

epythet.validation.model.SEVERITY_RANK = {'error': 0, 'info': 2, 'warning': 1}

Lower rank is worse. Used for --fail-on comparisons.

epythet.validation.model.TIERS: list[float] = [0, 0.5, 1, 2, 3]

Run order of the levels; index into this list is the CLI --level tier.

class epythet.validation.model.Timer(durations, key)[source]

Bases: object

Records how long each level took, as report.durations[level_name].

>>> durations = {}
>>> with Timer(durations, "parse"):
...     pass
>>> list(durations) == ["parse"] and durations["parse"] >= 0
True
epythet.validation.model.levels_for_tier(tier)[source]

The levels a CLI tier runs: every level up to and including the tier’s.

Return type:

list[float]

>>> levels_for_tier(0)
[0]
>>> levels_for_tier(1)
[0, 0.5]
>>> levels_for_tier(2)
[0, 0.5, 1]
epythet.validation.model.severity_at_or_above(severity, threshold)[source]

True when severity is at least as serious as threshold.

Return type:

bool

>>> severity_at_or_above("error", "warning")
True
>>> severity_at_or_above("info", "warning")
False
epythet.validation.model.sort_findings(findings)[source]

Stable order for every renderer: severity, then rule id, then location.

Return type:

list[Finding]