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.0.tar.gz (78.7 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.0-py3-none-any.whl (46.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: stubpy-0.3.0.tar.gz
  • Upload date:
  • Size: 78.7 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.0.tar.gz
Algorithm Hash digest
SHA256 88e74fffcd1e649d43c0a40d4af85f2e45158261ee57b37660cb5e75c461afa3
MD5 eed8060f3c1d924ddf351c5c0440bb14
BLAKE2b-256 6bc7071b1ee213fb3e64ef2d4cae0442b8262e1e0ddb1823889fd7ac46c0c3ce

See more details on using hashes here.

File details

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

File metadata

  • Download URL: stubpy-0.3.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 99948198a698efaff7a244a14ba0147bf8ecb2ed5e2fc33e99b26f3fa0f416ae
MD5 96ee6923b36b2975d9c5e5a5d7b057c5
BLAKE2b-256 0735fc99e99979d7e9bec7e7eb397834f9fdb74b2816983e6ab6b977ad5bdbef

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