From a2ed2d5a919987469848e705d6560f7e7aadccb0 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 13:26:35 -0400 Subject: [PATCH 1/7] fix: fix __set_name__ protocol in BaseModel --- pydantic/main.py | 5 + tests/test_create_model.py | 182 +++++++++++++++++++++++-------------- 2 files changed, 119 insertions(+), 68 deletions(-) diff --git a/pydantic/main.py b/pydantic/main.py index 2d19119313..b65c46607f 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -286,6 +286,11 @@ def is_untouched(v: Any) -> bool: if resolve_forward_refs: cls.__try_update_forward_refs__() + 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: diff --git a/tests/test_create_model.py b/tests/test_create_model.py index 9a17c5d664..42d1c59a6d 100644 --- a/tests/test_create_model.py +++ b/tests/test_create_model.py @@ -2,30 +2,38 @@ import pytest -from pydantic import BaseModel, Extra, Field, ValidationError, create_model, errors, validator +from pydantic import ( + BaseModel, + Extra, + Field, + ValidationError, + create_model, + errors, + validator, +) from pydantic.generics import GenericModel def test_create_model(): - model = create_model('FooModel', foo=(str, ...), bar=123) + model = create_model("FooModel", foo=(str, ...), bar=123) assert issubclass(model, BaseModel) assert issubclass(model.__config__, BaseModel.Config) - assert model.__name__ == 'FooModel' - assert model.__fields__.keys() == {'foo', 'bar'} + assert model.__name__ == "FooModel" + assert model.__fields__.keys() == {"foo", "bar"} assert model.__validators__ == {} - assert model.__config__.__name__ == 'Config' - assert model.__module__ == 'pydantic.main' + assert model.__config__.__name__ == "Config" + assert model.__module__ == "pydantic.main" def test_create_model_usage(): - model = create_model('FooModel', foo=(str, ...), bar=123) - m = model(foo='hello') - assert m.foo == 'hello' + model = create_model("FooModel", foo=(str, ...), bar=123) + m = model(foo="hello") + assert m.foo == "hello" assert m.bar == 123 with pytest.raises(ValidationError): model() with pytest.raises(ValidationError): - model(foo='hello', bar='xxx') + model(foo="hello", bar="xxx") def test_create_model_pickle(create_module): @@ -40,12 +48,14 @@ def module(): from pydantic import create_model - FooModel = create_model('FooModel', foo=(str, ...), bar=123, __module__=__name__) + FooModel = create_model( + "FooModel", foo=(str, ...), bar=123, __module__=__name__ + ) - m = FooModel(foo='hello') + m = FooModel(foo="hello") d = pickle.dumps(m) m2 = pickle.loads(d) - assert m2.foo == m.foo == 'hello' + assert m2.foo == m.foo == "hello" assert m2.bar == m.bar == 123 assert m2 == m assert m2 is not m @@ -53,18 +63,18 @@ def module(): def test_invalid_name(): with pytest.warns(RuntimeWarning): - model = create_model('FooModel', _foo=(str, ...)) + model = create_model("FooModel", _foo=(str, ...)) assert len(model.__fields__) == 0 def test_field_wrong_tuple(): with pytest.raises(errors.ConfigError): - create_model('FooModel', foo=(1, 2, 3)) + create_model("FooModel", foo=(1, 2, 3)) def test_config_and_base(): with pytest.raises(errors.ConfigError): - create_model('FooModel', __config__=BaseModel.Config, __base__=BaseModel) + create_model("FooModel", __config__=BaseModel.Config, __base__=BaseModel) def test_inheritance(): @@ -72,18 +82,18 @@ class BarModel(BaseModel): x = 1 y = 2 - model = create_model('FooModel', foo=(str, ...), bar=(int, 123), __base__=BarModel) - assert model.__fields__.keys() == {'foo', 'bar', 'x', 'y'} - m = model(foo='a', x=4) - assert m.dict() == {'bar': 123, 'foo': 'a', 'x': 4, 'y': 2} + model = create_model("FooModel", foo=(str, ...), bar=(int, 123), __base__=BarModel) + assert model.__fields__.keys() == {"foo", "bar", "x", "y"} + m = model(foo="a", x=4) + assert m.dict() == {"bar": 123, "foo": "a", "x": 4, "y": 2} def test_custom_config(): class Config: - fields = {'foo': 'api-foo-field'} + fields = {"foo": "api-foo-field"} - model = create_model('FooModel', foo=(int, ...), __config__=Config) - assert model(**{'api-foo-field': '987'}).foo == 987 + model = create_model("FooModel", foo=(int, ...), __config__=Config) + assert model(**{"api-foo-field": "987"}).foo == 987 assert issubclass(model.__config__, BaseModel.Config) with pytest.raises(ValidationError): model(foo=654) @@ -91,10 +101,10 @@ class Config: def test_custom_config_inherits(): class Config(BaseModel.Config): - fields = {'foo': 'api-foo-field'} + fields = {"foo": "api-foo-field"} - model = create_model('FooModel', foo=(int, ...), __config__=Config) - assert model(**{'api-foo-field': '987'}).foo == 987 + model = create_model("FooModel", foo=(int, ...), __config__=Config) + assert model(**{"api-foo-field": "987"}).foo == 987 assert issubclass(model.__config__, BaseModel.Config) with pytest.raises(ValidationError): model(foo=654) @@ -104,7 +114,7 @@ def test_custom_config_extras(): class Config(BaseModel.Config): extra = Extra.forbid - model = create_model('FooModel', foo=(int, ...), __config__=Config) + model = create_model("FooModel", foo=(int, ...), __config__=Config) assert model(foo=654) with pytest.raises(ValidationError): model(bar=654) @@ -112,53 +122,57 @@ class Config(BaseModel.Config): def test_inheritance_validators(): class BarModel(BaseModel): - @validator('a', check_fields=False) + @validator("a", check_fields=False) def check_a(cls, v): - if 'foobar' not in v: + if "foobar" not in v: raise ValueError('"foobar" not found in a') return v - model = create_model('FooModel', a='cake', __base__=BarModel) - assert model().a == 'cake' - assert model(a='this is foobar good').a == 'this is foobar good' + model = create_model("FooModel", a="cake", __base__=BarModel) + assert model().a == "cake" + assert model(a="this is foobar good").a == "this is foobar good" with pytest.raises(ValidationError): - model(a='something else') + model(a="something else") def test_inheritance_validators_always(): class BarModel(BaseModel): - @validator('a', check_fields=False, always=True) + @validator("a", check_fields=False, always=True) def check_a(cls, v): - if 'foobar' not in v: + if "foobar" not in v: raise ValueError('"foobar" not found in a') return v - model = create_model('FooModel', a='cake', __base__=BarModel) + model = create_model("FooModel", a="cake", __base__=BarModel) with pytest.raises(ValidationError): model() - assert model(a='this is foobar good').a == 'this is foobar good' + assert model(a="this is foobar good").a == "this is foobar good" with pytest.raises(ValidationError): - model(a='something else') + model(a="something else") def test_inheritance_validators_all(): class BarModel(BaseModel): - @validator('*') + @validator("*") def check_all(cls, v): return v * 2 - model = create_model('FooModel', a=(int, ...), b=(int, ...), __base__=BarModel) - assert model(a=2, b=6).dict() == {'a': 4, 'b': 12} + model = create_model("FooModel", a=(int, ...), b=(int, ...), __base__=BarModel) + assert model(a=2, b=6).dict() == {"a": 4, "b": 12} def test_funky_name(): - model = create_model('FooModel', **{'this-is-funky': (int, ...)}) - m = model(**{'this-is-funky': '123'}) - assert m.dict() == {'this-is-funky': 123} + model = create_model("FooModel", **{"this-is-funky": (int, ...)}) + m = model(**{"this-is-funky": "123"}) + assert m.dict() == {"this-is-funky": 123} with pytest.raises(ValidationError) as exc_info: model() assert exc_info.value.errors() == [ - {'loc': ('this-is-funky',), 'msg': 'field required', 'type': 'value_error.missing'} + { + "loc": ("this-is-funky",), + "msg": "field required", + "type": "value_error.missing", + } ] @@ -166,25 +180,25 @@ def test_repeat_base_usage(): class Model(BaseModel): a: str - assert Model.__fields__.keys() == {'a'} + assert Model.__fields__.keys() == {"a"} - model = create_model('FooModel', b=1, __base__=Model) + model = create_model("FooModel", b=1, __base__=Model) - assert Model.__fields__.keys() == {'a'} - assert model.__fields__.keys() == {'a', 'b'} + assert Model.__fields__.keys() == {"a"} + assert model.__fields__.keys() == {"a", "b"} - model2 = create_model('Foo2Model', c=1, __base__=Model) + model2 = create_model("Foo2Model", c=1, __base__=Model) - assert Model.__fields__.keys() == {'a'} - assert model.__fields__.keys() == {'a', 'b'} - assert model2.__fields__.keys() == {'a', 'c'} + assert Model.__fields__.keys() == {"a"} + assert model.__fields__.keys() == {"a", "b"} + assert model2.__fields__.keys() == {"a", "c"} - model3 = create_model('Foo2Model', d=1, __base__=model) + model3 = create_model("Foo2Model", d=1, __base__=model) - assert Model.__fields__.keys() == {'a'} - assert model.__fields__.keys() == {'a', 'b'} - assert model2.__fields__.keys() == {'a', 'c'} - assert model3.__fields__.keys() == {'a', 'b', 'd'} + assert Model.__fields__.keys() == {"a"} + assert model.__fields__.keys() == {"a", "b"} + assert model2.__fields__.keys() == {"a", "c"} + assert model3.__fields__.keys() == {"a", "b", "d"} def test_dynamic_and_static(): @@ -193,32 +207,64 @@ class A(BaseModel): y: float z: str - DynamicA = create_model('A', x=(int, ...), y=(float, ...), z=(str, ...)) + DynamicA = create_model("A", x=(int, ...), y=(float, ...), z=(str, ...)) - for field_name in ('x', 'y', 'z'): - assert A.__fields__[field_name].default == DynamicA.__fields__[field_name].default + for field_name in ("x", "y", "z"): + assert ( + A.__fields__[field_name].default == DynamicA.__fields__[field_name].default + ) def test_config_field_info_create_model(): class Config: - fields = {'a': {'description': 'descr'}} + fields = {"a": {"description": "descr"}} - m1 = create_model('M1', __config__=Config, a=(str, ...)) - assert m1.schema()['properties'] == {'a': {'title': 'A', 'description': 'descr', 'type': 'string'}} + m1 = create_model("M1", __config__=Config, a=(str, ...)) + assert m1.schema()["properties"] == { + "a": {"title": "A", "description": "descr", "type": "string"} + } - m2 = create_model('M2', __config__=Config, a=(str, Field(...))) - assert m2.schema()['properties'] == {'a': {'title': 'A', 'description': 'descr', 'type': 'string'}} + m2 = create_model("M2", __config__=Config, a=(str, Field(...))) + assert m2.schema()["properties"] == { + "a": {"title": "A", "description": "descr", "type": "string"} + } def test_generics_model(): - T = TypeVar('T') + T = TypeVar("T") class TestGenericModel(GenericModel): pass AAModel = create_model( - 'AAModel', __base__=(TestGenericModel, Generic[T]), __cls_kwargs__={'orm_mode': True}, aa=(int, Field(0)) + "AAModel", + __base__=(TestGenericModel, Generic[T]), + __cls_kwargs__={"orm_mode": True}, + aa=(int, Field(0)), ) result = AAModel[int](aa=1) assert result.aa == 1 assert result.__config__.orm_mode is True + + +def test_set_name(): + + from unittest.mock import Mock + from pydantic.fields import ModelPrivateAttr + + mock = Mock() + + class class_deco(ModelPrivateAttr): + def __init__(self, fn): + super().__init__() + self.fn = fn + + def __set_name__(self, owner, name): + mock(owner, name) + + class A(BaseModel): + @class_deco + def _some_func(self): + return self + + mock.assert_called_once_with(A, "_some_func") From c34f405b8653837c3a9ba64176e49d8bb08b8720 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 13:27:41 -0400 Subject: [PATCH 2/7] undo changes --- tests/test_create_model.py | 159 ++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 91 deletions(-) diff --git a/tests/test_create_model.py b/tests/test_create_model.py index 42d1c59a6d..4100516d0d 100644 --- a/tests/test_create_model.py +++ b/tests/test_create_model.py @@ -2,38 +2,30 @@ import pytest -from pydantic import ( - BaseModel, - Extra, - Field, - ValidationError, - create_model, - errors, - validator, -) +from pydantic import BaseModel, Extra, Field, ValidationError, create_model, errors, validator from pydantic.generics import GenericModel def test_create_model(): - model = create_model("FooModel", foo=(str, ...), bar=123) + model = create_model('FooModel', foo=(str, ...), bar=123) assert issubclass(model, BaseModel) assert issubclass(model.__config__, BaseModel.Config) - assert model.__name__ == "FooModel" - assert model.__fields__.keys() == {"foo", "bar"} + assert model.__name__ == 'FooModel' + assert model.__fields__.keys() == {'foo', 'bar'} assert model.__validators__ == {} - assert model.__config__.__name__ == "Config" - assert model.__module__ == "pydantic.main" + assert model.__config__.__name__ == 'Config' + assert model.__module__ == 'pydantic.main' def test_create_model_usage(): - model = create_model("FooModel", foo=(str, ...), bar=123) - m = model(foo="hello") - assert m.foo == "hello" + model = create_model('FooModel', foo=(str, ...), bar=123) + m = model(foo='hello') + assert m.foo == 'hello' assert m.bar == 123 with pytest.raises(ValidationError): model() with pytest.raises(ValidationError): - model(foo="hello", bar="xxx") + model(foo='hello', bar='xxx') def test_create_model_pickle(create_module): @@ -48,14 +40,12 @@ def module(): from pydantic import create_model - FooModel = create_model( - "FooModel", foo=(str, ...), bar=123, __module__=__name__ - ) + FooModel = create_model('FooModel', foo=(str, ...), bar=123, __module__=__name__) - m = FooModel(foo="hello") + m = FooModel(foo='hello') d = pickle.dumps(m) m2 = pickle.loads(d) - assert m2.foo == m.foo == "hello" + assert m2.foo == m.foo == 'hello' assert m2.bar == m.bar == 123 assert m2 == m assert m2 is not m @@ -63,18 +53,18 @@ def module(): def test_invalid_name(): with pytest.warns(RuntimeWarning): - model = create_model("FooModel", _foo=(str, ...)) + model = create_model('FooModel', _foo=(str, ...)) assert len(model.__fields__) == 0 def test_field_wrong_tuple(): with pytest.raises(errors.ConfigError): - create_model("FooModel", foo=(1, 2, 3)) + create_model('FooModel', foo=(1, 2, 3)) def test_config_and_base(): with pytest.raises(errors.ConfigError): - create_model("FooModel", __config__=BaseModel.Config, __base__=BaseModel) + create_model('FooModel', __config__=BaseModel.Config, __base__=BaseModel) def test_inheritance(): @@ -82,18 +72,18 @@ class BarModel(BaseModel): x = 1 y = 2 - model = create_model("FooModel", foo=(str, ...), bar=(int, 123), __base__=BarModel) - assert model.__fields__.keys() == {"foo", "bar", "x", "y"} - m = model(foo="a", x=4) - assert m.dict() == {"bar": 123, "foo": "a", "x": 4, "y": 2} + model = create_model('FooModel', foo=(str, ...), bar=(int, 123), __base__=BarModel) + assert model.__fields__.keys() == {'foo', 'bar', 'x', 'y'} + m = model(foo='a', x=4) + assert m.dict() == {'bar': 123, 'foo': 'a', 'x': 4, 'y': 2} def test_custom_config(): class Config: - fields = {"foo": "api-foo-field"} + fields = {'foo': 'api-foo-field'} - model = create_model("FooModel", foo=(int, ...), __config__=Config) - assert model(**{"api-foo-field": "987"}).foo == 987 + model = create_model('FooModel', foo=(int, ...), __config__=Config) + assert model(**{'api-foo-field': '987'}).foo == 987 assert issubclass(model.__config__, BaseModel.Config) with pytest.raises(ValidationError): model(foo=654) @@ -101,10 +91,10 @@ class Config: def test_custom_config_inherits(): class Config(BaseModel.Config): - fields = {"foo": "api-foo-field"} + fields = {'foo': 'api-foo-field'} - model = create_model("FooModel", foo=(int, ...), __config__=Config) - assert model(**{"api-foo-field": "987"}).foo == 987 + model = create_model('FooModel', foo=(int, ...), __config__=Config) + assert model(**{'api-foo-field': '987'}).foo == 987 assert issubclass(model.__config__, BaseModel.Config) with pytest.raises(ValidationError): model(foo=654) @@ -114,7 +104,7 @@ def test_custom_config_extras(): class Config(BaseModel.Config): extra = Extra.forbid - model = create_model("FooModel", foo=(int, ...), __config__=Config) + model = create_model('FooModel', foo=(int, ...), __config__=Config) assert model(foo=654) with pytest.raises(ValidationError): model(bar=654) @@ -122,57 +112,53 @@ class Config(BaseModel.Config): def test_inheritance_validators(): class BarModel(BaseModel): - @validator("a", check_fields=False) + @validator('a', check_fields=False) def check_a(cls, v): - if "foobar" not in v: + if 'foobar' not in v: raise ValueError('"foobar" not found in a') return v - model = create_model("FooModel", a="cake", __base__=BarModel) - assert model().a == "cake" - assert model(a="this is foobar good").a == "this is foobar good" + model = create_model('FooModel', a='cake', __base__=BarModel) + assert model().a == 'cake' + assert model(a='this is foobar good').a == 'this is foobar good' with pytest.raises(ValidationError): - model(a="something else") + model(a='something else') def test_inheritance_validators_always(): class BarModel(BaseModel): - @validator("a", check_fields=False, always=True) + @validator('a', check_fields=False, always=True) def check_a(cls, v): - if "foobar" not in v: + if 'foobar' not in v: raise ValueError('"foobar" not found in a') return v - model = create_model("FooModel", a="cake", __base__=BarModel) + model = create_model('FooModel', a='cake', __base__=BarModel) with pytest.raises(ValidationError): model() - assert model(a="this is foobar good").a == "this is foobar good" + assert model(a='this is foobar good').a == 'this is foobar good' with pytest.raises(ValidationError): - model(a="something else") + model(a='something else') def test_inheritance_validators_all(): class BarModel(BaseModel): - @validator("*") + @validator('*') def check_all(cls, v): return v * 2 - model = create_model("FooModel", a=(int, ...), b=(int, ...), __base__=BarModel) - assert model(a=2, b=6).dict() == {"a": 4, "b": 12} + model = create_model('FooModel', a=(int, ...), b=(int, ...), __base__=BarModel) + assert model(a=2, b=6).dict() == {'a': 4, 'b': 12} def test_funky_name(): - model = create_model("FooModel", **{"this-is-funky": (int, ...)}) - m = model(**{"this-is-funky": "123"}) - assert m.dict() == {"this-is-funky": 123} + model = create_model('FooModel', **{'this-is-funky': (int, ...)}) + m = model(**{'this-is-funky': '123'}) + assert m.dict() == {'this-is-funky': 123} with pytest.raises(ValidationError) as exc_info: model() assert exc_info.value.errors() == [ - { - "loc": ("this-is-funky",), - "msg": "field required", - "type": "value_error.missing", - } + {'loc': ('this-is-funky',), 'msg': 'field required', 'type': 'value_error.missing'} ] @@ -180,25 +166,25 @@ def test_repeat_base_usage(): class Model(BaseModel): a: str - assert Model.__fields__.keys() == {"a"} + assert Model.__fields__.keys() == {'a'} - model = create_model("FooModel", b=1, __base__=Model) + model = create_model('FooModel', b=1, __base__=Model) - assert Model.__fields__.keys() == {"a"} - assert model.__fields__.keys() == {"a", "b"} + assert Model.__fields__.keys() == {'a'} + assert model.__fields__.keys() == {'a', 'b'} - model2 = create_model("Foo2Model", c=1, __base__=Model) + model2 = create_model('Foo2Model', c=1, __base__=Model) - assert Model.__fields__.keys() == {"a"} - assert model.__fields__.keys() == {"a", "b"} - assert model2.__fields__.keys() == {"a", "c"} + assert Model.__fields__.keys() == {'a'} + assert model.__fields__.keys() == {'a', 'b'} + assert model2.__fields__.keys() == {'a', 'c'} - model3 = create_model("Foo2Model", d=1, __base__=model) + model3 = create_model('Foo2Model', d=1, __base__=model) - assert Model.__fields__.keys() == {"a"} - assert model.__fields__.keys() == {"a", "b"} - assert model2.__fields__.keys() == {"a", "c"} - assert model3.__fields__.keys() == {"a", "b", "d"} + assert Model.__fields__.keys() == {'a'} + assert model.__fields__.keys() == {'a', 'b'} + assert model2.__fields__.keys() == {'a', 'c'} + assert model3.__fields__.keys() == {'a', 'b', 'd'} def test_dynamic_and_static(): @@ -207,40 +193,31 @@ class A(BaseModel): y: float z: str - DynamicA = create_model("A", x=(int, ...), y=(float, ...), z=(str, ...)) + DynamicA = create_model('A', x=(int, ...), y=(float, ...), z=(str, ...)) - for field_name in ("x", "y", "z"): - assert ( - A.__fields__[field_name].default == DynamicA.__fields__[field_name].default - ) + for field_name in ('x', 'y', 'z'): + assert A.__fields__[field_name].default == DynamicA.__fields__[field_name].default def test_config_field_info_create_model(): class Config: - fields = {"a": {"description": "descr"}} + fields = {'a': {'description': 'descr'}} - m1 = create_model("M1", __config__=Config, a=(str, ...)) - assert m1.schema()["properties"] == { - "a": {"title": "A", "description": "descr", "type": "string"} - } + m1 = create_model('M1', __config__=Config, a=(str, ...)) + assert m1.schema()['properties'] == {'a': {'title': 'A', 'description': 'descr', 'type': 'string'}} - m2 = create_model("M2", __config__=Config, a=(str, Field(...))) - assert m2.schema()["properties"] == { - "a": {"title": "A", "description": "descr", "type": "string"} - } + m2 = create_model('M2', __config__=Config, a=(str, Field(...))) + assert m2.schema()['properties'] == {'a': {'title': 'A', 'description': 'descr', 'type': 'string'}} def test_generics_model(): - T = TypeVar("T") + T = TypeVar('T') class TestGenericModel(GenericModel): pass AAModel = create_model( - "AAModel", - __base__=(TestGenericModel, Generic[T]), - __cls_kwargs__={"orm_mode": True}, - aa=(int, Field(0)), + 'AAModel', __base__=(TestGenericModel, Generic[T]), __cls_kwargs__={'orm_mode': True}, aa=(int, Field(0)) ) result = AAModel[int](aa=1) assert result.aa == 1 From 1212571c51868c750e80446c8d393259f4c8c03f Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 13:33:08 -0400 Subject: [PATCH 3/7] add comment --- pydantic/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pydantic/main.py b/pydantic/main.py index b65c46607f..0490216d7a 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -286,6 +286,7 @@ 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): From aac07efa73e31cfdd12ae85a10b218d4b107be3f Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 13:43:58 -0400 Subject: [PATCH 4/7] fix lint --- tests/test_create_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_create_model.py b/tests/test_create_model.py index 4100516d0d..166371649a 100644 --- a/tests/test_create_model.py +++ b/tests/test_create_model.py @@ -244,4 +244,4 @@ class A(BaseModel): def _some_func(self): return self - mock.assert_called_once_with(A, "_some_func") + mock.assert_called_once_with(A, '_some_func') From 773b5158165ee3fcbd44dad19a3ef23c8b3db765 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 13:56:25 -0400 Subject: [PATCH 5/7] fix lint --- tests/test_create_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_create_model.py b/tests/test_create_model.py index 166371649a..fe9af14fb8 100644 --- a/tests/test_create_model.py +++ b/tests/test_create_model.py @@ -227,6 +227,7 @@ class TestGenericModel(GenericModel): def test_set_name(): from unittest.mock import Mock + from pydantic.fields import ModelPrivateAttr mock = Mock() From 60e6b03482343d81604036391bfcec8b5fcf43c7 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 20 Aug 2022 14:01:13 -0400 Subject: [PATCH 6/7] add change file --- changes/4407-tlambert03.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/4407-tlambert03.md diff --git a/changes/4407-tlambert03.md b/changes/4407-tlambert03.md new file mode 100644 index 0000000000..e315bb1487 --- /dev/null +++ b/changes/4407-tlambert03.md @@ -0,0 +1 @@ +Fix PEP487 protocol in `BaseModel`: call `__set_name__` on namespace values that implement the method. From b2a92c3a72f5d605c34b7a428a3ac3113d2f611a Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 21 Aug 2022 09:43:22 -0400 Subject: [PATCH 7/7] remove mock, move imports --- tests/test_create_model.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/test_create_model.py b/tests/test_create_model.py index fe9af14fb8..21c96e20fd 100644 --- a/tests/test_create_model.py +++ b/tests/test_create_model.py @@ -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 @@ -225,12 +226,7 @@ class TestGenericModel(GenericModel): def test_set_name(): - - from unittest.mock import Mock - - from pydantic.fields import ModelPrivateAttr - - mock = Mock() + calls = [] class class_deco(ModelPrivateAttr): def __init__(self, fn): @@ -238,11 +234,11 @@ def __init__(self, fn): self.fn = fn def __set_name__(self, owner, name): - mock(owner, name) + calls.append((owner, name)) class A(BaseModel): @class_deco def _some_func(self): return self - mock.assert_called_once_with(A, '_some_func') + assert calls == [(A, '_some_func')]