diff --git a/changes/2138-PrettyWood.md b/changes/2138-PrettyWood.md new file mode 100644 index 0000000..e58d0ed --- /dev/null +++ b/changes/2138-PrettyWood.md @@ -0,0 +1 @@ +fix: support `underscore_attrs_are_private` with generic models \ No newline at end of file diff --git a/pydantic/utils.py b/pydantic/utils.py index e3e613c..c75f4e1 100644 --- a/pydantic/utils.py +++ b/pydantic/utils.py @@ -636,5 +636,6 @@ def is_valid_private_name(name: str) -> bool: '__classcell__', '__doc__', '__module__', + '__orig_bases__', '__qualname__', } diff --git a/tests/test_private_attributes.py b/tests/test_private_attributes.py index 21f5209..d87e572 100644 --- a/tests/test_private_attributes.py +++ b/tests/test_private_attributes.py @@ -1,9 +1,13 @@ -from typing import ClassVar +import sys +from typing import ClassVar, Generic, TypeVar import pytest from pydantic import BaseModel, Extra, PrivateAttr from pydantic.fields import Undefined +from pydantic.generics import GenericModel + +skip_36 = pytest.mark.skipif(sys.version_info < (3, 7), reason='generics only supported for python 3.7 and above') def test_private_attribute(): @@ -180,3 +184,19 @@ def test_config_override_init(): m = MyModel(x='hello') assert m.dict() == {'x': 'hello'} assert m._private_attr == 123 + + +@skip_36 +def test_generic_private_attribute(): + T = TypeVar('T') + + class Model(GenericModel, Generic[T]): + value: T + _private_value: T + + class Config: + underscore_attrs_are_private = True + + m = Model[int](value=1, _private_attr=3) + m._private_value = 3 + assert m.dict() == {'value': 1}