Metaprogramming demos
macrotype can capture types that are generated dynamically. The example
below creates a new NewType for each subclass using __init_subclass__.
This pattern is similar to how SQLAlchemy models can define typed primary keys.
from __future__ import annotations
import sys
from typing import Generic, NewType, TypeVar
T = TypeVar("T")
class Mapped(Generic[T]):
"""Placeholder for ``sqlalchemy.orm.Mapped``."""
class Base:
def __init_subclass__(cls) -> None: # noqa: D401 - simple demo
typename = f"{cls.__name__}Id"
new_type = NewType(typename, int)
cls.id_type = new_type
cls.__annotations__["id"] = Mapped[new_type]
cls.__annotations__["id_type"] = type[new_type]
mod = sys.modules[cls.__module__]
setattr(mod, typename, new_type)
class Manager(Base): ...
class Employee(Base):
manager_id: Mapped[Manager.id_type]
Running macrotype generates the following stub:
# Generated via: macrotype demos/sqla_demos.py -o demos/sqla_demos.pyi
# Do not edit by hand
from typing import NewType, TypeVar
T = TypeVar("T")
class Mapped[T]:
pass
class Base:
@classmethod
def __init_subclass__(cls) -> None: ...
ManagerId = NewType("ManagerId", int)
class Manager(Base):
id: Mapped[ManagerId]
id_type: type[ManagerId]
EmployeeId = NewType("EmployeeId", int)
class Employee(Base):
manager_id: Mapped[ManagerId]
id: Mapped[EmployeeId]
id_type: type[EmployeeId]