forked from pool/python-pydantic
Compare commits
18 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
| 3228196e9e | |||
| 5af452c66c | |||
| 7adac6edca | |||
| 436ce7ee8c | |||
| 32e7f1b8ff | |||
| 3f75a1e5fd | |||
| dd01ec6e92 | |||
| eb3735017a | |||
| f4aac336ef | |||
| d90714d9d3 | |||
| de0fe174bb | |||
| 10800831eb | |||
| d5dea0fe9d | |||
| 419eb8bf13 | |||
| b20ae8e003 | |||
| 20e9354ff7 | |||
| 273690b8e0 | |||
| fd0f827663 |
@@ -1,9 +1,3 @@
|
|||||||
-------------------------------------------------------------------
|
|
||||||
Fri Aug 15 04:23:15 UTC 2025 - Steve Kowalik <steven.kowalik@suse.com>
|
|
||||||
|
|
||||||
- Add patch support-pydantic-core-2.39.0.patch:
|
|
||||||
* Support pydantic-core 2.39.0.
|
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Mon Jun 23 05:56:23 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
Mon Jun 23 05:56:23 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# spec file for package python-pydantic
|
# spec file for package python-pydantic
|
||||||
#
|
#
|
||||||
# Copyright (c) 2025 SUSE LLC and contributors
|
# Copyright (c) 2025 SUSE LLC
|
||||||
# Copyright (c) 2019, Martin Hauke <mardnh@gmx.de>
|
# Copyright (c) 2019, Martin Hauke <mardnh@gmx.de>
|
||||||
#
|
#
|
||||||
# All modifications and additions to the file contributed by third parties
|
# All modifications and additions to the file contributed by third parties
|
||||||
@@ -37,13 +37,11 @@ Source: https://github.com/pydantic/pydantic/archive/v%{version}.tar.gz#
|
|||||||
Patch0: bump-pydantic-core-2.35.1.patch
|
Patch0: bump-pydantic-core-2.35.1.patch
|
||||||
# PATCH-FIX-UPSTREAM field-name-validator-core-schemas.patch gh#pydantic/pydantic#11761
|
# PATCH-FIX-UPSTREAM field-name-validator-core-schemas.patch gh#pydantic/pydantic#11761
|
||||||
Patch1: field-name-validator-core-schemas.patch
|
Patch1: field-name-validator-core-schemas.patch
|
||||||
# PATCH-FIX-UPSTREAM Based on gh#pydantic/pydantic#11883
|
|
||||||
Patch2: support-pydantic-core-2.39.0.patch
|
|
||||||
BuildRequires: %{python_module hatch-fancy-pypi-readme}
|
BuildRequires: %{python_module hatch-fancy-pypi-readme}
|
||||||
BuildRequires: %{python_module hatchling}
|
BuildRequires: %{python_module hatchling}
|
||||||
BuildRequires: %{python_module packaging}
|
BuildRequires: %{python_module packaging}
|
||||||
BuildRequires: %{python_module pip}
|
BuildRequires: %{python_module pip}
|
||||||
BuildRequires: %{python_module pydantic-core = 2.39.0}
|
BuildRequires: %{python_module pydantic-core = 2.35.1}
|
||||||
BuildRequires: %{python_module wheel}
|
BuildRequires: %{python_module wheel}
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
BuildRequires: python-rpm-macros
|
BuildRequires: python-rpm-macros
|
||||||
@@ -64,7 +62,10 @@ BuildRequires: %{python_module rich}
|
|||||||
BuildRequires: %{python_module typing-inspection}
|
BuildRequires: %{python_module typing-inspection}
|
||||||
%endif
|
%endif
|
||||||
Requires: python-annotated-types >= 0.4.0
|
Requires: python-annotated-types >= 0.4.0
|
||||||
Requires: python-pydantic-core = 2.39.0
|
%if 0%{?python_version_nodots} < 310
|
||||||
|
Requires: python-eval-type-backport
|
||||||
|
%endif
|
||||||
|
Requires: python-pydantic-core = 2.35.1
|
||||||
Requires: python-typing-extensions >= 4.12.2
|
Requires: python-typing-extensions >= 4.12.2
|
||||||
Requires: python-typing-inspection
|
Requires: python-typing-inspection
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
|||||||
@@ -1,293 +0,0 @@
|
|||||||
From d6c65493a8436b22733d0f04d0bb3df1bc952ac9 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Viicos <65306057+Viicos@users.noreply.github.com>
|
|
||||||
Date: Fri, 16 May 2025 15:46:24 +0200
|
|
||||||
Subject: [PATCH 1/8] Add `UNSET` sentinel
|
|
||||||
|
|
||||||
---
|
|
||||||
pydantic/_internal/_generate_schema.py | 3 +
|
|
||||||
pydantic/fields.py | 4 +-
|
|
||||||
pydantic/json_schema.py | 7 +-
|
|
||||||
pyproject.toml | 2 +-
|
|
||||||
5 files changed, 15 insertions(+), 122 deletions(-)
|
|
||||||
|
|
||||||
Index: pydantic-2.11.7/pydantic/_internal/_generate_schema.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/pydantic/_internal/_generate_schema.py
|
|
||||||
+++ pydantic-2.11.7/pydantic/_internal/_generate_schema.py
|
|
||||||
@@ -42,6 +42,7 @@ from zoneinfo import ZoneInfo
|
|
||||||
|
|
||||||
import typing_extensions
|
|
||||||
from pydantic_core import (
|
|
||||||
+ MISSING,
|
|
||||||
CoreSchema,
|
|
||||||
MultiHostUrl,
|
|
||||||
PydanticCustomError,
|
|
||||||
@@ -1050,6 +1051,8 @@ class GenerateSchema:
|
|
||||||
return core_schema.multi_host_url_schema()
|
|
||||||
elif obj is None or obj is _typing_extra.NoneType:
|
|
||||||
return core_schema.none_schema()
|
|
||||||
+ if obj is MISSING:
|
|
||||||
+ return core_schema.missing_sentinel_schema()
|
|
||||||
elif obj in IP_TYPES:
|
|
||||||
return self._ip_schema(obj)
|
|
||||||
elif obj in TUPLE_TYPES:
|
|
||||||
Index: pydantic-2.11.7/pydantic/fields.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/pydantic/fields.py
|
|
||||||
+++ pydantic-2.11.7/pydantic/fields.py
|
|
||||||
@@ -15,7 +15,7 @@ from warnings import warn
|
|
||||||
|
|
||||||
import annotated_types
|
|
||||||
import typing_extensions
|
|
||||||
-from pydantic_core import PydanticUndefined
|
|
||||||
+from pydantic_core import MISSING, PydanticUndefined
|
|
||||||
from typing_extensions import Self, TypeAlias, Unpack, deprecated
|
|
||||||
from typing_inspection import typing_objects
|
|
||||||
from typing_inspection.introspection import UNKNOWN, AnnotationSource, ForbiddenQualifier, Qualifier, inspect_annotation
|
|
||||||
@@ -392,7 +392,7 @@ class FieldInfo(_repr.Representation):
|
|
||||||
Returns:
|
|
||||||
A field object with the passed values.
|
|
||||||
"""
|
|
||||||
- if annotation is default:
|
|
||||||
+ if annotation is not MISSING and annotation is default:
|
|
||||||
raise PydanticUserError(
|
|
||||||
'Error when building FieldInfo from annotated attribute. '
|
|
||||||
"Make sure you don't have any field name clashing with a type annotation.",
|
|
||||||
Index: pydantic-2.11.7/pydantic/json_schema.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/pydantic/json_schema.py
|
|
||||||
+++ pydantic-2.11.7/pydantic/json_schema.py
|
|
||||||
@@ -36,7 +36,7 @@ from typing import (
|
|
||||||
)
|
|
||||||
|
|
||||||
import pydantic_core
|
|
||||||
-from pydantic_core import CoreSchema, PydanticOmit, core_schema, to_jsonable_python
|
|
||||||
+from pydantic_core import MISSING, CoreSchema, PydanticOmit, core_schema, to_jsonable_python
|
|
||||||
from pydantic_core.core_schema import ComputedField
|
|
||||||
from typing_extensions import TypeAlias, assert_never, deprecated, final
|
|
||||||
from typing_inspection.introspection import get_literal_values
|
|
||||||
@@ -805,6 +805,17 @@ class GenerateJsonSchema:
|
|
||||||
result['type'] = 'null'
|
|
||||||
return result
|
|
||||||
|
|
||||||
+ def missing_sentinel_schema(self, schema: core_schema.MissingSentinelSchema) -> JsonSchemaValue:
|
|
||||||
+ """Generates a JSON schema that matches the `MISSING` sentinel value.
|
|
||||||
+
|
|
||||||
+ Args:
|
|
||||||
+ schema: The core schema.
|
|
||||||
+
|
|
||||||
+ Returns:
|
|
||||||
+ The generated JSON schema.
|
|
||||||
+ """
|
|
||||||
+ raise PydanticOmit
|
|
||||||
+
|
|
||||||
def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue:
|
|
||||||
"""Generates a JSON schema that matches an Enum value.
|
|
||||||
|
|
||||||
@@ -1109,7 +1120,7 @@ class GenerateJsonSchema:
|
|
||||||
json_schema = self.generate_inner(schema['schema'])
|
|
||||||
|
|
||||||
default = self.get_default_value(schema)
|
|
||||||
- if default is NoDefault:
|
|
||||||
+ if default is NoDefault or default is MISSING:
|
|
||||||
return json_schema
|
|
||||||
|
|
||||||
# we reflect the application of custom plain, no-info serializers to defaults for
|
|
||||||
Index: pydantic-2.11.7/pydantic/version.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/pydantic/version.py
|
|
||||||
+++ pydantic-2.11.7/pydantic/version.py
|
|
||||||
@@ -66,7 +66,7 @@ def version_info() -> str:
|
|
||||||
def check_pydantic_core_version() -> bool:
|
|
||||||
"""Check that the installed `pydantic-core` dependency is compatible."""
|
|
||||||
# Keep this in sync with the version constraint in the `pyproject.toml` dependencies:
|
|
||||||
- return __pydantic_core_version__ == '2.35.1'
|
|
||||||
+ return __pydantic_core_version__ == '2.39.0'
|
|
||||||
|
|
||||||
|
|
||||||
def parse_mypy_version(version: str) -> tuple[int, int, int]:
|
|
||||||
Index: pydantic-2.11.7/docs/concepts/experimental.md
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/docs/concepts/experimental.md
|
|
||||||
+++ pydantic-2.11.7/docs/concepts/experimental.md
|
|
||||||
@@ -502,3 +502,49 @@ args, kwargs = val.validate_json('{"args
|
|
||||||
print(args, kwargs)
|
|
||||||
#> ('arg1',) {'extra': 1}
|
|
||||||
```
|
|
||||||
+
|
|
||||||
+## `MISSING` sentinel
|
|
||||||
+
|
|
||||||
+The `MISSING` sentinel is a singleton indicating a field value was not provided during validation.
|
|
||||||
+
|
|
||||||
+This singleton can be used as a default value, as an alternative to `None` when it has an explicit
|
|
||||||
+meaning. During serialization, any field with `MISSING` as a value is excluded from the output.
|
|
||||||
+
|
|
||||||
+```python
|
|
||||||
+from typing import Union
|
|
||||||
+
|
|
||||||
+from pydantic import BaseModel
|
|
||||||
+from pydantic.experimental.missing_sentinel import MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class Configuration(BaseModel):
|
|
||||||
+ timeout: Union[int, None, MISSING] = MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+# configuration defaults, stored somewhere else:
|
|
||||||
+defaults = {'timeout': 200}
|
|
||||||
+
|
|
||||||
+conf = Configuration()
|
|
||||||
+
|
|
||||||
+# `timeout` is excluded from the serialization output:
|
|
||||||
+conf.model_dump()
|
|
||||||
+# {}
|
|
||||||
+
|
|
||||||
+# The `MISSING` value doesn't appear in the JSON Schema:
|
|
||||||
+Configuration.model_json_schema()['properties']['timeout']
|
|
||||||
+#> {'anyOf': [{'type': 'integer'}, {'type': 'null'}], 'title': 'Timeout'}}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+# `is` can be used to discrimate between the sentinel and other values:
|
|
||||||
+timeout = conf.timeout if conf.timeout is not MISSING else defaults['timeout']
|
|
||||||
+```
|
|
||||||
+
|
|
||||||
+This feature is marked as experimental because it relies on the draft [PEP 661](https://peps.python.org/pep-0661/), introducing sentinels in the standard library.
|
|
||||||
+
|
|
||||||
+As such, the following limitations currently apply:
|
|
||||||
+
|
|
||||||
+* Static type checking of sentinels is only supported with Pyright
|
|
||||||
+ [1.1.402](https://github.com/microsoft/pyright/releases/tag/1.1.402)
|
|
||||||
+ or greater, and the `enableExperimentalFeatures` type evaluation setting
|
|
||||||
+ should be enabled.
|
|
||||||
+* Pickling of models containing `MISSING` as a value is not supported.
|
|
||||||
Index: pydantic-2.11.7/docs/errors/validation_errors.md
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/docs/errors/validation_errors.md
|
|
||||||
+++ pydantic-2.11.7/docs/errors/validation_errors.md
|
|
||||||
@@ -1384,6 +1384,27 @@ except ValidationError as exc:
|
|
||||||
#> 'missing_positional_only_argument'
|
|
||||||
```
|
|
||||||
|
|
||||||
+## `missing_sentinel_error`
|
|
||||||
+
|
|
||||||
+This error is raised when the experimental `MISSING` sentinel is the only value allowed, and wasn't
|
|
||||||
+provided during validation:
|
|
||||||
+
|
|
||||||
+```python
|
|
||||||
+from pydantic import BaseModel, ValidationError
|
|
||||||
+from pydantic.experimental.missing_sentinel import MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class Model(BaseModel):
|
|
||||||
+ f: MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+try:
|
|
||||||
+ Model(f=1)
|
|
||||||
+except ValidationError as exc:
|
|
||||||
+ print(repr(exc.errors()[0]['type']))
|
|
||||||
+ #> 'missing_sentinel_error'
|
|
||||||
+```
|
|
||||||
+
|
|
||||||
## `model_attributes_type`
|
|
||||||
|
|
||||||
This error is raised when the input value is not a valid dictionary, model instance, or instance that fields can be extracted from:
|
|
||||||
Index: pydantic-2.11.7/pydantic/experimental/missing_sentinel.py
|
|
||||||
===================================================================
|
|
||||||
--- /dev/null
|
|
||||||
+++ pydantic-2.11.7/pydantic/experimental/missing_sentinel.py
|
|
||||||
@@ -0,0 +1,5 @@
|
|
||||||
+"""Experimental module exposing a function a `MISSING` sentinel."""
|
|
||||||
+
|
|
||||||
+from pydantic_core import MISSING
|
|
||||||
+
|
|
||||||
+__all__ = ('MISSING',)
|
|
||||||
Index: pydantic-2.11.7/pyproject.toml
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-2.11.7.orig/pyproject.toml
|
|
||||||
+++ pydantic-2.11.7/pyproject.toml
|
|
||||||
@@ -46,7 +46,7 @@ dependencies = [
|
|
||||||
'typing-extensions>=4.13.0',
|
|
||||||
'annotated-types>=0.6.0',
|
|
||||||
# Keep this in sync with the version in the `check_pydantic_core_version()` function:
|
|
||||||
- 'pydantic-core==2.35.1',
|
|
||||||
+ 'pydantic-core==2.39.0',
|
|
||||||
'typing-inspection>=0.4.0',
|
|
||||||
]
|
|
||||||
dynamic = ['version', 'readme']
|
|
||||||
Index: pydantic-2.11.7/tests/test_missing_sentinel.py
|
|
||||||
===================================================================
|
|
||||||
--- /dev/null
|
|
||||||
+++ pydantic-2.11.7/tests/test_missing_sentinel.py
|
|
||||||
@@ -0,0 +1,71 @@
|
|
||||||
+import pickle
|
|
||||||
+from typing import Union
|
|
||||||
+
|
|
||||||
+import pytest
|
|
||||||
+from pydantic_core import MISSING, PydanticSerializationUnexpectedValue
|
|
||||||
+
|
|
||||||
+from pydantic import BaseModel, TypeAdapter, ValidationError
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_missing_sentinel_model() -> None:
|
|
||||||
+ class Model(BaseModel):
|
|
||||||
+ f: Union[int, MISSING] = MISSING
|
|
||||||
+ g: MISSING = MISSING
|
|
||||||
+
|
|
||||||
+ m1 = Model()
|
|
||||||
+
|
|
||||||
+ assert m1.model_dump() == {}
|
|
||||||
+ assert m1.model_dump_json() == '{}'
|
|
||||||
+
|
|
||||||
+ m2 = Model.model_validate({'f': MISSING, 'g': MISSING})
|
|
||||||
+
|
|
||||||
+ assert m2.f is MISSING
|
|
||||||
+ assert m2.g is MISSING
|
|
||||||
+
|
|
||||||
+ m3 = Model(f=1)
|
|
||||||
+
|
|
||||||
+ assert m3.model_dump() == {'f': 1}
|
|
||||||
+ assert m3.model_dump_json() == '{"f":1}'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_missing_sentinel_type_adapter() -> None:
|
|
||||||
+ """Note that this usage isn't explicitly supported (and useless in practice)."""
|
|
||||||
+
|
|
||||||
+ # TODO Remove annotation with PEP 747:
|
|
||||||
+ ta: TypeAdapter[object] = TypeAdapter(MISSING)
|
|
||||||
+
|
|
||||||
+ assert ta.validate_python(MISSING) is MISSING
|
|
||||||
+
|
|
||||||
+ with pytest.raises(ValidationError) as exc_info:
|
|
||||||
+ ta.validate_python(1)
|
|
||||||
+
|
|
||||||
+ assert exc_info.value.errors()[0]['type'] == 'missing_sentinel_error'
|
|
||||||
+
|
|
||||||
+ assert ta.dump_python(MISSING) is MISSING
|
|
||||||
+
|
|
||||||
+ with pytest.raises(PydanticSerializationUnexpectedValue):
|
|
||||||
+ ta.dump_python(1)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+# Defined in module to be picklable:
|
|
||||||
+class ModelPickle(BaseModel):
|
|
||||||
+ f: Union[int, MISSING] = MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+@pytest.mark.xfail(reason="PEP 661 sentinels aren't picklable yet in the experimental typing-extensions implementation")
|
|
||||||
+def test_missing_sentinel_pickle() -> None:
|
|
||||||
+ m = ModelPickle()
|
|
||||||
+ m_reconstructed = pickle.loads(pickle.dumps(m))
|
|
||||||
+
|
|
||||||
+ assert m_reconstructed.f is MISSING
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_missing_sentinel_json_schema() -> None:
|
|
||||||
+ class Model(BaseModel):
|
|
||||||
+ f: Union[int, MISSING] = MISSING
|
|
||||||
+ g: MISSING = MISSING
|
|
||||||
+ h: MISSING
|
|
||||||
+
|
|
||||||
+ assert Model.model_json_schema()['properties'] == {
|
|
||||||
+ 'f': {'title': 'F', 'type': 'integer'},
|
|
||||||
+ }
|
|
||||||
Reference in New Issue
Block a user