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.2.1.tar.gz (78.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.2.1-py3-none-any.whl (46.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: stubpy-0.2.1.tar.gz
  • Upload date:
  • Size: 78.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.2.1.tar.gz
Algorithm Hash digest
SHA256 eec81c7ac89fad2ce163a7b5409364abd6b0ac9c7887f0fcbfd5eb32fd2222a7
MD5 dab3cad370e5fb1ae8508f7956e84420
BLAKE2b-256 9bcab0c1602de87938fc27e50e7bb947c38096de0d946bf5ec91ae7a63161537

See more details on using hashes here.

File details

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

File metadata

  • Download URL: stubpy-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 46.3 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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8590ce70856a5c03269ab5e3588780f7dde83f8b0f6df8f15e8179ef36ad1bb2
MD5 cd1e33c8f8d70ca9aded31b4b3ab08a6
BLAKE2b-256 8184e4cc8bf36872b8eb343e8d41b0ee7686d5cd2fd5aed64870f1429f65b72d

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