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]