ImportSpy
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.
See a denial before execution
Install ImportSpy with Python 3.10 or newer:
pip install importspy
Create 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: false
Both dependencies are installed with ImportSpy. Run:
importspy check plugin.py
The 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.
Start with your own module
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 json
init 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.
One admission pipeline
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.
A module and dependency contract
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: deny
Distribution 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.
Embed admission in a loader
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.
Evidence and integrations
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: true
When 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.
CI
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: python -m pip install importspy
- run: importspy check plugin.py --contract plugin.importspy.yml
Use --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.
Scope and community
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.
- Quickstart · Contract reference
- Architecture · Static preflight
- Migration from 0.4 · Examples
- Contributing · Security reporting
ImportSpy's earlier runtime-contract design and its community presentations remain part of the project's history. The GDG Basilicata talk describes that earlier generation; the 0.5 workflow begins with static admission.
MIT licensed. Created by Luca Atella.
Metadata
Release files for importspy 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| importspy-0.5.0.tar.gz | 47.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| importspy-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 102.8 kB
Release files / importspy-0.5.0.tar.gz
| Download URL | importspy-0.5.0.tar.gz |
|---|---|
| Size | 47.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
063856ca044962f0c469b51a8ded04a1627fa08bcd5ee99821b494d130de44a0
|
|
BLAKE2b-256 checksum How to use checksums |
a4da5678f93d04c0ea9ddf8ec3697f72b075a19a78641bf0fef3be16a0510d09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.0 CPython/3.14.4 Darwin/25.5.0
|
Release files / importspy-0.5.0-py3-none-any.whl
| Download URL | importspy-0.5.0-py3-none-any.whl |
|---|---|
| Size | 55.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
050956be3b9b52b549cf1d1b04b4ee64a358dab4b70a22a9267082503ce6b959
|
|
BLAKE2b-256 checksum How to use checksums |
da1c75dcdeed9d18b9cff9cbfc0b871c9db72c9bec06be3e0f03498941ed5001
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.0 CPython/3.14.4 Darwin/25.5.0
|