Type alias preservation¶
When a package uses a dedicated types module (e.g. types.py) to
define named type aliases, stubpy preserves those names in the stub
rather than expanding them to their underlying union.
Setup¶
Define your type aliases in a sub-module:
from typing import Literal, Sequence, Tuple, Union
Number = int | float
Length = str | float | int
Color = str | Tuple[float, float, float] | Tuple[float, float, float, float]
StrokeLineCap = Literal["butt", "round", "square"]
StrokeLineJoin = Literal["miter", "round", "bevel"]
DashArray = Union[str, Sequence[Number]]
Import the sub-module (not individual names) in your source:
from mypackage import types # ← import the module, not `from types import Length`
from mypackage.element import Element
class Shape(Element):
def __init__(
self,
fill: types.Color = "black",
stroke_width: types.Length = 1,
stroke_linecap: types.StrokeLineCap = "butt",
) -> None: ...
Generated stub¶
from __future__ import annotations
from typing import Optional
from mypackage import types
from mypackage.element import Element
class Shape(Element):
def __init__(
self,
fill: types.Color = 'black',
stroke_width: types.Length = 1,
stroke_linecap: types.StrokeLineCap = 'butt',
id: Optional[str] = None,
opacity: float = 1.0,
) -> None: ...
Compared to the unexpanded alternative:
def __init__(
self,
fill: str | Tuple[float, float, float] | Tuple[float, float, float, float] = 'black',
stroke_width: str | float | int = 1,
stroke_linecap: Literal['butt', 'round', 'square'] = 'butt',
...
The alias form is shorter, more readable, and stays in sync with your types module automatically.
Aliases propagate through **kwargs¶
Type aliases are preserved even when parameters arrive via **kwargs
backtracing. In the example above, id and opacity come from
Element.__init__ through **kwargs, but any aliased annotations
among them are still emitted as their alias names.
Only imported modules are scanned¶
stubpy only looks for aliases in sub-modules that are imported as a module object into your source file. The key is:
from mypackage import types # ✓ imports the module — aliases discovered
from mypackage.types import Length # ✗ imports a name — aliases NOT discovered
This is intentional: importing individual names gives you full control over whether stubpy treats them as aliases or expands them.