ppy_runtime¶
ppy_runtime holds what a built artifact needs at launch, and nothing
more. It never imports ppy_compiler.
A compiled application depends on the interpreter, this runtime, its native library, and its manifest. It keeps working with the compiler uninstalled.
Launching¶
ppy_runtime.launch
¶
Run a built artifact: load, bind, execute -- and never compile.
This is the whole runtime path of an AOT build. It reads the manifest, opens the native library, rebuilds the guarded bindings from the recorded ABI, loads the generated Python the build wrote, and runs the entry module. No parsing, no analysis, no LLVM: those all happened at build time.
PrebuiltBinder(manifest, library)
¶
Bases: LibraryBinder
Serves the entries a manifest recorded, out of one shared library.
region(module, function, fallback)
¶
Serve a compiled ATen region out of the extension the build shipped.
generated_modules(manifest)
¶
The generated Python modules a manifest names, read off disk.
ppy_runtime.manifest
¶
Loading the binding manifest: the artifact's contract with this runtime.
The manifest is data a build wrote once; loading it must stay cheap and must never reconstruct compiler state. Validation is schema, ABI version, interpreter compatibility, and file presence -- nothing that reads source.
ManifestError
¶
Bases: RuntimeError
The artifact cannot be run, with the reason and the remedy.
RegionLibrary(library, entries)
dataclass
¶
One generated module's compiled ATen regions: the extension holding them, and the C++ symbol that serves each function.
host_runs(target)
¶
Whether this machine is the one target names, by architecture and OS.
The runtime has no LLVM to ask, so it compares the words a triple is made of with what the platform says of itself.
Binding and the ABI¶
ppy_runtime.binding
¶
Python-ABI trampolines for natively lowered functions (spec 16.4, 25.3).
Runtime-only: a built artifact binds through this module with no compiler installed. JIT specialization is an optional hook the compiler passes in, and its machinery is imported only when it is actually used.
SanitizerFailure
¶
Bases: RuntimeError
A --sanitize check failed in native code; the message names the kind and the function.
GuardFailed
¶
Bases: Exception
A runtime guard rejected an argument, so the Python path must run.
NativeBinding(signature, wrapper, fallback, calls=0, fallbacks=0, specialized_calls=0, fast_entry=None, owner=None, selectors=list(), registered=0, key_counts=dict(), observations=0, observing=False)
dataclass
¶
A guarded native entry point with a Python fallback.
observation_wanted(specializer, policy, info)
¶
Whether this function still wants Python watching for specialization.
value_class_types(signature, fallback)
¶
The runtime classes a generated wrapper guards value parameters on.
Resolved from the defining module, so a class the wrapper cannot see means no fast entry rather than a wrong one.
remember(entry, signature)
¶
Record that entry, a generated C entry point, runs signature natively.
signature_of(function)
¶
The native signature calling function runs under here, or None when the
call is Python's: the wrapper's attribute, or the adopted C entry point.
adopt(signature, entry, fallback, *, owner=None)
¶
Adopt a generated wrapper that already holds its Python fallback in C.
Nothing stands between the caller and the C entry point, so per-call statistics are not collected on this path.
bind(signature, address, fallback, *, specializer=None, policy=None, info=None, fast_entry=None, owner=None, register=None)
¶
Build the Python-callable wrapper for one native function.
ctypes.CFUNCTYPE releases the GIL around the foreign call, which is what
a native region touching no Python objects is allowed to do (spec 16.6).
When the function asked for it, repeated argument shapes are compiled into guarded specializations and selected here (spec 16.9).
ppy_runtime.abi
¶
The PPY native ABI, as data: what a compiled artifact promises (spec 16.4).
This is the contract a binding manifest serializes and a runtime rebuilds. Nothing here may depend on the compiler: the same dataclasses describe a function to the lowering that emits it and to the runtime that calls it years later.
NativeParam(name, kind, element='', elements=(), fields=(), class_name='')
dataclass
¶
One source-level parameter and the ABI atoms it expands to.
is_pointer
property
¶
A ppy.native.ptr[T]: one machine address, no Python boundary.
is_handle
property
¶
A ppy.Vec or another collection: its runtime handle, no Python boundary.
is_borrowed
property
¶
A ppy.Buffer[T] is borrowed in place; a list is copied out.
A Python list holds boxed elements, so there is no contiguous array to point at. A buffer-protocol object already has one (spec 6.4, 13.8).
NativeSignature(qualname, symbol, parameters, returns, releases_gil=False, cpu_features=(), future='')
dataclass
¶
The PPY native ABI for one function (spec 16.4).
Runtimes native code calls into¶
ppy_runtime.arrow
¶
Arrow arrays across the native boundary: the C Data Interface, without a copy.
An Arrow array is handed to native code as the ArrowArray struct the
Arrow C Data Interface defines -- length, null count, offset, and the
buffers -- which arrow.import in the IR reads directly. No PyObject
crosses the boundary (spec 54). The producer keeps ownership: the struct's
release callback is called when the borrow ends, and never twice.
from ppy_runtime.arrow import exported
with exported(array) as struct: # the address of an ArrowArray
native(struct, ...)
ArrowArray
¶
Bases: Structure
struct ArrowArray of the Arrow C Data Interface.
ArrowSchema
¶
Bases: Structure
struct ArrowSchema of the Arrow C Data Interface.
release(struct)
¶
Give the producer its memory back, once; a released struct is inert.
exported(array)
¶
Borrow array (a pyarrow.Array) as an ArrowArray struct: its address.
The array's buffers are shared, not copied; the struct is released when the block ends, whatever happened inside it.
ppy_runtime.aio
¶
The native async runtime's Python face: its library, its futures, and how they run (spec 77).
The runtime is one C file beside this module. It is compiled once, on
first use, with the C compiler the machine has, into the user's cache, and
loaded into the process; compiled code the JIT makes and a built artifact
both reach the same functions, so a future one side started the other can
drive. Without a compiler, or off Linux, there is no runtime: available()
says so, reason() says why, and every coroutine runs under asyncio.
NativeGuardFailed
¶
Bases: RuntimeError
A guard failed inside a native coroutine, where nothing can fall back.
A compiled coroutine that has already slept or spoken on a socket cannot
hand the work back to its Python definition; the honest answer is this
error, naming the function. --safeguards off or a body the prover
clears keeps the guard out of the coroutine.
Runtime(library)
¶
ppy_aio_* through ctypes, on whichever library holds them.
NativeFuture(handle, kind, runtime_, function='')
¶
What a natively compiled async def hands back when called: a future the runtime owns.
ppy.aio.run drives the loop until it completes; awaited from asyncio,
it steps the native loop between the Python loop's turns. Its value is
read once.
compiler()
¶
The C compiler that builds the runtime: CC, else the first of cc, gcc, clang.
reason()
¶
Why the runtime is unavailable here; empty when it is.
library_path()
¶
The compiled runtime, built into the cache on first use; None with the reason kept.
runtime()
¶
The process's runtime, on the library library_path built; None without one.
runtime_for(owner)
¶
The runtime a native function's own library carries, else the process's.
ppy_runtime.cuda
¶
Launching a compiled kernel: the CUDA driver through ctypes, no toolkit needed (spec 75).
The build stages a kernel as PTX with the kinds of its parameters; a
Kernel loads it into the device's primary context once and launches it.
Scalars are passed by value; a native pointer's whole array goes to the
device before the launch and comes back after it when the pointer is
mutable, so a launch means exactly what the reference launch means, only on
the device. Memory made by cuda.device_alloc already lives there: its
address is passed and nothing is copied. A machine without the driver, or
without a device, has no kernel to bind: the reference runs instead, and
the binding says why.
CudaError
¶
Bases: RuntimeError
The driver refused: the message carries its error string.
Driver(library)
¶
The CUDA driver, loaded once, with the first device's primary context.
Kernel(function, symbol, ptx, params, threads=0)
¶
One compiled kernel: its PTX, its symbol, and the kinds of its parameters.
launch(grid, block, arguments)
¶
Run the kernel over grid blocks of block threads with arguments, and wait.
driver()
¶
The driver, loaded on first use; None where there is none, or PPY_NO_CUDA is set.
kernel_binding(function, payload, fallback)
¶
The staged kernel behind function, for ppy.cuda.launch to find.
Called directly, a kernel runs its own definition, as it would under CPython; launched, it runs on the device. Without a driver the binding is the fallback and says why.
ppy_runtime.xla
¶
Running a compiled ppy.xla function: the payload the build staged, on a device.
The payload is what the compiler wrote for an @ppy.xla.jit function: the
StableHLO module and the kinds of its parameters and results. runtime_call
turns it into a callable that places the arguments on the device, runs the
executable, and hands back Python values -- a float for a float, an
array for a tensor -- so the program sees what the Python path would have
given it (spec 64, 65, 66).
Staged artifacts¶
ppy_runtime.exported
¶
Calling a build-time exported graph region (spec 21.3, 21.4).
bind_exported(function, payload, fallback)
¶
Rehydrate an exported computation, falling back to the staged function.
The exported artifact carries its own StableHLO and calling metadata, so invoking it does not re-trace the Python source. Shapes outside the exported signature fall back, which is what shape polymorphism does not cover (spec 21.5).
ppy_runtime.regions
¶
Guarded dispatch into a compiled ATen region (spec 20.6).
bind_region(function, compiled, fallback)
¶
Wrap a compiled region in the guards its curated domain requires.
Every at:: call inside the region goes through the dispatcher, so
autograd and device selection need no guard. What does need one is the
Python override machinery the region bypasses (spec 20.6).