Search code examples
pythonpython-typingmypypydantic

How does `mypy` know the signature of the pydantic model?


How does mypy know the signature of the pydantic model in this manner?

from pydantic import BaseModel

class Model(BaseModel):
    a: int

Model(a='asd')  # error: Argument "a" to "Model" has incompatible type "str"; expected "int"

How can pydantic BaseModel's metaclass change the __init__ signature?


Solution

  • Pydantic uses PEP681 dataclass_transform on model metaclass to achieve behaviour similar to native dataclasses.dataclass decorator in order to generate an __init__ method for type checkers. Reading the source:

    @typing_extensions.dataclass_transform(kw_only_default=True, field_specifiers=(Field,))
    class ModelMetaclass(ABCMeta):
        ...  # [omitted by me]
    
    class BaseModel(_repr.Representation, metaclass=ModelMetaclass):
        ... # [omitted by me]
    

    In PEP:

    If dataclass_transform is applied to a class, dataclass-like semantics will be assumed for any class that directly or indirectly derives from the decorated class or uses the decorated class as a metaclass.

    PEP example (very similar to what pydantic does):

    # The ``ModelMeta`` metaclass and ``ModelBase`` class are defined by
    # a library. This could be in a type stub or inline.
    @typing.dataclass_transform()
    class ModelMeta(type): ...
    
    class ModelBase(metaclass=ModelMeta): ...
    
    # The ``ModelBase`` class can now be used to create new model
    # subclasses, like this:
    class CustomerModel(ModelBase):
        id: int
        name: str