This is an automated email from the ASF dual-hosted git repository.

jasonliu 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 e6448226ed5 Fix Enum-str interpolation in callback metrics in Python 
3.11+ (#58168)
e6448226ed5 is described below

commit e6448226ed5a68ec4cedba377750c242e10f5729
Author: Ramit Kataria <[email protected]>
AuthorDate: Tue Nov 11 00:33:49 2025 -0800

    Fix Enum-str interpolation in callback metrics in Python 3.11+ (#58168)
    
    Followup to 
https://github.com/apache/airflow/pull/57215#pullrequestreview-3426156849
    
    Even though CallbackState is a string Enum and works fine with string
    comparisons, the default __str__ method was changed in Python 3.11
    causing test failures due to unexpected metric name:
    
    airflow-core/tests/unit/models/test_callback.py:105: in test_get_metric_info
        assert metric_info["stat"] == "deadline_alerts.callback_success"
    E   AssertionError: assert equals failed
    E     'deadline_alerts.callback_CallbackState.SUCCESS'  
'deadline_alerts.callback_success'
    
    This overrides __str__ in CallbackState so it behaves consistently
    across python versions.
---
 airflow-core/src/airflow/models/callback.py     | 5 ++++-
 airflow-core/tests/unit/models/test_callback.py | 6 +++---
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/airflow-core/src/airflow/models/callback.py 
b/airflow-core/src/airflow/models/callback.py
index bfaa43db6b2..15925e16dd4 100644
--- a/airflow-core/src/airflow/models/callback.py
+++ b/airflow-core/src/airflow/models/callback.py
@@ -50,6 +50,9 @@ class CallbackState(str, Enum):
     SUCCESS = "success"
     FAILED = "failed"
 
+    def __str__(self) -> str:
+        return self.value
+
 
 ACTIVE_STATES = frozenset((CallbackState.QUEUED, CallbackState.RUNNING))
 TERMINAL_STATES = frozenset((CallbackState.SUCCESS, CallbackState.FAILED))
@@ -153,7 +156,7 @@ class Callback(Base):
     def queue(self):
         self.state = CallbackState.QUEUED
 
-    def get_metric_info(self, status: str, result: Any) -> dict:
+    def get_metric_info(self, status: CallbackState, result: Any) -> dict:
         tags = {"result": result, **self.data}
         tags.pop("prefix", None)
 
diff --git a/airflow-core/tests/unit/models/test_callback.py 
b/airflow-core/tests/unit/models/test_callback.py
index 36e09713a3d..ed640ab9a97 100644
--- a/airflow-core/tests/unit/models/test_callback.py
+++ b/airflow-core/tests/unit/models/test_callback.py
@@ -100,7 +100,7 @@ class TestCallback:
     def test_get_metric_info(self):
         callback = TriggererCallback(TEST_ASYNC_CALLBACK, 
prefix="deadline_alerts", dag_id=TEST_DAG_ID)
         callback.data["kwargs"] = {"context": {"dag_id": TEST_DAG_ID}, 
"email": "[email protected]"}
-        metric_info = callback.get_metric_info(CallbackState.SUCCESS.value, 
"0")
+        metric_info = callback.get_metric_info(CallbackState.SUCCESS, "0")
 
         assert metric_info["stat"] == "deadline_alerts.callback_success"
         assert metric_info["tags"] == {
@@ -122,7 +122,7 @@ class TestTriggererCallback:
         assert isinstance(retrieved, TriggererCallback)
         assert retrieved.fetch_method == CallbackFetchMethod.IMPORT_PATH
         assert retrieved.data == TEST_ASYNC_CALLBACK.serialize()
-        assert retrieved.state == CallbackState.PENDING
+        assert retrieved.state == CallbackState.PENDING.value
         assert retrieved.output is None
         assert retrieved.priority_weight == 1
         assert retrieved.created_at is not None
@@ -192,7 +192,7 @@ class TestExecutorCallback:
         assert isinstance(retrieved, ExecutorCallback)
         assert retrieved.fetch_method == CallbackFetchMethod.IMPORT_PATH
         assert retrieved.data == TEST_SYNC_CALLBACK.serialize()
-        assert retrieved.state == CallbackState.PENDING
+        assert retrieved.state == CallbackState.PENDING.value
         assert retrieved.output is None
         assert retrieved.priority_weight == 1
         assert retrieved.created_at is not None

Reply via email to