Skip to content

Commit

Permalink
support overwriting dunder attributes of BaseModel instances (#3907)
Browse files Browse the repository at this point in the history
* support overwriting dunder attributes of `BaseModel` instances

closes #3777

* suggestion from @adriangb

Co-authored-by: Samuel Colvin <s@muelcolvin.com>
  • Loading branch information
PrettyWood and samuelcolvin committed Aug 8, 2022
1 parent cf16f7c commit da8f0c0
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 10 deletions.
1 change: 1 addition & 0 deletions changes/3777-PrettyWood.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
support overwriting dunder attributes of `BaseModel` instances
7 changes: 5 additions & 2 deletions pydantic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
update_model_forward_refs,
)
from .utils import (
DUNDER_ATTRIBUTES,
ROOT_KEY,
ClassAttribute,
GetterDict,
Expand Down Expand Up @@ -350,7 +351,7 @@ def __init__(__pydantic_self__, **data: Any) -> None:

@no_type_check
def __setattr__(self, name, value): # noqa: C901 (ignore complexity)
if name in self.__private_attributes__:
if name in self.__private_attributes__ or name in DUNDER_ATTRIBUTES:
return object_setattr(self, name, value)

if self.__config__.extra is not Extra.allow and name not in self.__fields__:
Expand Down Expand Up @@ -891,7 +892,9 @@ def __eq__(self, other: Any) -> bool:

def __repr_args__(self) -> 'ReprArgs':
return [
(k, v) for k, v in self.__dict__.items() if k not in self.__fields__ or self.__fields__[k].field_info.repr
(k, v)
for k, v in self.__dict__.items()
if k not in DUNDER_ATTRIBUTES and (k not in self.__fields__ or self.__fields__[k].field_info.repr)
]


Expand Down
21 changes: 13 additions & 8 deletions pydantic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
'ROOT_KEY',
'get_unique_discriminator_alias',
'get_discriminator_alias_and_values',
'DUNDER_ATTRIBUTES',
'LimitedDict',
)

Expand Down Expand Up @@ -691,15 +692,19 @@ def is_valid_field(name: str) -> bool:
return ROOT_KEY == name


DUNDER_ATTRIBUTES = {
'__annotations__',
'__classcell__',
'__doc__',
'__module__',
'__orig_bases__',
'__orig_class__',
'__qualname__',
}


def is_valid_private_name(name: str) -> bool:
return not is_valid_field(name) and name not in {
'__annotations__',
'__classcell__',
'__doc__',
'__module__',
'__orig_bases__',
'__qualname__',
}
return not is_valid_field(name) and name not in DUNDER_ATTRIBUTES


_EMPTY = object()
Expand Down
14 changes: 14 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from uuid import UUID, uuid4

import pytest
from typing_extensions import Annotated

from pydantic import (
BaseConfig,
Expand Down Expand Up @@ -2174,6 +2175,19 @@ class Model(BaseModel):
}


def test_annotated_class():
class PydanticModel(BaseModel):
foo: str = '123'

PydanticAlias = Annotated[PydanticModel, 'bar baz']

pa = PydanticAlias()
assert isinstance(pa, PydanticModel)
pa.__doc__ = 'qwe'
assert repr(pa) == "PydanticModel(foo='123')"
assert pa.__doc__ == 'qwe'


@pytest.mark.parametrize(
'ann',
[Final, Final[int]],
Expand Down

0 comments on commit da8f0c0

Please sign in to comment.