This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 0e1832cd3cd Fix make_partial_model (#63716)
0e1832cd3cd is described below

commit 0e1832cd3cd586ea4a53cc012550fbc084fb0fc4
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Wed Mar 18 13:05:21 2026 +0100

    Fix make_partial_model (#63716)
    
    * Fix make_partial_model
    
    * Revert "Bump pydantic min version to 2.12.3 (#63570)"
    
    This reverts commit 9516a771f402da94ba35f0b53f66258716f04948.
    
    * Fix CI
---
 airflow-core/pyproject.toml                        |  2 +-
 .../src/airflow/api_fastapi/core_api/base.py       |  4 +--
 .../tests/unit/api_fastapi/core_api/test_base.py   | 30 ++++++++++++++++++++++
 dev/registry/pyproject.toml                        |  2 +-
 providers/edge3/docs/index.rst                     |  2 +-
 providers/edge3/pyproject.toml                     |  2 +-
 shared/secrets_masker/pyproject.toml               |  2 +-
 task-sdk/pyproject.toml                            |  2 +-
 uv.lock                                            | 10 ++++----
 9 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/airflow-core/pyproject.toml b/airflow-core/pyproject.toml
index c853c7de833..eaaf4538058 100644
--- a/airflow-core/pyproject.toml
+++ b/airflow-core/pyproject.toml
@@ -125,7 +125,7 @@ dependencies = [
     'pendulum>=3.1.0',
     "pluggy>=1.5.0",
     "psutil>=5.8.0",
-    "pydantic>=2.12.3",
+    "pydantic>=2.11.0",
     # Pygments 2.19.0 improperly renders .ini files with dictionaries as values
     # See https://github.com/pygments/pygments/issues/2834
     "pygments>=2.0.1,!=2.19.0",
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/base.py 
b/airflow-core/src/airflow/api_fastapi/core_api/base.py
index 600a6d896e2..b88cb955650 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/base.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/base.py
@@ -17,7 +17,6 @@
 from __future__ import annotations
 
 from abc import ABC, abstractmethod
-from copy import deepcopy
 from typing import TYPE_CHECKING, Generic, TypeVar, Union, get_args, get_origin
 
 from pydantic import BaseModel as PydanticBaseModel, ConfigDict, create_model
@@ -54,12 +53,13 @@ def make_partial_model(model: type[PydanticBaseModel]) -> 
type[PydanticBaseModel
     """Create a version of a Pydantic model where all fields are Optional with 
default=None."""
     field_overrides: dict = {}
     for field_name, field_info in model.model_fields.items():
-        new_info = deepcopy(field_info)
         ann = field_info.annotation
         origin = get_origin(ann)
         if not (origin is Union and type(None) in get_args(ann)):
             ann = ann | None  # type: ignore[operator, assignment]
+        new_info = field_info._copy()
         new_info.default = None
+        new_info._attributes_set["default"] = None
         field_overrides[field_name] = (ann, new_info)
 
     return create_model(
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/test_base.py 
b/airflow-core/tests/unit/api_fastapi/core_api/test_base.py
index 15a47efac11..e806ee1b80e 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/test_base.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/test_base.py
@@ -41,6 +41,16 @@ class SampleModel(StrictBaseModel):
 SampleModelPartial = make_partial_model(SampleModel)
 
 
+class ModelWithFieldAttributes(StrictBaseModel):
+    """Model with alias, title, and description to test full attribute 
preservation."""
+
+    name: str = Field(alias="user_name", title="User Name", description="The 
user's full name")
+    age: int = Field(ge=0, le=150, title="Age", description="Age in years")
+
+
+ModelWithFieldAttributesPartial = make_partial_model(ModelWithFieldAttributes)
+
+
 class TestMakePartialModel:
     def test_all_fields_become_optional(self):
         instance = SampleModelPartial()
@@ -76,3 +86,23 @@ class TestMakePartialModel:
 
     def test_partial_model_name(self):
         assert SampleModelPartial.__name__ == "SampleModelPartial"
+
+    def test_field_alias_preserved(self):
+        instance = ModelWithFieldAttributesPartial(user_name="Alice")
+        assert instance.name == "Alice"
+
+    def test_field_title_and_description_preserved(self):
+        name_field = ModelWithFieldAttributesPartial.model_fields["name"]
+        assert name_field.alias == "user_name"
+        assert name_field.title == "User Name"
+        assert name_field.description == "The user's full name"
+
+        age_field = ModelWithFieldAttributesPartial.model_fields["age"]
+        assert age_field.title == "Age"
+        assert age_field.description == "Age in years"
+
+    def test_field_ge_le_constraints_preserved(self):
+        with pytest.raises(ValidationError):
+            ModelWithFieldAttributesPartial(age=-1)
+        with pytest.raises(ValidationError):
+            ModelWithFieldAttributesPartial(age=200)
diff --git a/dev/registry/pyproject.toml b/dev/registry/pyproject.toml
index 6adc5ea8d5a..30d70642216 100644
--- a/dev/registry/pyproject.toml
+++ b/dev/registry/pyproject.toml
@@ -33,7 +33,7 @@ version = "0.0.1"
 requires-python = ">=3.10"
 classifiers = ["Private :: Do Not Upload"]
 dependencies = [
-    "pydantic>=2.12.3",
+    "pydantic>=2.12.0",
     "pyyaml>=6.0.3",
 ]
 
diff --git a/providers/edge3/docs/index.rst b/providers/edge3/docs/index.rst
index 6e68e0bd4b9..9e7d9acaf60 100644
--- a/providers/edge3/docs/index.rst
+++ b/providers/edge3/docs/index.rst
@@ -122,7 +122,7 @@ PIP package                                 Version required
 ==========================================  ===================
 ``apache-airflow``                          ``>=3.0.0,!=3.1.0``
 ``apache-airflow-providers-common-compat``  ``>=1.14.0``
-``pydantic``                                ``>=2.12.3``
+``pydantic``                                ``>=2.11.0``
 ``retryhttp``                               ``>=1.4.0``
 ``aiofiles``                                ``>=23.2.0``
 ``aiohttp``                                 ``>=3.9.2``
diff --git a/providers/edge3/pyproject.toml b/providers/edge3/pyproject.toml
index 2b234b8c244..f1209231b80 100644
--- a/providers/edge3/pyproject.toml
+++ b/providers/edge3/pyproject.toml
@@ -67,7 +67,7 @@ requires-python = ">=3.10"
 dependencies = [
     "apache-airflow>=3.0.0,!=3.1.0",
     "apache-airflow-providers-common-compat>=1.14.0",
-    "pydantic>=2.12.3",
+    "pydantic>=2.11.0",
     "retryhttp>=1.4.0",
     "aiofiles>=23.2.0",
     "aiohttp>=3.9.2",
diff --git a/shared/secrets_masker/pyproject.toml 
b/shared/secrets_masker/pyproject.toml
index 9826a2e908d..04e1e862f44 100644
--- a/shared/secrets_masker/pyproject.toml
+++ b/shared/secrets_masker/pyproject.toml
@@ -27,7 +27,7 @@ dependencies = [
     'pendulum>=3.1.0',
     "methodtools>=0.4.7",
     "colorlog>=6.8.2",
-    "pydantic>2.12.3",
+    "pydantic>2.11.0",
 ]
 
 [dependency-groups]
diff --git a/task-sdk/pyproject.toml b/task-sdk/pyproject.toml
index bd0382b9a89..1fcd6e8817c 100644
--- a/task-sdk/pyproject.toml
+++ b/task-sdk/pyproject.toml
@@ -68,7 +68,7 @@ dependencies = [
     # End of shared timezones dependencies
     # Start of shared secrets_masker dependencies
     "colorlog>=6.8.2",
-    "pydantic>2.12.3",
+    "pydantic>2.11.0",
     # End of shared secrets_masker dependencies
     # Start of shared logging dependencies
     "pygtrie>=2.5.0",
diff --git a/uv.lock b/uv.lock
index 2d162bd3776..f25e27ec5a1 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1759,7 +1759,7 @@ requires-dist = [
     { name = "pendulum", specifier = ">=3.1.0" },
     { name = "pluggy", specifier = ">=1.5.0" },
     { name = "psutil", specifier = ">=5.8.0" },
-    { name = "pydantic", specifier = ">=2.12.3" },
+    { name = "pydantic", specifier = ">=2.11.0" },
     { name = "pygments", specifier = ">=2.0.1,!=2.19.0" },
     { name = "pygtrie", specifier = ">=2.5.0" },
     { name = "pyjwt", specifier = ">=2.11.0" },
@@ -4420,7 +4420,7 @@ requires-dist = [
     { name = "aiohttp", specifier = ">=3.9.2" },
     { name = "apache-airflow", editable = "." },
     { name = "apache-airflow-providers-common-compat", editable = 
"providers/common/compat" },
-    { name = "pydantic", specifier = ">=2.12.3" },
+    { name = "pydantic", specifier = ">=2.11.0" },
     { name = "retryhttp", specifier = ">=1.4.0" },
 ]
 
@@ -7516,7 +7516,7 @@ dev = [
 
 [package.metadata]
 requires-dist = [
-    { name = "pydantic", specifier = ">=2.12.3" },
+    { name = "pydantic", specifier = ">=2.12.0" },
     { name = "pyyaml", specifier = ">=6.0.3" },
 ]
 
@@ -7847,7 +7847,7 @@ requires-dist = [
     { name = "colorlog", specifier = ">=6.8.2" },
     { name = "methodtools", specifier = ">=0.4.7" },
     { name = "pendulum", specifier = ">=3.1.0" },
-    { name = "pydantic", specifier = ">2.12.3" },
+    { name = "pydantic", specifier = ">2.11.0" },
 ]
 
 [package.metadata.requires-dev]
@@ -7990,7 +7990,7 @@ requires-dist = [
     { name = "pendulum", specifier = ">=3.1.0" },
     { name = "pluggy", specifier = ">=1.5.0" },
     { name = "psutil", specifier = ">=6.1.0" },
-    { name = "pydantic", specifier = ">2.12.3" },
+    { name = "pydantic", specifier = ">2.11.0" },
     { name = "pygtrie", specifier = ">=2.5.0" },
     { name = "python-dateutil", specifier = ">=2.7.0" },
     { name = "sentry-sdk", marker = "extra == 'all'", specifier = ">=2.30.0" },

Reply via email to