Skip to content

Static preflight

SourceInspector.inspect(path) reads and hashes Python source, parses its AST, and compiles the tree to check syntax and scope rules. It never executes that code, imports the target, imports its dependencies, or evaluates expressions. The exact inspected bytes remain available to the explicit execution boundary.

The inspector records imports, functions, parameter names and kinds, annotations, literal defaults, classes, base references, class attributes, module variables, and a literal __version__. Every import statement is recorded, including imports inside functions, optional branches, and exception handlers. References inside conditional control flow or function bodies are marked optional; this records uncertainty and does not authorize that dependency.

evaluate_structure(contract, inspection) is a pure comparison against the existing module contract fields. It returns structured violations together, without changing the source facts or importing anything. Checks use names and exact represented values/annotations. Unspecified values impose no value constraint; an explicit YAML null requires a known None value. For function parameters, an explicit value also requires a default to exist: value: null matches def run(value=None), but not def run(value). Parameter and class-attribute annotation requirements match written annotations; a literal default or attribute value does not supply a missing annotation. Module-variable annotation requirements retain compatibility with type inference from a known scalar literal when no annotation is written.

Evidence marked verified in the static phase verifies a source declaration. It does not claim that arbitrary execution will leave an equivalent runtime object. Decorated definitions, conditional definitions, dynamic namespace operations, computed values, instance attributes, and inherited implementations can require runtime verification. A structural requirement depending on those facts fails closed with ISPY-S103. Missing declarations use ISPY-S101, mismatches use ISPY-S102, and invalid or unreadable source uses ISPY-S001.

Decorated functions are always uncertain, including decorators that happen to be harmless in a particular runtime. Metaclasses and class decorators are also uncertain. Base class references can be matched as written, but resolving the base object's inherited implementation requires runtime evidence. The inspector does not invoke decorators, metaclasses, annotation expressions, or defaults. Explicit namespace mutation in definition expressions, class suites, and control-flow headers is tracked conservatively. This includes calls such as globals() in a default expression and module bindings declared global inside a class. Calls buried in unexecuted function bodies are not treated as observed definition-time effects.

Static inspection is not a sandbox or a full interpreter. It does not establish that a module is safe to execute, predict arbitrary mutations performed by called code, discover every dynamic __import__/importlib target, recursively inspect dependency code, or verify runtime identities. Python imports may execute dependency code when execution is explicitly requested after admission. AST parsing also consumes local resources; hostile, excessively large input requires resource isolation outside this library.

starter_contract(inspection) generates editable filename, function, class, and method requirements. It retains uncertain declarations as requirements so an unknown decorated declaration is not silently approved. Generation cannot infer which dependencies should be trusted or which policy is appropriate for a project.

Tests in tests/admission/test_inspection.py include source attempting terminal output, file writes, environment changes, subprocess execution, and socket connections. Inspection and a denying structural evaluation trigger none of those effects. Tests also cover compiler-invalid scope syntax, encoding, conditional imports, decorators, dynamic defaults, inheritance, and exact source hashes.