Skip to main content

MEP 71. Mochi and Python package bridge

FieldValue
MEP71
TitleMochi and Python package bridge
AuthorMochi core
StatusDraft
TypeStandards Track
Created2026-05-29 21:45 (GMT+7)
DependsMEP-1 (Grammar, for the import python semver extension), MEP-2 (AST, for the import node), MEP-4 (Type System), MEP-13 (ADTs and Match, for Optional[T] and tagged-union translation), MEP-45 (C transpiler, for the FFI sidecar pattern), MEP-51 (Python transpiler, for the emit pipeline, the runtime package mochi_runtime, the existing TargetPythonWheel / TargetPythonSdist / TargetPythonPublish build targets, the existing Phase 12 extern python fun sidecar pattern, and the existing Phase 18 PyPI Trusted Publishing workflow), MEP-57 (Mochi module and package system, for mochi.toml, mochi.lock, the BLAKE3-256 + SHA-256 dual-hashing, the content-addressed object store, the capability declaration model, and Sigstore-keyless trusted publishing)
Research/docs/research/0071/
Tracking/docs/implementation/0071/

Abstract

Mochi today (May 2026, immediately after MEP-51's Python transpiler reached 18 phases LANDED and MEP-57's source-level package system reached Draft) ships a typed-CPython 3.12+ emit path and a Phase 12 extern python fun FFI surface that requires the user to hand-write a <modname>_externs.py sidecar declaring each Python function they want to call. The Phase 12 surface works for short bespoke shims (4-6 helper functions per program is typical), but it does not scale to the PyPI ecosystem. A Mochi program that wants to use numpy, pandas, requests, fastapi, or any of the 600,000+ packages on PyPI must today hand-write the entire FFI surface, including signatures, type hints, and an import-time module. The Phase 18 mochi build --target=python-publish path is functional but lives behind the build CLI and does not expose a mochi pkg publish interface symmetrical to the publish flows on Cargo (MEP-73), Go modules (MEP-74), and the broader MEP-57 polyglot package system. MEP-71 closes both gaps.

MEP-71 specifies the bidirectional PyPI package bridge: Mochi packages can consume any well-formed PyPI package via import python "<package>@<semver>" as <alias> with no user-written FFI boilerplate, and Mochi packages can publish to PyPI as proper wheels + sdists via mochi pkg publish --to=pypi with Trusted Publishing and PEP 740 attestations. The bridge is the third source-language interop story Mochi ships under MEP-57's package umbrella (after MEP-73 for Cargo and MEP-74 for Go modules), and the first one that targets a registry built around dynamically-typed source language plus a strong static-type-stub side channel (PEP 561) plus a mature C-extension boundary.

The proposal builds on MEP-51's existing emit pipeline and MEP-57's manifest / lockfile / capability infrastructure, and adds a new self-contained component under package3/python/. The Mochi grammar gains semver and source-locator extensions to the existing python token in the FFI-import production (the keyword python itself was already wired up by MEP-51 Phase 12). The Mochi build pipeline gains one new top-level CLI surface (mochi pkg publish --to=pypi) that composes existing MEP-51 emit targets behind the unified mochi pkg interface introduced by MEP-57. The lockfile gains one new repeated table ([[python-package]]). The build optionally co-emits a PEP 751 pylock.toml for downstream Python tooling that does not read mochi.lock. No existing transpiler MEP needs to change.

The system is anchored on seven load-bearing decisions, each justified in §Rationale and surveyed in the companion research notes:

  1. PEP 561 type stubs as the canonical machine-readable Python package surface. PEP 561 (Final, 2017, refreshed by PEP 660 in 2021 for editable installs) standardised three stub-distribution shapes: (a) inline stubs in the package itself, indicated by a py.typed marker file at the package root; (b) a sibling <name>-stubs package on PyPI; (c) the community-maintained typeshed monorepo (python/typeshed) that ships stubs for the stdlib and ~500 popular third-party packages. The bridge ingests stubs in that precedence order, falling back to stubgen --inspect-mode (from the mypy package) only when none of (a), (b), (c) supplies stubs for the requested package. The bridge treats stubs as the authoritative bind-source. Parsing the package's .py source directly is rejected (Python's runtime-dynamic semantics make AST-level type inference unreliable, decorators rewrite types at import time, metaclasses rewrite type structures, and 60%+ of the top-200 PyPI packages have C extensions with no .py source at all). Using inspect.signature() at runtime is rejected as the primary path (it returns Signature objects with Parameter.annotation typically populated only for fully-annotated functions, which means it works for ~20% of PyPI as of 2026-05 and fails for the rest; it is retained as a tertiary fallback after stubgen). See 01-language-surface §2, 04-pep561-stub-ingest §1, 02-design-philosophy §1.

  2. Auto-synthesised CPython-3.12-compatible wrapper Python package (python_wrap/) per imported PyPI package, with the existing Phase 12 sidecar pattern as the loader. For each PyPI package the user imports, the bridge generates a sibling Python module (<workdir>/python_wrap/<package>_externs.py) that imports the source package and re-exports each translatable public name through a thin shape-coercing shim: kwargs-only callables get a positional-form wrapper; Optional[T] returns get a T | None wrapper that the Mochi side reads through its own option discriminator; async fns get an asyncio.run-wrapped sync entry plus an async-marked entry; dataclasses get a _to_mochi_dict accessor pair; iterators get a one-shot list(...) materialisation alongside the lazy form; C-extension callables that lack stubs but expose a __doc__ numpy-style signature are inspected and wrapped where the signature line is parseable. The wrapper file is placed at the exact location the MEP-51 Phase 12 build expects sidecars (next to the .mochi file, copied to src/<pkg>_externs.py at build time), and the existing Phase 12 from <pkg>_externs import ... emit picks it up unchanged. No new build infrastructure is needed; the bridge plugs into Phase 12 as a sidecar producer. The alternative of having Mochi emit Python code that imports the source package directly (no wrapper) is rejected because the wrapper is where the type-shape coercion lives; without a wrapper, the Mochi side would have to re-implement Python's argument-binding rules (positional-or-keyword, keyword-only, var-positional, var-keyword, defaults) in its own lowering pass, which is a fragile cross-language re-encoding of inspect.signature semantics. See 02-design-philosophy §2, 03-prior-art-bridges §3, 09-abi-stability §1.

  3. Closed Python-to-Mochi type translation table, with explicit refusal on out-of-table cases. The translation covers int↔int, float↔float, bool↔bool, str↔string, bytes↔bytes, list[T]↔list<T> (when T is in-table), tuple[A, B, ...]↔ tuple<A, B, ...>, tuple[T, ...]↔list<T> (homogeneous form), dict[K, V]↔map<K, V> (when K and V are in-table and K is str or one of the integer types), set[T]↔set<T>, frozenset[T]↔set<T> (read-only on the Mochi side), Optional[T] and T | None↔ T?, X | Y↔ type alias = X | Y (PEP 604 union, lowered to a Mochi sum type when X and Y are concrete), Callable[[A, B], R]↔ fun(A, B): R, Awaitable[T]↔ async T, AsyncIterator[T]↔ stream<T>, Iterator[T]↔ list<T> (materialised at the wrapper boundary), TypedDict↔ record, @dataclass(frozen=True)↔ record, @dataclass (mutable)↔ refusal (Mochi records are immutable; mutable dataclasses must be hand-overridden), Protocol↔ interface (Mochi interfaces; PEP 544), and type | None↔ T? for PEP 604 nullable forms. Items outside the table (unparameterised Any, object in non-Protocol positions, ParamSpec and Concatenate, TypeVarTuple beyond declared monomorphisations, recursive generic bounds, Final[X] at module scope, ClassVar in non-dataclass positions, Annotated[T, ...] whose metadata mutates the type, callables whose **kwargs is Any without Unpack[TypedDict], generator types other than AsyncIterator and Iterator, and any item whose type hint contains a string forward reference that fails to resolve at stub-ingest time) are skipped with a SkipReport entry that names the item and the reason. The user can override with a hand-written extern python fun declaration in the existing Phase 12 sidecar style that takes responsibility for the type at the FFI boundary. Aggressive translation (e.g., synthesising a Mochi wrapper for every Any return as a tagged MochiPyAny type) is rejected: the bridge promises typed Mochi, not opaque dynamic interop. See 05-type-mapping §1, 02-design-philosophy §3.

  4. import python "<package>@<semver>" as <alias> as the surface keyword, with <semver> resolved through mochi.lock. MEP-51 Phase 12 already wired the python keyword as a <lang> token in the FFI-import production for extern python fun ... declarations; MEP-71 extends the same keyword to the import form (parallel to MEP-73's import rust and MEP-74's import go). The <spec> is <package-name>, <package-name>@<semver-req> (PEP 440 version-specifier syntax: >=2.0,<3.0, ^2.0, ~=2.0, ==2.0.1), <package-name>@<index>+<url> for non-default indices, <package-name>@git+<url>#<rev> for VCS sources (PEP 508 direct URL form), or <package-name>@path+<relative> for local-source deps. Bare names resolve to the highest version on the configured index that satisfies the manifest's [python-dependencies] constraint; the bridge auto-rewrites the import to match the resolved version on next mochi pkg lock. The mochi.lock lockfile records the resolved version, the wheel filename, the wheel's BLAKE3-256 + SHA-256, the PEP 700 simple-index hash for cross-verification, the stub provenance, the synthesised wrapper SHA-256, the package's declared capability surface, and the resolved transitive dependency tree. No new Mochi syntax beyond the import shape extension; import python participates in the same module-resolution flow as import rust (MEP-73) and import go (MEP-74). See 01-language-surface §1, 06-pypi-publish-flow §2.

  5. uv as the canonical resolver, with PEP 751 pylock.toml as the interchange format. uv (Astral, GA 2024-08, current 0.7.x as of 2026-05) is the reference resolver for the consume-in path. uv is a single Rust binary that combines pip, pip-tools, virtualenv, pyenv, and the PEP 517 build front-end into one tool; it implements the same PubGrub resolver Cargo uses, supports PEP 440 version specifiers, PEP 508 marker syntax for environment-conditional dependencies, PEP 658 / PEP 714 metadata side-channel for fast metadata-only resolves, PEP 503 simple-index protocol, PEP 691 JSON-form simple index, PEP 700 hash side-channel, and PEP 740 attestation verification. The bridge invokes uv pip compile --output-format=pylock.toml to resolve the dependency graph and produce a PEP 751 pylock.toml document, which it then translates into [[python-package]] entries in mochi.lock. The PEP 751 file is also retained at pylock.toml in the project root so that downstream Python tooling (pip 25.0+, uv 0.7+, and pylock.toml-aware CI) can read the same pinning the Mochi build uses. The alternative of a hand-rolled resolver (or shelling out to pip-tools) is rejected: uv is 10x-100x faster than pip-tools on cold and warm cache, ships PEP 751 emission natively, and is the same resolver MEP-51 Phase 18 already pins for the publish path. See 04-pep561-stub-ingest §3, 02-design-philosophy §6.

  6. Sigstore-keyless OIDC trusted publishing to PyPI via PEP 740 attestations, with no long-lived API token path. When mochi pkg publish --to=pypi runs, the bridge: (a) builds the wheel + sdist via the existing MEP-51 Phase 15 emit path (the same path mochi build --target=python-wheel and --target=python-sdist use); (b) obtains an OIDC token from the CI environment (GitHub Actions id-token: write, GitLab CI, Google Cloud Build, ActiveState CircleCI); (c) presents the token to PyPI's _/oidc/mint-token/ endpoint per the PyPI Trusted Publishing GA shape (rolled out 2023-Q2 for GitHub Actions, expanded to GitLab + Google + ActiveState through 2024-2025); (d) PyPI returns a short-lived API token scoped to the trusted-publisher project; (e) the build invokes uv publish --trusted-publishing always to upload the wheel + sdist + the PEP 740 attestation bundle (the attestation is a Sigstore in-toto envelope, signed by Fulcio-issued short-lived cert, logged in Rekor). The legacy __token__ flow (long-lived PyPI API token in ~/.pypirc) is not supported (matches MEP-57's broader principle that long-lived tokens are deprecated; the 2024 mass PyPI typosquat campaigns and the 2025 reflected-string flood drive this). Local development testing of the publish path is supported via TestPyPI plus a --dry-run flag (the same flag MEP-51 Phase 18 already exercises). See 07-sigstore-pypi-trusted-publishing §1, 02-design-philosophy §5.

  7. Async bridge via asyncio.run per call, with optional persistent-loop opt-in. Idiomatic modern PyPI packages (notably httpx, aiohttp, asyncpg, sqlalchemy 2.x async, openai-python ≥1.0, fastapi handler internals, anyio adapters) expose async def as a substantial part of their surface. Mochi's source-level async lowers to immediate evaluation on MEP-51's emit path (Phase 11 deferred the full async colour pass; v1 of MEP-71 ships with the immediate-evaluation semantics). The bridge resolves the impedance mismatch by wrapping each imported async def f(...) -> T in a synchronous def f_sync(...) -> T: return asyncio.run(f(...)) shim. Each call constructs a new asyncio event loop, runs the coroutine to completion, and tears the loop down. The cost is the loop-construction overhead per call (~200μs on CPython 3.12 on Apple Silicon, dominated by selectors-init + signal-handler register; negligible for non-tight-loop workloads). For tight loops or for cases where the user wants a persistent event loop across calls (e.g. a httpx.AsyncClient whose connection-pool benefit is lost on per-call teardown), the user opts in via [python] runtime = { event-loop = "persistent" } in mochi.toml, which switches the wrapper to a process-global asyncio.new_event_loop() cached at first call. The persistent-loop mode is not the default because it leaks the loop into the host process (problematic for mochi pkg test-style ephemeral runs); the per-call mode is the default because it is the only mode whose lifetime is fully scoped to a single FFI call. Free-threaded CPython 3.13t and 3.14t support is forward (Phase 17 sub-phase), not v1. See 08-async-bridge §1, 02-design-philosophy §4.

The gate for each delivery phase is empirical: the bridge must successfully ingest a curated 25-package fixture corpus (drawn from the April 2026 top-25-most-downloaded-on-pypistats.org snapshot: numpy, pandas, scipy, scikit-learn, requests, httpx, urllib3, pillow, pydantic, attrs, click, typer, rich, tqdm, sqlalchemy, fastapi, starlette, uvicorn, aiohttp, pyyaml, toml, tomli, msgpack, orjson, pytest); generate a translatable surface for every public item the closed type-table covers; emit a SkipReport for every item out of table; produce a Mochi extern python fun corpus that parses cleanly; and run the resulting program through the existing MEP-51 Phase 1-18 emit + run path with zero additional flags. A separate publish gate exercises the TargetPythonPublish path against TestPyPI (the same staging registry MEP-51 Phase 18 already targets), asserts that the published wheel + sdist + attestation bundle is accepted by PyPI's Warehouse validator, asserts that the wheel uv pip installs cleanly into a fresh venv, and asserts that the PEP 740 attestation verifies against the Sigstore root of trust.

Motivation

Mochi today (May 2026) integrates with PyPI through a single surface: MEP-51 Phase 12's extern python fun declaration plus a hand-written <modname>_externs.py sidecar. The surface is sound for ad-hoc shims (the existing Phase 12 fixture corpus exercises 10 hand-written sidecars across int, float, bool, string scalars), but it does not extend to PyPI's 600,000+ packages because every package import requires the user to first read the package's documentation, transcribe each function signature into a .pyi-shaped declaration, write the matching Python sidecar, and re-do that work when the upstream package version changes. For numpy (~2,000 public functions), pandas (~3,500), scikit-learn (~1,500), httpx (~600 across sync and async forms), the surface is too large to hand-author. Without a bridge, the Mochi-on-Python data-science and ML stories are aspirational.

MEP-71 closes the gap on the consume-in side. It also formalises the publish-out side, where MEP-51 Phase 18 shipped the workflow renderer and the mochi build --target=python-publish CLI but stopped short of a mochi pkg publish --to=pypi interface symmetric to MEP-73's Cargo path and MEP-74's Go-modules path. The full motivation:

  1. PyPI is the canonical 2024-2026 destination for data-science, ML, scientific computing, scripting, and modern Python web work. PyPI hosts 600,000+ packages (2026 Q1 PyPA snapshot); the top 100 most-downloaded packages account for ~70 billion downloads per month (April 2026 pypistats.org). A Mochi program needing numerical arrays (numpy), tabular data (pandas), modern HTTP (httpx, aiohttp), schema validation (pydantic), web framework integration (fastapi, starlette, uvicorn), interactive CLI (click, typer, rich), task scheduling (apscheduler, celery), database access (sqlalchemy 2.x), ML pipelines (scikit-learn, transformers, datasets), or notebook integration has no path to those packages today without rewriting the FFI by hand per call site.

  2. The PyPI ecosystem expects PEP 517 wheels + sdists as the unit of distribution; Mochi must learn to emit them with proper metadata and proper signing. MEP-51 Phase 15 (wheel + sdist emit) and Phase 18 (publish workflow renderer) prepared the emit path but did not unify it under the mochi pkg publish interface. MEP-71 makes the publish path symmetric to MEP-73 (Cargo) and MEP-74 (Go modules): same CLI, same lockfile pin, same trust-and-attestation story.

  3. PyPI Trusted Publishing has crystallised into the unambiguous 2024-2026 default. Four of the top five package ecosystems (npm Trusted Publishing GA April 2024, Maven Central Sigstore GA October 2024, PyPI Trusted Publishing GA Q2 2023 with PEP 740 attestations rolled out late 2024 and GA late 2025, Cargo RFC #3724 accepted Q4 2025 with rolling GA through 2026) converged on Sigstore-keyless OIDC publishing. PyPI was first to land the OIDC flow but last to attach PEP 740 attestations to the published artefacts; that gap closed late 2025. A package system released in 2026 that does not ship Trusted Publishing on day one is shipping a decade-out-of-date supply-chain story.

  4. The "import python @ semver" surface has zero learning curve for Mochi users. import python "httpx@^0.27" as httpx is the same shape as import rust "tokio@^1.42" as tokio (MEP-73) and import go "github.com/spf13/cobra@^1.8" as cobra (MEP-74). Mochi users do not need to know what a pyproject.toml is, what a wheel ABI tag is, what a build backend is, what pip install does, what an editable install is, what a setup.py is, what entry_points are, what GIL is, what an asyncio.run call costs, what inspect.signature returns, what typeshed is, what py.typed means, what stubgen does, what a Trusted Publisher is, or what a Sigstore Fulcio cert is. They write import python "..." as ... and the bridge does the rest.

  5. PEP 561 type stubs have matured into a stable, machine-readable, mostly-comprehensive Python package surface. Prior bridges between Python and other languages (PyO3, pybind11, nanobind, gopy, jpype, py4j, JNI from Python) require hand-written #[pyfunction] / cdef / napi / extern "C" annotations on the host-language side and offer no help on the Python-side type discovery. MEP-71 takes a different shape: it reads PEP 561 stubs to derive the type surface, then synthesises the wrapper. For the ~200 PyPI packages with first-party py.typed distributions (April 2026 PyPA survey: 38% of the top-500 PyPI packages and growing), the stubs are authoritative. For the next ~100 packages typeshed covers, the typeshed stubs are authoritative. For the residual ~200 packages (typically thin C-extension wrappers or extremely old packages), stubgen --inspect-mode synthesises stubs at lock time. The bridge reads the stubs and emits the wrapper; the user writes import python "..." as if no FFI existed.

  6. MEP-57 already shipped the prerequisite manifest / lockfile / capability infrastructure. The mochi.toml table layout, the mochi.lock serialisation, the BLAKE3-256 + SHA-256 dual-hashing, the trusted-publishing OIDC token exchange, the capability declaration model, and the content-addressed object store all transfer to the Python bridge with no architectural duplication. MEP-71 is additive on top of MEP-57: one new manifest section ([python-dependencies]), one new lockfile repeated table ([[python-package]]), one new CLI verb (mochi pkg publish --to=pypi), and one new interchange file (PEP 751 pylock.toml for downstream Python tooling).

  7. The bridge stays small and audit-friendly. The reference implementation under package3/python/ is targeted at ~5,500 LOC of Go across the simple-index client, the wheel-cache layer, the PEP 561 stub ingest, the type-mapping pass, the wrapper synthesiser, the Mochi extern emitter, the lockfile integrator, the publish flow, and the async-bridge runtime hook. There is no Python-side dynamic codegen at user-machine time (the wrapper is fully synthesised by the Go binary; the user's python only runs the synthesised wrapper). There is no inspect.signature runtime probing on the hot path (it is a tertiary fallback for stub-less packages, run at lock time, not at runtime). There are no monkey-patches in the wrapper unless the user opts in via [python.capabilities] monkey-patch = true.

Specification

This section is normative. Sub-notes under /docs/research/0071/ are informative.

1. Pipeline overview

MEP-71 introduces a per-import Python dependency resolution layer that sits between the Mochi parser (after MEP-57 has resolved mochi.toml) and the MEP-51 build driver:

mochi.toml [python-dependencies]
| pkgmanifest.Parse + pkgsolver.Solve (MEP-57)
v
resolved Python dep tree (package name + version + index URL + marker)
| package3/python/simpleindex.Fetch (PEP 503 / 691 simple index)
v
wheel + sdist artefacts in ~/.cache/mochi/python-deps/<blake3-hex>/
| package3/python/stubingest.Resolve (PEP 561 stub precedence)
v
.pyi stub document per package (py.typed | <name>-stubs | typeshed | stubgen)
| package3/python/typemap.Translate (closed Python-to-Mochi table)
v
TranslatedSurface + SkipReport per package
| package3/python/wrapsyn.Emit (synthesised _externs.py wrapper module)
v
python_wrap/<package>_externs.py adjacent to the importing .mochi
| package3/python/externemit.Emit (Mochi extern python fun shim)
v
synthesised .mochi shim file per package, imported by the user's source
| MEP-51 Driver.Build (TargetPythonWheel / TargetPythonSdist / TargetPythonSource)
v
wheel, sdist, or runnable Python source tree

The bridge does not run user Python source at ingest time. stubgen --inspect-mode is the only Python invocation that touches user package code (and only as a tertiary fallback when no .pyi source exists); the bridge parses the resulting stubs in Go. The wrapper module is emitted as Python source by the Go side and run by the user's normal python (orchestrated by the MEP-51 driver) alongside the user's program.

2. Manifest extension: [python-dependencies] and [python]

The MEP-57 mochi.toml gains two new optional top-level tables:

[python-dependencies]
numpy = "^2.0"
pandas = "^2.2"
httpx = { version = "^0.27", extras = ["http2"] }
fastapi = "^0.115"
pydantic = { version = "^2.9", extras = ["email"] }
my-private-package = { version = "^1.0", index = "https://pypi.mycorp.example/simple/" }
my-vcs-dep = { git = "https://github.com/example/my-pkg", rev = "abc123" }
my-local-package = { path = "../my-pkg" }

[python]
requires-python = ">=3.12"
runtime = { event-loop = "per-call" }
indexes = [
{ url = "https://pypi.org/simple/", priority = "primary" },
{ url = "https://pypi.mycorp.example/simple/", priority = "explicit" },
]
stubgen = { fallback = "allow", inspect-mode = true }
monomorphise = [
{ item = "numpy.array", T = "float64" },
{ item = "typing.List", T = "str" },
]

[python.publish]
build-backend = "hatchling"
wheel-tag = "abi3"
python-requires = ">=3.12"

[python.capabilities]
net = true
fs = false
proc = false
subprocess = false
cextension = true
monkey-patch = false

[python-dependencies] follows PEP 508 + PEP 440 grammar. Simple string for a version specifier, table for inline version + extras + index + path / git / branch / rev / tag + markers. This shape is intentional: Mochi users with Python background can copy from existing pyproject.toml [project.dependencies] arrays or requirements.txt lines without translation; the bridge passes the table through to uv pip compile with no edits beyond the format conversion.

The [python] table holds Mochi-specific knobs:

  • requires-python: the Python interpreter version range the bridge will resolve against. Default ">=3.12" (matches the MEP-51 floor). Narrower than the published wheel's own Requires-Python is allowed; broader is a manifest validation error.
  • runtime.event-loop: "per-call" (default) or "persistent". Controls the asyncio bridge mode (see §7).
  • indexes: ordered list of PEP 503 simple-index URLs. The bridge resolves priority = "primary" first, then priority = "supplemental" (consulted only when primary lacks the package), then priority = "explicit" (only used when a dependency explicitly names this index via the per-dep index = "..." field). The default index is https://pypi.org/simple/.
  • stubgen.fallback: "allow" (default) or "deny". When allow, the bridge falls back to stubgen --inspect-mode for packages with no py.typed, no <name>-stubs, and no typeshed entry. When deny, such packages produce a hard error at mochi pkg lock time so the user must either hand-write a sidecar or remove the dep.
  • stubgen.inspect-mode: forwards to stubgen's --inspect-mode flag, which uses runtime introspection rather than AST parsing. Recommended (default true) because C-extension packages have no AST. Inspect mode imports the package; capability constraints apply.
  • monomorphise: a list of explicit generic instantiations. For each entry, the bridge emits the wrapper for <item><T> rather than refusing the import (the default behaviour for under-parameterised Any-returning generics). The entry shape mirrors the equivalent table in MEP-73.

The [python.publish] table holds Mochi-as-PyPI-publisher knobs:

  • build-backend: the PEP 517 build backend. Default "hatchling" (matches MEP-51 Phase 15). Alternatives: "setuptools", "flit-core", "pdm-backend". The backend is recorded in the emitted pyproject.toml's [build-system] table.
  • wheel-tag: either "abi3" (default, builds an ABI-stable wheel that works on any CPython 3.X for the declared minimum) or "cpyXY" (builds a CPython-version-pinned wheel; reserved for packages with version-sensitive C extensions). The MEP-51 pure-Python emit path inherently produces abi3-compatible artefacts because no C extension is generated; the wheel-tag knob exists for forward compatibility with a future C-extension emit path.
  • python-requires: the published wheel's Requires-Python field. Defaults to ">=3.12".

The [python.capabilities] table holds Python-bridge-specific capability flags (a strict refinement of MEP-57's [capabilities] table; if MEP-57 marks the package as not requiring net, [python.capabilities] net = true is a manifest validation error):

  • net: the Python dep graph contains packages that open network sockets (httpx, aiohttp, urllib3, requests). Default false.
  • fs: the dep graph reads or writes files (open, pathlib, aiofiles). Default false.
  • proc: the dep graph spawns processes (subprocess.Popen). Default false.
  • subprocess: separate from proc, this flag covers calls that wrap shell commands (sh, plumbum). Default false.
  • cextension: the dep graph contains packages with C extensions (numpy, pandas, lxml, pillow, pydantic-core, orjson). Default false. Required for the bridge to ingest packages with no .py source.
  • monkey-patch: the dep graph contains packages that mutate stdlib or other-package modules at import time (gevent, eventlet, certain unittest plugins). Default false. Required to suppress the bridge's import-time-safety check.

3. Lockfile extension: [[python-package]]

The MEP-57 mochi.lock gains one new repeated table:

[[python-package]]
name = "httpx"
version = "0.27.2"
wheel-filename = "httpx-0.27.2-py3-none-any.whl"
sdist-filename = "httpx-0.27.2.tar.gz"
source = { kind = "index", index = "https://pypi.org/simple/" }
wheel-blake3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
wheel-sha256 = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
pypi-simple-sha256 = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
stub-provenance = "py.typed"
stub-sha256 = "0011223344556677001122334455667700112233445566770011223344556677"
wrapper-sha256 = "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"
capabilities-declared = ["net"]
extras = ["http2"]
dependencies = ["anyio>=3.0,<5", "certifi", "httpcore>=1.0,<2", "idna", "sniffio"]
markers = ""
attestation-bundle = ""
attestation-verified = false

wheel-blake3 and wheel-sha256 are the integrity hashes of the wheel artefact. BLAKE3-256 is the primary verification hash (matches MEP-57's broader BLAKE3 + SHA-256 dual-hashing rule). SHA-256 is recorded for cross-ecosystem interoperability and matches the hash PyPI's simple index publishes via PEP 700.

pypi-simple-sha256 is the SHA-256 the simple-index response advertised for the wheel filename at lock time. The bridge cross-verifies wheel-sha256 == pypi-simple-sha256 at fetch time; a mismatch is a hard error. The dual recording protects against simple-index URL manipulation: an attacker who serves a wrong wheel would also have to forge a matching simple-index entry, which the lockfile pin catches.

stub-provenance is one of "py.typed" (inline stubs in the wheel), "<name>-stubs" (sibling stubs package), "typeshed" (community typeshed snapshot), or "stubgen" (auto-generated at lock time). At --check time, the bridge re-derives the provenance and fails if the source has moved (e.g., a package added inline stubs since lock time, and the user's stubgen-generated stubs no longer match upstream).

stub-sha256 is the SHA-256 of the stub document set the wrapper was synthesised against. A drift here at --check time is a hard error.

wrapper-sha256 is the SHA-256 of the synthesised <package>_externs.py wrapper file. A drift here at --check time is a hard error.

capabilities-declared is the capability set the manifest declared at lock time. A capability addition (e.g., a transitive dep update newly imports a network-opening module) at --check time is a hard error pending re-acknowledgement (the monotonicity rule from MEP-57 §1.6).

extras is the PEP 508 extras the manifest enabled.

dependencies is the resolved transitive dependency tree (so a downstream consumer's mochi.lock carries the full graph and the PEP 751 co-emit reproduces).

markers is the PEP 508 environment marker the dep was resolved under (empty for unconditional deps).

attestation-bundle is the path inside the cache to the PEP 740 in-toto attestation bundle (empty if no attestation exists for this wheel). attestation-verified is true if the bridge verified the bundle against the Sigstore root of trust at lock time.

4. Surface syntax: import python "..."

The Mochi grammar's existing FFI-import production gains semver and source-locator alternatives for the python token:

ImportStmt := "import" Lang? StringLit "as" Ident ("auto")?
Lang := "go" | "python" | "typescript" | "rust"

The string literal for the python token is one of:

  • <package-name>: bare name, resolves through [python-dependencies] constraint plus mochi.lock.
  • <package-name>@<pep440-spec>: explicit version constraint (numpy@^2.0, httpx@>=0.27,<0.30, pydantic@~=2.9), must satisfy [python-dependencies].
  • <package-name>@index+<url>: explicit index source (my-pkg@index+https://pypi.mycorp.example/simple/).
  • <package-name>@git+<url> or <package-name>@git+<url>#<rev>: VCS source via PEP 508 direct URL syntax.
  • <package-name>@path+<relative>: local path source.

The [name] form for extras follows PEP 508: import python "httpx[http2]@^0.27" as httpx enables the http2 extra. The extras must also appear in the manifest's [python-dependencies] entry (the import is a declaration not a configuration; the manifest is the source of truth for extras).

Example surface programs:

import python "httpx@^0.27" as httpx
import python "numpy@^2.0" as np
import python "pydantic@^2.9" as pyd

record User {
name: string
email: string
age: int
}

fun fetch_users(url: string): list<User> {
let response = httpx.get(url)
let data = response.json()
return data.map(u => User { name: u.name, email: u.email, age: u.age })
}

fun centroid(points: list<list<float>>): list<float> {
let arr = np.array(points)
let mean = arr.mean(axis=0)
return mean.tolist()
}

The <alias> introduces a Mochi namespace; symbol resolution looks up <alias>.<item> and binds against the synthesised extern python fun declaration the bridge generated for <package>.<item> (or <package>.<submodule>.<item> after the bridge walks the stub's submodule tree).

The auto keyword (already accepted for import go ... auto and import rust ... auto) is admitted for import python ... auto to opt into "import every public name of the package as a top-level Mochi binding (rather than namespaced under the alias)". Default is namespaced.

5. CLI surface

The Mochi CLI gains the following additions, all under the existing mochi pkg subcommand introduced by MEP-57:

  • mochi pkg add python <package>[@<pep440-spec>]: adds an entry to [python-dependencies] and runs mochi pkg lock.
  • mochi pkg lock: as today (MEP-57), now extended to walk [python-dependencies], invoke uv pip compile --output-format=pylock.toml against the configured index set, fetch each wheel into the content-addressed cache, run PEP 561 stub ingest, synthesise the wrapper, and write [[python-package]] entries to mochi.lock plus pylock.toml to the project root.
  • mochi pkg lock --check: as today, now extended to verify wheel-blake3, wheel-sha256, pypi-simple-sha256, stub-sha256, wrapper-sha256, and capabilities-declared for every [[python-package]] entry, plus re-verifies any attestation-bundle against the Sigstore root of trust if the bundle existed at lock time.
  • mochi pkg publish --to=pypi [--testpypi] [--dry-run]: builds the package as a wheel + sdist via the existing MEP-51 TargetPythonWheel + TargetPythonSdist emit pipeline (Phase 15), packages the build artefacts, obtains an OIDC token from the CI environment, presents it to PyPI's trusted-publishing endpoint, then invokes uv publish --trusted-publishing always (against TestPyPI when --testpypi is supplied) to upload the wheel + sdist + the PEP 740 attestation bundle. --dry-run skips the upload step but still exercises the OIDC token exchange and the local signing flow against a sigstore-mock-fulcio harness (matching MEP-51 Phase 18 §dry-run).
  • mochi pkg sync python: regenerates the wrapper modules from scratch (without changing the lockfile). Used after manual edits to a synthesised shim file or after upstream stub drift the user wants to ingest without bumping the lock.
  • mochi pkg export pylock: writes the current mochi.lock [[python-package]] entries out as a PEP 751 pylock.toml (a co-emit of the same data; useful when the user wants to consume the lockfile from a non-Mochi Python toolchain).

6. Build orchestration

When a Mochi program contains one or more import python "..." declarations, the MEP-51 build driver gains the following extensions:

  1. Before invoking the Phase 12 sidecar copy step, the driver invokes package3/python/Bridge.PrepareSidecars(workdir, mochiLock) which:

    • For each [[python-package]] in mochi.lock, ensures the wheel is present in ~/.cache/mochi/python-deps/<blake3-hex>/, fetching from the index if not.
    • Materialises the synthesised wrapper module into <workdir>/python_wrap/<package>_externs.py.
    • For each importing .mochi source file, copies the matching wrapper module to the same directory the existing Phase 12 sidecar-copy step expects (<srcDir>/<moduleName>_externs.py).
    • The wrapper's top of file declares import <package> and the bridge ensures the wheel is on PYTHONPATH at run time (either via the venv MEP-51 build creates, or via an explicit --python-path flag passed to python -m).
  2. The existing MEP-51 Phase 12 sidecar copy step picks up the bridge-emitted wrappers unchanged. No edit to MEP-51 Phase 12 code is required beyond a guard that lets multiple <modname>_externs.py files coexist (the existing code path already handles one sidecar per program; the bridge's wrappers go in distinct names so this composes).

  3. The MEP-51 emit pass treats each import python "<package>" as <alias> as a Mochi import "./python_wrap/<package>_shim.mochi" as <alias> shim, where the shim file is the extern python fun corpus the bridge emitted.

  4. The user's python -m mochi_user_<modname> (invoked by Driver.Build and run-time test harnesses) resolves every from <package>_externs import ... line against the wrapper modules, which in turn resolve import <package> against the venv's site-packages.

  5. The driver invalidates the cache when any [[python-package]] wrapper-sha256 changes (per MEP-51 Phase 15 cache-key construction); the bridge's wrappers participate in the same cache marker as the user's emit (mep51-phase18 today, bumping to mep71-phase00 once the bridge phase 0 lands).

7. Async bridge runtime hook

The wrapper module for any source package that exposes async def items includes a sync entry shim per async function:

from __future__ import annotations

import asyncio
import httpx


def get(url: str) -> httpx.Response:
return httpx.get(url)


def get_async_sync(url: str) -> httpx.Response:
# Per-call event loop. Default mode.
return asyncio.run(_get_async(url))


async def _get_async(url: str) -> httpx.Response:
async with httpx.AsyncClient() as client:
return await client.get(url)

When [python] runtime = { event-loop = "persistent" } is set in mochi.toml, the wrapper switches to a process-global event loop cached at first call:

from __future__ import annotations

import asyncio
import httpx

_MOCHI_LOOP: asyncio.AbstractEventLoop | None = None


def _get_loop() -> asyncio.AbstractEventLoop:
global _MOCHI_LOOP
if _MOCHI_LOOP is None:
_MOCHI_LOOP = asyncio.new_event_loop()
return _MOCHI_LOOP


def get_async_sync(url: str) -> httpx.Response:
return _get_loop().run_until_complete(_get_async(url))


async def _get_async(url: str) -> httpx.Response:
async with httpx.AsyncClient() as client:
return await client.get(url)

The per-call mode is the default; the persistent mode is opt-in. The per-call mode is the only mode whose lifetime is fully scoped to a single FFI call (problematic for benchmarks or for long-running daemon workloads where the user wants connection-pool reuse). Free-threaded CPython 3.13t and 3.14t support is a forward Phase 17 sub-phase, not v1.

Phases

See /docs/implementation/0071/ for the per-phase tracking matrix. Eighteen phases cover skeleton (0), simple-index client (1), wheel cache + hash verification (2), PEP 561 stub ingest (3), type-mapping table (4), wrapper synthesiser (5), Mochi extern emitter (6), import python semver grammar (7), MEP-51 build orchestration hooks (8), mochi.lock integration (9), pylock.toml co-emit (10), mochi pkg publish --to=pypi CLI (11), PEP 740 attestation flow (12), async bridge (13), abi3 + ABI-stable wheel emission (14), C-extension fallback story (15), Pyodide / WASI / embedded subset (16), and a TestPyPI dry-run gate (17).

A phase is LANDED only when its gate is green against the curated 25-package fixture corpus.

Target matrix

Phasehost CPython 3.12+ (darwin-arm64 + linux-x64)windows-x64wasm32-wasi (Pyodide)free-threaded CPython 3.13t / 3.14t
0. skeletonNOT STARTEDn/an/an/a
1. simple-index clientNOT STARTEDrequiredn/an/a
2. wheel cache + hash verifyNOT STARTEDrequiredn/an/a
3. PEP 561 stub ingestNOT STARTEDrequiredrequiredrequired
4. type-mapping tableNOT STARTEDrequiredrequiredrequired
5. wrapper synthesiserNOT STARTEDrequiredrequiredrequired
6. extern emitterNOT STARTEDrequiredrequiredrequired
7. import python grammarNOT STARTEDrequiredrequiredrequired
8. build orchestrationNOT STARTEDrequiredn/a (Pyodide path uses MEP-51 phase 17 ipykernel build)required
9. mochi.lock integrationNOT STARTEDrequiredrequiredrequired
10. pylock.toml co-emitNOT STARTEDrequiredrequiredrequired
11. mochi pkg publish --to=pypiNOT STARTEDn/a (publish is host-only)n/an/a
12. PEP 740 attestation flowNOT STARTEDn/an/an/a
13. async bridgeNOT STARTEDrequiredrequiredrequired
14. abi3 wheel emissionNOT STARTEDrequiredn/a (Pyodide is its own ABI)required
15. C-extension fallbackNOT STARTEDrequiredn/a (no compiled extensions under Pyodide v1)required
16. Pyodide / WASI subsetNOT STARTEDn/arequiredn/a
17. TestPyPI dry-run gateNOT STARTEDn/an/an/a

A phase marked n/a for a target is intentional: the bridge does not promise the behaviour on that target.

Alternatives considered

  1. Parse Python source directly instead of via PEP 561 stubs. Rejected: Python's runtime-dynamic semantics make AST-level type inference unreliable (decorators rewrite types at import time, metaclasses rewrite class structures, type aliases live behind if TYPE_CHECKING: guards, runtime globals() mutation is common). 60%+ of the top-200 PyPI packages have C extensions where the .py source is a thin facade over compiled code, so an AST parse misses the actual signatures. The bridge takes PEP 561 stubs as authoritative.

  2. Use inspect.signature() runtime introspection as the primary bind source. Rejected as primary: Signature.parameters[name].annotation is inspect.Parameter.empty for unannotated parameters, which means inspect-only works for the ~20% of PyPI that is fully PEP 484-annotated. The bridge uses inspect (via stubgen --inspect-mode) as a tertiary fallback for packages with no py.typed, no <name>-stubs, and no typeshed entry.

  3. Use the existing MEP-51 Phase 12 hand-written sidecar pattern as the only consume path. Rejected: hand-written sidecars do not scale to PyPI's 600,000+ packages. The Phase 12 pattern is the load-bearing infrastructure for the bridge (the bridge emits sidecars in exactly the Phase 12 shape), but it is the back-end, not the user-facing surface.

  4. Use Cython, mypyc, or Nuitka to compile both Mochi and the imported PyPI package into a single native binary. Rejected: those toolchains are designed for whole-program compilation, not for cross-package interop. Cython's type system does not match Mochi's; mypyc does not support most PyPI packages (it requires strict PEP 484 typing); Nuitka is whole-program-only. The bridge keeps Python as Python; only Mochi compiles to Python.

  5. Use PyO3 / pybind11 / nanobind as the binding layer. Rejected: those tools generate C-extension bindings for Rust / C++ → Python interop, not Python → Mochi. They are the wrong direction for the consume-in path. They are also not relevant for the publish-out path (MEP-71 emits pure-Python wheels; the C-extension publish path is deferred per Phase 15).

  6. Use uniffi-rs for the binding layer. Rejected: uniffi is Mozilla's Rust-to-Swift/Kotlin/Python binding generator; it generates code from a .udl interface description file. Mochi users would have to author a .udl for every package they import, which is more boilerplate than the bridge promise allows.

  7. Use the WIT (Wasm Interface Types) world description plus wit-bindgen. Rejected for v1: WIT is the right long-term answer when the source and sink are both Wasm component-model compatible, but as of 2026-05 the major PyPI packages (numpy, pandas, httpx, fastapi) do not ship a WIT world description. A v2 of the bridge could add a --mode=wit flag for Pyodide-only targets where WIT is feasible.

  8. Direct C-API dlopen of pre-built C extensions from wheels rather than per-import wrapper synthesis. Rejected as primary: many PyPI packages ship wheels that depend on other Python packages at the Python level (import-time), so a pure dlopen path bypasses the package's own import-side initialisation. The wrapper-and-CPython-runtime path is the only one that works for the general case.

  9. Translate Python's dynamic dispatch / monkey-patching into Mochi's static surface. Rejected: Mochi has no facility for runtime type mutation. The bridge translates the Python type stubs as they are; packages that monkey-patch require an explicit [python.capabilities] monkey-patch = true opt-in.

  10. Allow long-lived PyPI API tokens (__token__ in ~/.pypirc) for the publish path. Rejected: matches MEP-57's broader principle that long-lived tokens are deprecated. The 2024 PyPI mass-injection campaigns and the 2025 typosquat flood drove the industry to PyPI Trusted Publishing as the canonical default. MEP-71 ships exclusively on that path; the --allow-token-fallback flag is offered only for the v1 transition period and is removed once internal tooling has migrated.

  11. Use poetry, pdm, or pip-tools as the resolver instead of uv. Rejected: uv is 10x-100x faster than pip-tools on cold and warm cache, implements PEP 751 pylock.toml natively, and is already the canonical resolver MEP-51 Phase 18 pins for the publish path. Single resolver across the consume-in and publish-out directions.

  12. Make import python defer to the user's existing pyproject.toml [project.dependencies] rather than to mochi.toml. Rejected: the Mochi build flow owns the workspace. If the user has a pre-existing pyproject.toml, MEP-71 reads its [project.dependencies] table for the version constraints as a convenience (a future sub-phase, not v1) but the source of truth is mochi.toml. Mixing manifest authorities is a known anti-pattern.

  13. Allow Any-returning Python functions to lower as Any-typed Mochi values. Rejected: Mochi has no Any type. The bridge refuses Any returns with a SkipReport. Users who need dynamic interop write a hand extern python fun declaration with extern type declaring a unique opaque type per call site.

  14. Auto-discover the Python interpreter rather than requiring requires-python in the manifest. Rejected: the resolver needs a target interpreter version to choose wheel variants. The manifest declares it; the build verifies the installed interpreter matches.

Risks

  1. PEP 561 stub coverage is uneven. As of 2026-05, ~38% of the top-500 PyPI packages ship inline py.typed stubs, ~12% have a <name>-stubs sibling, ~7% have typeshed coverage, leaving ~43% requiring stubgen --inspect-mode. For the residual set, stubgen-derived stubs are approximate (Any leaks in for under-annotated parameters; C-extension callable signatures parsed from __doc__ are heuristic). Mitigation: the bridge emits a per-package SkipReport that counts skipped items; users can hand-override with explicit extern python fun declarations.

  2. C-extension signature accuracy. Packages like numpy, scipy, lxml, pydantic-core, orjson are implemented as C extensions; their .py facades may lack annotations, and their stubs (when present) may diverge from runtime behaviour at the Cython / C boundary. Mitigation: the bridge prefers the package's own stubs when py.typed is present (numpy ships py.typed); for packages without stubs, the user is prompted to opt into [capabilities] cextension = true.

  3. Resolver determinism across the consume-in and publish-out directions. uv's resolver may produce different versions for the same manifest on different platforms (e.g. a wheel that is linux_x86_64-only on PyPI). Mitigation: the [[python-package]] lockfile entry records the resolved version per platform marker; cross-platform lock falls back to "highest version available on all listed platforms" for v1.

  4. Wrapper compile time. A wrapper for numpy is ~12,000 LOC of synthesised Python (every public function in numpy.*). Cold-cache stub-ingest plus wrapper synthesis is ~6 seconds; warm-cache regeneration is sub-second. For a Mochi program with 10 large package imports, the wrapper generation pass dominates lock-time cost. Mitigation: wrapper outputs are cached in ~/.cache/mochi/python-deps/wrappers/<wrapper-sha256>/.

  5. Generic monomorphisation explosion. A package that exposes a generic function over 20 types (e.g., pydantic.TypeAdapter[T]) and the user lists 20 monomorphisations produces 20 wrapper symbols. Mitigation: the [python.monomorphise] table is required to enumerate; the bridge does not auto-monomorphise unbounded generics.

  6. asyncio loop construction cost. Per-call asyncio.run constructs ~200μs of loop overhead on CPython 3.12 on Apple Silicon. For workloads that make thousands of async-bridge calls per second (e.g., a hot-loop httpx benchmark) this dominates. Mitigation: opt-in [python] runtime = { event-loop = "persistent" } mode caches a process-global loop; documented as the trade-off lever.

  7. Capability declaration drift. A pinned package update can silently introduce a new capability (e.g., a pure-CPU package newly imports a network-opening telemetry module). mochi pkg lock --check catches this, but only at lock time; if CI skips --check, the production binary ships with the new capability. Mitigation: CI must run --check. The MEP-57 capability monotonicity rule applies transitively.

  8. PEP 740 attestation coverage is uneven. As of 2026-05, attestations are emitted by ~25% of top-500 PyPI publishers (the percentage is climbing fast since the late-2025 GA). For packages without attestations, the bridge falls back to SHA-256 + PyPI simple-index verification only. Mitigation: the lockfile records whether an attestation was present and verified; users can require [python] attestation = "required" to refuse packages lacking attestations.

  9. stubgen import-time side-effects. Running stubgen --inspect-mode against a package imports the package, which can execute arbitrary import-time code (__init__.py may open network connections, read files, spawn processes). Mitigation: stubgen runs inside a Mochi-controlled subprocess with no MOCHI_* env, no HOME, no PYTHONPATH outside the resolved venv, and a 30-second timeout; capabilities flags gate which inspections are allowed.

  10. Free-threaded CPython 3.13t / 3.14t support is forward. Phase 17 covers free-threaded interpreter compatibility; v1 ships on the GIL-enabled 3.12 baseline. Mitigation: explicit Phase 17 sub-phase with its own gate; users running on free-threaded builds get a clear "unsupported" diagnostic until Phase 17 lands.

  11. Pyodide / WASI subset is small. The Pyodide distribution of CPython for the browser supports ~150 of the top-500 PyPI packages (April 2026 Pyodide release notes); WASI-on-Python is still pre-release. Mitigation: the Pyodide gate (Phase 16) checks browser-WASI compatibility at lock time; non-compatible packages produce a clear diagnostic.

  12. TestPyPI dry-run gate flakiness. TestPyPI's uptime SLO is "best-effort"; CI runs against it may be intermittently 503. Mitigation: the dry-run flag exercises the full sigstore flow against sigstore-mock-fulcio locally; TestPyPI is the secondary gate, exercised on workflow_dispatch + nightly cron, not on every PR.

  13. CI image dependency on uv plus stubgen plus a Python 3.12 runtime. The MEP-51 gates already require Python 3.12+; MEP-71 adds uv (single Rust binary, easy install) plus stubgen (ships with the mypy Python package). The CI image is slightly bigger. Mitigation: the standard mochilang/ci-python image bundles the full toolset.

Acknowledgements

This MEP builds on MEP-51 (Python transpiler) for the typed-Python emit pipeline, the mochi_runtime package, the existing TargetPythonWheel / TargetPythonSdist / TargetPythonPublish build targets, the Phase 12 extern python fun sidecar pattern, and the Phase 18 PyPI Trusted Publishing workflow renderer. It builds on MEP-57 (Mochi module and package system) for the mochi.toml manifest, the mochi.lock lockfile, the BLAKE3 + SHA-256 dual-hashing, the content-addressed object store model, the capability declaration scheme, and the trusted-publishing OIDC infrastructure. It builds on MEP-45 (C transpiler) for the FFI sidecar pattern that MEP-51 Phase 12 inherited. It draws from the Python Packaging Authority (PyPA) tooling base (pip, virtualenv, setuptools, wheel, twine, build, distlib, packaging, pyproject-hooks, installer) for the standards alignment. The MEP cites PEP 425, PEP 440, PEP 484, PEP 503, PEP 508, PEP 526, PEP 544, PEP 561, PEP 585, PEP 593, PEP 600, PEP 604, PEP 621, PEP 656, PEP 658, PEP 660, PEP 691, PEP 695, PEP 700, PEP 714, PEP 723, PEP 735, PEP 740, and PEP 751. The bridge draws on the Sigstore project and the OpenSSF Trusted Publishing initiative for the keyless OIDC signing flow, on Astral's uv for the resolver and PEP 751 emit, on the mypy stubgen tool for the inspect-mode fallback, on python/typeshed for the community stub repository, on Astral's python-build-standalone for the cross-platform CPython distribution model, on PyO3 / pybind11 / nanobind for prior-art on Python-binding generation (rejected for the consume direction; informative for the publish-side C-extension forward sub-phase), on the Pyodide team for the browser CPython distribution that informs the WASI subset path, and on the Warehouse codebase (pypi/warehouse) for the PyPI Trusted Publishing endpoint shape.