Project integration: PixelForge

This example walks through using stubpy on a realistic project — PixelForge, a small SVG/canvas drawing library. The demo package (demo/ in the repository) is designed so that each module exercises a different set of stubpy features while remaining a plausible real-world graphics library.

Project layout

demo/
├── types.py         type aliases (Color, Length, BoundingBox)
├── element.py       Element ABC — base for all drawables
├── primitives.py    Circle, Rect, Text; dataclass, Enum, TypedDict, ABC
├── scene.py         Generic containers (Stack[T], SpatialIndex[K,V]),
│                    TypeVar, Protocol, TypeAlias, NewType
├── style.py         @overload (parse_color, blend, Brush factory),
│                    GradientStop NamedTuple
├── container.py     Container / Layer / Scene — **kwargs backtracing
│                    through a three-level inheritance chain
├── functions.py     Module-level **kwargs forwarding
│                    (make_color_red → make_color)
├── export.py        Cross-file imports, TYPE_CHECKING guard, async export
├── graphics.py      Canvas, Renderer — complex class hierarchy
├── variables.py     Module-level annotated variables
└── mixed.py         Mix of all the above

Generating stubs

# Stub the entire package alongside sources:
stubpy demo/

# Or write to a separate stubs/ tree:
stubpy demo/ -o stubs/

# Or stub multiple modules at once with a glob:
stubpy "demo/*.py"

Configuration via pyproject.toml

[tool.stubpy]
include_private   = false
union_style       = "modern"
alias_style       = "compatible"
execution_mode    = "runtime"
output_dir        = "stubs"
exclude           = ["demo/__pycache__/**"]
infer_types       = false    # infer types from docstrings (# type: comments)
incremental       = false    # preserve manual edits outside auto-generated markers
respect_all       = true     # false stubs everything even when __all__ is present
stubpy demo/   # reads pyproject.toml automatically
stubpy demo/ --infer-types     # add docstring-inferred type comments
stubpy demo/ --incremental     # merge-safe: preserves manual .pyi edits
stubpy demo/ --exclude "demo/migrations/*.py" --exclude "demo/tests/*.py"
stubpy demo/ --no-respect-all  # stub everything even without __all__ listing

**kwargs resolution through a class hierarchy

The demo container.py inherits Element.__init__ through three levels of **kwargs:

# element.py
class Element:
    def __init__(self, x: float = 0, y: float = 0,
                 label: str | None = None, **kwargs) -> None: ...

# container.py
class Container(Element):
    def __init__(self, *elements: Element,
                 clip: bool = False, **kwargs) -> None: ...

class Layer(Container):
    def __init__(self, name: str, locked: bool = False, **kwargs) -> None: ...

class Scene(Layer):
    def __init__(self, width: float, height: float, **kwargs) -> None: ...

stubpy walks the MRO and emits the full concrete signature for each class:

# Generated stub for Scene.__init__
class Scene(Layer):
    def __init__(
        self,
        width: float,
        height: float,
        name: str = ...,
        locked: bool = False,
        clip: bool = False,
        x: float = 0,
        y: float = 0,
        label: str | None = None,
    ) -> None: ...

Module-level function **kwargs forwarding

stubpy also expands **kwargs for standalone functions:

# functions.py
def make_color(r: float, g: float, b: float, a: float = 1.0) -> Color: ...
def make_color_red(r: float = 1.0, **kwargs) -> Color:
    return make_color(r=r, **kwargs)

Generated stub — **kwargs fully expanded:

def make_color_red(
    r: float = 1.0,
    *,
    g: float,
    b: float,
    a: float = 1.0,
) -> Color: ...

Special class forms

demo/primitives.py exercises every major class form in one file:

# TypedDict
class RenderOptions(TypedDict, total=False):
    compact: bool
    dpi: float

# Enum — emits from enum import Enum automatically
class BlendMode(enum.Enum):
    NORMAL   = "normal"
    MULTIPLY = "multiply"

# ABC with @abstractmethod + **kwargs MRO backtracing
class Shape(ABC):
    def __init__(self, *, fill=None, stroke=None,
                 opacity=1.0, blend_mode=BlendMode.NORMAL, **kwargs): ...

class Circle(Shape):
    def __init__(self, cx, cy, radius, **kwargs): ...

Generated stubs — Enum defaults rendered correctly, kwargs expanded:

class BlendMode(Enum):
    def __new__(self, value) -> None: ...

class Circle(Shape):
    def __init__(
        self,
        cx: float,
        cy: float,
        radius: float,
        *,
        fill: Color | None = None,
        stroke: StrokeStyle | None = None,
        opacity: float = 1.0,
        blend_mode: BlendMode = BlendMode.NORMAL,
        visible: bool = True,
    ) -> None: ...