Fix duplicate calls to __set_name__ for non-private attributes in BaseModel.__new__ (#4410)

* fix: double call for public attrs, extend test

* add changes

* remove change file, update 4407 file
This commit is contained in:
Talley Lambert
2022-08-22 07:16:53 -04:00
committed by GitHub
parent d501c39f2b
commit beb3e4c8f1
3 changed files with 23 additions and 7 deletions
+1 -1
View File
@@ -1 +1 @@
Fix PEP487 protocol in `BaseModel`: call `__set_name__` on namespace values that implement the method.
Fix PEP487 `__set_name__` protocol in `BaseModel` for PrivateAttrs.
+5 -3
View File
@@ -287,10 +287,12 @@ class ModelMetaclass(ABCMeta):
cls.__try_update_forward_refs__()
# preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
# for attributes not in `new_namespace` (e.g. private attributes)
for name, obj in namespace.items():
set_name = getattr(obj, '__set_name__', None)
if callable(set_name):
set_name(cls, name)
if name not in new_namespace:
set_name = getattr(obj, '__set_name__', None)
if callable(set_name):
set_name(cls, name)
return cls
+17 -3
View File
@@ -225,10 +225,11 @@ def test_generics_model():
assert result.__config__.orm_mode is True
def test_set_name():
@pytest.mark.parametrize('base', [ModelPrivateAttr, object])
def test_set_name(base):
calls = []
class class_deco(ModelPrivateAttr):
class class_deco(base):
def __init__(self, fn):
super().__init__()
self.fn = fn
@@ -236,9 +237,22 @@ def test_set_name():
def __set_name__(self, owner, name):
calls.append((owner, name))
def __get__(self, obj, type=None):
return self.fn(obj) if obj else self
class A(BaseModel):
x: int
@class_deco
def _some_func(self):
return self
return self.x
assert calls == [(A, '_some_func')]
a = A(x=2)
# we don't test whether calling the method on a PrivateAttr works:
# attribute access on privateAttributes is more complicated, it doesn't
# get added to the class namespace (and will also get set on the instance
# with _init_private_attributes), so the descriptor protocol won't work.
if base is object:
assert a._some_func == 2