Skip to content

symfonic.diagnostics.inspection.source_checks

source_checks

What the adopter's source imports, judged without importing any of it.

Two checks share one scan, because they share one hard part: deciding what an import means without executing it. The framework's own tree is read with the T1.2.4 AST scanner -- the same scanner the repository's architecture gate uses -- so a name's public status is answered by the declaration in the shipped package rather than by a list transcribed into this module.

The rule for the architecture check is T4.3.1's, unchanged: a name is public when the module it is imported from declares it. What is new is where it is applied. T4.3.1 applied it to code the framework writes; this applies it to code the adopter wrote, which is the only place a broken import path can still be sitting today -- and T1.1.3's F-02 found four of those in the documentation alone.

architecture_violations

architecture_violations(context: InspectionContext, refs: tuple[ImportRef, ...]) -> list[InspectionFinding]

Adopter imports that reach past what the framework declares.

Source code in src/symfonic/diagnostics/inspection/source_checks.py
def architecture_violations(
    context: InspectionContext, refs: tuple[ImportRef, ...]
) -> list[InspectionFinding]:
    """Adopter imports that reach past what the framework declares."""
    modules = _framework_modules(context.resolved_framework_root())
    if not modules:  # pragma: no cover - defensive
        return []
    judged = [(ref, _judge(ref, modules)) for ref in refs]
    undeclared = [
        ref
        for ref, finding in judged
        if finding is not None and finding.code == "UNDECLARED-MODULE"
    ]
    findings = [
        finding
        for _, finding in judged
        if finding is not None and finding.code != "UNDECLARED-MODULE"
    ]
    return findings + _undeclared_module_rows(undeclared)

deprecated_imports

deprecated_imports(refs: tuple[ImportRef, ...]) -> list[InspectionFinding]

Imports whose behaviour has a documented new owner.

Source code in src/symfonic/diagnostics/inspection/source_checks.py
def deprecated_imports(refs: tuple[ImportRef, ...]) -> list[InspectionFinding]:
    """Imports whose behaviour has a documented new owner."""
    findings: list[InspectionFinding] = []
    for ref in refs:
        row = replacement_for(ref)
        if row is None:
            continue
        findings.append(
            InspectionFinding(
                code=row.domain,
                category="deprecated-import",
                severity=row.severity,
                subject=row.name,
                detail=(
                    f"imported from {ref.module}; still supported, and "
                    f"{row.reason}"
                ),
                migration_action=(
                    f"The behaviour now lives in {row.replacement} "
                    f"(guide 22, {row.domain})."
                ),
                location=_where(ref),
            )
        )
    return findings

project_imports

project_imports(context: InspectionContext) -> tuple[ImportRef, ...]

Every symfonic import in the adopter's own source, in file order.

Source code in src/symfonic/diagnostics/inspection/source_checks.py
def project_imports(context: InspectionContext) -> tuple[ImportRef, ...]:
    """Every ``symfonic`` import in the adopter's own source, in file order."""
    refs: list[ImportRef] = []
    for source, text in context.sources():
        refs.extend(scan_symfonic_imports(text, source))
    return tuple(refs)