Skip to content

Commit f4e4553

Browse files
committed
Honor async hook subclass overrides in get_async_connection
The async connection helper resolved connections through the literal BaseHook class, so a hook that overrides aget_connection/get_connection was ignored in the triggerer even though the sync path honors it on the worker. Accept the calling hook (instance or class) and dispatch through it, defaulting to BaseHook, and pass the hook instance from HttpAsyncHook so a custom subclass behaves the same in the triggerer as on the worker. The debug log now names the hook that actually resolved the connection. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
1 parent d3fa64a commit f4e4553

4 files changed

Lines changed: 37 additions & 10 deletions

File tree

providers/common/compat/src/airflow/providers/common/compat/connection/__init__.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,25 @@
2929
log = logging.getLogger(__name__)
3030

3131

32-
async def get_async_connection(conn_id: str) -> Connection:
32+
async def get_async_connection(conn_id: str, hook: BaseHook | type[BaseHook] | None = None) -> Connection:
3333
"""
3434
Get an asynchronous Airflow connection that is backwards compatible.
3535
3636
:param conn_id: The provided connection ID.
37+
:param hook: Hook (class or instance) to resolve the connection through, so a
38+
subclass override of ``aget_connection``/``get_connection`` is honored.
39+
Defaults to ``BaseHook``.
3740
:returns: Connection
3841
"""
3942
from asgiref.sync import sync_to_async
4043

41-
if hasattr(BaseHook, "aget_connection"):
42-
log.debug("Get connection using `BaseHook.aget_connection().")
43-
return await BaseHook.aget_connection(conn_id=conn_id)
44-
log.debug("Get connection using `BaseHook.get_connection().")
45-
return await sync_to_async(BaseHook.get_connection)(conn_id=conn_id)
44+
hook = hook or BaseHook
45+
hook_name = hook.__name__ if isinstance(hook, type) else type(hook).__name__
46+
if hasattr(hook, "aget_connection"):
47+
log.debug("Get connection using `%s.aget_connection()`.", hook_name)
48+
return await hook.aget_connection(conn_id=conn_id)
49+
log.debug("Get connection using `%s.get_connection()`.", hook_name)
50+
return await sync_to_async(hook.get_connection)(conn_id=conn_id)
4651

4752

4853
__all__ = [

providers/common/compat/tests/unit/common/compat/connection/test_connection.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def test_get_async_connection_with_aget(self, _, caplog):
5757
conn = await get_async_connection("test_conn")
5858
assert conn.password == "secret_token_aget"
5959
assert conn.conn_type == "http"
60-
assert "Get connection using `BaseHook.aget_connection()." in caplog.text
60+
assert "Get connection using `MockAgetBaseHook.aget_connection()`." in caplog.text
6161

6262
@mock.patch("airflow.providers.common.compat.connection.BaseHook", new_callable=MockBaseHook)
6363
@pytest.mark.asyncio
@@ -66,4 +66,26 @@ async def test_get_async_connection_with_get_connection(self, _, caplog):
6666
conn = await get_async_connection("test_conn")
6767
assert conn.password == "secret_token"
6868
assert conn.conn_type == "http"
69-
assert "Get connection using `BaseHook.get_connection()." in caplog.text
69+
assert "Get connection using `MockBaseHook.get_connection()`." in caplog.text
70+
71+
@mock.patch("airflow.providers.common.compat.connection.BaseHook", new_callable=MockAgetBaseHook)
72+
@pytest.mark.asyncio
73+
async def test_get_async_connection_honors_passed_hook(self, _):
74+
class OverrideHook:
75+
@classmethod
76+
async def aget_connection(cls, conn_id: str):
77+
return Connection(conn_id="override", conn_type="http", password="override_token")
78+
79+
conn = await get_async_connection("test_conn", hook=OverrideHook)
80+
assert conn.password == "override_token"
81+
82+
@mock.patch("airflow.providers.common.compat.connection.BaseHook", new_callable=MockBaseHook)
83+
@pytest.mark.asyncio
84+
async def test_get_async_connection_honors_passed_hook_get_connection(self, _):
85+
class OverrideHook:
86+
@classmethod
87+
def get_connection(cls, conn_id: str):
88+
return Connection(conn_id="override", conn_type="http", password="override_token")
89+
90+
conn = await get_async_connection("test_conn", hook=OverrideHook)
91+
assert conn.password == "override_token"

providers/http/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ requires-python = ">=3.10"
6060
# After you modify the dependencies, and rebuild your Breeze CI image with ``breeze ci-image build``
6161
dependencies = [
6262
"apache-airflow>=2.11.0",
63-
"apache-airflow-providers-common-compat>=1.12.0",
63+
"apache-airflow-providers-common-compat>=1.12.0", # use next version
6464
# The 2.26.0 release of requests got rid of the chardet LGPL mandatory dependency, allowing us to
6565
# release it as a requirement for airflow
6666
"requests>=2.32.0,<3",

providers/http/src/airflow/providers/http/hooks/http.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ async def config(self) -> SessionConfig:
615615
extra_options: dict[str, Any] = {}
616616

617617
if self.http_conn_id:
618-
conn = await get_async_connection(conn_id=self.http_conn_id)
618+
conn = await get_async_connection(conn_id=self.http_conn_id, hook=self)
619619

620620
if conn.host and "://" in conn.host:
621621
base_url = conn.host

0 commit comments

Comments
 (0)