This is an automated email from the ASF dual-hosted git repository.
vincbeck 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 05011bdc714 Fix BaseBranchOperator import on Airflow 2 without
standard provider (#73140)
05011bdc714 is described below
commit 05011bdc714b2508b420c2f6575e62dad2fd6f36
Author: Vincent <[email protected]>
AuthorDate: Mon Sep 14 14:53:02 2026 -0400
Fix BaseBranchOperator import on Airflow 2 without standard provider
(#73140)
The standard provider is only an optional extra of the providers that use
these classes, so a deployment on Airflow 2 can legitimately not have it
installed. Falling back to the Airflow 2 core module keeps the compat layer
resolvable with just the `apache-airflow` dependency it already declares,
matching how every other standard-provider entry in this layer behaves.
---
.../src/airflow/providers/common/compat/sdk.py | 12 ++++++++++--
.../compat/tests/unit/common/compat/test_sdk.py | 22 ++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/providers/common/compat/src/airflow/providers/common/compat/sdk.py
b/providers/common/compat/src/airflow/providers/common/compat/sdk.py
index 93174df7b2a..f8c7ef049cc 100644
--- a/providers/common/compat/src/airflow/providers/common/compat/sdk.py
+++ b/providers/common/compat/src/airflow/providers/common/compat/sdk.py
@@ -153,8 +153,16 @@ _IMPORT_MAP: dict[str, str | tuple[str, ...]] = {
#
============================================================================
# Branching
#
============================================================================
- "BaseBranchOperator": ("airflow.sdk.bases.branch",
"airflow.providers.standard.operators.branch"),
- "BranchMixIn": ("airflow.sdk.bases.branch",
"airflow.providers.standard.operators.branch"),
+ "BaseBranchOperator": (
+ "airflow.sdk.bases.branch",
+ "airflow.operators.branch",
+ "airflow.providers.standard.operators.branch",
+ ),
+ "BranchMixIn": (
+ "airflow.sdk.bases.branch",
+ "airflow.operators.branch",
+ "airflow.providers.standard.operators.branch",
+ ),
"SkipMixin": (
"airflow.sdk.bases.skipmixin",
"airflow.models.skipmixin",
diff --git a/providers/common/compat/tests/unit/common/compat/test_sdk.py
b/providers/common/compat/tests/unit/common/compat/test_sdk.py
index 08b5107e493..d6f819ec9e5 100644
--- a/providers/common/compat/tests/unit/common/compat/test_sdk.py
+++ b/providers/common/compat/tests/unit/common/compat/test_sdk.py
@@ -18,8 +18,12 @@
from __future__ import annotations
+import builtins
+
import pytest
+from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
+
def test_all_compat_imports_work():
"""
@@ -46,6 +50,24 @@ def test_all_compat_imports_work():
pytest.fail(error_msg)
[email protected](AIRFLOW_V_3_0_PLUS, reason="Test requires Airflow < 3.0")
[email protected]("name", ["BaseBranchOperator", "BranchMixIn"])
+def test_branching_imports_work_without_standard_provider(name, monkeypatch):
+ """On Airflow 2 the standard provider is optional, so core paths must be
used as fallback."""
+ from airflow.providers.common.compat import sdk
+
+ real_import = builtins.__import__
+
+ def fake_import(module_name, *args, **kwargs):
+ if module_name.startswith("airflow.providers.standard"):
+ raise ModuleNotFoundError(f"No module named {module_name!r}")
+ return real_import(module_name, *args, **kwargs)
+
+ monkeypatch.setattr(builtins, "__import__", fake_import)
+
+ assert getattr(sdk, name) is not None
+
+
def test_invalid_import_raises_attribute_error():
"""Test that importing non-existent attribute raises AttributeError."""
from airflow.providers.common.compat import sdk