kaxil commented on code in PR #67877:
URL: https://github.com/apache/airflow/pull/67877#discussion_r3498389471


##########
task-sdk/tests/task_sdk/execution_time/test_task_runner.py:
##########
@@ -5597,6 +5600,141 @@ def test_exception_in_context_manager_propagates(self):
                         raise ValueError("boom")
 
 
+class TestRunExecuteCallable:
+    """Tests for ``_run_execute_callable``.
+
+    It runs the task's execute callable inside an isolated contextvars copy 
(with
+    the ExecutorSafeguard tracker set), applies the execution timeout when one 
is
+    configured, and wraps the call in a ``task.execute`` detail span.
+    """
+
+    @pytest.fixture(autouse=True)
+    def _sampled_carrier_provider(self):
+        """Make new_dagrun_trace_carrier produce a SAMPLED carrier (see 
TestDetailSpan)."""
+        provider = TracerProvider()
+        with mock.patch(
+            "airflow._shared.observability.traces.trace.get_tracer_provider",
+            return_value=provider,
+        ):
+            yield
+
+    @staticmethod
+    def _make_task(execution_timeout=None):
+        task = mock.MagicMock(spec=BaseOperator)
+        task.execution_timeout = execution_timeout
+        return task
+
+    def test_runs_in_isolated_context_with_safeguard_tracker_set(self):
+        """The callable runs in an internal context copy that has the 
safeguard tracker set and does not leak."""
+        var = contextvars.ContextVar("marker")
+        var.set("outer")
+        task = self._make_task()
+        seen = {}
+
+        def execute(context):
+            var.set("inner")
+            seen["tracker"] = ExecutorSafeguard.tracker.get(None)
+            return context["value"] * 2
+
+        result = _run_execute_callable(context={"value": 21}, execute=execute, 
task=task)
+
+        assert result == 42
+        # The safeguard tracker is set to the task inside the copy used to run 
execute.
+        assert seen["tracker"] is task
+        # The mutation happened inside the copy, so it does not leak to the 
caller's context.
+        assert var.get() == "outer"

Review Comment:
   Small gap on the test named for it: the "does not leak" assertion here 
(`assert var.get() == "outer"`) covers the separate `marker` var, but nothing 
asserts the tracker itself is absent from the caller's real context after the 
call. The headline behavior -- `ctx.run(ExecutorSafeguard.tracker.set, task)` 
confining the `.set` to the copy -- isn't directly pinned. Would adding 
something like `assert ExecutorSafeguard.tracker.get(None) is not task` after 
the call be worth it? (No autouse fixture resets the tracker between tests, so 
`is not task` is safer than `is None`.)
   



##########
task-sdk/src/airflow/sdk/execution_time/task_runner.py:
##########
@@ -2065,6 +2065,43 @@ def _send_error_email_notification(
         log.exception("Failed to send email notification")
 
 
+@detail_span("task.execute")
+def _run_execute_callable(
+    context: Context,
+    execute: Callable[..., Any] | functools.partial[Any],
+    task: BaseOperator,
+) -> Any:
+    """
+    Run the task's execute callable, applying the execution timeout if one is 
set.
+
+    The contextvars snapshot is taken here, after the ``task.execute`` span is
+    current, so spans the operator emits during ``execute`` nest under it 
rather
+    than under the caller. ``ExecutorSafeguard``'s tracker is set into that 
copy
+    so the operator's ``execute`` passes the safeguard check, while the copy 
keeps
+    the change from leaking into the surrounding context.
+    """
+    ctx = contextvars.copy_context()

Review Comment:
   Confirming intent on the snapshot timing: moving `copy_context()` here 
(after `pre_execute` / `on_execute_callback` run in `_execute_task`) means 
`execute` now inherits any `ContextVar` those callbacks set, whereas before the 
snapshot was taken at the top of `_execute_task`, ahead of them. I traced the 
SDK contextvars (`_preset_connections` self-resets in 
`Connection.test_connection`'s `finally`; `ExecutorSafeguard.tracker` is set 
into this copy), so I don't see a real leak today, and sharing context with the 
pre-hooks is arguably more consistent. Flagging the delta in case any operator 
deliberately relied on `execute` not seeing pre-hook contextvar mutations -- 
since `on_execute_callback` exceptions are swallowed, a callback that sets a 
var and then raises would now be visible to `execute`. Intended?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to