Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Principle:Nautechsystems Nautilus trader PyO3 Stub Generation

From Leeroopedia


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

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment