Search code examples
pythonpython-typingpydanticpyright

Pydantic model field with default value does not pass type checking


The following code shows a pylance (pyright) error for AModel() of Argument missing for parameter "field_b":

from pydantic import BaseModel, Field
from typing import Optional, Any

class AModel(BaseModel):
   field_a: str = Field()
   field_b: Optional[bool] = Field(None)

instance_1 = AModel(field_a="", field_b=None)  # No error
instance_2 = AModel(field_a="")  # Error
#            ^^^^^^^^^^^^^^^^^^

kwargs: dict[str, Any] = {"field_a": "", "field_bad": True}
instance_3 = AModel(**kwargs)  # No error but no type checking

Is it possible to instantiate this model without providing field_b=None and whilst retaining type checking?


Solution

  • You need to either specify default= explicitly or drop the Field() call altogether:

    class AModel(BaseModel):
        field_a: str = Field()
        field_b: Optional[bool] = Field(default=None)
        ### or
        field_b: Optional[bool] = None
    

    As it is written, Pydantic's Field allows default to be passed positionally:

    Field(
        default: Any = PydanticUndefined,
        *,
        # ...
    ) -> Any
    

    However, default is a field specifier parameter, and the typing specification requires such parameters to be keyword-only for them to be detectable by type checkers (emphasis mine):

    Libraries that support dataclass-like semantics and support field specifier classes typically use common parameter names to construct these field specifiers. This specification formalizes the names and meanings of the parameters that must be understood for static type checkers. These standardized parameters must be keyword-only.

    See also this GitHub discussion, where a maintainer of Pyright posted the same answer.