Introduction
Pyvorin Edge is more than a pipeline framework. Under the hood it manages a family of runtime shared objects—dict_runtime.so, list_runtime.so, nexus_runtime.so—that provide typed containers, inline caches, and guard-based fallback machinery. This article explains how those pieces fit together and how you can extend them.
The Runtime Shared Objects
The edge runtime ships three core native libraries:
dict_runtime.so— Typed dictionaries with inline key-type caches.list_runtime.so— Typed lists with pre-allocated growth buffers.nexus_runtime.so— The dispatch layer that routes hot paths to compiled kernels or falls back to Python.
These libraries are loaded by the same ModuleLoader mechanism you use for custom kernels. The runtime holds a private singleton loader that caches symbols across pipeline executions.
The Object Model: Typed Dicts and Typed Lists
Standard Python dict and list are generic containers. In the edge runtime, typed variants store a single ob_type tag in the header. As long as every inserted value matches that tag, the C layer skips PyObject overhead and stores values inline.
/* Simplified header from dict_runtime internals */
typedef struct {
PyObject_HEAD
PyTypeObject *value_type; /* expected type; NULL = generic fallback */
size_t inline_count; /* number of inline slots used */
char inline_buffer[0]; /* variadic inline storage */
} TypedDictObject;
When a key is looked up, the inline cache checks the key’s hash against a small fixed-size ring. On cache hit the value pointer is returned without traversing the full hash table. On cache miss the full lookup runs and the cache line is replaced.
Inline Cache Invalidation
The cache is invalidated when the container’s version_tag changes (insert, delete, resize). This is identical in principle to the PEP 509 ma_version_tag used by CPython, but specialized for the typed fast-path.
Extending the Runtime with Custom Types
You can register a custom C type with the runtime so that typed containers accept it. The contract requires three functions:
size_t sizeof_custom(void)— size of the inline value.int check_custom(PyObject *obj)— returns 1 ifobjis an instance.void copy_custom(void *dst, PyObject *src)— bit-copies the inline representation.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stddef.h>
typedef struct {
double x;
double y;
} Point;
static size_t sizeof_point(void) { return sizeof(Point); }
static int check_point(PyObject *obj) {
/* Assume a Python wrapper named PointObj */
return PyObject_IsInstance(obj, (PyObject *)PointObjType);
}
static void copy_point(void *dst, PyObject *src) {
Point *p = (Point *)dst;
p->x = PyFloat_AsDouble(PyObject_GetAttrString(src, "x"));
p->y = PyFloat_AsDouble(PyObject_GetAttrString(src, "y"));
}
After building the extension module, register it with the runtime:
from pyv_edge_agent.module_host.loader import ModuleLoader
from pyv_edge_agent.module_host.abi import ABIContract
import ctypes
abi = ABIContract(
function_name="register_custom_type",
arg_types=[
ctypes.c_char_p, # type name
ctypes.c_void_p, # sizeof_fn
ctypes.c_void_p, # check_fn
ctypes.c_void_p, # copy_fn
],
return_type=ctypes.c_int,
)
loader = ModuleLoader()
loader.load("./libcustom_types.so", abi)
# ... obtain function pointers for sizeof_point, check_point, copy_point ...
loader.call("register_custom_type", b"Point", sizeof_fp, check_fp, copy_fp)
ModuleLoader.verify_signature() Deep Dive
ModuleLoader in module_host/loader.py performs two safety checks before a compiled module is trusted:
- Symbol presence —
generate_abi()inCompilerBridgeloads the.sowithctypes.CDLLand callsgetattr(lib, function_name). If the symbol is missing, loading fails immediately. - Integrity hash —
verify_signature(expected_hash)computes the SHA-256 of the on-disk file and compares it to the manifest.
# From module_host/loader.py (simplified flow)
loader.load("/opt/edge/modules/libvecadd.so", abi)
if not loader.verify_signature(expected_sha256):
raise SecurityError("Module hash mismatch — possible tampering.")
ExplicitFallback.run_with_guard() Internals
The fallback machinery in module_host/fallback.py exists because compiled code can crash or diverge from the Python reference. The SDK provides two modes:
- Strict validation —
fallback_to_python()runs both paths and compares outputs. Any mismatch raisesFallbackMismatchError. - Production guard —
run_with_guard()runs only the compiled path by default. If it fails andfallback_enabled=True, it silently falls back to Python. Iffallback_enabled=False(the default), it raisesCompiledModeFailure.
How run_with_guard Works
# From module_host/fallback.py
@classmethod
def run_with_guard(cls, compiled_func, reference_func, *args,
tolerance=1e-9, fallback_enabled=False):
try:
result = compiled_func(*args)
return result
except Exception as comp_exc:
if not fallback_enabled:
raise CompiledModeFailure(...) from comp_exc
return reference_func(*args)
The logging surface is comprehensive: every call logs the function names, argument shapes, success or failure, and the fallback decision. In production you should ship logs to an aggregator and alert on CompiledModeFailure spikes.
Complete Guard Example
import logging
from pyv_edge_agent.module_host.fallback import (
ExplicitFallback,
CompiledModeFailure,
)
from pyv_edge_agent.module_host.loader import ModuleLoader
from pyv_edge_agent.module_host.abi import ABIContract
import ctypes
logging.basicConfig(level=logging.INFO)
abi = ABIContract(
function_name="risky_kernel",
arg_types=[ctypes.c_double],
return_type=ctypes.c_double,
)
loader = ModuleLoader()
loader.load("./librisky.so", abi)
compiled = lambda x: loader.call("risky_kernel", x)
def reference(x: float) -> float:
return x * 2.0 + 1.0
# Production call — compiled path only, no silent fallback
try:
out = ExplicitFallback.run_with_guard(
compiled, reference, 3.14, fallback_enabled=False
)
print(f"Result: {out}")
except CompiledModeFailure as exc:
print(f"Compiled path failed: {exc}")
# Development call — validate both paths and compare
try:
out = ExplicitFallback.fallback_to_python(compiled, reference, 3.14)
print(f"Validated result: {out}")
except Exception as exc:
print(f"Mismatch or crash: {exc}")
loader.unload()
ABIContract and ctypes Binding
The bridge between Python and C is ABIContract in module_host/abi.py. It is a dataclass with four fields:
function_name: strarg_types: List[Type[Any]]return_type: Optional[Type[Any]]calling_convention: str(default"cdecl")
When ModuleLoader.call() is invoked, it lazily resolves the symbol from ctypes.CDLL. If the requested name matches the stored ABIContract.function_name, the loader sets sym.argtypes and sym.restype automatically:
# From module_host/loader.py
sym = getattr(self._lib, function_name)
if self._abi is not None and self._abi.function_name == function_name:
if self._abi.arg_types:
sym.argtypes = list(self._abi.arg_types)
if self._abi.return_type is not None:
sym.restype = self._abi.return_type
This lazy binding is thread-safe because ModuleLoader wraps symbol resolution and invocation in an RLock. However, as noted in the threading article, you should not call Pipeline.run() from multiple threads concurrently; the loader itself is safe, but pipeline state is not.
Manifest Round-Trip
ABIContract.to_dict() serializes ctypes types to their string names, and from_manifest() maps them back through a curated type map plus a fallback to getattr(ctypes, name). This lets you ship ABI descriptions as JSON inside manifest.json without importing ctypes on the manifest generator host.
abi = ABIContract(
function_name="vec_add_f32",
arg_types=[ctypes.POINTER(ctypes.c_float), ctypes.c_size_t],
return_type=None,
)
manifest_entry = abi.to_dict()
# {'function_name': 'vec_add_f32',
# 'arg_types': ['LP_c_float', 'c_size_t'],
# 'return_type': None,
# 'calling_convention': 'cdecl'}
restored = ABIContract.from_manifest(manifest_entry)
Summary
The Pyvorin Edge runtime is a thin, fast layer built on typed containers, inline caches, and explicit fallback guards. ModuleLoader provides thread-safe symbol resolution and SHA-256 integrity checking. ABIContract defines the boundary between Python and C. ExplicitFallback.run_with_guard() gives you production-grade safety without sacrificing speed. Understanding these internals lets you extend the runtime with custom types and debug failures at the ABI boundary with confidence.