all2md.linter
Document linter for the all2md AST.
The linter inspects a Document (regardless of the source format it was
parsed from) and reports structural, heading, link, and typography issues
via a rule-based engine.
Typical use:
from all2md import to_ast
from all2md.linter import lint_document, LintConfig
doc = to_ast("whitepaper.pdf")
result = lint_document(doc)
for violation in result.violations:
print(violation)
The CLI entry point is exposed as all2md lint.
- class all2md.linter.AppliedFix
Bases:
objectRecord of a fix that was (or would have been) applied.
- rule_code: str
- line: int | None
- description: str
- safety: FixSafety
- to_dict() dict
Serialise to a plain dict for the JSON reporter.
- __init__(rule_code: str, line: int | None, description: str, safety: FixSafety) None
- class all2md.linter.FixContext
Bases:
objectMutation primitives passed to
LintFix.apply.The parent map is built lazily on first use, so fixes that only mutate
Text.contentnever pay for it.Initialise a context for
document.The parent map is not built until the first call to
parent_of(),remove(), orreplace().- __init__(document: Document) None
Initialise a context for
document.The parent map is not built until the first call to
parent_of(),remove(), orreplace().
- remove(node: Node) bool
Detach
nodefrom its parent.Returns
Trueon success,Falseif the node has no parent or the parent’s container does not hold it (e.g. the node has already been detached by an earlier fix in the same run).
- class all2md.linter.FixSafety
Bases:
IntEnumSafety classification for an auto-fix.
Lower numeric value means safer / less invasive.
apply_fixesuses<= max_safetyto decide which fixes to apply, soSAFEfixes run under both--fixand--fix-unsafewhileSUGGESTEDfixes run only under--fix-unsafe.- SAFE = 1
- SUGGESTED = 2
- MANUAL = 3
- property label: str
Lowercase label suitable for human output (‘safe’, ‘suggested’, ‘manual’).
- __new__(value)
- class all2md.linter.LintConfig
Bases:
CloneFrozenMixinFrozen configuration for a lint run.
- Parameters:
enabled_rules (frozenset[str] or None) – When set, only rules whose code appears in this whitelist run.
None(the default) means “every registered rule is enabled”.disabled_rules (frozenset[str]) – Rules to skip. Applied after
enabled_rules.severity_overrides (dict[str, Severity]) – Map of rule code to severity, overriding the rule’s
default_severity.rule_options (dict[str, dict[str, Any]]) – Per-rule option dictionaries, keyed by rule code. Forwarded to rules via
all2md.linter.rule.LintContext.config.severity_threshold (Severity) – Minimum severity to report. Violations below this level are dropped by the runner before results are returned. Defaults to
INFO(everything is reported).
- enabled_rules: frozenset[str] | None = None
- disabled_rules: frozenset[str]
- severity_overrides: dict[str, Severity]
- rule_options: dict[str, dict[str, Any]]
- severity_threshold: Severity = 1
- is_rule_enabled(code: str) bool
Return True if the rule with
codeshould run under this config.
- get_severity(rule_cls: type[LintRule]) Severity
Return the effective severity for
rule_cls, honouring overrides.
- get_rule_options(code: str) dict[str, Any]
Return the option dict for
code(empty dict if none configured).
- classmethod from_dict(data: dict[str, Any] | None) LintConfig
Build a
LintConfigfrom a plain dict (as produced by the TOML loader).Recognised keys:
enable/enabled_rules: list of rule codes (whitelist)disable/disabled_rules: list of rule codes (blacklist)severity: dict of rule code -> severity namerules: dict of rule code -> per-rule options dictseverity_threshold: severity name (info/warning/error)
Unknown keys are silently ignored so config files can evolve without breaking older installs.
- __init__(enabled_rules: frozenset[str] | None = None, disabled_rules: frozenset[str] = <factory>, severity_overrides: dict[str, ~all2md.linter.violations.Severity]=<factory>, rule_options: dict[str, dict[str, ~typing.Any]]=<factory>, severity_threshold: Severity = Severity.INFO) None
- class all2md.linter.LintContext
Bases:
objectInput passed to every
LintRule.check()call.- document: Document
- file_path: str | None
- config: dict[str, Any]
- __init__(document: Document, file_path: str | None = None, config: dict[str, Any]=<factory>) None
- class all2md.linter.LintFix
Bases:
objectA fix attached to a
Violation.- Parameters:
target (Node) – The AST node this fix mutates or removes. Used for conflict detection (two fixes targeting the same node — the first one wins).
apply (Callable[[FixContext], None]) – In-place mutation of the AST. For text mutations the callback usually closes over
targetand rewritestarget.content. For structural fixes the callback usesctx.remove(target)orctx.replace(old, new).safety (FixSafety) – How aggressively to apply this fix.
description (str) – Short human-readable description of what the fix does, for reporters and logs.
- target: Node
- apply: Callable[[FixContext], None]
- safety: FixSafety
- description: str
- __init__(target: Node, apply: Callable[[FixContext], None], safety: FixSafety, description: str = '') None
- class all2md.linter.LintFixResult
Bases:
objectResult of running
lint --fixagainst a single document.Carries both the pre-fix and post-fix lint results so reporters can show “applied N fixes, M remaining” without recomputing.
- file_path: str | None
- initial: LintResult
- final: LintResult
- applied: list[AppliedFix]
- skipped_conflicts: list[AppliedFix]
- rewritten: bool = False
- property violations: list[Violation]
Post-fix violations — the ones the user still needs to address.
- property rules_checked: int
Forward to the post-fix lint result.
- property error_count: int
Forward to the post-fix lint result.
- property warning_count: int
Forward to the post-fix lint result.
- property info_count: int
Forward to the post-fix lint result.
- property total: int
Forward to the post-fix lint result.
- __init__(file_path: str | None, initial: LintResult, final: LintResult, applied: list[AppliedFix] = <factory>, skipped_conflicts: list[AppliedFix] = <factory>, rewritten: bool = False) None
- class all2md.linter.LintResult
Bases:
objectResult of linting a single document.
- file_path: str | None
- violations: list[Violation]
- rules_checked: int = 0
- property error_count: int
Return the number of ERROR-severity violations.
- property warning_count: int
Return the number of WARNING-severity violations.
- property info_count: int
Return the number of INFO-severity violations.
- property total: int
Return the total number of violations in this result.
- __init__(file_path: str | None, violations: list[Violation] = <factory>, rules_checked: int = 0) None
- class all2md.linter.LintRule
Bases:
ABCAbstract base class for lint rules.
Subclasses must set the class-level metadata attributes (
code,name,category,description,default_severity) and implementcheck().- code: str
- name: str
- category: str
- description: str
- default_severity: Severity
- abstractmethod check(ctx: LintContext) list[Violation]
Inspect
ctx.documentand return any violations found.
- build_violation(message: str, *, severity: Severity | None = None, line: int | None = None, column: int | None = None, node_type: str | None = None, suggestion: str | None = None, context: str | None = None, fix: 'LintFix' | None = None) Violation
Construct a
Violationpre-populated with this rule’s metadata.
- class all2md.linter.LintRunner
Bases:
objectRun a
LintConfigover one or more documents.Initialise the runner with a config and a registry.
Either argument can be omitted; defaults are a blank
LintConfigand the globalrule_registry.- __init__(config: LintConfig | None = None, registry: RuleRegistry | None = None) None
Initialise the runner with a config and a registry.
Either argument can be omitted; defaults are a blank
LintConfigand the globalrule_registry.
- lint_document(doc: Document, file_path: str | None = None) LintResult
Run all enabled rules against
docand return aLintResult.
- lint_file(file_path: str | Path) LintResult
Parse
file_pathto an AST and lint it.
- lint_files(file_paths: list[str | Path]) list[LintResult]
Run
lint_file()against every path in the list.
- lint_and_fix_document(doc: Document, *, file_path: str | None = None, max_safety: FixSafety = FixSafety.SAFE, max_passes: int = 5) LintFixResult
Lint
doc, apply attached fixes (in place) to fixpoint, re-lint.When two fixes target the same node,
apply_fixes()skips the later one — so a single pass may leave correctable violations untouched. The runner re-runs the lint+fix cycle until no fixes apply (ormax_passesis reached), which converts the per-call “first-wins” policy into “run to fixpoint” at the user level.max_passesis a safety cap that surfaces non-idempotent fixes (the loop would otherwise oscillate forever). Five passes is plenty for the v2.0 SAFE fixes — the deepest natural cascade is TYP001 → TYP002 on the same node, which converges in two.The document is mutated in place. Callers that need to write the result back to disk should serialise via the markdown renderer when
LintFixResult.rewrittenis true.
- lint_and_fix_file(file_path: str | Path, *, max_safety: FixSafety = FixSafety.SAFE, write: bool = True) LintFixResult
Parse
file_path, lint+fix, and (optionally) rewrite the file.When
write=True(the default) and at least one fix was applied, the mutated AST is serialised back tofile_pathviaall2md.renderers.markdown.MarkdownRenderer. A clean file (no fixes applied) is never rewritten, sidestepping spurious renderer-canonicalisation drift.
- class all2md.linter.RuleRegistry
Bases:
objectSingleton registry mapping rule codes to
LintRuleclasses.Return the process-wide singleton instance.
- static __new__(cls) RuleRegistry
Return the process-wide singleton instance.
- unregister(code: str) bool
Remove a registered rule by code. Returns True if found.
- has_rule(code: str) bool
Return True if
codeis registered.
- list_rules(category: str | None = None) list[str]
List all registered rule codes, optionally filtered by category.
- clear() None
Remove every registered rule and reset initialisation state.
Primarily intended for tests that need a clean slate.
- discover_plugins() int
Load rule classes from the
all2md.lint_rulesentry point group.Each entry point is expected to return a
LintRulesubclass. Failures are logged but do not abort discovery.
- class all2md.linter.Severity
Bases:
IntEnumSeverity levels for lint violations.
Higher numeric value means more severe. The ordering enables simple threshold filtering: drop any violation whose severity is below the configured threshold.
- INFO = 1
- WARNING = 2
- ERROR = 3
- classmethod from_name(name: str) Severity
Parse a severity from a case-insensitive name (info/warning/error).
- property label: str
Lowercase label suitable for human output (‘error’, ‘warning’, ‘info’).
- __new__(value)
- class all2md.linter.Violation
Bases:
objectA single lint violation emitted by a rule.
- rule_code: str
- rule_name: str
- message: str
- severity: Severity
- line: int | None
- column: int | None
- node_type: str | None
- suggestion: str | None
- context: str | None
- fix: 'LintFix' | None
- property fixable: bool
True iff an auto-fix is attached to this violation.
- to_dict() dict
Serialize the violation to a plain dict (used by the JSON reporter).
- __init__(rule_code: str, rule_name: str, message: str, severity: Severity, line: int | None = None, column: int | None = None, node_type: str | None = None, suggestion: str | None = None, context: str | None = None, fix: 'LintFix' | None = None) None
- all2md.linter.apply_fixes(doc: Document, violations: list['Violation'], max_safety: FixSafety) tuple[list[AppliedFix], list[AppliedFix]]
Apply every fix attached to
violationswhose safety is<= max_safety.Conflict policy: when two fixes target the same node (by
id()), the first one (in deterministic outer-to-inner, top-to-bottom order) is applied; subsequent fixes targeting that node are deferred and returned inskipped_conflicts. Users re-run--fixto converge.- Returns:
Two lists of
AppliedFixrecords — the first describes fixes that ran, the second describes fixes that were deferred because an earlier fix already touched their target.- Return type:
- all2md.linter.lint_and_fix_document(doc: Document, config: LintConfig | None = None, *, file_path: str | None = None, max_safety: FixSafety = FixSafety.SAFE) LintFixResult
Lint and fix
docin place; re-lint and return the combined result.
- all2md.linter.lint_and_fix_file(file_path: str | Path, config: LintConfig | None = None, *, max_safety: FixSafety = FixSafety.SAFE, write: bool = True) LintFixResult
Parse, lint+fix, and rewrite
file_path.
- all2md.linter.lint_document(doc: Document, config: LintConfig | None = None, file_path: str | None = None) LintResult
Lint an already-parsed
Document.
- all2md.linter.lint_file(file_path: str | Path, config: LintConfig | None = None) LintResult
Parse
file_pathinto an AST and lint it withconfig.
For a high-level overview and worked examples, see Linter.