Generate .pyi stub files with full **kwargs / *args MRO backtracing
Project description
stubpy
Generate .pyi stub files for Python modules with full **kwargs / *args MRO backtracing, type-alias preservation, and cross-file import resolution.
Features
**kwargsbacktracing — walks the entire MRO to expand**kwargsinto concrete, named parameters at every inheritance level.cls()detection —@classmethodmethods that forward**kwargsintocls(...)are resolved againstcls.__init__, not MRO siblings.- Typed
*argspreserved — explicitly annotated*args(e.g.*elements: Element) always survive the resolution chain. - Type-alias preservation —
types.Lengthstaystypes.Lengthrather than expanding tostr | float | int. Aliases insideOptional[...],tuple[...],list[...], andUnion[..., None]are also preserved. - Cross-file imports — base classes and annotation types from other local modules are re-emitted in the
.pyiheader automatically. - AST pre-pass — a read-only AST harvest runs before module execution, recovering alias names that Python's
typing.Unionwould otherwise flatten at runtime. - Structured diagnostics — every pipeline stage records
INFO,WARNING, andERRORentries in aDiagnosticCollectorrather than swallowing exceptions silently. - Unified symbol table — classes, functions, variables, type aliases, and overload groups are all represented as typed
StubSymbolentries in aSymbolTable. - Zero runtime dependencies — stdlib only.
Environment setup
Requires Python 3.10+. The steps below use the Windows Python Launcher (
py).
On macOS / Linux replacepy -3.10withpython3.10.
# 1. Clone the repository
git clone https://github.com/wzjoriv/stubpy.git
cd stubpy
# 2. Create a virtual environment with Python 3.11
py -3.11 -m venv .venv
# 3. Activate the environment
.venv\Scripts\activate # Windows CMD / PowerShell
# source .venv/bin/activate # macOS / Linux
# 4. Install in editable mode with development dependencies
pip install -e ".[dev]"
# 5. Verify — run the full test suite
pytest
How it works
stubpy runs a nine-stage pipeline, each stage in its own module:
generate_stub(filepath)
│
├─ 1. loader load_module() load source as a live module
├─ 2. ast_pass ast_harvest() read-only AST pre-pass
├─ 3. imports scan_import_statements() parse AST → {name: import_stmt}
├─ 4. aliases build_alias_registry() discover type-alias sub-modules
├─ 5. symbols build_symbol_table() merge AST + runtime → SymbolTable
├─ 6. generator collect_classes() gather classes in source order
│ └─ for each class:
│ emitter generate_class_stub()
│ └─ for each method:
│ resolver resolve_params() ← MRO backtracing
│ emitter generate_method_stub() ← AST raw annotations
└─ 7. generator assemble header + body → write .pyi
resolve_params uses three strategies in order:
- No variadics — if the method has neither
*argsnor**kwargs, return its own parameters unchanged. cls()detection — if a@classmethodbody containscls(..., **kwargs), the**kwargsis resolved againstcls.__init__via AST analysis. Parameters hardcoded in the call are excluded.- MRO walk — walk ancestor classes that define the same method, collecting concrete parameters until all variadics are fully resolved.
StubContext carries all mutable state for one run. A fresh instance is created per generate_stub() call, making the generator fully re-entrant.
CLI
stubpy path/to/module.py # writes module.pyi alongside source
stubpy path/to/module.py -o out/module.pyi # custom output path
stubpy path/to/module.py --print # also print to stdout
stubpy path/to/module.py --verbose # print all diagnostics to stderr
stubpy path/to/module.py --strict # exit 1 if any ERROR diagnostic
Python API
from stubpy import generate_stub
content = generate_stub("path/to/module.py")
content = generate_stub("path/to/module.py", "out/module.pyi")
Extended public API
# Diagnostics
from stubpy import DiagnosticCollector, DiagnosticLevel, DiagnosticStage, Diagnostic
# AST pre-pass
from stubpy import ast_harvest, ASTSymbols
# Symbol table
from stubpy import (
SymbolTable, SymbolKind,
ClassSymbol, FunctionSymbol, VariableSymbol, AliasSymbol, OverloadGroup,
build_symbol_table,
)
# Configuration
from stubpy import StubContext, StubConfig, ExecutionMode
Documentation (optional)
# Install documentation dependencies
pip install -e ".[docs]"
# Build the HTML site
cd docs && make html
# → open docs/_build/html/index.html in a browser
Installation (end users)
pip install stubpy
# or
uv add stubpy
Example
# shapes.py
class Shape:
def __init__(self, color: str = "black", opacity: float = 1.0) -> None: ...
class Circle(Shape):
def __init__(self, radius: float, **kwargs) -> None:
super().__init__(**kwargs)
@classmethod
def unit(cls, **kwargs) -> "Circle":
return cls(radius=1.0, **kwargs)
stubpy shapes.py --print
# shapes.pyi (generated)
from __future__ import annotations
class Shape:
def __init__(self, color: str = 'black', opacity: float = 1.0) -> None: ...
class Circle(Shape):
def __init__(
self,
radius: float,
color: str = 'black',
opacity: float = 1.0,
) -> None: ...
@classmethod
def unit(cls, color: str = 'black', opacity: float = 1.0) -> Circle: ...
Project layout
stubpy/
├── stubpy/ ← package (stdlib only, no runtime deps)
│ ├── context.py StubContext, AliasEntry, StubConfig, ExecutionMode
│ ├── diagnostics.py DiagnosticCollector, Diagnostic
│ ├── ast_pass.py ast_harvest, ASTSymbols
│ ├── symbols.py SymbolTable, StubSymbol hierarchy
│ ├── loader.py load_module
│ ├── aliases.py build_alias_registry
│ ├── imports.py scan / collect imports
│ ├── annotations.py dispatch-table annotation_to_str
│ ├── resolver.py resolve_params (3 strategies)
│ ├── emitter.py generate_class / method stub
│ └── generator.py generate_stub orchestrator
├── demo/ demo package used for integration tests
├── tests/ pytest suite (435+ tests)
├── docs/ Sphinx + Furo documentation
├── LICENSE
└── pyproject.toml
Contributing
git clone https://github.com/wzjoriv/stubpy.git
cd stubpy
py -3.11 -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
pytest
License
MIT © 2026 Josue N Rivera
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stubpy-0.2.0.tar.gz.
File metadata
- Download URL: stubpy-0.2.0.tar.gz
- Upload date:
- Size: 67.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fa4f08c339c5254279f8c71a6c55194ec154449cd94169c874c8dbffdf240c9
|
|
| MD5 |
715417f41033d4c437e2868a6984b984
|
|
| BLAKE2b-256 |
478223e08f2f6e0ccb68fba6f93d0b75594c7ffc7f4cad497884b7be7517b4bc
|
File details
Details for the file stubpy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: stubpy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 43.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e280d09b5eeacee18a368c376f87850ef763303cf144256a9f463101b1a04414
|
|
| MD5 |
7db04200c7bf7821f050429dc2eedf40
|
|
| BLAKE2b-256 |
27e0517ed966f350641d8ec09c9583ecd596b3005092db321d86fd67ad053aa8
|