monty-compat
Release-aware Python source transpilation and capability discovery for the Monty interpreter.
monty-compat learns the exact surface of a released Monty runtime, records
that evidence in a versioned manifest, and lowers unsupported Python into
semantically equivalent constructs only when the rewrite can be proven safe.
When Monty's supported feature set cannot preserve observable Python behavior,
the transpiler raises an explicit error instead of inventing semantics.
Documentation · Python API · Release selection · Changelog
Why this exists
Monty intentionally implements a Python subset. File names and Rust symbols can
tell us that pathlib.Path exists, but not whether a nested surface such as
pathlib.Path.is_dir is available or whether a syntax form parses, type-checks,
and behaves like CPython. monty-compat combines three evidence sources:
- Static extraction scans Monty's Rust source for builtins, exceptions, modules, module attributes, constructors, and runtime-type attributes.
- Behavioral discovery runs atomic probes on both CPython and the exact linked Monty release and classifies the result.
- Conservative lowering rewrites only evidence-backed seams for which the required Python semantics can be represented by Monty's supported subset.
Discovery is an offline release-maintenance operation. Default verified
transpilation does not download Monty, run probes, start Python workers, or
execute the input. The opt-in latest mode performs one bounded, validated
manifest-channel resolution per process.
Published packages
| Registry | Package | Purpose |
|---|---|---|
| PyPI | monty-compat |
Python API and native PyO3 transpiler |
| crates.io | monty-compat |
Rust lowering engine and monty-lower CLI |
| crates.io | monty-compat-extract |
Rust extractor and monty-extract CLI |
monty-compat-python and monty-compat-discover are internal workspace crates
and are not published separately. All Python, Rust, manifests, documentation,
and release automation live in this repository.
Installation
Python runtime:
pip install monty-compat
Python discovery tooling, including exact Monty, pysource-codegen, and
pysource-minimize:
pip install 'monty-compat[discovery]'
Rust embedding:
cargo add monty-compat@0.5.0
cargo add monty-compat-extract@0.5.0
The Python wheel uses abi3 and supports CPython 3.10 and newer.
Quick start: transpile and run
The public Python hot path is intentionally one function:
from monty_compat import transpiler
from pydantic_monty import Monty
source = """
value = 2
match value:
case 2:
result = f"ok:{value}"
case _:
result = "other"
result
"""
lowered = transpiler(source) # release="verified"
with Monty() as pool:
with pool.checkout() as session:
assert session.feed_run(lowered) == "ok:2"
Pin the exact bundled manifest when reproducibility matters:
lowered = transpiler(source, release="0.0.19")
assert lowered == transpiler(source, release="v0.0.19")
verified is the default and resolves to the newest manifest compiled into the
installed wheel (0.0.19 in version 0.5.0). It is deterministic and performs
no network access. latest is an explicit freshness mode:
lowered = transpiler(source, release="latest")
It downloads the bounded manifest channel from the project documentation,
accepts only a SHA-256-matching manifest that declares compatibility with the
installed lowering engine, and caches the resulting transpiler for the process.
An unavailable, incompatible, or malformed channel fails closed. Exact bundled
versions such as 0.0.19 and v0.0.19 remain offline.
The function returns ordinary Python source, so monty-compat does not wrap,
own, or replace Monty's execution API.
Failure is explicit
from monty_compat import TranspilationError, transpiler
try:
transpiler("def values():\n yield 1\n", release="0.0.19")
except TranspilationError as exc:
print(exc)
TranspilationError covers:
- an unbundled release;
- failure to resolve or validate the opt-in
latestchannel; - invalid Python input;
- manifest validation failure;
needs_reviewornot_lowerablediagnostics for semantics Monty cannot currently represent.
The Python binding releases the GIL while Rust parses and lowers the source. Successful exact-source results are retained in a process-global, release-pinned, bounded cache. Failures are never cached and cache entries never cross manifests.
Performance
Release-mode measurements below use a 100 KiB source, Rust 1.95.0, and an Apple
M1 MacBook Air. p50 and median are the same statistic and are both shown
because release reports expose both labels.
| Operation | Samples | p50 | Median | p99 |
|---|---|---|---|---|
| Supported source, cache disabled | 200 | 4.224 ms | 4.224 ms | 5.661 ms |
| Supported source, exact cache hit | 20,000 | 0.0317 ms | 0.0317 ms | 0.0424 ms |
| Match-heavy lowering, cache disabled | 200 | 30.957 ms | 30.957 ms | 32.102 ms |
| Match-heavy lowering, exact cache hit | 20,000 | 0.0316 ms | 0.0316 ms | 0.0429 ms |
| Monty 0.0.19 local source extraction | 200 | 113.419 ms | 113.419 ms | 132.644 ms |
| Monty 0.0.19 in-memory ZIP extraction | 200 | 119.257 ms | 119.257 ms | 136.816 ms |
These are local measurements, not latency guarantees. See Benchmark methodology for workloads, commands, raw results, cache-miss interpretation, and reproduction notes.
Capability graph API
Use MontyCapabilities to inspect the exact static surface extracted from
Monty's Rust source:
from monty_compat import MontyCapabilities
caps = MontyCapabilities.from_github() # latest released source
# caps = MontyCapabilities.from_github(only_released=False) # main branch
# caps = MontyCapabilities.from_local("/path/to/monty")
assert "pathlib" in caps.modules
assert caps.supports_path("pathlib.Path")
assert caps.supports_path("pathlib.Path.is_dir")
path_methods = caps.get_attributes("pathlib.Path")
print(sorted(path_methods))
print(caps.summary())
This graph answers precise questions about known paths; it does not classify a
complete Python program or prove that a snippet can run. Use transpiler(...)
for the fail-closed source compatibility decision.
The graph contains:
builtin_functionstype_constructorsexception_typesmodulesmodule_attributestype_attributes, including paths such asstr.upper,pathlib.Path.is_dir, andre.Pattern.search
See the Python API reference for every public function, argument, return value, limitation, and error.
Lowering contract
Every non-supported feature in the bundled manifest has one auditable outcome:
| Availability | Meaning |
|---|---|
automatic |
Every represented occurrence has a semantics-preserving rewrite. |
contextual |
Rewriting is allowed only when conservative static facts prove its preconditions. |
not_lowerable |
Monty's current surface cannot preserve the required behavior. |
Current lowering families include:
- literal, sequence, mapping, OR, guarded, and selected class
matchcases; - function decorators and complex
for/withtargets; - selected class, descriptor, dataclass, and user-class protocol seams;
- percent formatting,
str.format, and custom f-string formatting; - dict union, assert messages, Unicode decimal conversion, and static bytes;
- contextual repairs for selected lazy iterators, class-comprehension scope,
closure late binding,
async with, andasyncio.gatherbehavior.
Unsupported semantics remain explicit. The engine does not synthesize exception inheritance, traceback objects, generator suspension, exception groups, runtime type mutation, or dispatch precedence that Monty does not implement.
See Lowering semantics for before/after examples, diagnostics, contextual preconditions, and the non-goals that protect semantic correctness.
Rust API
The core Rust API accepts a caller-supplied exact manifest:
use monty_compat::{CacheConfig, CapabilityIndex, Transpiler};
let manifest = std::fs::read_to_string("manifests/monty-v0.0.19.json")?;
let capabilities = CapabilityIndex::from_json(&manifest)?;
let transpiler = Transpiler::with_cache_config(
capabilities,
CacheConfig::default(),
);
let output = transpiler.transpile(
"value = 1\nmatch value:\n case 1:\n result = 'one'\nresult\n",
)?;
assert!(output.changed);
assert_eq!(output.target_tag, "v0.0.19");
println!("{}", output.code);
# Ok::<(), Box<dyn std::error::Error>>(())
Transpiler is thread-safe, owns an immutable manifest index, and returns an
Arc<LoweringOutput> so exact cache hits reuse the canonical artifact. Use
CacheConfig::disabled() for deterministic cache-free measurements.
The extractor is a separate public crate:
use monty_compat_extract::{extract_release, resolve_release};
let release = resolve_release("0.0.19")?;
let graph = extract_release(&release)?;
assert!(graph.modules.contains("pathlib"));
# Ok::<(), Box<dyn std::error::Error>>(())
See the Rust API and CLI reference for public structs, errors, caching, extraction bounds, and command exit behavior.
Command-line tools
Lower a file and retain a machine-readable diagnostic report:
cargo run -p monty-compat -- \
--manifest manifests/monty-v0.0.19.json \
--input example.py \
--output example.lowered.py \
--report lowering-report.json \
--deny-needs-review
monty-lower exits 2 when any seam is needs_review or not_lowerable
under --deny-needs-review, and 1 for loading/parsing failures.
Extract a static graph from a checkout or ZIP archive:
cargo run -p monty-compat-extract -- \
--root /path/to/monty \
--output monty-static-capabilities.json
cargo run -p monty-compat-extract -- \
--archive monty-v0.0.19.zip \
--output monty-static-capabilities.json
ZIP inputs and downloads are size-bounded, validated, and scanned in memory; archives are never unpacked onto the filesystem.
Exact-release discovery
The recommended release pipeline is Rust-orchestrated and links the exact Monty version being measured:
cargo run --release --manifest-path crates/monty-discover/Cargo.toml -- \
--release 0.0.19 \
--seeds 1000 \
--python .venv/bin/python \
--output manifests/monty-v0.0.19.json
The pipeline:
- resolves the requested release and downloads that exact archive;
- rejects a source/runtime version mismatch;
- extracts the static graph in Rust;
- runs baseline semantic probes through killable workers;
- generates deterministic inert AST corpora with
pysource-codegen; - minimizes failures with
pysource-minimizewhile Rust retains the final same-fingerprint verdict; - atomically writes the versioned manifest.
Generated source is parsed and compiled by CPython, wrapped under if False,
and then submitted to Monty. It is never executed directly. Generated failures
are discovery evidence and promotion candidates; they never become “supported”
without a reviewed semantic probe.
See Discovery and manifest generation for status classes, worker boundaries, manifest schema, minimization, and adding a new Monty release.
Examples
- Transpile and run with Monty
- Handle non-lowerable source
- Inspect nested capabilities
- Embed the Rust transpiler
- Inspect a release from Rust
- Reproduce benchmarks
Development and verification
uv sync --locked --extra dev
cargo +1.95.0 fmt --all -- --check
cargo +1.95.0 clippy --workspace --all-targets --locked -- -D warnings
cargo +1.95.0 test --workspace --locked
cargo +1.95.0 test \
--manifest-path crates/monty-discover/Cargo.toml \
--locked
uv run pytest -q
uv run ruff check src tests examples
uv run mypy src
uvx --from zensical==0.0.50 zensical build --clean
The differential suite compares original CPython, lowered CPython, and lowered exact-Monty result/exception envelopes plus stdout and stderr:
MONTY_COMPAT_CPYTHON=python3.11 \
cargo test --manifest-path crates/monty-discover/Cargo.toml --test differential
The workspace forbids Rust unsafe. Checked byte ranges, fallible edit planning,
malformed-input tests, exact release fingerprints, and RustSec/cargo-deny checks
are release gates.
More documentation
- Documentation site
- Release selection
- Python API
- Rust API and CLIs
- Lowering semantics
- Discovery pipeline
- Benchmarks
- Examples
- Changelog
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file monty_compat-0.5.0.tar.gz.
File metadata
- Download URL: monty_compat-0.5.0.tar.gz
- Upload date:
- Size: 335.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d67b898dbca498671fd8f2584b0f7fa691854e220e1b9e8c83cecaf88e22371
|
|
| MD5 |
695937811d42f257a784e6b54ae26383
|
|
| BLAKE2b-256 |
a51284d5875a569f08c6ece26af096b7a7d4e8597845c0140ed30e776bfd46aa
|
File details
Details for the file monty_compat-0.5.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: monty_compat-0.5.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0353f1ac54a16c4f1240a682124367cc680a632ee23e9bc782dc27aa811e7092
|
|
| MD5 |
172c0b8be1e75d5d9896d59694cdaf34
|
|
| BLAKE2b-256 |
c65b9376761a870125e9588a2e5837457f88a3c28816fb8907ed7b7d39b1a44c
|
File details
Details for the file monty_compat-0.5.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: monty_compat-0.5.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3f4ba00aa11bef1e01f12947938268de68e0c697e3053953982dc0280d04d34
|
|
| MD5 |
a8d3cbd07494d8e3daf8d58adb7042a9
|
|
| BLAKE2b-256 |
04e9f99faebc59e60ab83311a2da186c08ca130bfcfc92556ca44bc1b71dca43
|
File details
Details for the file monty_compat-0.5.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: monty_compat-0.5.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f6f868deb5fbd3491c92838d101f95a8e19df2136d5a4439097a32d4579821f
|
|
| MD5 |
dd892db86d0daac25a4af25f66aa99a4
|
|
| BLAKE2b-256 |
e78757937c3e91f6c562fe31d3c4e9de039de2d3c836c2196bf85ae5df782275
|
File details
Details for the file monty_compat-0.5.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: monty_compat-0.5.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb4f8d36d5304a660ee85820620170f082b36f064ffcec4ab54c3c23e28d8487
|
|
| MD5 |
90edf5cc1130b2cd9adb9f8c4e205581
|
|
| BLAKE2b-256 |
ce7c40740e2b79c62ecae46ec94c074f7268a53ea422b51547376ad94dbe33e0
|