Principle:Nautechsystems Nautilus trader PyO3 Stub Generation
| Knowledge Sources | |
|---|---|
| Domains | Build_Tooling, Python, Rust |
| Last Updated | 2026-02-10 08:00 GMT |
Overview
A build-time process that extracts type information from Rust PyO3 bindings and produces Python .pyi stub files for IDE autocompletion and static type checking.
Description
When Rust code is exposed to Python via PyO3, the resulting .so binary contains no type information visible to Python tooling. Without type stubs, mypy cannot type-check code that uses PyO3 classes, and IDEs cannot provide autocompletion for PyO3 methods. PyO3 Stub Generation solves this by: (1) Using pyo3-stub-gen to introspect PyO3 proc-macro annotations (#[pyclass], #[pymethods]) at compile time and produce raw .pyi files. (2) Post-processing the generated stubs to fix naming conventions (e.g., py_new to __init__), normalize type references, and relocate classes from the monolithic extension module stub into their logical submodule locations. This relocation is critical because PyO3 compiles everything into a single _libnautilus.so, but the public Python API exposes classes through submodules like nautilus_trader.model and nautilus_trader.adapters.blockchain.
Usage
Apply this principle whenever a Python package wraps Rust code via PyO3 and needs to support static type checking or IDE autocompletion. It is a build-time concern, not a runtime concern. Stubs should be regenerated whenever the Rust API surface changes.
Theoretical Basis
The stub generation follows a compile-time introspection + relocation pattern:
# Abstract algorithm (NOT real implementation)
# Step 1: Introspect PyO3 annotations
raw_stubs = pyo3_stub_gen.generate(crate="nautilus-pyo3")
# Produces: _libnautilus.pyi with ALL classes in flat namespace
# Step 2: Post-process
for module, fixup in MODULE_FIXUPS.items():
for class_name in fixup.classes:
move_class(raw_stubs["_libnautilus"], target=module, class_name=class_name)
add_imports(module, fixup.imports)
update_all_exports(module, fixup.all_exports)
# Step 3: Fix method names
for stub_file in all_stubs:
rename_methods(stub_file, {"py_new": "__init__"})
add_header(stub_file, ruff_ignore_directives)
Key constraints:
- Single binary, multiple modules — PyO3 compiles into one .so, but Python expects submodule structure
- Name mismatches — PyO3 uses py_new for constructors that Python sees as __init__
- PEP 561 compliance — stubs must follow the PEP 561 standard for type information distribution