From 16ba9a0802152d43df811fc18bb304fe14e9c85a Mon Sep 17 00:00:00 2001 From: Amogh Desai Date: Thu, 27 Aug 2026 15:30:20 +0530 Subject: [PATCH 1/2] Add common.compat alias for BaseEventTrigger --- providers/apache/iceberg/pyproject.toml | 2 +- .../apache/iceberg/triggers/iceberg.py | 11 +----- .../providers/common/compat/triggers.py | 38 +++++++++++++++++++ .../tests/unit/common/compat/test_triggers.py | 35 +++++++++++++++++ 4 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 providers/common/compat/src/airflow/providers/common/compat/triggers.py create mode 100644 providers/common/compat/tests/unit/common/compat/test_triggers.py diff --git a/providers/apache/iceberg/pyproject.toml b/providers/apache/iceberg/pyproject.toml index 238b066ceb9de..b25cfaf7f6dbe 100644 --- a/providers/apache/iceberg/pyproject.toml +++ b/providers/apache/iceberg/pyproject.toml @@ -60,7 +60,7 @@ requires-python = ">=3.10" # After you modify the dependencies, and rebuild your Breeze CI image with ``breeze ci-image build`` dependencies = [ "apache-airflow>=2.11.0", - "apache-airflow-providers-common-compat>=1.8.0", + "apache-airflow-providers-common-compat>=1.8.0", # use next version "pyiceberg>=0.8.0", ] diff --git a/providers/apache/iceberg/src/airflow/providers/apache/iceberg/triggers/iceberg.py b/providers/apache/iceberg/src/airflow/providers/apache/iceberg/triggers/iceberg.py index 6777982eefb5a..63f8d28ea4945 100644 --- a/providers/apache/iceberg/src/airflow/providers/apache/iceberg/triggers/iceberg.py +++ b/providers/apache/iceberg/src/airflow/providers/apache/iceberg/triggers/iceberg.py @@ -22,15 +22,8 @@ from pyiceberg.exceptions import NoSuchNamespaceError, NoSuchTableError from airflow.providers.apache.iceberg.hooks.iceberg import IcebergHook -from airflow.providers.apache.iceberg.version_compat import AIRFLOW_V_3_0_PLUS - -if AIRFLOW_V_3_0_PLUS: - from airflow.triggers.base import BaseEventTrigger, TriggerEvent -else: - from airflow.triggers.base import ( # type: ignore[assignment] - BaseTrigger as BaseEventTrigger, - TriggerEvent, - ) +from airflow.providers.common.compat.triggers import BaseEventTrigger +from airflow.triggers.base import TriggerEvent if TYPE_CHECKING: from collections.abc import AsyncIterator diff --git a/providers/common/compat/src/airflow/providers/common/compat/triggers.py b/providers/common/compat/src/airflow/providers/common/compat/triggers.py new file mode 100644 index 0000000000000..77197e7f40188 --- /dev/null +++ b/providers/common/compat/src/airflow/providers/common/compat/triggers.py @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +"""Compatibility imports for core trigger base classes across Airflow 2 and Airflow 3.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from airflow.providers.common.compat._compat_utils import create_module_getattr + +if TYPE_CHECKING: + from airflow.triggers.base import BaseEventTrigger as BaseEventTrigger + + +# Airflow 3.0 added BaseEventTrigger to mark triggers usable as event-driven scheduling sources. +# Airflow 2.x has no such marker, so it falls back to BaseTrigger: subclasses still work as +# deferred triggers there, they just cannot drive an asset watcher. +_RENAME_MAP: dict[str, tuple[str, str, str]] = { + "BaseEventTrigger": ("airflow.triggers.base", "airflow.triggers.base", "BaseTrigger"), +} + +__getattr__ = create_module_getattr(import_map={}, rename_map=_RENAME_MAP) + +__all__ = sorted(_RENAME_MAP.keys()) diff --git a/providers/common/compat/tests/unit/common/compat/test_triggers.py b/providers/common/compat/tests/unit/common/compat/test_triggers.py new file mode 100644 index 0000000000000..1fc9f6d8e0560 --- /dev/null +++ b/providers/common/compat/tests/unit/common/compat/test_triggers.py @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from __future__ import annotations + +import pytest + +import airflow.triggers.base +from airflow.providers.common.compat import triggers + + +class TestBaseEventTrigger: + def test_falls_back_to_base_trigger_without_base_event_trigger(self, monkeypatch): + """On Airflow 2.x, where the class does not exist, the alias resolves to BaseTrigger.""" + monkeypatch.delattr(airflow.triggers.base, "BaseEventTrigger") + + assert triggers.BaseEventTrigger is airflow.triggers.base.BaseTrigger + + def test_unknown_attribute_raises(self): + with pytest.raises(AttributeError, match="module has no attribute 'NotATrigger'"): + triggers.NotATrigger From 63f827ae5c8306d8e0d97da5a512601d9b10d962 Mon Sep 17 00:00:00 2001 From: Amogh Desai Date: Thu, 27 Aug 2026 16:56:38 +0530 Subject: [PATCH 2/2] fixing compat tests --- .../common/compat/tests/unit/common/compat/test_triggers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/common/compat/tests/unit/common/compat/test_triggers.py b/providers/common/compat/tests/unit/common/compat/test_triggers.py index 1fc9f6d8e0560..b458a851d2ae3 100644 --- a/providers/common/compat/tests/unit/common/compat/test_triggers.py +++ b/providers/common/compat/tests/unit/common/compat/test_triggers.py @@ -26,7 +26,8 @@ class TestBaseEventTrigger: def test_falls_back_to_base_trigger_without_base_event_trigger(self, monkeypatch): """On Airflow 2.x, where the class does not exist, the alias resolves to BaseTrigger.""" - monkeypatch.delattr(airflow.triggers.base, "BaseEventTrigger") + # Already absent when the tests run against Airflow 2.x, which is the case being simulated. + monkeypatch.delattr(airflow.triggers.base, "BaseEventTrigger", raising=False) assert triggers.BaseEventTrigger is airflow.triggers.base.BaseTrigger