Migrating from 0.4 to 0.5
ImportSpy 0.5 introduces static admission: inspect source, resolve dependencies, collect evidence, and evaluate policy before choosing whether to execute a module. Runtime introspection remains available for compatibility.
Replace the primary workflow
importspy init plugin.py
importspy check plugin.py
importspy check plugin.py --contract policy.yml --format json
init creates plugin.importspy.yml from source declarations without importing
the target. Review generated dependency approvals and add the constraints you
need. check discovers that sidecar automatically or accepts --contract.
It evaluates admission without executing the target, including when admission
succeeds. Human, JSON, and SARIF output share the same decision model.
The old positional CLI invocation is deprecated and forwards to check.
--spymodel remains an alias for --contract. A static check cannot reproduce
arbitrary runtime reflection: dynamically created objects, computed values, and
conditional definitions can remain unknown. Consult the static preflight
reference before translating a runtime-only contract.
Exit codes are 0 for admission, 1 for denial, 2 for invalid configuration,
and 3 for a tool failure. Update CI jobs that previously assumed every error
had the same exit code.
Contract compatibility and stricter validation
Existing structural fields such as filename, variables, functions,
classes, and deployments remain supported. The admission policy adds
schema_version: 1. The existing version field continues to mean the target
module's version; do not replace it with the contract schema number.
schema_version: 1
filename: plugin.py
functions:
- name: run
arguments:
- name: request
annotation: Request
return_annotation: Response
Unknown fields now fail validation at every structural nesting level. For
example, returns: str is rejected; use return_annotation: str. Previously
ignored misspellings can therefore make old files invalid. Correct the field
names instead of dropping the requirement.
Annotations now accept arbitrary strings, including list[User] | None, rather
than a fixed enumeration of built-in types. Existing annotation enum values
remain accepted. Annotation text is not evaluated as a Python expression.
Unresolved imports are denied by default. Correct missing installations or project-root configuration, or explicitly configure the unresolved policy when that uncertainty is acceptable. Generated contracts mark unresolved imports as denied until reviewed. An import name is distinct from its distribution name; use the dependency policy reference when translating existing dependency rules.
Runtime compatibility API
Spy.importspy(...) is deprecated in 0.5, remains available throughout the 0.5
series, and is scheduled for removal in 1.0. It emits DeprecationWarning and
validates a module that has already executed. Use AdmissionEngine.check or
importspy check for static admission.
from importspy import Spy
import trusted_plugin
# Runtime validation: trusted_plugin has already executed above.
validated = Spy().importspy(
filepath="trusted-plugin-runtime.yml",
info_module=trusted_plugin,
)
assert validated is trusted_plugin
The method now returns the exact provided module. It does not reload it, remove
it from sys.modules, or discard runtime mutations. Contracts without
deployments validate their top-level structural constraints successfully.
Code relying on the former repeated execution must move that initialization
into an explicit application operation.
SpyModel.from_module(module) also inspects the supplied, already loaded object
without reloading or unregistering it. The returned model exposes the module
structure directly and retains the legacy deployment representation. Runtime
inspection can invoke dynamic Python attributes, so it belongs after admission
and is appropriate only for trusted code. Embedding a Spy call inside a module
cannot prevent that module's earlier top-level effects.
Environment and YAML handling
Environment validation diagnostics redact values; debug logs report counts instead of dumping expected and observed variables. Secret requirements test names, and environment string representations list names only. The legacy runtime model still contains observed environment values in memory for value comparison: do not serialize or publish a full runtime model containing secrets. Admission reports follow their separate evidence-redaction rules.
Contract files use safe YAML construction. Python object tags, unknown tags, duplicate keys, and a non-mapping document root are rejected. YAML reading no longer preserves arbitrary custom tags or round-trip formatting. Ordinary YAML mappings remain supported.