Policy-as-code admission for Python modules and dependencies.
Decide whether Python source satisfies your module, dependency, and runtime policies before admitting it for execution.
Install ImportSpy with Python 3.10 or newer:
pip install importspyCreate plugin.py:
import packaging
import typer
print("THIS MUST NOT RUN")Create plugin.importspy.yml:
schema_version: 1
dependencies:
packaging:
allowed: true
version: ">=24"
typer:
allowed: falseBoth dependencies are installed with ImportSpy. Run:
importspy check plugin.pyThe report includes:
ISPY-D101 [error] Dependency `typer` is not permitted by policy.
Decision: DENY
Target module was not executed.
The command exits with code 1. The print statement never runs.
The complete example and automated regression test
verify this behavior. A successful check also leaves the target unexecuted.
importspy init path/to/plugin.py
# Review path/to/plugin.importspy.yml and edit its dependency approvals.
importspy check path/to/plugin.py
importspy check path/to/plugin.py --format jsoninit inspects source without importing it. The starter contract records source
declarations and installed dependency identities; review it and add your own
version, origin, environment, and evidence requirements. Unresolved imports are
denied until reviewed.
Source → static preflight → dependency resolution → evidence → policy → ADMIT / DENY
│
explicit load after ADMIT
│
execution → runtime validation
- Structure: require functions, parameters, annotations, classes, and literal declarations.
- Dependencies: resolve import names to distributions, including namespace contributors. Enforce allow/deny, required presence, versions, declarations, origins, and editable policy.
- Host runtime: constrain Python, operating system, architecture, implementation, and environment.
- Evidence: consume observations from explicitly selected providers and require verified signals.
- Decisions: use the same structured facts and violations for human, JSON, and SARIF reports.
Checks that cannot establish a required static fact fail closed. A computed value or dynamically constructed interface is reported as unknown; ImportSpy does not execute the target to fill the gap.
schema_version: 1
policy_id: payments-plugin
filename: plugin.py
functions:
- name: process
arguments:
- name: amount
annotation: int
return_annotation: str
runtime:
python: ">=3.10"
environment:
PAYMENT_MODE: production
dependencies:
requests:
version: ">=2.32,<3"
declared: true
internal-payment-sdk:
origin:
type: vcs
repository: https://github.com/acme/payment-sdk
editable: false
dependency_options:
unresolved: deny
undeclared: deny
unlisted: denyDistribution names are distinct from import names. Origin metadata is an observation, not verified provenance; missing direct-URL metadata does not prove installation from PyPI. Read the dependency reference for declaration modes, namespace behavior, and uncertainty.
from pathlib import Path
from importspy.domain import AdmissionRequest
from importspy.engine import AdmissionEngine
request = AdmissionRequest(
subject=Path("plugin.py"),
contract=Path("plugin.importspy.yml"),
)
engine = AdmissionEngine()
decision = engine.check(request) # No target execution.
if decision.admitted:
module, executed_decision = engine.load(request) # Fresh admission, then execution.load performs fresh admission and executes the inspected source bytes. Optional
runtime validators run after execution and cannot undo side effects. It does not
freeze the environment or recursively admit every transitive import. See the
library workflow and security model.
Supply-chain tools produce signals. ImportSpy turns relevant signals into admission decisions. Providers can integrate vulnerability results, attestations, or provenance without turning core into a scanner:
dependencies:
cryptography:
provenance:
required: trueWhen cryptography is observed, this denies admission unless the selected provider
supplies sufficient verified evidence. Add required: true if the dependency must
also be present. ImportSpy does not manufacture cryptographic verification from package
metadata. The extension API documents EvidenceProvider,
PolicyValidator, RuntimeValidator, Reporter, and explicit entry-point loading.
Core checks work offline, with no account or telemetry. Network providers require explicit selection and network authorization. Plugins are trusted Python code.
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install importspy
- run: importspy check plugin.py --contract plugin.importspy.ymlUse --format json for automation or --format sarif for code-scanning
integration. Exit codes are 0 admitted, 1 denied, 2 invalid configuration,
and 3 tool failure. importspy check . checks explicit project subjects.
See CI integration.
ImportSpy enforces policy at Python admission boundaries. It is not a sandbox, vulnerability scanner, package manager, or proof that admitted code is safe. It complements tools such as pip-audit, OSV, Sigstore, and SLSA by consuming their evidence through providers.
- Where ImportSpy fits
- Security model
- Quickstart
- Contract reference
- Architecture
- Static preflight
- Migration from 0.4
- Examples
- Contributing
- Security reporting
-
The Missing Admission Layer in Python Plugin Systems
A case study on the architectural problem behind ImportSpy: why importability is not the same as compatibility, and how the project evolved from import validation toward policy-driven admission boundaries. -
ImportSpy — GDG Basilicata talk
A presentation of ImportSpy's earlier runtime-contract generation. The current 0.5 workflow begins with static admission before execution.
MIT licensed. Created by Luca Atella.

