KEMBAR78
v2 regression: `__setattr__` hides bugs from type checkers · Issue #8643 · pydantic/pydantic · GitHub
Skip to content

v2 regression: __setattr__ hides bugs from type checkers #8643

@bluenote10

Description

@bluenote10

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

Apparently the type safety of Pydantic models has deteriorated significantly from v1 to v2 due to the exposure of a __setattr__ method that accepts Any. This opens the door for a lot of serious bugs, because the type checkers fail to catch fundamental bugs like the following.

Example Code

from pydantic import BaseModel

class Foo(BaseModel):
    some_field: int

def patch_object(foo: Foo) -> None:
    # Ohoh, we got the field name wrong...
    foo.some_feeld = 42

foo = Foo(some_field=1)
patch_object(foo)
assert foo.some_field == 42

With Pydantic V1 this obvious bug was detected by both mypy and pyright. Mypy would for instance produce a very clear and helpful type error here:

error: "Foo" has no attribute "some_feeld"; maybe "some_field"?  [attr-defined]

With Pydantic V2, the type checker completely misses the bug.

It should be quite straightforward to make the field access of BaseModel type safe again, by simply not exposing the __setattr__ method towards type checkers. Basically just this pattern (example on mypy playground):

from typing import Any, TYPE_CHECKING

class BaseModel:
    if not TYPE_CHECKING:
        def __setattr__(self, name: str, value: Any) -> None:
            ...

class Foo(BaseModel):
    some_field: int

def patch_object(foo: Foo) -> None:
    # Now the type checker will see the bug...
    foo.some_feeld = 42

Of course, in a completely strict sense this would be a "breaking" change, but since it looks rather like an bug/accident compared to V1, it would be really nice if this could be made type safe again. We just ran into such a fundamental bug resulting from this regression. This behavior is highly bug prone and also totally unexpected: Any type annotated dataclass-like type (including Pydantic V1 models) is naturally expected to respect the field annotations. After all, getting a field name wrong is one of the most basic of all bugs, and a primary reason for people to use type annotations and type checkers. If the code does not work at runtime (it crashes!), it should be flagged at type checking time.

Python, Pydantic & OS Version

I tried a range of pydantic versions including the upcoming 2.6.0b1. It looks like this used to work in 1.X but has started to fail in 2.X.

             pydantic version: 2.6.0b1
        pydantic-core version: 2.16.1
          pydantic-core build: profile=release pgo=true
                 install path: /home/ekf2abt/git/scripts/pi_23/debug_pydantic_v2/venv/lib/python3.8/site-packages/pydantic
               python version: 3.8.10 (default, Nov 22 2023, 10:22:35)  [GCC 9.4.0]
                     platform: Linux-5.4.0-166-generic-x86_64-with-glibc2.29
             related packages: typing_extensions-4.9.0 mypy-1.8.0
                       commit: unknown

Metadata

Metadata

Assignees

Labels

bug V2Bug related to Pydantic V2

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions