From 30ee8e2203267d3ea86ca2a51472738d98d05cd5 Mon Sep 17 00:00:00 2001 From: Davis Kirkendall Date: Fri, 9 Oct 2020 14:25:24 +0200 Subject: [PATCH] Force fields.Undefined to be a singleton objectIn various places of the code, we compare directly to `fields.Undefined`since we assume it to be constant.When new models get created however, the object is deepcopied andis no longer identical with the original object.We therefore add `__copy__` and `__deepcopy__` methods to ensurethat the copied objects are actually the same original object. (#1981) --- changes/1981-daviskirk.md | 1 + pydantic/fields.py | 8 ++++++++ tests/test_generics.py | 20 ++++++++++++++++++++ tests/test_utils.py | 7 ++++++- 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 changes/1981-daviskirk.md diff --git a/changes/1981-daviskirk.md b/changes/1981-daviskirk.md new file mode 100644 index 0000000..ef52cb1 --- /dev/null +++ b/changes/1981-daviskirk.md @@ -0,0 +1 @@ +Force `fields.Undefined` to be a singleton object, fixing inherited generic model schemas diff --git a/pydantic/fields.py b/pydantic/fields.py index f9114a1..d251237 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -40,11 +40,19 @@ from .validators import constant_validator, dict_validator, find_validators, val Required: Any = Ellipsis +T = TypeVar('T') + class UndefinedType: def __repr__(self) -> str: return 'PydanticUndefined' + def __copy__(self: T) -> T: + return self + + def __deepcopy__(self: T, _: Any) -> T: + return self + Undefined = UndefinedType() diff --git a/tests/test_generics.py b/tests/test_generics.py index b1821e7..452fe3b 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -410,6 +410,26 @@ def test_custom_schema(): assert schema['properties']['a'].get('description') == 'Custom' +@skip_36 +def test_child_schema(): + + T = TypeVar('T') + + class Model(GenericModel, Generic[T]): + a: T + + class Child(Model[T], Generic[T]): + pass + + schema = Child[int].schema() + assert schema == { + 'title': 'Child[int]', + 'type': 'object', + 'properties': {'a': {'title': 'A', 'type': 'integer'}}, + 'required': ['a'], + } + + @skip_36 def test_custom_generic_naming(): T = TypeVar('T') diff --git a/tests/test_utils.py b/tests/test_utils.py index 935038f..989176c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,7 @@ import os import re import string -from copy import deepcopy +from copy import copy, deepcopy from distutils.version import StrictVersion from enum import Enum from typing import NewType, Union @@ -296,6 +296,11 @@ def test_undefined_repr(): assert repr(Undefined) == 'PydanticUndefined' +def test_undefined_copy(): + copy(Undefined) is Undefined + deepcopy(Undefined) is Undefined + + def test_get_model(): class A(BaseModel): a: str