From 147a06f1a2baa33d567383103ce250c2378cf7c1 Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Wed, 6 May 2026 16:03:28 -0700 Subject: [PATCH 1/7] Thread version_data through BundleInfo to worker-side bundle initialization Add version_data to the push path so structured bundle metadata (e.g., S3 manifests) reaches workers at task execution time. Changes: - Add version_data field to BundleInfo (workloads/base.py) - Populate version_data from DagVersion in ExecuteTask.make() - Add selectinload(TI.dag_version) to scheduler enqueue query to avoid N+1 queries when reading version_data - Add version_data parameter to BaseDagBundle.__init__ (stored as self.version_data) and DagBundlesManager.get_bundle() - Pass version_data through task_runner.py and callback_supervisor.py - Regenerate task-sdk datamodels to include version_data in BundleInfo Existing bundles ignore version_data (defaults to None). The S3 bundle will use self.version_data in initialize() to fetch specific object versions (follow-up PR). --- airflow-core/src/airflow/dag_processing/bundles/base.py | 2 ++ .../src/airflow/dag_processing/bundles/manager.py | 9 +++++++-- airflow-core/src/airflow/executors/workloads/base.py | 1 + airflow-core/src/airflow/executors/workloads/task.py | 4 ++++ airflow-core/src/airflow/jobs/scheduler_job_runner.py | 1 + task-sdk/src/airflow/sdk/api/datamodels/_generated.py | 1 + .../airflow/sdk/execution_time/callback_supervisor.py | 1 + task-sdk/src/airflow/sdk/execution_time/task_runner.py | 1 + 8 files changed, 18 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/dag_processing/bundles/base.py b/airflow-core/src/airflow/dag_processing/bundles/base.py index b6b55f9251cfe..2b7a5734e76c7 100644 --- a/airflow-core/src/airflow/dag_processing/bundles/base.py +++ b/airflow-core/src/airflow/dag_processing/bundles/base.py @@ -304,10 +304,12 @@ def __init__( name: str, refresh_interval: int = conf.getint("dag_processor", "refresh_interval"), version: str | None = None, + version_data: dict | None = None, view_url_template: str | None = None, ) -> None: self.name = name self.version = version + self.version_data = version_data self.refresh_interval = refresh_interval self.is_initialized: bool = False diff --git a/airflow-core/src/airflow/dag_processing/bundles/manager.py b/airflow-core/src/airflow/dag_processing/bundles/manager.py index f2f66cd7d2ad5..804e93a9f4533 100644 --- a/airflow-core/src/airflow/dag_processing/bundles/manager.py +++ b/airflow-core/src/airflow/dag_processing/bundles/manager.py @@ -324,19 +324,24 @@ def _extract_template_params(bundle_instance: BaseDagBundle) -> dict: return params - def get_bundle(self, name: str, version: str | None = None) -> BaseDagBundle: + def get_bundle( + self, name: str, version: str | None = None, version_data: dict | None = None + ) -> BaseDagBundle: """ Get a DAG bundle by name. :param name: The name of the DAG bundle. :param version: The version of the DAG bundle you need (optional). If not provided, ``tracking_ref`` will be used instead. + :param version_data: Optional structured data associated with this version (e.g., S3 manifest). :return: The DAG bundle. """ cfg_bundle = self._bundle_config.get(name) if not cfg_bundle: raise ValueError(f"Requested bundle '{name}' is not configured.") - return cfg_bundle.bundle_class(name=name, version=version, **cfg_bundle.kwargs) + return cfg_bundle.bundle_class( + name=name, version=version, version_data=version_data, **cfg_bundle.kwargs + ) def get_all_dag_bundles(self) -> Iterable[BaseDagBundle]: """ diff --git a/airflow-core/src/airflow/executors/workloads/base.py b/airflow-core/src/airflow/executors/workloads/base.py index 503cab7b3965a..a7f4d0c14622a 100644 --- a/airflow-core/src/airflow/executors/workloads/base.py +++ b/airflow-core/src/airflow/executors/workloads/base.py @@ -66,6 +66,7 @@ class BundleInfo(BaseModel): name: str version: str | None = None + version_data: dict | None = None class BaseWorkloadSchema(BaseModel): diff --git a/airflow-core/src/airflow/executors/workloads/task.py b/airflow-core/src/airflow/executors/workloads/task.py index 9af3f33c10efd..a983964bc2861 100644 --- a/airflow-core/src/airflow/executors/workloads/task.py +++ b/airflow-core/src/airflow/executors/workloads/task.py @@ -118,9 +118,13 @@ def make( ser_ti = TaskInstanceDTO.model_validate(ti, from_attributes=True) if not bundle_info: + version_data = None + if ti.dag_version is not None: + version_data = ti.dag_version.version_data bundle_info = BundleInfo( name=ti.dag_model.bundle_name, version=ti.dag_run.bundle_version, + version_data=version_data, ) fname = log_filename_template_renderer()(ti=ti) diff --git a/airflow-core/src/airflow/jobs/scheduler_job_runner.py b/airflow-core/src/airflow/jobs/scheduler_job_runner.py index 6acd92fbe9285..fd75e680a48dc 100644 --- a/airflow-core/src/airflow/jobs/scheduler_job_runner.py +++ b/airflow-core/src/airflow/jobs/scheduler_job_runner.py @@ -650,6 +650,7 @@ def _executable_task_instances_to_queued(self, max_tis: int, session: Session) - ranked_query.c.map_index_for_ordering, ) .options(selectinload(TI.dag_model)) + .options(selectinload(TI.dag_version)) ) query = query.limit(max_tis) diff --git a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py index 62c43ac17d10b..9b660b048645d 100644 --- a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py +++ b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py @@ -513,6 +513,7 @@ class BundleInfo(BaseModel): name: Annotated[str, Field(title="Name")] version: Annotated[str | None, Field(title="Version")] = None + version_data: Annotated[dict[str, Any] | None, Field(title="Version Data")] = None class TerminalTIState(str, Enum): diff --git a/task-sdk/src/airflow/sdk/execution_time/callback_supervisor.py b/task-sdk/src/airflow/sdk/execution_time/callback_supervisor.py index 579833f413df0..7844a6fac460c 100644 --- a/task-sdk/src/airflow/sdk/execution_time/callback_supervisor.py +++ b/task-sdk/src/airflow/sdk/execution_time/callback_supervisor.py @@ -201,6 +201,7 @@ def _target(): bundle = DagBundlesManager().get_bundle( name=bundle_info.name, version=bundle_info.version, + version_data=bundle_info.version_data, ) bundle.initialize() if (bundle_path := str(bundle.path)) not in sys.path: diff --git a/task-sdk/src/airflow/sdk/execution_time/task_runner.py b/task-sdk/src/airflow/sdk/execution_time/task_runner.py index 313309fa40100..1f55be068b00d 100644 --- a/task-sdk/src/airflow/sdk/execution_time/task_runner.py +++ b/task-sdk/src/airflow/sdk/execution_time/task_runner.py @@ -844,6 +844,7 @@ def parse(what: StartupDetails, log: Logger) -> RuntimeTaskInstance: bundle_instance = DagBundlesManager().get_bundle( name=bundle_info.name, version=bundle_info.version, + version_data=bundle_info.version_data, ) bundle_instance.initialize() _verify_bundle_access(bundle_instance, log) From be30b30e886d7887a791bb192159236dd5851b0b Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Tue, 19 May 2026 17:14:44 -0700 Subject: [PATCH 2/7] Fix: add dag_version mock and update edge OpenAPI spec for version_data field --- .../unit/amazon/aws/executors/ecs/test_ecs_executor.py | 1 + .../providers/edge3/worker_api/v2-edge-generated.yaml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/providers/amazon/tests/unit/amazon/aws/executors/ecs/test_ecs_executor.py b/providers/amazon/tests/unit/amazon/aws/executors/ecs/test_ecs_executor.py index f350c88498124..49e0a934feff9 100644 --- a/providers/amazon/tests/unit/amazon/aws/executors/ecs/test_ecs_executor.py +++ b/providers/amazon/tests/unit/amazon/aws/executors/ecs/test_ecs_executor.py @@ -1308,6 +1308,7 @@ def test_try_adopt_task_instances(self, mock_executor): task.dag_model = mock.Mock() task.dag_model.bundle_name = "test_bundle" task.dag_model.relative_fileloc = "test_dag.py" + task.dag_version = mock.Mock(version_data=None) task.dag_run = mock.Mock() task.dag_run.bundle_version = "1.0.0" task.dag_run.context_carrier = {} diff --git a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml index 6b006a929162d..c12999b9c3a4a 100644 --- a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml +++ b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml @@ -959,6 +959,12 @@ components: - type: string - type: 'null' title: Version + version_data: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Version Data type: object required: - name From d4477f636ff54a3875fa2fa4de8fae72dcf34def Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Tue, 19 May 2026 17:51:36 -0700 Subject: [PATCH 3/7] Tighten version_data type to dict[str, Any] and add unit tests Address review feedback: - Use dict[str, Any] | None instead of bare dict | None for version_data in both BaseDagBundle.__init__ and BundleInfo - Add minimal tests verifying version_data plumbing through the bundle constructor --- .../src/airflow/dag_processing/bundles/base.py | 2 +- .../src/airflow/executors/workloads/base.py | 4 ++-- .../tests/unit/dag_processing/bundles/test_base.py | 13 +++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/dag_processing/bundles/base.py b/airflow-core/src/airflow/dag_processing/bundles/base.py index 2b7a5734e76c7..4fde5750b39e4 100644 --- a/airflow-core/src/airflow/dag_processing/bundles/base.py +++ b/airflow-core/src/airflow/dag_processing/bundles/base.py @@ -304,7 +304,7 @@ def __init__( name: str, refresh_interval: int = conf.getint("dag_processor", "refresh_interval"), version: str | None = None, - version_data: dict | None = None, + version_data: dict[str, Any] | None = None, view_url_template: str | None = None, ) -> None: self.name = name diff --git a/airflow-core/src/airflow/executors/workloads/base.py b/airflow-core/src/airflow/executors/workloads/base.py index a7f4d0c14622a..8a480bcf41107 100644 --- a/airflow-core/src/airflow/executors/workloads/base.py +++ b/airflow-core/src/airflow/executors/workloads/base.py @@ -21,7 +21,7 @@ import os from abc import ABC, abstractmethod from collections.abc import Hashable -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from pydantic import BaseModel, ConfigDict, Field @@ -66,7 +66,7 @@ class BundleInfo(BaseModel): name: str version: str | None = None - version_data: dict | None = None + version_data: dict[str, Any] | None = None class BaseWorkloadSchema(BaseModel): diff --git a/airflow-core/tests/unit/dag_processing/bundles/test_base.py b/airflow-core/tests/unit/dag_processing/bundles/test_base.py index 6fc7ba39a0a12..f092f3e00e770 100644 --- a/airflow-core/tests/unit/dag_processing/bundles/test_base.py +++ b/airflow-core/tests/unit/dag_processing/bundles/test_base.py @@ -323,3 +323,16 @@ def test_bundle_version_inequality(self): bv1 = BundleVersion(version="abc", data={"key": "val"}) bv2 = BundleVersion(version="abc", data={"key": "other"}) assert bv1 != bv2 + + +def test_version_data_stored_on_bundle(): + """Test that version_data passed to a bundle constructor is stored on the instance.""" + manifest = {"schema_version": 1, "files": {"dags/my_dag.py": "S3VersionId123"}} + bundle = BasicBundle(name="test", version="abc", version_data=manifest) + assert bundle.version_data == manifest + + +def test_version_data_defaults_to_none(): + """Test that version_data defaults to None when not provided.""" + bundle = BasicBundle(name="test") + assert bundle.version_data is None From c3a1fe9d0342b26311a52edf6e61b22eda92ac69 Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Wed, 20 May 2026 12:46:00 -0700 Subject: [PATCH 4/7] Fix: revert state_class_for_key to permissive fallback for non-TaskInstanceKey types The strict TypeError for unknown key types broke executor tests that pass Mock objects or raw tuples as keys (Lambda, Batch, ECS, Kubernetes). Restore the original fallback to CallbackState for any non-TaskInstanceKey, matching main's behavior. --- airflow-core/src/airflow/executors/workloads/types.py | 4 +--- airflow-core/tests/unit/executors/test_workloads.py | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/airflow-core/src/airflow/executors/workloads/types.py b/airflow-core/src/airflow/executors/workloads/types.py index 09cd2c3b359e8..6d5b37ee0bb57 100644 --- a/airflow-core/src/airflow/executors/workloads/types.py +++ b/airflow-core/src/airflow/executors/workloads/types.py @@ -41,6 +41,4 @@ def state_class_for_key(key: WorkloadKey) -> type[TaskInstanceState] | type[CallbackState]: if isinstance(key, TaskInstanceKey): return TaskInstanceState - if isinstance(key, CallbackKey): - return CallbackState - raise TypeError(f"Unknown workload key type: {type(key)!r}") + return CallbackState diff --git a/airflow-core/tests/unit/executors/test_workloads.py b/airflow-core/tests/unit/executors/test_workloads.py index 6f027a4d3be6c..fe0165476ac2c 100644 --- a/airflow-core/tests/unit/executors/test_workloads.py +++ b/airflow-core/tests/unit/executors/test_workloads.py @@ -118,11 +118,12 @@ def test_callback_key_is_not_a_string(): assert not isinstance(key, str) -def test_state_class_for_key_raises_on_unknown_type(): - """state_class_for_key should raise TypeError for unrecognized key types.""" +def test_state_class_for_key_falls_back_to_callback_state(): + """state_class_for_key should fall back to CallbackState for non-TaskInstanceKey types.""" + from airflow.utils.state import CallbackState - with pytest.raises(TypeError, match="Unknown workload key type"): - state_class_for_key("bare-string-is-not-a-key") # type: ignore[arg-type] + result = state_class_for_key("bare-string-is-not-a-key") # type: ignore[arg-type] + assert result is CallbackState def test_callback_dto_key_returns_callback_key_instance(): From 854b27b184af8441e23c5f4830b23a4fc3e8d4bd Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Wed, 20 May 2026 13:59:16 -0700 Subject: [PATCH 5/7] Fix: guard CallbackKey dataclass test with AIRFLOW_V_3_3_PLUS for compat with 3.2.x The test_process_workloads_routes_execute_callback test uses CallbackKey(id=...) which requires the dataclass form introduced in 3.3. In Airflow 3.2.x, CallbackKey is a str type alias and does not accept keyword arguments. Change the skipif guard from AIRFLOW_V_3_2_PLUS to AIRFLOW_V_3_3_PLUS. --- .../celery/tests/unit/celery/executors/test_celery_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/celery/tests/unit/celery/executors/test_celery_executor.py b/providers/celery/tests/unit/celery/executors/test_celery_executor.py index c11ea80a5baaf..6242f0ac10ce5 100644 --- a/providers/celery/tests/unit/celery/executors/test_celery_executor.py +++ b/providers/celery/tests/unit/celery/executors/test_celery_executor.py @@ -885,7 +885,7 @@ def test_celery_tasks_registered_on_import(): ) -@pytest.mark.skipif(not AIRFLOW_V_3_2_PLUS, reason="ExecuteCallback requires Airflow 3.2+") +@pytest.mark.skipif(not AIRFLOW_V_3_3_PLUS, reason="CallbackKey dataclass requires Airflow 3.3+") @pytest.mark.parametrize( ("callback_data", "expected_queue"), [ From a345f69ac3f190a4ced9c009a499b74ecf334dd1 Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Mon, 25 May 2026 16:30:18 -0700 Subject: [PATCH 6/7] Address review feedback: guard version_data for unpinned runs, fix serialization - Only populate version_data when dag_run.bundle_version is not None, mirroring the pinning rule for bundle_version (kaxil feedback) - Add model_serializer to BundleInfo so version_data is absent (not null) on the wire when None (ashb feedback) - Update edge OpenAPI spec: version_data is type:object, not anyOf with null - Add :param version_data: to BaseDagBundle docstring (kaxil feedback) - Remove unrelated changes from bad rebase (types.py, celery test) that were fixed separately in #66973 (ashb feedback) --- airflow-core/src/airflow/dag_processing/bundles/base.py | 2 ++ airflow-core/src/airflow/executors/workloads/base.py | 9 ++++++++- airflow-core/src/airflow/executors/workloads/task.py | 2 +- airflow-core/src/airflow/executors/workloads/types.py | 4 +++- airflow-core/tests/unit/executors/test_workloads.py | 9 ++++----- .../tests/unit/celery/executors/test_celery_executor.py | 2 +- .../providers/edge3/worker_api/v2-edge-generated.yaml | 6 ++---- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/dag_processing/bundles/base.py b/airflow-core/src/airflow/dag_processing/bundles/base.py index 4fde5750b39e4..344a3349fecab 100644 --- a/airflow-core/src/airflow/dag_processing/bundles/base.py +++ b/airflow-core/src/airflow/dag_processing/bundles/base.py @@ -292,6 +292,8 @@ class BaseDagBundle(ABC): :param refresh_interval: How often the bundle should be refreshed from the source in seconds (Optional - defaults to [dag_processor] refresh_interval) :param version: Version of the DAG bundle (Optional) + :param version_data: Structured metadata for this bundle version, e.g. an S3 manifest. + Only populated for pinned runs (where dag_run.bundle_version is not None). (Optional) """ supports_versioning: bool = False diff --git a/airflow-core/src/airflow/executors/workloads/base.py b/airflow-core/src/airflow/executors/workloads/base.py index 8a480bcf41107..3dd5efbdfb82d 100644 --- a/airflow-core/src/airflow/executors/workloads/base.py +++ b/airflow-core/src/airflow/executors/workloads/base.py @@ -23,7 +23,7 @@ from collections.abc import Hashable from typing import TYPE_CHECKING, Any -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, model_serializer from airflow.configuration import conf @@ -68,6 +68,13 @@ class BundleInfo(BaseModel): version: str | None = None version_data: dict[str, Any] | None = None + @model_serializer(mode="wrap") + def _serialize(self, handler: Any) -> dict[str, Any]: + data = handler(self) + if data.get("version_data") is None: + data.pop("version_data", None) + return data + class BaseWorkloadSchema(BaseModel): """Base Pydantic schema for executor workload DTOs.""" diff --git a/airflow-core/src/airflow/executors/workloads/task.py b/airflow-core/src/airflow/executors/workloads/task.py index a983964bc2861..63cbf75a749e3 100644 --- a/airflow-core/src/airflow/executors/workloads/task.py +++ b/airflow-core/src/airflow/executors/workloads/task.py @@ -119,7 +119,7 @@ def make( ser_ti = TaskInstanceDTO.model_validate(ti, from_attributes=True) if not bundle_info: version_data = None - if ti.dag_version is not None: + if ti.dag_version is not None and ti.dag_run.bundle_version is not None: version_data = ti.dag_version.version_data bundle_info = BundleInfo( name=ti.dag_model.bundle_name, diff --git a/airflow-core/src/airflow/executors/workloads/types.py b/airflow-core/src/airflow/executors/workloads/types.py index 6d5b37ee0bb57..09cd2c3b359e8 100644 --- a/airflow-core/src/airflow/executors/workloads/types.py +++ b/airflow-core/src/airflow/executors/workloads/types.py @@ -41,4 +41,6 @@ def state_class_for_key(key: WorkloadKey) -> type[TaskInstanceState] | type[CallbackState]: if isinstance(key, TaskInstanceKey): return TaskInstanceState - return CallbackState + if isinstance(key, CallbackKey): + return CallbackState + raise TypeError(f"Unknown workload key type: {type(key)!r}") diff --git a/airflow-core/tests/unit/executors/test_workloads.py b/airflow-core/tests/unit/executors/test_workloads.py index fe0165476ac2c..6f027a4d3be6c 100644 --- a/airflow-core/tests/unit/executors/test_workloads.py +++ b/airflow-core/tests/unit/executors/test_workloads.py @@ -118,12 +118,11 @@ def test_callback_key_is_not_a_string(): assert not isinstance(key, str) -def test_state_class_for_key_falls_back_to_callback_state(): - """state_class_for_key should fall back to CallbackState for non-TaskInstanceKey types.""" - from airflow.utils.state import CallbackState +def test_state_class_for_key_raises_on_unknown_type(): + """state_class_for_key should raise TypeError for unrecognized key types.""" - result = state_class_for_key("bare-string-is-not-a-key") # type: ignore[arg-type] - assert result is CallbackState + with pytest.raises(TypeError, match="Unknown workload key type"): + state_class_for_key("bare-string-is-not-a-key") # type: ignore[arg-type] def test_callback_dto_key_returns_callback_key_instance(): diff --git a/providers/celery/tests/unit/celery/executors/test_celery_executor.py b/providers/celery/tests/unit/celery/executors/test_celery_executor.py index 6242f0ac10ce5..c11ea80a5baaf 100644 --- a/providers/celery/tests/unit/celery/executors/test_celery_executor.py +++ b/providers/celery/tests/unit/celery/executors/test_celery_executor.py @@ -885,7 +885,7 @@ def test_celery_tasks_registered_on_import(): ) -@pytest.mark.skipif(not AIRFLOW_V_3_3_PLUS, reason="CallbackKey dataclass requires Airflow 3.3+") +@pytest.mark.skipif(not AIRFLOW_V_3_2_PLUS, reason="ExecuteCallback requires Airflow 3.2+") @pytest.mark.parametrize( ("callback_data", "expected_queue"), [ diff --git a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml index c12999b9c3a4a..67f93fd60de0f 100644 --- a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml +++ b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml @@ -960,10 +960,8 @@ components: - type: 'null' title: Version version_data: - anyOf: - - additionalProperties: true - type: object - - type: 'null' + additionalProperties: true + type: object title: Version Data type: object required: From 87dd9a8e518616c4e2355a18b43464b5cb975892 Mon Sep 17 00:00:00 2001 From: Niko Oliveira Date: Mon, 25 May 2026 17:20:41 -0700 Subject: [PATCH 7/7] Fix: remove model_serializer that broke OpenAPI schema generation, regenerate artifacts The model_serializer(mode='wrap') on BundleInfo caused Pydantic to lose JSON schema information, making the OpenAPI generator produce a generic 'additionalProperties: true, type: object' instead of the full BundleInfo schema with name/version/version_data fields. Removing the custom serializer restores correct schema generation. The version_data field is Optional so receivers already handle null. Also regenerates: - edge OpenAPI spec (v2-edge-generated.yaml) - supervisor schema snapshot (schema.json) - uv.lock (reflects upstream dependency changes after rebase) --- .../src/airflow/executors/workloads/base.py | 9 +-------- .../edge3/worker_api/v2-edge-generated.yaml | 6 ++++-- .../airflow/sdk/execution_time/schema/schema.json | 13 +++++++++++++ uv.lock | 6 +++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/executors/workloads/base.py b/airflow-core/src/airflow/executors/workloads/base.py index 3dd5efbdfb82d..8a480bcf41107 100644 --- a/airflow-core/src/airflow/executors/workloads/base.py +++ b/airflow-core/src/airflow/executors/workloads/base.py @@ -23,7 +23,7 @@ from collections.abc import Hashable from typing import TYPE_CHECKING, Any -from pydantic import BaseModel, ConfigDict, Field, model_serializer +from pydantic import BaseModel, ConfigDict, Field from airflow.configuration import conf @@ -68,13 +68,6 @@ class BundleInfo(BaseModel): version: str | None = None version_data: dict[str, Any] | None = None - @model_serializer(mode="wrap") - def _serialize(self, handler: Any) -> dict[str, Any]: - data = handler(self) - if data.get("version_data") is None: - data.pop("version_data", None) - return data - class BaseWorkloadSchema(BaseModel): """Base Pydantic schema for executor workload DTOs.""" diff --git a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml index 67f93fd60de0f..c12999b9c3a4a 100644 --- a/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml +++ b/providers/edge3/src/airflow/providers/edge3/worker_api/v2-edge-generated.yaml @@ -960,8 +960,10 @@ components: - type: 'null' title: Version version_data: - additionalProperties: true - type: object + anyOf: + - additionalProperties: true + type: object + - type: 'null' title: Version Data type: object required: diff --git a/task-sdk/src/airflow/sdk/execution_time/schema/schema.json b/task-sdk/src/airflow/sdk/execution_time/schema/schema.json index fec9596e49391..0d2c24040e743 100644 --- a/task-sdk/src/airflow/sdk/execution_time/schema/schema.json +++ b/task-sdk/src/airflow/sdk/execution_time/schema/schema.json @@ -4961,6 +4961,19 @@ ], "default": null, "title": "Version" + }, + "version_data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Version Data" } }, "required": [ diff --git a/uv.lock b/uv.lock index 471df443b5a51..0959f3428d08b 100644 --- a/uv.lock +++ b/uv.lock @@ -5092,7 +5092,7 @@ requires-dist = [ { name = "cachetools", specifier = ">=6.0" }, { name = "flask", specifier = ">=2.2.1" }, { name = "flask-appbuilder", specifier = "==5.2.1" }, - { name = "flask-limiter", specifier = ">3,!=3.13" }, + { name = "flask-limiter", specifier = ">3" }, { name = "flask-login", marker = "python_full_version < '3.14'", specifier = ">=0.6.2" }, { name = "flask-login", marker = "python_full_version >= '3.14'", specifier = ">=0.6.3" }, { name = "flask-session", specifier = ">=0.8.0" }, @@ -6043,7 +6043,7 @@ requires-dist = [ { name = "azure-datalake-store", specifier = ">=0.0.45" }, { name = "azure-identity", specifier = ">=1.3.1" }, { name = "azure-keyvault-secrets", specifier = ">=4.1.0" }, - { name = "azure-kusto-data", specifier = ">=4.1.0,!=4.6.0,!=5.0.0" }, + { name = "azure-kusto-data", specifier = ">=4.1.0,!=5.0.0" }, { name = "azure-mgmt-compute", specifier = ">=33.0.0" }, { name = "azure-mgmt-containerinstance", specifier = ">=10.1.0" }, { name = "azure-mgmt-containerregistry", specifier = ">=8.0.0" }, @@ -7659,7 +7659,7 @@ docs = [ requires-dist = [ { name = "apache-airflow", editable = "." }, { name = "apache-airflow-providers-common-compat", editable = "providers/common/compat" }, - { name = "tableauserverclient", specifier = ">=0.27,!=0.39" }, + { name = "tableauserverclient", specifier = ">=0.27" }, ] [package.metadata.requires-dev]