mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
support overwriting dunder attributes of BaseModel instances (#3907)
* support overwriting dunder attributes of `BaseModel` instances closes #3777 * suggestion from @adriangb Co-authored-by: Samuel Colvin <s@muelcolvin.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
support overwriting dunder attributes of `BaseModel` instances
|
||||
+5
-2
@@ -53,6 +53,7 @@ from .typing import (
|
||||
update_model_forward_refs,
|
||||
)
|
||||
from .utils import (
|
||||
DUNDER_ATTRIBUTES,
|
||||
ROOT_KEY,
|
||||
ClassAttribute,
|
||||
GetterDict,
|
||||
@@ -350,7 +351,7 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
|
||||
|
||||
@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__:
|
||||
@@ -891,7 +892,9 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
|
||||
|
||||
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)
|
||||
]
|
||||
|
||||
|
||||
|
||||
+13
-8
@@ -78,6 +78,7 @@ __all__ = (
|
||||
'ROOT_KEY',
|
||||
'get_unique_discriminator_alias',
|
||||
'get_discriminator_alias_and_values',
|
||||
'DUNDER_ATTRIBUTES',
|
||||
'LimitedDict',
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -19,6 +19,7 @@ from typing import (
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pytest
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from pydantic import (
|
||||
BaseConfig,
|
||||
@@ -2174,6 +2175,19 @@ def test_new_union_origin():
|
||||
}
|
||||
|
||||
|
||||
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]],
|
||||
|
||||
Reference in New Issue
Block a user