You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by "pierrejeambrun (via GitHub)" <gi...@apache.org> on 2026/03/16 11:00:11 UTC

[PR] Fix make_partial_model [airflow]

pierrejeambrun opened a new pull request, #63716:
URL: https://github.com/apache/airflow/pull/63716

   Fixing implementation which was brittle and not working on pydantic 2.12.0
   
    <!-- SPDX-License-Identifier: Apache-2.0
         https://www.apache.org/licenses/LICENSE-2.0 -->
   
   <!--
   Thank you for contributing!
   
   Please provide above a brief description of the changes made in this pull request.
   Write a good git commit message following this guide: http://chris.beams.io/posts/git-commit/
   
   Please make sure that your code changes are covered with tests.
   And in case of new features or big changes remember to adjust the documentation.
   
   Feel free to ping (in general) for the review if you do not see reaction for a few days
   (72 Hours is the minimum reaction time you can expect from volunteers) - we sometimes miss notifications.
   
   In case of an existing issue, reference it using one of the following:
   
   * closes: #ISSUE
   * related: #ISSUE
   -->
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   <!--
   If generative AI tooling has been used in the process of authoring this PR, please
   change below checkbox to `[X]` followed by the name of the tool, uncomment the "Generated-by".
   -->
   
   - [ ] Yes (please specify the tool below)
   
   <!--
   Generated-by: [Tool Name] following [the guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   -->
   
   ---
   
   * Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)** for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
   * For fundamental code changes, an Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals)) is needed.
   * When adding dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
   * For significant user-facing changes create newsfragment: `{pr_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments). You can add this file in a follow-up commit after the PR is created so you know the PR number.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "pierrejeambrun (via GitHub)" <gi...@apache.org>.
pierrejeambrun closed pull request #63716: Fix make_partial_model
URL: https://github.com/apache/airflow/pull/63716


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "kaxil (via GitHub)" <gi...@apache.org>.
kaxil commented on code in PR #63716:
URL: https://github.com/apache/airflow/pull/63716#discussion_r2941429171


##########
airflow-core/src/airflow/api_fastapi/core_api/base.py:
##########
@@ -54,12 +54,12 @@ 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.default = None
+        new_info = FieldInfo(default=None)

Review Comment:
   Thanks for the context Pierre! I dug into this and found the root cause.
   
   `deepcopy` itself works fine on 2.12.0, but the issue is that after `deepcopy` + setting `.default = None`, pydantic doesn't know the default was explicitly set because `_attributes_set` (an internal dict tracking which attributes were user-provided) doesn't include `"default"`. So `create_model` still treats those fields as required.
   
   The fix that works on both 2.12.0 and 2.12.5 is to use `FieldInfo._copy()` (pydantic's own copy method, exists since at least 2.12.0) and then register the default in `_attributes_set`:
   
   ```python
   new_info = field_info._copy()
   # ... annotation handling ...
   new_info.default = None
   new_info._attributes_set["default"] = None
   ```
   
   This preserves all field attributes (aliases, title, description, etc.) and correctly marks the default as explicitly set. Verified on both 2.12.0 and 2.12.5.
   
   With this fix we could revert the pydantic bump from #63570 back to `>=2.11.0` (or `>=2.12.0` if needed for other reasons).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "pierrejeambrun (via GitHub)" <gi...@apache.org>.
pierrejeambrun commented on PR #63716:
URL: https://github.com/apache/airflow/pull/63716#issuecomment-4068364875

   Closing since we're good with the pydantic bump. If we want to remove the bump, we can re-open and fix the aliasing as mentioned by kaxil.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "kaxil (via GitHub)" <gi...@apache.org>.
kaxil commented on code in PR #63716:
URL: https://github.com/apache/airflow/pull/63716#discussion_r2940827928


##########
airflow-core/src/airflow/api_fastapi/core_api/base.py:
##########
@@ -54,12 +54,12 @@ 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.default = None
+        new_info = FieldInfo(default=None)

Review Comment:
   Constructing a fresh `FieldInfo(default=None)` and only copying `.metadata` drops every other FieldInfo attribute from the original field: `alias`, `validation_alias`, `serialization_alias`, `alias_priority`, `title`, `description`, `json_schema_extra`, `repr`, `frozen`, `deprecated`, `exclude`, `discriminator`, and `examples`.
   
   This breaks real models. `ConnectionBody.connection_id` has `serialization_alias='conn_id'`, `ConnectionBody.schema_` has `alias='schema'`, and `VariableBody.value` has `serialization_alias='val'`. After this change, those partial models lose all their aliases. With `extra='forbid'` on `ConnectionBody`, a PATCH sending `{"schema": "public"}` would get rejected as "extra inputs not permitted" because the `schema` validation alias on `schema_` is gone.
   
   I verified this on pydantic 2.12.5 (the installed version). The old `deepcopy` approach works fine there too, so I'm not sure what breakage this is fixing. If there is a specific pydantic version or scenario where `deepcopy` fails, could you share it? The fix would need to preserve all field attributes, not just metadata.



##########
airflow-core/src/airflow/api_fastapi/core_api/base.py:
##########
@@ -54,12 +54,12 @@ 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.default = None
+        new_info = FieldInfo(default=None)
+        new_info.metadata = list(field_info.metadata)

Review Comment:
   The existing test suite doesn't catch this because `SampleModel` has no aliased fields. Adding a field like `value: str = Field(serialization_alias='val')` or `schema_: str | None = Field(None, alias='schema')` to the test model and asserting the partial model preserves those aliases would prevent this from slipping through.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "pierrejeambrun (via GitHub)" <gi...@apache.org>.
pierrejeambrun commented on code in PR #63716:
URL: https://github.com/apache/airflow/pull/63716#discussion_r2940978381


##########
airflow-core/src/airflow/api_fastapi/core_api/base.py:
##########
@@ -54,12 +54,12 @@ 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.default = None
+        new_info = FieldInfo(default=None)

Review Comment:
   Main implementation isn't working with `2.12.0`. It broke main and that's why we had to bump pydantic in #63570.
   
   Since this forces a quite new pydantic version, I figured we should try to fix it for older pydantic version so we don't have that aggressive bump.
   
   But apparently it's not working well :(.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Fix make_partial_model [airflow]

Posted by "pierrejeambrun (via GitHub)" <gi...@apache.org>.
pierrejeambrun commented on code in PR #63716:
URL: https://github.com/apache/airflow/pull/63716#discussion_r2945551907


##########
airflow-core/src/airflow/api_fastapi/core_api/base.py:
##########
@@ -54,12 +54,12 @@ 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.default = None
+        new_info = FieldInfo(default=None)

Review Comment:
   Done, and manually reverted the bump on the last commit. Lets see what the CI says.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org