From 7d6f5fec2b75e1d394f5a654d0e797df338f24ed Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 6 Sep 2022 16:55:43 +0100 Subject: [PATCH 01/38] test on the 1.10.X-fixes branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5370167fe7a..55a421c6c3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - 1.10.X-fixes tags: - '**' pull_request: {} From 3ddca7e28e9ae3cbd1c3bb59fb5859db222c58b3 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 6 Sep 2022 17:08:48 +0100 Subject: [PATCH 02/38] fix copy_on_model_validation type hint (#4491) --- pydantic/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pydantic/config.py b/pydantic/config.py index 74687ca0363..3ce8d6df71a 100644 --- a/pydantic/config.py +++ b/pydantic/config.py @@ -68,9 +68,7 @@ class ConfigDict(TypedDict, total=False): json_encoders: Dict[Type[object], AnyCallable] underscore_attrs_are_private: bool allow_inf_nan: bool - - # whether or not inherited models as fields should be reconstructed as base model - copy_on_model_validation: bool + copy_on_model_validation: Literal['none', 'deep', 'shallow'] # whether dataclass `__post_init__` should be run after validation post_init_call: Literal['before_validation', 'after_validation'] From 873543d8546d419791535aa0028da1decceab6ad Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 7 Sep 2022 22:28:44 +0100 Subject: [PATCH 03/38] use "field_specifiers", fix #4500 (#4501) --- changes/4500-samuelcolvin.md | 1 + pydantic/dataclasses.py | 10 +++++----- pydantic/main.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changes/4500-samuelcolvin.md diff --git a/changes/4500-samuelcolvin.md b/changes/4500-samuelcolvin.md new file mode 100644 index 00000000000..24cda1cbf4f --- /dev/null +++ b/changes/4500-samuelcolvin.md @@ -0,0 +1 @@ +Correct `dataclass_transform` keyword argument name from `field_descriptors` to `field_specifiers` diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py index 6833112702d..9a72533bd76 100644 --- a/pydantic/dataclasses.py +++ b/pydantic/dataclasses.py @@ -105,7 +105,7 @@ def __validate__(cls: Type['DataclassT'], v: Any) -> 'DataclassT': if sys.version_info >= (3, 10): - @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) @overload def dataclass( *, @@ -121,7 +121,7 @@ def dataclass( ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: ... - @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) @overload def dataclass( _cls: Type[_T], @@ -140,7 +140,7 @@ def dataclass( else: - @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) @overload def dataclass( *, @@ -155,7 +155,7 @@ def dataclass( ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: ... - @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) @overload def dataclass( _cls: Type[_T], @@ -172,7 +172,7 @@ def dataclass( ... -@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) +@dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) def dataclass( _cls: Optional[Type[_T]] = None, *, diff --git a/pydantic/main.py b/pydantic/main.py index 69f3b75120d..16b759f7ce0 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -118,7 +118,7 @@ def hash_function(self_: Any) -> int: _is_base_model_class_defined = False -@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) +@dataclass_transform(kw_only_default=True, field_specifiers=(Field, FieldInfo)) class ModelMetaclass(ABCMeta): @no_type_check # noqa C901 def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901 From 38f1d617698f200eafcfe550ac0a5c159211b780 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 8 Sep 2022 16:59:04 +0100 Subject: [PATCH 04/38] add hooky config --- .hooky.toml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .hooky.toml diff --git a/.hooky.toml b/.hooky.toml new file mode 100644 index 00000000000..78c95dca4e7 --- /dev/null +++ b/.hooky.toml @@ -0,0 +1,4 @@ +# configuring https://github.com/pydantic/hooky +[tool.hooky] +reviewers = ['samuelcolvin', 'PrettyWood', 'hramezani'] +require_change_file = true From e9f92dc22e66bce75814ac455ea4111970a77d94 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Thu, 15 Sep 2022 17:35:03 +0200 Subject: [PATCH 05/38] fix: missing file extension in model_config.md link (#4512) (#4513) Co-authored-by: Dmitry Lavrentev --- docs/usage/validation_decorator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/validation_decorator.md b/docs/usage/validation_decorator.md index fad7a5f5f3b..02535b0889d 100644 --- a/docs/usage/validation_decorator.md +++ b/docs/usage/validation_decorator.md @@ -55,7 +55,7 @@ as the default value of the field: {!.tmp_examples/validation_decorator_field.md!} -The [alias](model_config#alias-precedence) can be used with the decorator as normal. +The [alias](model_config.md#alias-precedence) can be used with the decorator as normal. {!.tmp_examples/validation_decorator_field_alias.md!} From 3461bc97e4cf02393a9b60d16dbb2aa1a3ef1314 Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 16 Sep 2022 21:23:50 +0200 Subject: [PATCH 06/38] Update mkdocs.yml (#4524) (#4532) Co-authored-by: Marcelo Trylesinski --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 6804017a0e2..38e9749a922 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ theme: repo_name: pydantic/pydantic repo_url: https://github.com/pydantic/pydantic +edit_uri: edit/main/docs/ extra: analytics: provider: google From b171a7ecfabf0e37c1089a49ad8af313ff321d85 Mon Sep 17 00:00:00 2001 From: Sigurd Spieckermann <2206639+sisp@users.noreply.github.com> Date: Tue, 20 Sep 2022 14:57:16 +0200 Subject: [PATCH 07/38] [1.10.X] Fix field regex with StrictStr type annotation (#4538) * Fix field regex with StrictStr type annotation * Add change file * Improve regex caching --- changes/4538-sisp.md | 1 + pydantic/types.py | 12 ++++++++---- tests/test_types.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 changes/4538-sisp.md diff --git a/changes/4538-sisp.md b/changes/4538-sisp.md new file mode 100644 index 00000000000..afb3312ba55 --- /dev/null +++ b/changes/4538-sisp.md @@ -0,0 +1 @@ +Fix field regex with `StrictStr` type annotation. diff --git a/pydantic/types.py b/pydantic/types.py index f98dba3de49..eaf679d48ec 100644 --- a/pydantic/types.py +++ b/pydantic/types.py @@ -403,7 +403,7 @@ class ConstrainedStr(str): min_length: OptionalInt = None max_length: OptionalInt = None curtail_length: OptionalInt = None - regex: Optional[Pattern[str]] = None + regex: Optional[Union[str, Pattern[str]]] = None strict = False @classmethod @@ -412,7 +412,7 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: field_schema, minLength=cls.min_length, maxLength=cls.max_length, - pattern=cls.regex and cls.regex.pattern, + pattern=cls.regex and cls._get_pattern(cls.regex), ) @classmethod @@ -430,11 +430,15 @@ def validate(cls, value: Union[str]) -> Union[str]: value = value[: cls.curtail_length] if cls.regex: - if not cls.regex.match(value): - raise errors.StrRegexError(pattern=cls.regex.pattern) + if not re.match(cls.regex, value): + raise errors.StrRegexError(pattern=cls._get_pattern(cls.regex)) return value + @staticmethod + def _get_pattern(regex: Union[str, Pattern[str]]) -> str: + return regex if isinstance(regex, str) else regex.pattern + def constr( *, diff --git a/tests/test_types.py b/tests/test_types.py index af4a91ef1d7..785e335b916 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1759,6 +1759,45 @@ class Model(BaseModel): Model(u='1234567') +def test_strict_str_regex(): + class Model(BaseModel): + u: StrictStr = Field(..., regex=r'^[0-9]+$') + + assert Model(u='123').u == '123' + + with pytest.raises(ValidationError, match='str type expected'): + Model(u=123) + + with pytest.raises(ValidationError) as exc_info: + Model(u='abc') + assert exc_info.value.errors() == [ + { + 'loc': ('u',), + 'msg': 'string does not match regex "^[0-9]+$"', + 'type': 'value_error.str.regex', + 'ctx': {'pattern': '^[0-9]+$'}, + } + ] + + +def test_string_regex(): + class Model(BaseModel): + u: str = Field(..., regex=r'^[0-9]+$') + + assert Model(u='123').u == '123' + + with pytest.raises(ValidationError) as exc_info: + Model(u='abc') + assert exc_info.value.errors() == [ + { + 'loc': ('u',), + 'msg': 'string does not match regex "^[0-9]+$"', + 'type': 'value_error.str.regex', + 'ctx': {'pattern': '^[0-9]+$'}, + } + ] + + def test_strict_bool(): class Model(BaseModel): v: StrictBool From da0b756e523ddb3db63f22d90ac34dc2fcf9f55f Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 21 Sep 2022 22:20:29 +0200 Subject: [PATCH 08/38] v2 docs broken link fix (#4533) (#4550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hasan Ramezani Co-authored-by: Miloš Mandić --- docs/blog/pydantic-v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blog/pydantic-v2.md b/docs/blog/pydantic-v2.md index f9ed30d0150..c1fd3b74692 100644 --- a/docs/blog/pydantic-v2.md +++ b/docs/blog/pydantic-v2.md @@ -123,7 +123,7 @@ The motivation for building pydantic-core in Rust is as follows: pydantic-core is usable now, albeit with an unintuitive API, if you're interested, please give it a try. pydantic-core provides validators for common data types, -[see a list here](https://github.com/pydantic/pydantic-core/blob/main/pydantic_core/_types.py#L291). +[see a list here](https://github.com/pydantic/pydantic-core/blob/main/pydantic_core/schema_types.py#L314). Other, less commonly used data types will be supported via validator functions implemented in pydantic, in Python. See [pydantic-core#153](https://github.com/pydantic/pydantic-core/issues/153) From 5f777a01ffc293740118f777106abc4720a27f86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 09:19:05 +0200 Subject: [PATCH 09/38] Fix "trough" typo in Model Config docs (#4593) (#4594) Co-authored-by: Zach Kirsch --- docs/usage/model_config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/model_config.md b/docs/usage/model_config.md index 656cf5144e9..7e0efd5d5d5 100644 --- a/docs/usage/model_config.md +++ b/docs/usage/model_config.md @@ -55,7 +55,7 @@ Similarly, if using the `@dataclass` decorator: **`fields`** : a `dict` containing schema information for each field; this is equivalent to using [the `Field` class](schema.md), except when a field is already - defined trough annotation or the Field class, in which case only + defined through annotation or the Field class, in which case only `alias`, `include`, `exclude`, `min_length`, `max_length`, `regex`, `gt`, `lt`, `gt`, `le`, `multiple_of`, `max_digits`, `decimal_places`, `min_items`, `max_items`, `unique_items` and allow_mutation can be set (for example you cannot set default of default_factory) From 4e9115a5c63c940816a50b762afac6340a4d1ada Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 13:36:52 +0200 Subject: [PATCH 10/38] Add missing typing to example (#4596) (#4605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add missing typing to example * Update example typing. Co-authored-by: Rémy HUBSCHER --- docs/examples/schema_with_field.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/examples/schema_with_field.py b/docs/examples/schema_with_field.py index 9288efd8cdf..860903e55be 100644 --- a/docs/examples/schema_with_field.py +++ b/docs/examples/schema_with_field.py @@ -1,5 +1,5 @@ # output-json -from typing import Optional +from typing import Any, Callable, Dict, Generator, Optional from pydantic import BaseModel, Field from pydantic.fields import ModelField @@ -7,18 +7,20 @@ class RestrictedAlphabetStr(str): @classmethod - def __get_validators__(cls): + def __get_validators__(cls) -> Generator[Callable, None, None]: yield cls.validate @classmethod - def validate(cls, value, field: ModelField): + def validate(cls, value: str, field: ModelField): alphabet = field.field_info.extra['alphabet'] if any(c not in alphabet for c in value): raise ValueError(f'{value!r} is not restricted to {alphabet!r}') return cls(value) @classmethod - def __modify_schema__(cls, field_schema, field: Optional[ModelField]): + def __modify_schema__( + cls, field_schema: Dict[str, Any], field: Optional[ModelField] + ): if field: alphabet = field.field_info.extra['alphabet'] field_schema['examples'] = [c * 3 for c in alphabet] From b516de7f5333b17290059b9ae7b5e76259ec2898 Mon Sep 17 00:00:00 2001 From: Matt Fulgo Date: Mon, 17 Oct 2022 11:13:52 -0400 Subject: [PATCH 11/38] Allows Optional lists with unique_items check (#4568) When using `unique_items` with an `Optional[List[T]]` field, the field validator would raise the following error if the field was not provided: > `'NoneType' object is not iterable (type=type_error)` Updating the validator to return `None` in these cases avoids the issue. Fixes #3957, #4050, #4119 --- changes/4568-mfulgo.md | 1 + pydantic/types.py | 5 ++++- tests/test_validators.py | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 changes/4568-mfulgo.md diff --git a/changes/4568-mfulgo.md b/changes/4568-mfulgo.md new file mode 100644 index 00000000000..dfbe63d9fc0 --- /dev/null +++ b/changes/4568-mfulgo.md @@ -0,0 +1 @@ +Fixes error passing None for optional lists with `unique_items` diff --git a/pydantic/types.py b/pydantic/types.py index eaf679d48ec..9438a6a829f 100644 --- a/pydantic/types.py +++ b/pydantic/types.py @@ -599,7 +599,10 @@ def list_length_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]': return v @classmethod - def unique_items_validator(cls, v: 'List[T]') -> 'List[T]': + def unique_items_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]': + if v is None: + return None + for i, value in enumerate(v, start=1): if value in v[i:]: raise errors.ListUniqueItemsError() diff --git a/tests/test_validators.py b/tests/test_validators.py index 778085880dc..de67ffe4720 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -7,7 +7,7 @@ import pytest from typing_extensions import Literal -from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, errors, validator +from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, conlist, errors, validator from pydantic.class_validators import make_generic_validator, root_validator @@ -1329,3 +1329,19 @@ def post_root(cls, values): B(x='pika') assert validate_stub.call_args_list == [mocker.call('B', 'pre'), mocker.call('B', 'post')] + + +def test_list_unique_items_with_optional(): + class Model(BaseModel): + foo: Optional[List[str]] = Field(None, unique_items=True) + bar: conlist(str, unique_items=True) = Field(None) + + assert Model().dict() == {'foo': None, 'bar': None} + assert Model(foo=None, bar=None).dict() == {'foo': None, 'bar': None} + assert Model(foo=['k1'], bar=['k1']).dict() == {'foo': ['k1'], 'bar': ['k1']} + with pytest.raises(ValidationError) as exc_info: + Model(foo=['k1', 'k1'], bar=['k1', 'k1']) + assert exc_info.value.errors() == [ + {'loc': ('foo',), 'msg': 'the list has duplicated items', 'type': 'value_error.list.unique_items'}, + {'loc': ('bar',), 'msg': 'the list has duplicated items', 'type': 'value_error.list.unique_items'}, + ] From 36be4c9f4e708a15472d55a09f15481943e86ee6 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Thu, 27 Oct 2022 08:16:03 -0700 Subject: [PATCH 12/38] Allow dicts to have both patternProperties and additionalProperties (#4641) Support for `patternProperties` was introduced in #332, but that logic unfortunately made `patternProperties` and `additionalProperties` mutually exclusive. JSON Schema supports their combination: https://json-schema.org/understanding-json-schema/reference/object.html#additional-properties This prevented well-typed code generation for dictionaries that use regex-constrained string keys. --- changes/4641-jparise.md | 1 + pydantic/schema.py | 2 +- tests/test_schema.py | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 changes/4641-jparise.md diff --git a/changes/4641-jparise.md b/changes/4641-jparise.md new file mode 100644 index 00000000000..b671f3c6209 --- /dev/null +++ b/changes/4641-jparise.md @@ -0,0 +1 @@ +Allow dict schemas to have both `patternProperties` and `additionalProperties` diff --git a/pydantic/schema.py b/pydantic/schema.py index e7af56f120d..1348fbd5eef 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -490,7 +490,7 @@ def field_type_schema( # Dict keys have a regex pattern # items_schema might be a schema or empty dict, add it either way f_schema['patternProperties'] = {regex.pattern: items_schema} - elif items_schema: + if items_schema: # The dict values are not simply Any, so they need a schema f_schema['additionalProperties'] = items_schema elif field.shape == SHAPE_TUPLE or (field.shape == SHAPE_GENERIC and not issubclass(field.type_, BaseModel)): diff --git a/tests/test_schema.py b/tests/test_schema.py index 143768a8005..60e31776098 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1654,7 +1654,13 @@ class Foo(BaseModel): 'title': 'Foo', 'type': 'object', 'properties': { - 'a': {'type': 'object', 'title': 'A', 'default': {}, 'patternProperties': {regex_str: {'type': 'string'}}} + 'a': { + 'type': 'object', + 'title': 'A', + 'default': {}, + 'additionalProperties': {'type': 'string'}, + 'patternProperties': {regex_str: {'type': 'string'}}, + } }, } From e43e4552e2434534cd1370b0a19483d89e51bd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=B2rian?= Date: Mon, 31 Oct 2022 11:37:57 +0100 Subject: [PATCH 13/38] Add postgresql+psycopg as allowed scheme for PostgreDsn (#4689) (#4690) --- changes/4689-morian.md | 1 + docs/usage/types.md | 1 + pydantic/networks.py | 1 + tests/test_networks.py | 1 + 4 files changed, 4 insertions(+) create mode 100644 changes/4689-morian.md diff --git a/changes/4689-morian.md b/changes/4689-morian.md new file mode 100644 index 00000000000..10f8f429f32 --- /dev/null +++ b/changes/4689-morian.md @@ -0,0 +1 @@ +Add `postgresql+psycopg` as allowed scheme for `PostgreDsn` to make it usable with SQLAlchemy 2 diff --git a/docs/usage/types.md b/docs/usage/types.md index 27109f281fb..b436d63d9a2 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -598,6 +598,7 @@ For URI/URL validation the following types are available: - `postgresql` - `postgresql+asyncpg` - `postgresql+pg8000` + - `postgresql+psycopg` - `postgresql+psycopg2` - `postgresql+psycopg2cffi` - `postgresql+py-postgresql` diff --git a/pydantic/networks.py b/pydantic/networks.py index c7d97186b93..e1eef7b576f 100644 --- a/pydantic/networks.py +++ b/pydantic/networks.py @@ -490,6 +490,7 @@ class PostgresDsn(MultiHostDsn): 'postgresql', 'postgresql+asyncpg', 'postgresql+pg8000', + 'postgresql+psycopg', 'postgresql+psycopg2', 'postgresql+psycopg2cffi', 'postgresql+py-postgresql', diff --git a/tests/test_networks.py b/tests/test_networks.py index 2f4ed2dfe6a..4d8c5a3ff64 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -36,6 +36,7 @@ 'postgres://just-user@localhost:5432/app', 'postgresql+asyncpg://user:pass@localhost:5432/app', 'postgresql+pg8000://user:pass@localhost:5432/app', + 'postgresql+psycopg://postgres:postgres@localhost:5432/hatch', 'postgresql+psycopg2://postgres:postgres@localhost:5432/hatch', 'postgresql+psycopg2cffi://user:pass@localhost:5432/app', 'postgresql+py-postgresql://user:pass@localhost:5432/app', From 83cf4646ab4319c5663cea8320ce13f391c4f08b Mon Sep 17 00:00:00 2001 From: Matt Fulgo Date: Mon, 31 Oct 2022 12:28:06 -0400 Subject: [PATCH 14/38] Fix TypeError for GenericModel with Callable param (#4653) * Fix TypeError for GenericModel with Callable param Introduced in 1.10.2, a TypeError would be raised upon creation of a GenericModel class that used a Callable type parameter. This would happen when `typing.get_args` returned a list for the first type agruments in a Callable and pydantic would try to use the value as a dictionary key. To avoid the issue, we convert the list to a tuple before using it as a key. The possible approach of modifying pydantic's `get_args` function to return a tuple instead of a list didn't work out because the return values are used in more places, some of which expect the list and not a tuple. Fixes #4551 * change as markdown Co-authored-by: Samuel Colvin --- changes/4551-mfulgo.md | 1 + pydantic/generics.py | 6 +++++- tests/test_generics.py | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changes/4551-mfulgo.md diff --git a/changes/4551-mfulgo.md b/changes/4551-mfulgo.md new file mode 100644 index 00000000000..1d9871a41aa --- /dev/null +++ b/changes/4551-mfulgo.md @@ -0,0 +1 @@ +Fix `GenericModel` with `Callable` param raising a `TypeError` diff --git a/pydantic/generics.py b/pydantic/generics.py index a3f52bfee96..ece421e8ba2 100644 --- a/pydantic/generics.py +++ b/pydantic/generics.py @@ -64,7 +64,11 @@ def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[T """ def _cache_key(_params: Any) -> Tuple[Type[GenericModelT], Any, Tuple[Any, ...]]: - return cls, _params, get_args(_params) + args = get_args(_params) + # python returns a list for Callables, which is not hashable + if len(args) == 2 and isinstance(args[0], list): + args = (tuple(args[0]), args[1]) + return cls, _params, args cached = _generic_types_cache.get(_cache_key(params)) if cached is not None: diff --git a/tests/test_generics.py b/tests/test_generics.py index 39adc45f20e..371272b1caf 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -7,6 +7,7 @@ ClassVar, Dict, Generic, + Iterable, List, Mapping, Optional, @@ -234,6 +235,32 @@ class Model(GenericModel, Generic[T]): assert len(_generic_types_cache) == cache_size + 2 +def test_cache_keys_are_hashable(): + cache_size = len(_generic_types_cache) + T = TypeVar('T') + C = Callable[[str, Dict[str, Any]], Iterable[str]] + + class MyGenericModel(GenericModel, Generic[T]): + t: T + + # Callable's first params get converted to a list, which is not hashable. + # Make sure we can handle that special case + Simple = MyGenericModel[Callable[[int], str]] + assert len(_generic_types_cache) == cache_size + 2 + # Nested Callables + MyGenericModel[Callable[[C], Iterable[str]]] + assert len(_generic_types_cache) == cache_size + 4 + MyGenericModel[Callable[[Simple], Iterable[int]]] + assert len(_generic_types_cache) == cache_size + 6 + MyGenericModel[Callable[[MyGenericModel[C]], Iterable[int]]] + assert len(_generic_types_cache) == cache_size + 10 + + class Model(BaseModel): + x: MyGenericModel[Callable[[C], Iterable[str]]] = Field(...) + + assert len(_generic_types_cache) == cache_size + 10 + + def test_generic_config(): data_type = TypeVar('data_type') From 62f96e7fce090d1f4a977bbe96ad4760935963c9 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 3 Nov 2022 13:59:04 +0000 Subject: [PATCH 15/38] uprev 3.11, add check job (#4662) --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55a421c6c3b..c615e616e27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-rc.1'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] env: PYTHON: ${{ matrix.python-version }} OS: ubuntu @@ -367,9 +367,32 @@ jobs: name: pypi_files path: dist + # https://github.com/marketplace/actions/alls-green#why + check: # This job does nothing and is only used for the branch protection + + if: always() + + needs: + - lint + - docs-build + - test-linux-compiled + - test-not-compiled + - test-old-mypy + - test-fastapi + + runs-on: ubuntu-latest + + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} + deploy: name: Deploy - needs: build + needs: + - check + - build if: "success() && startsWith(github.ref, 'refs/tags/')" runs-on: ubuntu-latest From 4e06d29592e4ee04c0be1160f263946d949832ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Nov 2022 12:09:49 +0000 Subject: [PATCH 16/38] Fix improper parsing of Enum document (#4736) * Fix improper parsing of Enum document (#4734) * Clean schema document for Enum * Import inspect * Make inspect import locally * Add test * bump Co-authored-by: Johnny Hsieh <32300164+mnixry@users.noreply.github.com> Co-authored-by: Samuel Colvin --- pydantic/schema.py | 4 +++- tests/test_schema.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pydantic/schema.py b/pydantic/schema.py index 1348fbd5eef..41fc8824da0 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -657,11 +657,13 @@ def enum_process_schema(enum: Type[Enum], *, field: Optional[ModelField] = None) This is similar to the `model_process_schema` function, but applies to ``Enum`` objects. """ + import inspect + schema_: Dict[str, Any] = { 'title': enum.__name__, # Python assigns all enums a default docstring value of 'An enumeration', so # all enums will have a description field even if not explicitly provided. - 'description': enum.__doc__ or 'An enumeration.', + 'description': inspect.cleandoc(enum.__doc__ or 'An enumeration.'), # Add enum values and the enum field type to the schema. 'enum': [item.value for item in cast(Iterable[Enum], enum)], } diff --git a/tests/test_schema.py b/tests/test_schema.py index 60e31776098..9942ea2f948 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -459,6 +459,34 @@ class Model(BaseModel): } +def test_enum_schema_cleandoc(): + class FooBar(str, Enum): + """ + This is docstring which needs to be cleaned up + """ + + foo = 'foo' + bar = 'bar' + + class Model(BaseModel): + enum: FooBar + + assert Model.schema() == { + 'title': 'Model', + 'type': 'object', + 'properties': {'enum': {'$ref': '#/definitions/FooBar'}}, + 'required': ['enum'], + 'definitions': { + 'FooBar': { + 'title': 'FooBar', + 'description': 'This is docstring which needs to be cleaned up', + 'enum': ['foo', 'bar'], + 'type': 'string', + } + }, + } + + def test_json_schema(): class Model(BaseModel): a = b'foobar' From 35b1358ccf77b566afe079d86b27b64a4f3da3f1 Mon Sep 17 00:00:00 2001 From: Volker Hilsenstein Date: Thu, 10 Nov 2022 17:32:49 +0100 Subject: [PATCH 17/38] Fix broken cross-references (#4743) Co-authored-by: Volker Hilsenstein --- docs/usage/models.md | 4 ++-- docs/usage/types.md | 2 +- docs/usage/validation_decorator.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/usage/models.md b/docs/usage/models.md index bf5d765bb34..24a72441420 100644 --- a/docs/usage/models.md +++ b/docs/usage/models.md @@ -435,7 +435,7 @@ as the value: {!.tmp_examples/models_required_fields.md!} -Where `Field` refers to the [field function](schema.md#field-customisation). +Where `Field` refers to the [field function](schema.md#field-customization). Here `a`, `b` and `c` are all required. However, use of the ellipses in `b` will not work well with [mypy](mypy.md), and as of **v1.0** should be avoided in most cases. @@ -471,7 +471,7 @@ Example of usage: {!.tmp_examples/models_default_factory.md!} -Where `Field` refers to the [field function](schema.md#field-customisation). +Where `Field` refers to the [field function](schema.md#field-customization). !!! warning The `default_factory` expects the field type to be set. diff --git a/docs/usage/types.md b/docs/usage/types.md index b436d63d9a2..5c9f3e8e141 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -781,7 +781,7 @@ The value of numerous common types can be restricted using `con*` type functions {!.tmp_examples/types_constrained.md!} -Where `Field` refers to the [field function](schema.md#field-customisation). +Where `Field` refers to the [field function](schema.md#field-customization). ### Arguments to `conlist` The following arguments are available when using the `conlist` type function diff --git a/docs/usage/validation_decorator.md b/docs/usage/validation_decorator.md index 02535b0889d..d0dc1474b73 100644 --- a/docs/usage/validation_decorator.md +++ b/docs/usage/validation_decorator.md @@ -48,7 +48,7 @@ To demonstrate all the above parameter types: ## Using Field to describe function arguments -[Field](schema.md#field-customisation) can also be used with `validate_arguments` to provide extra information about +[Field](schema.md#field-customization) can also be used with `validate_arguments` to provide extra information about the field and validations. In general it should be used in a type hint with [Annotated](schema.md#typingannotated-fields), unless `default_factory` is specified, in which case it should be used as the default value of the field: From 3088d337cf45ff63ba22e71fc2db2b4feaaa9650 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:46:12 +0100 Subject: [PATCH 18/38] Renaming BaseModel (#4766) * Renaming BaseModel (#4758) Hello BaseModel is an import from pydantic. Using that same name for the Base from sqlachemy can be misleading, and not aligned to the other examples. I therefore suggest to use the same naming that the other example. Pierre * bump Co-authored-by: Pierre Bonneau Co-authored-by: Samuel Colvin --- docs/examples/models_orm_mode_reserved_name.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/models_orm_mode_reserved_name.py b/docs/examples/models_orm_mode_reserved_name.py index 4ebe1e83c3d..76a67dff240 100644 --- a/docs/examples/models_orm_mode_reserved_name.py +++ b/docs/examples/models_orm_mode_reserved_name.py @@ -12,10 +12,10 @@ class Config: orm_mode = True -BaseModel = declarative_base() +Base = declarative_base() -class SQLModel(BaseModel): +class SQLModel(Base): __tablename__ = 'my_table' id = sa.Column('id', sa.Integer, primary_key=True) # 'metadata' is reserved by SQLAlchemy, hence the '_' From 82f2148e99e8dd61f4ac42066b801e9de3be039b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:12:33 +0000 Subject: [PATCH 19/38] fix typo in docs (#4765) * fix typo in docs (#4764) * bump * bump Co-authored-by: Irfanuddin Shafi Ahmed Co-authored-by: Samuel Colvin --- docs/usage/types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/types.md b/docs/usage/types.md index 5c9f3e8e141..9d6a89ce49f 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -607,9 +607,9 @@ For URI/URL validation the following types are available: - `cockroachdb+asyncpg` - `cockroachdb+psycopg2` - `AmqpDsn`: schema `amqp` or `amqps`, user info not required, TLD not required, host not required -- `RedisDsn`: scheme `redis` or `rediss`, user info not required, tld not required, host not required (CHANGED: user info +- `RedisDsn`: scheme `redis` or `rediss`, user info not required, tld not required, host not required (CHANGED: user info) (e.g., `rediss://:pass@localhost`) - `MongoDsn` : scheme `mongodb`, user info not required, database name not required, port - not required from **v1.6** onwards), user info may be passed without user part (e.g., `rediss://:pass@localhost`) + not required from **v1.6** onwards), user info may be passed without user part (e.g., `mongodb://mongodb0.example.com:27017`) - `stricturl`: method with the following keyword arguments: - `strip_whitespace: bool = True` - `min_length: int = 1` From 3aedaf34d3cabe24c8f75b18d5ca2923d0f4bc46 Mon Sep 17 00:00:00 2001 From: kgolawski Date: Tue, 22 Nov 2022 11:54:48 +0100 Subject: [PATCH 20/38] Fix code typo in docs (#4775) --- docs/examples/postponed_annotations_broken.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/postponed_annotations_broken.py b/docs/examples/postponed_annotations_broken.py index 9dec4f5553b..e62470cc6da 100644 --- a/docs/examples/postponed_annotations_broken.py +++ b/docs/examples/postponed_annotations_broken.py @@ -4,7 +4,7 @@ def this_is_broken(): - from pydantic import HttpUrl # HttpUrl is defined in functuon local scope + from pydantic import HttpUrl # HttpUrl is defined in function local scope class Model(BaseModel): a: HttpUrl From fab44bed46bca5205fd259b408a574a7ed4ff053 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 22 Nov 2022 22:27:51 +0000 Subject: [PATCH 21/38] Add Jina AI to sponsors (#4767) * add Jina AI to sponsors * add change description --- changes/4767-samuelcolvin.md | 1 + docs/extra/tweaks.css | 2 +- docs/index.md | 6 ++++++ docs/sponsor_logos/jina-ai.png | Bin 0 -> 10412 bytes 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changes/4767-samuelcolvin.md create mode 100644 docs/sponsor_logos/jina-ai.png diff --git a/changes/4767-samuelcolvin.md b/changes/4767-samuelcolvin.md new file mode 100644 index 00000000000..85f14a670f1 --- /dev/null +++ b/changes/4767-samuelcolvin.md @@ -0,0 +1 @@ +Add Jina AI to sponsors on docs index page. diff --git a/docs/extra/tweaks.css b/docs/extra/tweaks.css index 374129d04fa..7a615a3bc28 100644 --- a/docs/extra/tweaks.css +++ b/docs/extra/tweaks.css @@ -24,7 +24,7 @@ .sponsors > div { text-align: center; - width: 25%; + width: 33%; padding-bottom: 20px; } diff --git a/docs/index.md b/docs/index.md index d526cea1954..949eb434d1b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -66,6 +66,12 @@ Development of *pydantic* is made possible by the following sponsors: SendCloud + And many more who kindly sponsor Samuel Colvin on [GitHub Sponsors](https://github.com/sponsors/samuelcolvin#sponsors). diff --git a/docs/sponsor_logos/jina-ai.png b/docs/sponsor_logos/jina-ai.png new file mode 100644 index 0000000000000000000000000000000000000000..39242cbb2e04111962a3a0f0c3a5cbec5251089e GIT binary patch literal 10412 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 literal 0 HcmV?d00001 From c326c782fa9b1ff0dca8446e9165237089cb3423 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 23 Nov 2022 09:29:48 +0000 Subject: [PATCH 22/38] bust CI cache, fix docs-build --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c615e616e27..e0a3dd1e6a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: with: path: ${{ env.pythonLocation }} key: > - docs-build + docs-build-v2 ${{ runner.os }} ${{ env.pythonLocation }} ${{ hashFiles('setup.py') }} From 78ca710fe769552d91c12adf972a9c88a2a1a90f Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Fri, 25 Nov 2022 11:44:13 +0000 Subject: [PATCH 23/38] bust CI test cache (#4786) * bust CI test cache * bust ci lint cache --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0a3dd1e6a2..1269b271281 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: ${{ env.pythonLocation }} .mypy_cache key: > - lint + lint-v2 ${{ runner.os }} ${{ env.pythonLocation }} ${{ hashFiles('tests/requirements-linting.txt') }} @@ -122,7 +122,7 @@ jobs: with: path: ${{ env.pythonLocation }} key: > - test-linux-compiled + test-linux-compiled-v2 ${{ runner.os }} ${{ env.pythonLocation }} ${{ hashFiles('setup.py') }} @@ -183,7 +183,7 @@ jobs: with: path: ${{ env.pythonLocation }} key: > - test-not-compiled + test-not-compiled-v2 ${{ runner.os }} ${{ env.pythonLocation }} ${{ hashFiles('setup.py') }} From 18359d79853f57a5a892cace2e4e286dd159a300 Mon Sep 17 00:00:00 2001 From: Arseny Boykov <36469655+Bobronium@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:13:50 +0400 Subject: [PATCH 24/38] [1.10.x] Properly encode model and dataclass default for schema (#4781) * Properly encode model and dataclass default for schema * Wrap type annotation in quotes * Fix compatibility * Add changes file * Remove unnecessary import Co-authored-by: Hasan Ramezani * Add a blank line back * Reorder conditions As per suggestion in https://github.com/pydantic/pydantic/pull/4781#discussion_r1034709528. Co-authored-by: Hasan Ramezani --- changes/4781-Bobronium.md | 1 + pydantic/schema.py | 12 +++++++++--- tests/test_schema.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 changes/4781-Bobronium.md diff --git a/changes/4781-Bobronium.md b/changes/4781-Bobronium.md new file mode 100644 index 00000000000..26d1221db96 --- /dev/null +++ b/changes/4781-Bobronium.md @@ -0,0 +1 @@ +Fix `schema` and `schema_json` on models where a model instance is a one of default values. diff --git a/pydantic/schema.py b/pydantic/schema.py index 41fc8824da0..727700f70af 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -1,6 +1,7 @@ import re import warnings from collections import defaultdict +from dataclasses import is_dataclass from datetime import date, datetime, time, timedelta from decimal import Decimal from enum import Enum @@ -971,7 +972,14 @@ def multitypes_literal_field_for_schema(values: Tuple[Any, ...], field: ModelFie def encode_default(dft: Any) -> Any: - if isinstance(dft, Enum): + from .main import BaseModel + + if isinstance(dft, BaseModel) or is_dataclass(dft): + dft = cast('dict[str, Any]', pydantic_encoder(dft)) + + if isinstance(dft, dict): + return {encode_default(k): encode_default(v) for k, v in dft.items()} + elif isinstance(dft, Enum): return dft.value elif isinstance(dft, (int, float, str)): return dft @@ -979,8 +987,6 @@ def encode_default(dft: Any) -> Any: t = dft.__class__ seq_args = (encode_default(v) for v in dft) return t(*seq_args) if is_namedtuple(t) else t(seq_args) - elif isinstance(dft, dict): - return {encode_default(k): encode_default(v) for k, v in dft.items()} elif dft is None: return None else: diff --git a/tests/test_schema.py b/tests/test_schema.py index 9942ea2f948..6b070f98ed6 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1523,6 +1523,38 @@ class UserModel(BaseModel): } +def test_model_default(): + """Make sure inner model types are encoded properly""" + + class Inner(BaseModel): + a: Dict[Path, str] = {Path(): ''} + + class Outer(BaseModel): + inner: Inner = Inner() + + assert Outer.schema() == { + 'definitions': { + 'Inner': { + 'properties': { + 'a': { + 'additionalProperties': {'type': 'string'}, + 'default': {'.': ''}, + 'title': 'A', + 'type': 'object', + } + }, + 'title': 'Inner', + 'type': 'object', + } + }, + 'properties': { + 'inner': {'allOf': [{'$ref': '#/definitions/Inner'}], 'default': {'a': {'.': ''}}, 'title': 'Inner'} + }, + 'title': 'Outer', + 'type': 'object', + } + + @pytest.mark.parametrize( 'kwargs,type_,expected_extra', [ From 201e1435337e1c579c8fdf2d8bcfcde92be142ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:46:41 +0100 Subject: [PATCH 25/38] Remove trailing "```" (#4799) (#4800) Co-authored-by: Marcelo Trylesinski --- docs/usage/schema.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/usage/schema.md b/docs/usage/schema.md index 57b22fa1ef1..061d2dbddfe 100644 --- a/docs/usage/schema.md +++ b/docs/usage/schema.md @@ -190,7 +190,3 @@ The callable is expected to mutate the schema dictionary *in-place*; the return For example, the `title` key can be removed from the model's `properties`: {!.tmp_examples/schema_extra_callable.md!} - - - -``` From a801b0da56519a50084755a9580b6507f981ec76 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Tue, 6 Dec 2022 14:12:38 +0000 Subject: [PATCH 26/38] Moving docs to `docs.pydantic.dev` (#4818) * moving docs to docs.pydantic.dev * use the wrangler gh action * :face-palm: * :facepalm: disable tag check * uncomment ci checks * set branch for cf pages publish --- .github/workflows/ci.yml | 7 +++++-- HISTORY.md | 32 ++++++++++++++++---------------- Makefile | 6 ------ README.md | 6 +++--- docs/blog/pydantic-v2.md | 4 ++-- mkdocs.yml | 2 +- pydantic/_hypothesis_plugin.py | 2 +- pydantic/schema.py | 2 +- tests/test_networks.py | 4 ++-- 9 files changed, 31 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1269b271281..98e6d5a7eb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -436,6 +436,9 @@ jobs: - name: publish docs if: '!fromJSON(steps.check-tag.outputs.IS_PRERELEASE)' - run: make publish-docs + uses: cloudflare/wrangler-action@2.0.0 + with: + apiToken: ${{ secrets.cloudflare_api_token }} + command: pages publish --project-name=pydantic-docs --branch=main site env: - NETLIFY: ${{ secrets.netlify_token }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.cloudflare_account_id }} diff --git a/HISTORY.md b/HISTORY.md index 48b91929dae..13a66fde09d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -159,10 +159,10 @@ for their kind support. ### Highlights * add Python 3.10 support, #2885 by @PrettyWood -* [Discriminated unions](https://pydantic-docs.helpmanual.io/usage/types/#discriminated-unions-aka-tagged-unions), #619 by @PrettyWood -* [`Config.smart_union` for better union logic](https://pydantic-docs.helpmanual.io/usage/model_config/#smart-union), #2092 by @PrettyWood +* [Discriminated unions](https://docs.pydantic.dev/usage/types/#discriminated-unions-aka-tagged-unions), #619 by @PrettyWood +* [`Config.smart_union` for better union logic](https://docs.pydantic.dev/usage/model_config/#smart-union), #2092 by @PrettyWood * Binaries for Macos M1 CPUs, #3498 by @samuelcolvin -* Complex types can be set via [nested environment variables](https://pydantic-docs.helpmanual.io/usage/settings/#parsing-environment-variable-values), e.g. `foo___bar`, #3159 by @Air-Mark +* Complex types can be set via [nested environment variables](https://docs.pydantic.dev/usage/settings/#parsing-environment-variable-values), e.g. `foo___bar`, #3159 by @Air-Mark * add a dark mode to _pydantic_ documentation, #2913 by @gbdlin * Add support for autocomplete in VS Code via `__dataclass_transform__`, #2721 by @tiangolo * Add "exclude" as a field parameter so that it can be configured using model config, #660 by @daviskirk @@ -193,7 +193,7 @@ for their kind support. `pydantic.fields.ModelField`) to `__modify_schema__()` if present, #3434 by @jasujm * Fix issue when pydantic fail to parse `typing.ClassVar` string type annotation, #3401 by @uriyyo * Mention Python >= 3.9.2 as an alternative to `typing_extensions.TypedDict`, #3374 by @BvB93 -* Changed the validator method name in the [Custom Errors example](https://pydantic-docs.helpmanual.io/usage/models/#custom-errors) +* Changed the validator method name in the [Custom Errors example](https://docs.pydantic.dev/usage/models/#custom-errors) to more accurately describe what the validator is doing; changed from `name_must_contain_space` to ` value_must_equal_bar`, #3327 by @michaelrios28 * Add `AmqpDsn` class, #3254 by @kludex * Always use `Enum` value as default in generated JSON schema, #3190 by @joaommartins @@ -215,7 +215,7 @@ for their kind support. just as when sourced from environment variables, #2917 by @davidmreed * add a dark mode to _pydantic_ documentation, #2913 by @gbdlin * Make `pydantic-mypy` plugin compatible with `pyproject.toml` configuration, consistent with `mypy` changes. - See the [doc](https://pydantic-docs.helpmanual.io/mypy_plugin/#configuring-the-plugin) for more information, #2908 by @jrwalk + See the [doc](https://docs.pydantic.dev/mypy_plugin/#configuring-the-plugin) for more information, #2908 by @jrwalk * add Python 3.10 support, #2885 by @PrettyWood * Correctly parse generic models with `Json[T]`, #2860 by @geekingfrog * Update contrib docs re: Python version to use for building docs, #2856 by @paxcodes @@ -260,7 +260,7 @@ for their kind support. * Support generating schema for `Generic` fields with subtypes, #2375 by @maximberg * fix(encoder): serialize `NameEmail` to str, #2341 by @alecgerona * add `Config.smart_union` to prevent coercion in `Union` if possible, see - [the doc](https://pydantic-docs.helpmanual.io/usage/model_config/#smart-union) for more information, #2092 by @PrettyWood + [the doc](https://docs.pydantic.dev/usage/model_config/#smart-union) for more information, #2092 by @PrettyWood * Add ability to use `typing.Counter` as a model field type, #2060 by @uriyyo * Add parameterised subclasses to `__bases__` when constructing new parameterised classes, so that `A <: B => A[int] <: B[int]`, #2007 by @diabolo-dan * Create `FileUrl` type that allows URLs that conform to [RFC 8089](https://tools.ietf.org/html/rfc8089#section-2). @@ -310,10 +310,10 @@ for their kind support. ### Highlights -* [Hypothesis plugin](https://pydantic-docs.helpmanual.io/hypothesis_plugin/) for testing, #2097 by @Zac-HD -* support for [`NamedTuple` and `TypedDict`](https://pydantic-docs.helpmanual.io/usage/types/#annotated-types), #2216 by @PrettyWood -* Support [`Annotated` hints on model fields](https://pydantic-docs.helpmanual.io/usage/schema/#typingannotated-fields), #2147 by @JacobHayes -* [`frozen` parameter on `Config`](https://pydantic-docs.helpmanual.io/usage/model_config/) to allow models to be hashed, #1880 by @rhuille +* [Hypothesis plugin](https://docs.pydantic.dev/hypothesis_plugin/) for testing, #2097 by @Zac-HD +* support for [`NamedTuple` and `TypedDict`](https://docs.pydantic.dev/usage/types/#annotated-types), #2216 by @PrettyWood +* Support [`Annotated` hints on model fields](https://docs.pydantic.dev/usage/schema/#typingannotated-fields), #2147 by @JacobHayes +* [`frozen` parameter on `Config`](https://docs.pydantic.dev/usage/model_config/) to allow models to be hashed, #1880 by @rhuille ### Changes @@ -370,7 +370,7 @@ for their kind support. to validate parameters without actually calling the function, #2127 by @PrettyWood * Add the ability to customize settings sources (add / disable / change priority order), #2107 by @kozlek * Fix mypy complaints about most custom _pydantic_ types, #2098 by @PrettyWood -* Add a [Hypothesis](https://hypothesis.readthedocs.io/) plugin for easier [property-based testing](https://increment.com/testing/in-praise-of-property-based-testing/) with Pydantic's custom types - [usage details here](https://pydantic-docs.helpmanual.io/hypothesis_plugin/), #2097 by @Zac-HD +* Add a [Hypothesis](https://hypothesis.readthedocs.io/) plugin for easier [property-based testing](https://increment.com/testing/in-praise-of-property-based-testing/) with Pydantic's custom types - [usage details here](https://docs.pydantic.dev/hypothesis_plugin/), #2097 by @Zac-HD * add validator for `None`, `NoneType` or `Literal[None]`, #2095 by @PrettyWood * Handle properly fields of type `Callable` with a default value, #2094 by @PrettyWood * Updated `create_model` return type annotation to return type which inherits from `__base__` argument, #2071 by @uriyyo @@ -440,9 +440,9 @@ for their kind support. ### Highlights * Python 3.9 support, thanks @PrettyWood -* [Private model attributes](https://pydantic-docs.helpmanual.io/usage/models/#private-model-attributes), thanks @Bobronium -* ["secrets files" support in `BaseSettings`](https://pydantic-docs.helpmanual.io/usage/settings/#secret-support), thanks @mdgilene -* [convert stdlib dataclasses to pydantic dataclasses and use stdlib dataclasses in models](https://pydantic-docs.helpmanual.io/usage/dataclasses/#stdlib-dataclasses-and-pydantic-dataclasses), thanks @PrettyWood +* [Private model attributes](https://docs.pydantic.dev/usage/models/#private-model-attributes), thanks @Bobronium +* ["secrets files" support in `BaseSettings`](https://docs.pydantic.dev/usage/settings/#secret-support), thanks @mdgilene +* [convert stdlib dataclasses to pydantic dataclasses and use stdlib dataclasses in models](https://docs.pydantic.dev/usage/dataclasses/#stdlib-dataclasses-and-pydantic-dataclasses), thanks @PrettyWood ### Changes @@ -585,7 +585,7 @@ Thank you to pydantic's sponsors: @matin, @tiangolo, @chdsbd, @jorgecarleitao, a * **Breaking Change:** alias precedence logic changed so aliases on a field always take priority over an alias from `alias_generator` to avoid buggy/unexpected behaviour, - see [here](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-precedence) for details, #1178 by @samuelcolvin + see [here](https://docs.pydantic.dev/usage/model_config/#alias-precedence) for details, #1178 by @samuelcolvin * Add support for unicode and punycode in TLDs, #1182 by @jamescurtin * Fix `cls` argument in validators during assignment, #1172 by @samuelcolvin * completing Luhn algorithm for `PaymentCardNumber`, #1166 by @cuencandres @@ -627,7 +627,7 @@ Thank you to pydantic's sponsors: @matin, @tiangolo, @chdsbd, @jorgecarleitao, a * **Possible Breaking Change:** Add support for required `Optional` with `name: Optional[AnyType] = Field(...)` and refactor `ModelField` creation to preserve `required` parameter value, #1031 by @tiangolo; - see [here](https://pydantic-docs.helpmanual.io/usage/models/#required-optional-fields) for details + see [here](https://docs.pydantic.dev/usage/models/#required-optional-fields) for details * Add benchmarks for `cattrs`, #513 by @sebastianmika * Add `exclude_none` option to `dict()` and friends, #587 by @niknetniko * Add benchmarks for `valideer`, #670 by @gsakkis diff --git a/Makefile b/Makefile index 5b3dc1e4d17..7ec83515876 100644 --- a/Makefile +++ b/Makefile @@ -124,9 +124,3 @@ docs: docs-serve: python docs/build/main.py mkdocs serve - -.PHONY: publish-docs -publish-docs: - zip -r site.zip site - @curl -H "Content-Type: application/zip" -H "Authorization: Bearer ${NETLIFY}" \ - --data-binary "@site.zip" https://api.netlify.com/api/v1/sites/pydantic-docs.netlify.com/deploys diff --git a/README.md b/README.md index ab27e4d51da..b645489ee8a 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ Define how data should be in pure, canonical Python 3.7+; validate it with *pyda ## Help -See [documentation](https://pydantic-docs.helpmanual.io/) for more details. +See [documentation](https://docs.pydantic.dev/) for more details. ## Installation Install using `pip install -U pydantic` or `conda install pydantic -c conda-forge`. For more installation options to make *pydantic* even faster, -see the [Install](https://pydantic-docs.helpmanual.io/install/) section in the documentation. +see the [Install](https://docs.pydantic.dev/install/) section in the documentation. ## A Simple Example @@ -48,7 +48,7 @@ print(user.id) For guidance on setting up a development environment and how to make a contribution to *pydantic*, see -[Contributing to Pydantic](https://pydantic-docs.helpmanual.io/contributing/). +[Contributing to Pydantic](https://docs.pydantic.dev/contributing/). ## Reporting a Security Vulnerability diff --git a/docs/blog/pydantic-v2.md b/docs/blog/pydantic-v2.md index c1fd3b74692..24f12df9de6 100644 --- a/docs/blog/pydantic-v2.md +++ b/docs/blog/pydantic-v2.md @@ -639,7 +639,7 @@ The word "parse" will no longer be used except when talking about JSON parsing, Since the core structure of validators has changed from "a list of validators to call one after another" to "a tree of validators which call each other", the -[`__get_validators__`](https://pydantic-docs.helpmanual.io/usage/types/#classes-with-__get_validators__) +[`__get_validators__`](https://docs.pydantic.dev/usage/types/#classes-with-__get_validators__) way of defining custom field types no longer makes sense. Instead, we'll look for the attribute `__pydantic_validation_schema__` which must be a @@ -745,7 +745,7 @@ The emoji here is just for variation, I'm not frowning about any of this, these specific need to keep the type, you can use wrap validators or custom type validation as described above 6. integers are represented in rust code as `i64`, meaning if you want to use ints where `abs(v) > 2^63 − 1` (9,223,372,036,854,775,807), you'll need to use a [wrap validator](#validator-function-improvements) and your own logic -7. [Settings Management](https://pydantic-docs.helpmanual.io/usage/settings/) ??? - I definitely don't want to +7. [Settings Management](https://docs.pydantic.dev/usage/settings/) ??? - I definitely don't want to remove the functionality, but it's something of a historical curiosity that it lives within pydantic, perhaps it should move to a separate package, perhaps installable alongside pydantic with `pip install pydantic[settings]`? diff --git a/mkdocs.yml b/mkdocs.yml index 38e9749a922..0fd68a5986c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ site_name: pydantic site_description: Data validation and settings management using Python type hints strict: true -site_url: https://pydantic-docs.helpmanual.io/ +site_url: https://docs.pydantic.dev/ theme: name: 'material' diff --git a/pydantic/_hypothesis_plugin.py b/pydantic/_hypothesis_plugin.py index a56d2b98df8..d175d207f2f 100644 --- a/pydantic/_hypothesis_plugin.py +++ b/pydantic/_hypothesis_plugin.py @@ -10,7 +10,7 @@ https://hypothesis.readthedocs.io/en/latest/strategies.html#registering-strategies-via-setuptools-entry-points https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.register_type_strategy https://hypothesis.readthedocs.io/en/latest/strategies.html#interaction-with-pytest-cov -https://pydantic-docs.helpmanual.io/usage/types/#pydantic-types +https://docs.pydantic.dev/usage/types/#pydantic-types Note that because our motivation is to *improve user experience*, the strategies are always sound (never generate invalid data) but sacrifice completeness for diff --git a/pydantic/schema.py b/pydantic/schema.py index 727700f70af..a73916458df 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -1019,7 +1019,7 @@ def get_annotation_from_field_info( raise ValueError( f'On field "{field_name}" the following field constraints are set but not enforced: ' f'{", ".join(unused_constraints)}. ' - f'\nFor more details see https://pydantic-docs.helpmanual.io/usage/schema/#unenforced-field-constraints' + f'\nFor more details see https://docs.pydantic.dev/usage/schema/#unenforced-field-constraints' ) return annotation diff --git a/tests/test_networks.py b/tests/test_networks.py index 4d8c5a3ff64..eb717f96d21 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -263,9 +263,9 @@ def test_at_in_path(): def test_fragment_without_query(): - url = validate_url('https://pydantic-docs.helpmanual.io/usage/types/#constrained-types') + url = validate_url('https://docs.pydantic.dev/usage/types/#constrained-types') assert url.scheme == 'https' - assert url.host == 'pydantic-docs.helpmanual.io' + assert url.host == 'docs.pydantic.dev' assert url.path == '/usage/types/' assert url.query is None assert url.fragment == 'constrained-types' From 952bc51afb607f075fea1a8c239b3659b5aca89e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:33:00 +0000 Subject: [PATCH 27/38] Add newline before `Outputs:` when printing json output in docs (#4802) (#4848) Co-authored-by: Hasan Ramezani --- docs/build/exec_examples.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/build/exec_examples.py b/docs/build/exec_examples.py index 34a4a4f7f1b..648a6268045 100755 --- a/docs/build/exec_examples.py +++ b/docs/build/exec_examples.py @@ -40,11 +40,12 @@ ``` """.strip() JSON_OUTPUT_MD_TMPL = """ + Outputs: ```json {output} ``` -""".strip() +""" def to_string(value: Any) -> str: From 736a80d6a574fe55110deaea2b4d0c27e906e6e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:33:23 +0000 Subject: [PATCH 28/38] Fix typo in types usage documentation. (#4825) (#4849) Co-authored-by: Hasan Ramezani --- docs/usage/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/types.md b/docs/usage/types.md index 9d6a89ce49f..ef474e2b850 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -490,7 +490,7 @@ With proper ordering in an annotated `Union`, you can use this to parse types of `CockroachDsn` : a cockroachdb DSN style URL; see [URLs](#urls) -`RabbitMqDsn` +`AmqpDsn` : an `AMQP` DSN style URL as used by RabbitMQ, StormMQ, ActiveMQ etc.; see [URLs](#urls) `RedisDsn` From 6a27124c15e25b99a9cc9d8ba5f6a691673a595d Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Wed, 21 Dec 2022 15:45:13 +0330 Subject: [PATCH 29/38] Replace deprecated `get_event_loop` with `asyncio.run` (#4859) (#4860) --- tests/test_decorator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 686f253c78c..b6b7bb492d1 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -266,10 +266,9 @@ async def run(): v = await foo(1, 2) assert v == 'a=1 b=2' - loop = asyncio.get_event_loop_policy().get_event_loop() - loop.run_until_complete(run()) + asyncio.run(run()) with pytest.raises(ValidationError) as exc_info: - loop.run_until_complete(foo('x')) + asyncio.run(foo('x')) assert exc_info.value.errors() == [{'loc': ('b',), 'msg': 'field required', 'type': 'value_error.missing'}] From dc33b473516eb04cb1544686e0240ff2681c87a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Dec 2022 12:21:18 +0000 Subject: [PATCH 30/38] fixed documentation typo (#4855) (#4858) Co-authored-by: Muhammad Abdur Rakib <103581704+rifatrakib@users.noreply.github.com> --- docs/usage/model_config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/model_config.md b/docs/usage/model_config.md index 7e0efd5d5d5..d149385acde 100644 --- a/docs/usage/model_config.md +++ b/docs/usage/model_config.md @@ -127,7 +127,7 @@ with the following means (see [#4093](https://github.com/pydantic/pydantic/pull/ or after (value `'after_validation'`) parsing and validation when they are [converted](dataclasses.md#stdlib-dataclasses-and-_pydantic_-dataclasses). **`allow_inf_nan`** -: whether to allows infinity (`+inf` an `-inf`) and NaN values to float fields, defaults to `True`, +: whether to allow infinity (`+inf` an `-inf`) and NaN values to float fields, defaults to `True`, set to `False` for compatibility with `JSON`, see [#3994](https://github.com/pydantic/pydantic/pull/3994) for more details, added in **V1.10** From 4f13260cd540061f9500ede8956aa76670ee806c Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 21 Dec 2022 14:26:17 +0000 Subject: [PATCH 31/38] Reduce binary sizes (#4862) * inspect assets sizes * add -g0 to CFLAGS * don't wait to build, add build to check --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++-- changes/2276-samuelcolvin.md | 1 + setup.py | 4 ++-- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 changes/2276-samuelcolvin.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98e6d5a7eb7..00cc0b5c6cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -308,8 +308,6 @@ jobs: build: name: build py3.${{ matrix.python-version }} on ${{ matrix.platform || matrix.os }} - needs: [lint, test-linux-compiled, test-not-compiled, test-old-mypy, test-fastapi] - if: "success() && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main')" strategy: fail-fast: false matrix: @@ -379,6 +377,7 @@ jobs: - test-not-compiled - test-old-mypy - test-fastapi + - build runs-on: ubuntu-latest @@ -388,6 +387,35 @@ jobs: with: jobs: ${{ toJSON(needs) }} + inspect-pypi-assets: + needs: [build] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: get dist artifacts + uses: actions/download-artifact@v3 + with: + name: pypi_files + path: dist + + - name: list dist files + run: | + ls -lh dist/ + echo "`ls dist | wc -l` files" + + - name: extract and list sdist file + run: | + mkdir sdist-files + tar -xvf dist/*.tar.gz -C sdist-files + tree -a sdist-files + + - name: extract and list wheel file + run: | + ls dist/*cp310-manylinux*x86_64.whl | head -n 1 + python -m zipfile --list `ls dist/*cp310-manylinux*x86_64.whl | head -n 1` + deploy: name: Deploy needs: diff --git a/changes/2276-samuelcolvin.md b/changes/2276-samuelcolvin.md new file mode 100644 index 00000000000..ad2bbd41b03 --- /dev/null +++ b/changes/2276-samuelcolvin.md @@ -0,0 +1 @@ +Reduce the size of binary wheels. diff --git a/setup.py b/setup.py index d677cd7765a..81cf6d545bc 100644 --- a/setup.py +++ b/setup.py @@ -82,9 +82,9 @@ def extra(self): compiler_directives = {} if 'CYTHON_TRACE' in sys.argv: compiler_directives['linetrace'] = True - # Set CFLAG to all optimizations (-O3) + # Set CFLAG to all optimizations (-O3), add `-g0` to reduce size of binaries, see #2276 # Any additional CFLAGS will be appended. Only the last optimization flag will have effect - os.environ['CFLAGS'] = '-O3 ' + os.environ.get('CFLAGS', '') + os.environ['CFLAGS'] = '-O3 -g0 ' + os.environ.get('CFLAGS', '') ext_modules = cythonize( 'pydantic/*.py', exclude=['pydantic/generics.py'], From 1a1497e298d1a2a5c859cc0cf0887cdd3ee8791c Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 22 Dec 2022 12:03:08 +0000 Subject: [PATCH 32/38] fix history links (#4866) --- docs/build/main.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build/main.py b/docs/build/main.py index f55237867d0..296d1e24b07 100755 --- a/docs/build/main.py +++ b/docs/build/main.py @@ -10,7 +10,7 @@ def main() -> int: history = (PROJECT_ROOT / 'HISTORY.md').read_text() - history = re.sub(r'#(\d+)', r'[#\1](https://github.com/pydantic/pydantic/issues/\1)', history) + history = re.sub(r'(\s)#(\d+)', r'\1[#\2](https://github.com/pydantic/pydantic/issues/\2)', history) history = re.sub(r'(\s)@([\w\-]+)', r'\1[@\2](https://github.com/\2)', history, flags=re.I) history = re.sub('@@', '@', history) diff --git a/setup.py b/setup.py index 81cf6d545bc..609cd1bbe10 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ def extra(self): THIS_DIR = Path(__file__).resolve().parent try: history = (THIS_DIR / 'HISTORY.md').read_text(encoding='utf-8') - history = re.sub(r'#(\d+)', r'[#\1](https://github.com/pydantic/pydantic/issues/\1)', history) + history = re.sub(r'(\s)#(\d+)', r'\1[#\2](https://github.com/pydantic/pydantic/issues/\2)', history) history = re.sub(r'( +)@([\w\-]+)', r'\1[@\2](https://github.com/\2)', history, flags=re.I) history = re.sub('@@', '@', history) From 18a91e389f5af42d6cdc08ac82ea8436541a2eb7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 08:44:38 +0000 Subject: [PATCH 33/38] Fixed datetime format specifier in docs (#4876) (#4879) Co-authored-by: gr8jam <23422130+gr8jam@users.noreply.github.com> --- docs/usage/types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/types.md b/docs/usage/types.md index ef474e2b850..0b6d06c957d 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -310,7 +310,7 @@ types: * `int` or `float`, assumed as Unix time, i.e. seconds (if >= `-2e10` or <= `2e10`) or milliseconds (if < `-2e10`or > `2e10`) since 1 January 1970 * `str`, following formats work: - * `YYYY-MM-DD[T]HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]]]` + * `YYYY-MM-DD[T]HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]` * `int` or `float` as a string (assumed as Unix time) * `date` fields can be: @@ -327,7 +327,7 @@ types: * `time`, existing `time` object * `str`, following formats work: - * `HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]]]` + * `HH:MM[:SS[.ffffff]][Z or [±]HH[:]MM]` * `timedelta` fields can be: From c04923ca97b511c1a961cb018e0a5df3dd501ea2 Mon Sep 17 00:00:00 2001 From: Eric Jolibois Date: Wed, 28 Dec 2022 18:13:46 +0100 Subject: [PATCH 34/38] fix: support assignment on DataclassProxy (#4880) --- changes/4695-PrettyWood.md | 1 + pydantic/dataclasses.py | 3 +++ tests/test_dataclasses.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 changes/4695-PrettyWood.md diff --git a/changes/4695-PrettyWood.md b/changes/4695-PrettyWood.md new file mode 100644 index 00000000000..c26b609d570 --- /dev/null +++ b/changes/4695-PrettyWood.md @@ -0,0 +1 @@ +fix: support assignment on `DataclassProxy` diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py index 9a72533bd76..00e413a6666 100644 --- a/pydantic/dataclasses.py +++ b/pydantic/dataclasses.py @@ -254,6 +254,9 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any: def __getattr__(self, name: str) -> Any: return getattr(self.__dataclass__, name) + def __setattr__(self, __name: str, __value: Any) -> None: + return setattr(self.__dataclass__, __name, __value) + def __instancecheck__(self, instance: Any) -> bool: return isinstance(instance, self.__dataclass__) diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index e2027476bc0..9d7d55595ae 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -1472,3 +1472,19 @@ def __post_init__(self): self.a *= 3 assert C().a == 6 # 1 * 3 + 3 + + +def test_dataclass_setattr(): + class Foo: + bar: str = 'cat' + + default_config = dataclasses.make_dataclass( + cls_name=Foo.__name__, + bases=(dataclasses.dataclass(Foo),), + fields=[('bar', ClassVar[str], dataclasses.field(default=Foo.bar))], + ) + + config = pydantic.dataclasses.dataclass(default_config) + assert config.bar == 'cat' + setattr(config, 'bar', 'dog') + assert config.bar == 'dog' From a825106c2c5f2599643e1ad6d4caa1bfc6e1e999 Mon Sep 17 00:00:00 2001 From: Eric Jolibois Date: Thu, 29 Dec 2022 11:18:00 +0100 Subject: [PATCH 35/38] fix: avoid multiple calls of `__post_init__` when dataclasses are inherited (#4493) --- changes/4487-PrettyWood.md | 1 + pydantic/dataclasses.py | 5 ++++- tests/test_dataclasses.py | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changes/4487-PrettyWood.md diff --git a/changes/4487-PrettyWood.md b/changes/4487-PrettyWood.md new file mode 100644 index 00000000000..8f18dd0c50c --- /dev/null +++ b/changes/4487-PrettyWood.md @@ -0,0 +1 @@ +fix: avoid multiple calls of `__post_init__` when dataclasses are inherited diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py index 00e413a6666..1856a1203c5 100644 --- a/pydantic/dataclasses.py +++ b/pydantic/dataclasses.py @@ -288,7 +288,10 @@ def handle_extra_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: init(self, *args, **kwargs) if hasattr(dc_cls, '__post_init__'): - post_init = dc_cls.__post_init__ + try: + post_init = dc_cls.__post_init__.__wrapped__ # type: ignore[attr-defined] + except AttributeError: + post_init = dc_cls.__post_init__ @wraps(post_init) def new_post_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index 9d7d55595ae..65151801ccf 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -1474,6 +1474,31 @@ def __post_init__(self): assert C().a == 6 # 1 * 3 + 3 +def test_inheritance_post_init_2(): + post_init_calls = 0 + post_init_post_parse_calls = 0 + + @pydantic.dataclasses.dataclass + class BaseClass: + def __post_init__(self): + nonlocal post_init_calls + post_init_calls += 1 + + @pydantic.dataclasses.dataclass + class AbstractClass(BaseClass): + pass + + @pydantic.dataclasses.dataclass + class ConcreteClass(AbstractClass): + def __post_init_post_parse__(self): + nonlocal post_init_post_parse_calls + post_init_post_parse_calls += 1 + + ConcreteClass() + assert post_init_calls == 1 + assert post_init_post_parse_calls == 1 + + def test_dataclass_setattr(): class Foo: bar: str = 'cat' From a220f87aac0433eb52d5661b50499e634367a89c Mon Sep 17 00:00:00 2001 From: gou177 <53315286+gou177@users.noreply.github.com> Date: Thu, 29 Dec 2022 16:14:24 +0500 Subject: [PATCH 36/38] fix: Parsing of custom root models (#4883) (#4884) --- changes/4883-gou177.md | 1 + pydantic/main.py | 1 + tests/test_parse.py | 1 + 3 files changed, 3 insertions(+) create mode 100644 changes/4883-gou177.md diff --git a/changes/4883-gou177.md b/changes/4883-gou177.md new file mode 100644 index 00000000000..b72d82c6c46 --- /dev/null +++ b/changes/4883-gou177.md @@ -0,0 +1 @@ +fix parsing of custom root models \ No newline at end of file diff --git a/pydantic/main.py b/pydantic/main.py index 16b759f7ce0..361c9669d79 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -508,6 +508,7 @@ def json( def _enforce_dict_if_root(cls, obj: Any) -> Any: if cls.__custom_root_type__ and ( not (isinstance(obj, dict) and obj.keys() == {ROOT_KEY}) + and not (isinstance(obj, BaseModel) and obj.__fields__.keys() == {ROOT_KEY}) or cls.__fields__[ROOT_KEY].shape in MAPPING_LIKE_SHAPES ): return {ROOT_KEY: obj} diff --git a/tests/test_parse.py b/tests/test_parse.py index a0260fba8b8..a7d2287fff1 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -46,6 +46,7 @@ class MyModel(BaseModel): m = MyModel.parse_obj('a') assert m.dict() == {'__root__': 'a'} assert m.__root__ == 'a' + assert MyModel.parse_obj(m) == m def test_parse_root_list(): From bf4b5ceb49043c7165499a51b41e340928ddfd8e Mon Sep 17 00:00:00 2001 From: Eric Jolibois Date: Thu, 29 Dec 2022 12:15:12 +0100 Subject: [PATCH 37/38] fix: use dataclasses proxy for frozen or empty dataclasses (#4878) * add tests * fix: dataclasses * chore: add change file * refactor: remove useless kwarg * test: add new test * keep old kwarg to avoid breaking change --- changes/4878-PrettyWood.md | 1 + pydantic/dataclasses.py | 40 +++++++--------- tests/test_dataclasses.py | 95 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 changes/4878-PrettyWood.md diff --git a/changes/4878-PrettyWood.md b/changes/4878-PrettyWood.md new file mode 100644 index 00000000000..bd05c96d159 --- /dev/null +++ b/changes/4878-PrettyWood.md @@ -0,0 +1 @@ +fix: use dataclass proxy for frozen or empty dataclasses diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py index 1856a1203c5..913d8cc6934 100644 --- a/pydantic/dataclasses.py +++ b/pydantic/dataclasses.py @@ -34,20 +34,7 @@ class M: import sys from contextlib import contextmanager from functools import wraps -from typing import ( - TYPE_CHECKING, - Any, - Callable, - ClassVar, - Dict, - Generator, - Optional, - Set, - Type, - TypeVar, - Union, - overload, -) +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Generator, Optional, Type, TypeVar, Union, overload from typing_extensions import dataclass_transform @@ -117,6 +104,7 @@ def dataclass( frozen: bool = False, config: Union[ConfigDict, Type[object], None] = None, validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, kw_only: bool = ..., ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: ... @@ -134,6 +122,7 @@ def dataclass( frozen: bool = False, config: Union[ConfigDict, Type[object], None] = None, validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, kw_only: bool = ..., ) -> 'DataclassClassOrWrapper': ... @@ -152,6 +141,7 @@ def dataclass( frozen: bool = False, config: Union[ConfigDict, Type[object], None] = None, validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: ... @@ -168,6 +158,7 @@ def dataclass( frozen: bool = False, config: Union[ConfigDict, Type[object], None] = None, validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, ) -> 'DataclassClassOrWrapper': ... @@ -184,6 +175,7 @@ def dataclass( frozen: bool = False, config: Union[ConfigDict, Type[object], None] = None, validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, kw_only: bool = False, ) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']: """ @@ -197,7 +189,15 @@ def dataclass( def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper': import dataclasses - if is_builtin_dataclass(cls) and _extra_dc_args(_cls) == _extra_dc_args(_cls.__bases__[0]): # type: ignore + should_use_proxy = ( + use_proxy + if use_proxy is not None + else ( + is_builtin_dataclass(cls) + and (cls.__bases__[0] is object or set(dir(cls)) == set(dir(cls.__bases__[0]))) + ) + ) + if should_use_proxy: dc_cls_doc = '' dc_cls = DataclassProxy(cls) default_validate_on_init = False @@ -437,14 +437,6 @@ def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value: object.__setattr__(self, name, value) -def _extra_dc_args(cls: Type[Any]) -> Set[str]: - return { - x - for x in dir(cls) - if x not in getattr(cls, '__dataclass_fields__', {}) and not (x.startswith('__') and x.endswith('__')) - } - - def is_builtin_dataclass(_cls: Type[Any]) -> bool: """ Whether a class is a stdlib dataclass @@ -482,4 +474,4 @@ def make_dataclass_validator(dc_cls: Type['Dataclass'], config: Type[BaseConfig] and yield the validators It retrieves the parameters of the dataclass and forwards them to the newly created dataclass """ - yield from _get_validators(dataclass(dc_cls, config=config, validate_on_init=False)) + yield from _get_validators(dataclass(dc_cls, config=config, use_proxy=True)) diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index 65151801ccf..1686ff2672f 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -1513,3 +1513,98 @@ class Foo: assert config.bar == 'cat' setattr(config, 'bar', 'dog') assert config.bar == 'dog' + + +def test_frozen_dataclasses(): + @dataclasses.dataclass(frozen=True) + class First: + a: int + + @dataclasses.dataclass(frozen=True) + class Second(First): + @property + def b(self): + return self.a + + class My(BaseModel): + my: Second + + assert My(my=Second(a='1')).my.b == 1 + + +def test_empty_dataclass(): + """should be able to inherit without adding a field""" + + @dataclasses.dataclass + class UnvalidatedDataclass: + a: int = 0 + + @pydantic.dataclasses.dataclass + class ValidatedDerivedA(UnvalidatedDataclass): + ... + + @pydantic.dataclasses.dataclass() + class ValidatedDerivedB(UnvalidatedDataclass): + b: int = 0 + + @pydantic.dataclasses.dataclass() + class ValidatedDerivedC(UnvalidatedDataclass): + ... + + +def test_proxy_dataclass(): + @dataclasses.dataclass + class Foo: + a: Optional[int] = dataclasses.field(default=42) + b: List = dataclasses.field(default_factory=list) + + @dataclasses.dataclass + class Bar: + pass + + @dataclasses.dataclass + class Model1: + foo: Foo + + class Model2(BaseModel): + foo: Foo + + m1 = Model1(foo=Foo()) + m2 = Model2(foo=Foo()) + + assert m1.foo.a == m2.foo.a == 42 + assert m1.foo.b == m2.foo.b == [] + assert m1.foo.Bar() is not None + assert m2.foo.Bar() is not None + + +def test_proxy_dataclass_2(): + @dataclasses.dataclass + class M1: + a: int + b: str = 'b' + c: float = dataclasses.field(init=False) + + def __post_init__(self): + self.c = float(self.a) + + @dataclasses.dataclass + class M2: + a: int + b: str = 'b' + c: float = dataclasses.field(init=False) + + def __post_init__(self): + self.c = float(self.a) + + @pydantic.validator('b') + def check_b(cls, v): + if not v: + raise ValueError('b should not be empty') + return v + + m1 = pydantic.parse_obj_as(M1, {'a': 3}) + m2 = pydantic.parse_obj_as(M2, {'a': 3}) + assert m1.a == m2.a == 3 + assert m1.b == m2.b == 'b' + assert m1.c == m2.c == 3.0 From 0a9e000db7bc13b76b728d82df53a3e4c24b43f4 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 29 Dec 2022 11:25:31 +0000 Subject: [PATCH 38/38] prepare for v1.10.3, fix #4552 --- HISTORY.md | 16 ++++++++++++++++ changes/2276-samuelcolvin.md | 1 - changes/4487-PrettyWood.md | 1 - changes/4500-samuelcolvin.md | 1 - changes/4538-sisp.md | 1 - changes/4551-mfulgo.md | 1 - changes/4568-mfulgo.md | 1 - changes/4641-jparise.md | 1 - changes/4689-morian.md | 1 - changes/4695-PrettyWood.md | 1 - changes/4767-samuelcolvin.md | 1 - changes/4781-Bobronium.md | 1 - changes/4878-PrettyWood.md | 1 - changes/4883-gou177.md | 1 - pydantic/version.py | 2 +- 15 files changed, 17 insertions(+), 14 deletions(-) delete mode 100644 changes/2276-samuelcolvin.md delete mode 100644 changes/4487-PrettyWood.md delete mode 100644 changes/4500-samuelcolvin.md delete mode 100644 changes/4538-sisp.md delete mode 100644 changes/4551-mfulgo.md delete mode 100644 changes/4568-mfulgo.md delete mode 100644 changes/4641-jparise.md delete mode 100644 changes/4689-morian.md delete mode 100644 changes/4695-PrettyWood.md delete mode 100644 changes/4767-samuelcolvin.md delete mode 100644 changes/4781-Bobronium.md delete mode 100644 changes/4878-PrettyWood.md delete mode 100644 changes/4883-gou177.md diff --git a/HISTORY.md b/HISTORY.md index 13a66fde09d..9185449e176 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,19 @@ +## v1.10.3 (2022-12-29) + +* fix parsing of custom root models, #4883 by @gou177 +* fix: use dataclass proxy for frozen or empty dataclasses, #4878 by @PrettyWood +* Fix `schema` and `schema_json` on models where a model instance is a one of default values, #4781 by @Bobronium +* Add Jina AI to sponsors on docs index page, #4767 by @samuelcolvin +* fix: support assignment on `DataclassProxy`, #4695 by @PrettyWood +* Add `postgresql+psycopg` as allowed scheme for `PostgreDsn` to make it usable with SQLAlchemy 2, #4689 by @morian +* Allow dict schemas to have both `patternProperties` and `additionalProperties`, #4641 by @jparise +* Fixes error passing None for optional lists with `unique_items`, #4568 by @mfulgo +* Fix `GenericModel` with `Callable` param raising a `TypeError`, #4551 by @mfulgo +* Fix field regex with `StrictStr` type annotation, #4538 by @sisp +* Correct `dataclass_transform` keyword argument name from `field_descriptors` to `field_specifiers`, #4500 by @samuelcolvin +* fix: avoid multiple calls of `__post_init__` when dataclasses are inherited, #4487 by @PrettyWood +* Reduce the size of binary wheels, #2276 by @samuelcolvin + ## v1.10.2 (2022-09-05) * **Revert Change:** Revert percent encoding of URL parts which was originally added in #4224, #4470 by @samuelcolvin diff --git a/changes/2276-samuelcolvin.md b/changes/2276-samuelcolvin.md deleted file mode 100644 index ad2bbd41b03..00000000000 --- a/changes/2276-samuelcolvin.md +++ /dev/null @@ -1 +0,0 @@ -Reduce the size of binary wheels. diff --git a/changes/4487-PrettyWood.md b/changes/4487-PrettyWood.md deleted file mode 100644 index 8f18dd0c50c..00000000000 --- a/changes/4487-PrettyWood.md +++ /dev/null @@ -1 +0,0 @@ -fix: avoid multiple calls of `__post_init__` when dataclasses are inherited diff --git a/changes/4500-samuelcolvin.md b/changes/4500-samuelcolvin.md deleted file mode 100644 index 24cda1cbf4f..00000000000 --- a/changes/4500-samuelcolvin.md +++ /dev/null @@ -1 +0,0 @@ -Correct `dataclass_transform` keyword argument name from `field_descriptors` to `field_specifiers` diff --git a/changes/4538-sisp.md b/changes/4538-sisp.md deleted file mode 100644 index afb3312ba55..00000000000 --- a/changes/4538-sisp.md +++ /dev/null @@ -1 +0,0 @@ -Fix field regex with `StrictStr` type annotation. diff --git a/changes/4551-mfulgo.md b/changes/4551-mfulgo.md deleted file mode 100644 index 1d9871a41aa..00000000000 --- a/changes/4551-mfulgo.md +++ /dev/null @@ -1 +0,0 @@ -Fix `GenericModel` with `Callable` param raising a `TypeError` diff --git a/changes/4568-mfulgo.md b/changes/4568-mfulgo.md deleted file mode 100644 index dfbe63d9fc0..00000000000 --- a/changes/4568-mfulgo.md +++ /dev/null @@ -1 +0,0 @@ -Fixes error passing None for optional lists with `unique_items` diff --git a/changes/4641-jparise.md b/changes/4641-jparise.md deleted file mode 100644 index b671f3c6209..00000000000 --- a/changes/4641-jparise.md +++ /dev/null @@ -1 +0,0 @@ -Allow dict schemas to have both `patternProperties` and `additionalProperties` diff --git a/changes/4689-morian.md b/changes/4689-morian.md deleted file mode 100644 index 10f8f429f32..00000000000 --- a/changes/4689-morian.md +++ /dev/null @@ -1 +0,0 @@ -Add `postgresql+psycopg` as allowed scheme for `PostgreDsn` to make it usable with SQLAlchemy 2 diff --git a/changes/4695-PrettyWood.md b/changes/4695-PrettyWood.md deleted file mode 100644 index c26b609d570..00000000000 --- a/changes/4695-PrettyWood.md +++ /dev/null @@ -1 +0,0 @@ -fix: support assignment on `DataclassProxy` diff --git a/changes/4767-samuelcolvin.md b/changes/4767-samuelcolvin.md deleted file mode 100644 index 85f14a670f1..00000000000 --- a/changes/4767-samuelcolvin.md +++ /dev/null @@ -1 +0,0 @@ -Add Jina AI to sponsors on docs index page. diff --git a/changes/4781-Bobronium.md b/changes/4781-Bobronium.md deleted file mode 100644 index 26d1221db96..00000000000 --- a/changes/4781-Bobronium.md +++ /dev/null @@ -1 +0,0 @@ -Fix `schema` and `schema_json` on models where a model instance is a one of default values. diff --git a/changes/4878-PrettyWood.md b/changes/4878-PrettyWood.md deleted file mode 100644 index bd05c96d159..00000000000 --- a/changes/4878-PrettyWood.md +++ /dev/null @@ -1 +0,0 @@ -fix: use dataclass proxy for frozen or empty dataclasses diff --git a/changes/4883-gou177.md b/changes/4883-gou177.md deleted file mode 100644 index b72d82c6c46..00000000000 --- a/changes/4883-gou177.md +++ /dev/null @@ -1 +0,0 @@ -fix parsing of custom root models \ No newline at end of file diff --git a/pydantic/version.py b/pydantic/version.py index 32c61633f70..d65155a00c9 100644 --- a/pydantic/version.py +++ b/pydantic/version.py @@ -1,6 +1,6 @@ __all__ = 'compiled', 'VERSION', 'version_info' -VERSION = '1.10.2' +VERSION = '1.10.3' try: import cython # type: ignore