Skip to content

Commit

Permalink
Fix PEP487 __set_name__ protocol in BaseModel (#4407)
Browse files Browse the repository at this point in the history
* fix: fix __set_name__ protocol in BaseModel

* undo changes

* add comment

* fix lint

* fix lint

* add change file

* remove mock, move imports
  • Loading branch information
tlambert03 committed Aug 21, 2022
1 parent abcf81e commit d501c39
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 0 deletions.
1 change: 1 addition & 0 deletions changes/4407-tlambert03.md
@@ -0,0 +1 @@
Fix PEP487 protocol in `BaseModel`: call `__set_name__` on namespace values that implement the method.
6 changes: 6 additions & 0 deletions pydantic/main.py
Expand Up @@ -286,6 +286,12 @@ def is_untouched(v: Any) -> bool:
if resolve_forward_refs:
cls.__try_update_forward_refs__()

# preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
for name, obj in namespace.items():
set_name = getattr(obj, '__set_name__', None)
if callable(set_name):
set_name(cls, name)

return cls

def __instancecheck__(self, instance: Any) -> bool:
Expand Down
20 changes: 20 additions & 0 deletions tests/test_create_model.py
Expand Up @@ -3,6 +3,7 @@
import pytest

from pydantic import BaseModel, Extra, Field, ValidationError, create_model, errors, validator
from pydantic.fields import ModelPrivateAttr
from pydantic.generics import GenericModel


Expand Down Expand Up @@ -222,3 +223,22 @@ class TestGenericModel(GenericModel):
result = AAModel[int](aa=1)
assert result.aa == 1
assert result.__config__.orm_mode is True


def test_set_name():
calls = []

class class_deco(ModelPrivateAttr):
def __init__(self, fn):
super().__init__()
self.fn = fn

def __set_name__(self, owner, name):
calls.append((owner, name))

class A(BaseModel):
@class_deco
def _some_func(self):
return self

assert calls == [(A, '_some_func')]

0 comments on commit d501c39

Please sign in to comment.