From 54f5e314b14e0c883d248caa729ac63874d9e8bb Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Thu, 19 May 2022 14:28:50 -0400 Subject: [PATCH 01/12] allow for shallow copies --- pydantic/config.py | 5 ++++- pydantic/main.py | 4 +++- tests/test_main.py | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pydantic/config.py b/pydantic/config.py index ef4b3c008f..0a3abf6bc4 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -62,8 +62,11 @@ class BaseConfig: json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {} underscore_attrs_are_private: bool = False - # whether inherited models as fields should be reconstructed as base model + # whether inherited models as fields should be reconstructed as base model, + # and whether such a copy should be shallow or deep copy_on_model_validation: bool = True + copy_on_model_validation_shallow: bool = False + # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index 0c20d9e69d..bd52e22a97 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -675,7 +675,9 @@ def __get_validators__(cls) -> 'CallableGenerator': @classmethod def validate(cls: Type['Model'], value: Any) -> 'Model': if isinstance(value, cls): - if cls.__config__.copy_on_model_validation: + if cls.__config__.copy_on_model_validation and cls.__config__.copy_on_model_validation_shallow: + return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False) + elif cls.__config__.copy_on_model_validation: return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=True) else: return value diff --git a/tests/test_main.py b/tests/test_main.py index f4d3844c4a..ba185b2fd3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1567,6 +1567,28 @@ class Config: assert t.dict() == {'id': '1234567890', 'user': {'id': 42, 'hobbies': ['scuba diving']}} +def test_model_exclude_copy_on_model_validation_shallow(): + """When `Config.copy_on_model_validation` is set and `Config.copy_on_model_validation_shallow` is set, + do the same as the previous test but perform a shallow copy""" + + class User(BaseModel): + class Config: + copy_on_model_validation_shallow = True + + hobbies: List[str] + + my_user = User(hobbies=['scuba diving']) + + class Transaction(BaseModel): + user: User = Field(...) + + t = Transaction( + user=my_user, + ) + + assert t.user.hobbies is my_user.hobbies # unlike above, this should be a shallow copy + + def test_validation_deep_copy(): """By default, Config.copy_on_model_validation should do a deep copy""" From 49461f331c14c492b5f7ecae5b71d08f3b03e08e Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Thu, 19 May 2022 14:30:24 -0400 Subject: [PATCH 02/12] Add changes file --- changes/4093-timkpaine.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/4093-timkpaine.md diff --git a/changes/4093-timkpaine.md b/changes/4093-timkpaine.md new file mode 100644 index 0000000000..bfa0d67d1d --- /dev/null +++ b/changes/4093-timkpaine.md @@ -0,0 +1 @@ +Allow for shallow copies of attributes, adjusting the behavior of #3642 From a1690d1a675f3243b7410be50bea2ec2d78bd32b Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Thu, 19 May 2022 14:31:19 -0400 Subject: [PATCH 03/12] tweak change --- changes/4093-timkpaine.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changes/4093-timkpaine.md b/changes/4093-timkpaine.md index bfa0d67d1d..f0e4ed1ebb 100644 --- a/changes/4093-timkpaine.md +++ b/changes/4093-timkpaine.md @@ -1 +1,2 @@ Allow for shallow copies of attributes, adjusting the behavior of #3642 +`Config.copy_on_model_validation` does a shallow copy and not a deep one if `Config.copy_on_model_validation_shallow` is also `True`. From 556231f44a209e3b865362d413bd681e424155dc Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Fri, 20 May 2022 09:29:15 -0400 Subject: [PATCH 04/12] update for comments --- changes/4093-timkpaine.md | 2 +- pydantic/config.py | 9 +++++++-- pydantic/main.py | 4 ++-- tests/test_main.py | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/changes/4093-timkpaine.md b/changes/4093-timkpaine.md index f0e4ed1ebb..0a8c38b5c3 100644 --- a/changes/4093-timkpaine.md +++ b/changes/4093-timkpaine.md @@ -1,2 +1,2 @@ Allow for shallow copies of attributes, adjusting the behavior of #3642 -`Config.copy_on_model_validation` does a shallow copy and not a deep one if `Config.copy_on_model_validation_shallow` is also `True`. +`Config.copy_on_model_validation` is now a str enum of `["", "deep", "shallow"]` corresponding to reference, deep copy, shallow copy. diff --git a/pydantic/config.py b/pydantic/config.py index 0a3abf6bc4..ae9199cee5 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -36,6 +36,12 @@ class Extra(str, Enum): forbid = 'forbid' +class Copy(str, Enum): + none = '' + deep = 'deep' + shallow = 'shallow' + + class BaseConfig: title: Optional[str] = None anystr_lower: bool = False @@ -64,8 +70,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: bool = True - copy_on_model_validation_shallow: bool = False + copy_on_model_validation: Copy = Copy.deep # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index bd52e22a97..06bb894848 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -25,7 +25,7 @@ ) from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators -from .config import BaseConfig, Extra, inherit_config, prepare_config +from .config import BaseConfig, Copy, Extra, inherit_config, prepare_config from .error_wrappers import ErrorWrapper, ValidationError from .errors import ConfigError, DictError, ExtraError, MissingError from .fields import MAPPING_LIKE_SHAPES, Field, FieldInfo, ModelField, ModelPrivateAttr, PrivateAttr, Undefined @@ -675,7 +675,7 @@ def __get_validators__(cls) -> 'CallableGenerator': @classmethod def validate(cls: Type['Model'], value: Any) -> 'Model': if isinstance(value, cls): - if cls.__config__.copy_on_model_validation and cls.__config__.copy_on_model_validation_shallow: + if cls.__config__.copy_on_model_validation == Copy.shallow: return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False) elif cls.__config__.copy_on_model_validation: return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=True) diff --git a/tests/test_main.py b/tests/test_main.py index ba185b2fd3..f5891e0e55 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1573,7 +1573,7 @@ def test_model_exclude_copy_on_model_validation_shallow(): class User(BaseModel): class Config: - copy_on_model_validation_shallow = True + copy_on_model_validation_shallow = "shallow" hobbies: List[str] From f8a2705c9fd0ccd514864a4456d4aa7d4cbe1653 Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Fri, 20 May 2022 09:32:19 -0400 Subject: [PATCH 05/12] rename attr --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index f5891e0e55..4c77872de7 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1573,7 +1573,7 @@ def test_model_exclude_copy_on_model_validation_shallow(): class User(BaseModel): class Config: - copy_on_model_validation_shallow = "shallow" + copy_on_model_validation = "shallow" hobbies: List[str] From 4eeb6df85f4aee3b82ff44f9fc656e7474931634 Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Fri, 20 May 2022 09:33:58 -0400 Subject: [PATCH 06/12] use single quotes --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 4c77872de7..edd0ba7807 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1573,7 +1573,7 @@ def test_model_exclude_copy_on_model_validation_shallow(): class User(BaseModel): class Config: - copy_on_model_validation = "shallow" + copy_on_model_validation = 'shallow' hobbies: List[str] From 64bf6f80b140c151c1781a4e1b139d1bf68f94cd Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Fri, 20 May 2022 09:48:30 -0400 Subject: [PATCH 07/12] bump ci From bb33a0487931f44f8a45db0a0b9e72032958fae0 Mon Sep 17 00:00:00 2001 From: Tim Paine Date: Mon, 23 May 2022 09:38:10 -0500 Subject: [PATCH 08/12] add warning if not a string, switch to string literals --- pydantic/config.py | 9 +++------ pydantic/main.py | 11 +++++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pydantic/config.py b/pydantic/config.py index ae9199cee5..e8fe153717 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -1,6 +1,6 @@ import json from enum import Enum -from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Literal, Optional, Tuple, Type, Union from .typing import AnyCallable from .utils import GetterDict @@ -36,10 +36,7 @@ class Extra(str, Enum): forbid = 'forbid' -class Copy(str, Enum): - none = '' - deep = 'deep' - shallow = 'shallow' +Copy = Literal["none", "deep", "shallow"] class BaseConfig: @@ -70,7 +67,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: Copy = Copy.deep + copy_on_model_validation: Copy = "deep" # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index 06bb894848..4df9302161 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -675,11 +675,18 @@ def __get_validators__(cls) -> 'CallableGenerator': @classmethod def validate(cls: Type['Model'], value: Any) -> 'Model': if isinstance(value, cls): - if cls.__config__.copy_on_model_validation == Copy.shallow: + if isinstance(cls.__config__.copy_on_model_validation, bool): + # Warn about deprecated behavior + warnings.warn('`copy_on_model_validation` should be a string of value ("deep", "shallow", "none")', RuntimeWarning) + + if cls.__config__.copy_on_model_validation == "shallow": + # shallow copy return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False) - elif cls.__config__.copy_on_model_validation: + elif cls.__config__.copy_on_model_validation == "deep": + # deep copy return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=True) else: + # ref return value value = cls._enforce_dict_if_root(value) From c05597683ae0aa1b63a4f0c55481dbf60ca49fa4 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 9 Aug 2022 12:40:03 +0100 Subject: [PATCH 09/12] fix linting, prompt ci --- .github/workflows/ci.yml | 1 + pydantic/config.py | 4 ++-- pydantic/main.py | 10 ++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cb3916630..31c6a087c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: - 1.9.X-fixes tags: - '**' + pull_request: {} jobs: lint: diff --git a/pydantic/config.py b/pydantic/config.py index 83ad08f4de..eb1c82b437 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -36,7 +36,7 @@ class Extra(str, Enum): forbid = 'forbid' -Copy = Literal["none", "deep", "shallow"] +Copy = Literal['none', 'deep', 'shallow'] class BaseConfig: @@ -68,7 +68,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: Copy = "deep" + copy_on_model_validation: Copy = 'deep' # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index 4df9302161..95ce0c3843 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -25,7 +25,7 @@ ) from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators -from .config import BaseConfig, Copy, Extra, inherit_config, prepare_config +from .config import BaseConfig, Extra, inherit_config, prepare_config from .error_wrappers import ErrorWrapper, ValidationError from .errors import ConfigError, DictError, ExtraError, MissingError from .fields import MAPPING_LIKE_SHAPES, Field, FieldInfo, ModelField, ModelPrivateAttr, PrivateAttr, Undefined @@ -677,12 +677,14 @@ def validate(cls: Type['Model'], value: Any) -> 'Model': if isinstance(value, cls): if isinstance(cls.__config__.copy_on_model_validation, bool): # Warn about deprecated behavior - warnings.warn('`copy_on_model_validation` should be a string of value ("deep", "shallow", "none")', RuntimeWarning) + warnings.warn( + "`copy_on_model_validation` should be a string of value ('deep', 'shallow', 'none')", RuntimeWarning + ) - if cls.__config__.copy_on_model_validation == "shallow": + if cls.__config__.copy_on_model_validation == 'shallow': # shallow copy return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False) - elif cls.__config__.copy_on_model_validation == "deep": + elif cls.__config__.copy_on_model_validation == 'deep': # deep copy return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=True) else: From 0b998a530a9faabecfb8bbcc068dbfe1a540b8c2 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 9 Aug 2022 12:55:11 +0100 Subject: [PATCH 10/12] fix ci --- docs/requirements.txt | 14 +++++++------- pydantic/config.py | 11 ++++------- pydantic/main.py | 23 +++++++++++++++-------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 348505f4d9..6df8b4ee69 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,12 +1,12 @@ -ansi2html==1.6.0 -flake8==4.0.1 +ansi2html==1.8.0 +flake8==5.0.4 flake8-quotes==3.3.1 -hypothesis==6.46.3 -markdown-include==0.6.0 -mdx-truly-sane-lists==1.2 -mkdocs==1.3.0 +hypothesis==6.54.1 +markdown-include==0.7.0 +mdx-truly-sane-lists==1.3 +mkdocs==1.3.1 mkdocs-exclude==1.0.2 -mkdocs-material==8.2.14 +mkdocs-material==8.3.9 sqlalchemy orjson ujson diff --git a/pydantic/config.py b/pydantic/config.py index eb1c82b437..58b217adcd 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -1,6 +1,6 @@ import json from enum import Enum -from typing import TYPE_CHECKING, Any, Callable, Dict, Literal, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type, Union from .typing import AnyCallable from .utils import GetterDict @@ -8,14 +8,14 @@ if TYPE_CHECKING: from typing import overload - import typing_extensions + from typing_extensions import Literal, Protocol from .fields import ModelField from .main import BaseModel ConfigType = Type['BaseConfig'] - class SchemaExtraCallable(typing_extensions.Protocol): + class SchemaExtraCallable(Protocol): @overload def __call__(self, schema: Dict[str, Any]) -> None: pass @@ -36,9 +36,6 @@ class Extra(str, Enum): forbid = 'forbid' -Copy = Literal['none', 'deep', 'shallow'] - - class BaseConfig: title: Optional[str] = None anystr_lower: bool = False @@ -68,7 +65,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: Copy = 'deep' + copy_on_model_validation: 'Literal["none", "deep", "shallow"]' = 'deep' # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index 95ce0c3843..3e4651c905 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -675,21 +675,28 @@ def __get_validators__(cls) -> 'CallableGenerator': @classmethod def validate(cls: Type['Model'], value: Any) -> 'Model': if isinstance(value, cls): - if isinstance(cls.__config__.copy_on_model_validation, bool): + copy_on_model_validation = cls.__config__.copy_on_model_validation + # whether to deep or shallow copy the model on validation, None means do not copy + deep_copy: Optional[bool] = None + if copy_on_model_validation not in {'deep', 'shallow', 'none'}: # Warn about deprecated behavior warnings.warn( - "`copy_on_model_validation` should be a string of value ('deep', 'shallow', 'none')", RuntimeWarning + "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", RuntimeWarning ) + if copy_on_model_validation: + deep_copy = False - if cls.__config__.copy_on_model_validation == 'shallow': + if copy_on_model_validation == 'shallow': # shallow copy - return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False) - elif cls.__config__.copy_on_model_validation == 'deep': + deep_copy = False + elif copy_on_model_validation == 'deep': # deep copy - return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=True) - else: - # ref + deep_copy = True + + if deep_copy is None: return value + else: + return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=deep_copy) value = cls._enforce_dict_if_root(value) From c5da8e218896df10f93b5930c55c839273e6eeff Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 9 Aug 2022 13:09:41 +0100 Subject: [PATCH 11/12] extend and fix tests --- changes/4093-timkpaine.md | 3 ++- pydantic/config.py | 6 +++--- pydantic/main.py | 2 +- tests/test_main.py | 31 +++++++++++++++++++++++++++---- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/changes/4093-timkpaine.md b/changes/4093-timkpaine.md index 0a8c38b5c3..8d77b01db7 100644 --- a/changes/4093-timkpaine.md +++ b/changes/4093-timkpaine.md @@ -1,2 +1,3 @@ Allow for shallow copies of attributes, adjusting the behavior of #3642 -`Config.copy_on_model_validation` is now a str enum of `["", "deep", "shallow"]` corresponding to reference, deep copy, shallow copy. +`Config.copy_on_model_validation` is now a str enum of `["none", "deep", "shallow"]` corresponding to +not copying, deep copy, shallow copy. diff --git a/pydantic/config.py b/pydantic/config.py index 58b217adcd..b01470d991 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -2,14 +2,14 @@ from enum import Enum from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type, Union +from typing_extensions import Literal, Protocol + from .typing import AnyCallable from .utils import GetterDict if TYPE_CHECKING: from typing import overload - from typing_extensions import Literal, Protocol - from .fields import ModelField from .main import BaseModel @@ -65,7 +65,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: 'Literal["none", "deep", "shallow"]' = 'deep' + copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'deep' # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/pydantic/main.py b/pydantic/main.py index 3e4651c905..c57d48400c 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -681,7 +681,7 @@ def validate(cls: Type['Model'], value: Any) -> 'Model': if copy_on_model_validation not in {'deep', 'shallow', 'none'}: # Warn about deprecated behavior warnings.warn( - "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", RuntimeWarning + "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", DeprecationWarning ) if copy_on_model_validation: deep_copy = False diff --git a/tests/test_main.py b/tests/test_main.py index 6a4bbd78d0..7a267bbf52 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1582,13 +1582,36 @@ class Config: class Transaction(BaseModel): user: User = Field(...) - t = Transaction( - user=my_user, - ) + t = Transaction(user=my_user) + assert t.user is not my_user assert t.user.hobbies is my_user.hobbies # unlike above, this should be a shallow copy +@pytest.mark.parametrize('comv_value', [True, False]) +def test_copy_on_model_validation_warning(comv_value): + class User(BaseModel): + class Config: + # True interpreted as 'shallow', False interpreted as 'none' + copy_on_model_validation = comv_value + + hobbies: List[str] + + my_user = User(hobbies=['scuba diving']) + + class Transaction(BaseModel): + user: User + + with pytest.warns(DeprecationWarning, match="`copy_on_model_validation` should be a string: 'deep', 'shallow' or"): + t = Transaction(user=my_user) + + if comv_value: + assert t.user is not my_user + else: + assert t.user is my_user + assert t.user.hobbies is my_user.hobbies + + def test_validation_deep_copy(): """By default, Config.copy_on_model_validation should do a deep copy""" @@ -2009,7 +2032,7 @@ def __hash__(self): return id(self) class Config: - copy_on_model_validation = False + copy_on_model_validation = 'none' class Item(BaseModel): images: List[Image] From cc39d7f35520a425d340bf8b0bc2305fec2b8922 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 10 Aug 2022 14:15:36 +0100 Subject: [PATCH 12/12] change default to "shallow" --- changes/4093-timkpaine.md | 2 +- pydantic/config.py | 2 +- tests/test_main.py | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changes/4093-timkpaine.md b/changes/4093-timkpaine.md index 8d77b01db7..8f22eb7119 100644 --- a/changes/4093-timkpaine.md +++ b/changes/4093-timkpaine.md @@ -1,3 +1,3 @@ Allow for shallow copies of attributes, adjusting the behavior of #3642 `Config.copy_on_model_validation` is now a str enum of `["none", "deep", "shallow"]` corresponding to -not copying, deep copy, shallow copy. +not copying, deep copy, shallow copy, default `"shallow"`. diff --git a/pydantic/config.py b/pydantic/config.py index b01470d991..b9d9bebccb 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -65,7 +65,7 @@ class BaseConfig: # whether inherited models as fields should be reconstructed as base model, # and whether such a copy should be shallow or deep - copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'deep' + copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow' # whether `Union` should check all allowed types before even trying to coerce smart_union: bool = False diff --git a/tests/test_main.py b/tests/test_main.py index 7a267bbf52..0b96f96a21 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1561,7 +1561,7 @@ class Config: assert t.user is not my_user assert t.user.hobbies == ['scuba diving'] - assert t.user.hobbies is not my_user.hobbies # `Config.copy_on_model_validation` does a deep copy + assert t.user.hobbies is my_user.hobbies # `Config.copy_on_model_validation` does a shallow copy assert t.user._priv == 13 assert t.user.password.get_secret_value() == 'hashedpassword' assert t.dict() == {'id': '1234567890', 'user': {'id': 42, 'hobbies': ['scuba diving']}} @@ -1618,6 +1618,9 @@ def test_validation_deep_copy(): class A(BaseModel): name: str + class Config: + copy_on_model_validation = 'deep' + class B(BaseModel): list_a: List[A]