Skip to main content

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.

PyPI Python License: MIT

Features

  • **kwargs backtracing — walks the entire MRO to expand **kwargs into concrete, named parameters at every inheritance level.
  • cls() detection@classmethod methods that forward **kwargs into cls(...) are resolved against cls.__init__, not MRO siblings.
  • Typed *args preserved — explicitly annotated *args (e.g. *elements: Element) always survive the resolution chain.
  • Type-alias preservationtypes.Length stays types.Length rather than expanding to str | float | int. Aliases inside Optional[...], tuple[...], list[...], and Union[..., None] are also preserved.
  • Cross-file imports — base classes and annotation types from other local modules are re-emitted in the .pyi header automatically.
  • AST pre-pass — a read-only AST harvest runs before module execution, recovering alias names that Python's typing.Union would otherwise flatten at runtime.
  • Structured diagnostics — every pipeline stage records INFO, WARNING, and ERROR entries in a DiagnosticCollector rather than swallowing exceptions silently.
  • Unified symbol table — classes, functions, variables, type aliases, and overload groups are all represented as typed StubSymbol entries in a SymbolTable.
  • Zero runtime dependencies — stdlib only.

Environment setup

Requires Python 3.10+. The steps below use the Windows Python Launcher (py).
On macOS / Linux replace py -3.10 with python3.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:

  1. No variadics — if the method has neither *args nor **kwargs, return its own parameters unchanged.
  2. cls() detection — if a @classmethod body contains cls(..., **kwargs), the **kwargs is resolved against cls.__init__ via AST analysis. Parameters hardcoded in the call are excluded.
  3. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stubpy-0.3.1.tar.gz (79.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stubpy-0.3.1-py3-none-any.whl (46.5 kB view details)

Uploaded Python 3

File details

Details for the file stubpy-0.3.1.tar.gz.

File metadata

  • Download URL: stubpy-0.3.1.tar.gz
  • Upload date:
  • Size: 79.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for stubpy-0.3.1.tar.gz
Algorithm Hash digest
SHA256 e29ab9a46915e4673eeb974da1780ee607650e616f8054810d7d0dced6de8b32
MD5 3c046f5bfd20e3819a515505d9b50a8d
BLAKE2b-256 51cd9db555fd10827ba4631e5436e0e8b9c0f83b30dcdc3759e8150a02edcda4

See more details on using hashes here.

File details

Details for the file stubpy-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: stubpy-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 46.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for stubpy-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2b2a784f5bf77a005c6097903a9085c83499377bebfbc5176ce2da1f5d422d32
MD5 1043c79fb7711f6bc40382d100b337e1
BLAKE2b-256 5cb3f3e06b1d0fdea1e8021d8d1981fe9e4d5e62ed4e3fe2037ff6e8838c66f4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page