Ignore a Field in a type annotation

75 Views Asked by At

If I have a pydantic class like:

from typing import Annotated, get_origin
from pydantic import BaseModel

class IgnorableBaseModel(BaseModel):
    _ignored: ClassVar[dict[str, Any]] = {}

    def __getattr__(self, attr):
        """do something with _ignored, else fallback to default"""

    def __init_subclass__(cls, **kwargs) -> None:
        del_keys = []
        for key, annotation in cls.__annotations__.items():
            if key.startswith("_"):  # exclude protected/private attributes
                continue
            if get_origin(annotation) is Annotated:
                if get_args(annotation)[1] == "ignore me"
                    cls._ignored[key] = cls.__annotations__[key]
                    del_keys.append(key)
        for key in del_keys:
            del cls.__annotations__[key]

class MyClass(IgnorableBaseModel):
    name: Annotated[str, "ignore me"]   # ignore this
    x: int

I am using this name for a variable that is defined during the __init__ and accessed via __getattr__ so I can't set a value for it. Is there a way to tell mypy to ignore this, or do I need to always override the init args like:

class MyClass(IgnorableBaseModel):
    name: Annotated[str, "ignore me"]
    x: int

    def __init__(self, x: int):
        self.x = x

It'd be great if there was an annotation I could add that would inform mypy that this variable was not needed during the init.

"ignore me" is used here to indicate that I don't want to see the error:

Missing named argument "name" for "MyClass"Mypycall-arg

To provide some background, I'm trying to make a python DSL and so it helps to be able to have some attributes type hinted, but not actually require values.

1

There are 1 best solutions below

6
chepner On BEST ANSWER

If you want name to be defined by __init__ without having to pass its value as an argument, use the init argument to Field.

from typing import Annotated
from pydantic import BaseModel, Field

class MyClass(BaseModel):
    name: Annotated[str, Field(init=False)]
    x: int

This can also be written as

class MyClass(BaseModel):
    name: str = Field(init=False)
    x: int

Now the autogenerated __init__ method will only expect an argument for x, and it will be up to use to ensure the name attribute on an instance of MyClass gets defined. (This could be by using a default value, or a default factory, or an explicitly defined __init__ method, or some other technique that Pydantic provides.)