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

kaxil 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 ebd339fa620 Test that execute_tasks_new_python_interpreter only 
affects the task process (#73149)
ebd339fa620 is described below

commit ebd339fa6202a8d100c18f0fc7b21876c992e515
Author: Kaxil Naik <[email protected]>
AuthorDate: Wed Sep 16 11:59:47 2026 +0100

    Test that execute_tasks_new_python_interpreter only affects the task 
process (#73149)
    
    The existing fork+exec wiring tests patch the platform gate and check it 
flows
    through to WatchedSubprocess.start; none set the option itself. These tests 
set
    [core] execute_tasks_new_python_interpreter and assert the Dag processor, 
the
    triggerer and the callback supervisor still start their child with a bare 
fork.
---
 .../tests/unit/dag_processing/test_processor.py    | 26 ++++++++++++++++++++++
 airflow-core/tests/unit/jobs/test_triggerer_job.py | 16 +++++++++++++
 .../execution_time/test_callback_supervisor.py     | 13 +++++++++++
 3 files changed, 55 insertions(+)

diff --git a/airflow-core/tests/unit/dag_processing/test_processor.py 
b/airflow-core/tests/unit/dag_processing/test_processor.py
index f54b82fef82..d51118f2b61 100644
--- a/airflow-core/tests/unit/dag_processing/test_processor.py
+++ b/airflow-core/tests/unit/dag_processing/test_processor.py
@@ -512,6 +512,32 @@ def test_start_opts_into_fork_exec(monkeypatch, mocker, 
platform_uses_exec, targ
     assert base_start.call_args.kwargs["use_exec"] is expected_use_exec
 
 
[email protected]("option_value", ["True", "False"])
+def test_start_ignores_execute_tasks_new_python_interpreter(mocker, 
option_value):
+    """
+    ``[core] execute_tasks_new_python_interpreter`` is a task-process opt-in 
and must not reach the
+    parsing child, which only follows the platform gate (pinned to bare fork 
by ``_force_bare_fork``).
+    """
+    base_start = mocker.patch(
+        "airflow.sdk.execution_time.supervisor.WatchedSubprocess.start", 
return_value=MagicMock()
+    )
+    
mocker.patch("airflow.dag_processing.processor._pre_import_airflow_modules")
+
+    with conf_vars({("core", "execute_tasks_new_python_interpreter"): 
option_value}):
+        DagFileProcessorProcess.start(
+            path="some_dag.py",
+            bundle_path=pathlib.Path("/tmp/bundle"),
+            bundle_name="testing",
+            dag_file_rel_path="some_dag.py",
+            callbacks=[],
+            client=MagicMock(spec=Client),
+            target=_parse_file_entrypoint,
+            logger=MagicMock(),
+        )
+
+    assert base_start.call_args.kwargs["use_exec"] is False
+
+
 def write_dag_in_a_fn_to_file(fn: Callable[[], None], folder: pathlib.Path) -> 
pathlib.Path:
     # Create the dag in a fn, and use inspect.getsource to write it to a file 
so that
     # a) the test dag is directly viewable here in the tests
diff --git a/airflow-core/tests/unit/jobs/test_triggerer_job.py 
b/airflow-core/tests/unit/jobs/test_triggerer_job.py
index 8231dc2cbec..438bc5f68ec 100644
--- a/airflow-core/tests/unit/jobs/test_triggerer_job.py
+++ b/airflow-core/tests/unit/jobs/test_triggerer_job.py
@@ -298,6 +298,22 @@ def test_start_opts_into_fork_exec(monkeypatch, mocker, 
platform_uses_exec):
     assert base_start.call_args.kwargs["target"] == 
TriggerRunnerSupervisor.run_in_process
 
 
[email protected]("option_value", ["True", "False"])
+def test_start_ignores_execute_tasks_new_python_interpreter(mocker, 
option_value):
+    """
+    ``[core] execute_tasks_new_python_interpreter`` is a task-process opt-in 
and must not reach the
+    runner child, which only follows the platform gate (pinned to bare fork by 
``_force_bare_fork``).
+    """
+    base_start = mocker.patch(
+        "airflow.sdk.execution_time.supervisor.WatchedSubprocess.start", 
return_value=MagicMock()
+    )
+
+    with conf_vars({("core", "execute_tasks_new_python_interpreter"): 
option_value}):
+        TriggerRunnerSupervisor.start(job=Job(id=999), capacity=10)
+
+    assert base_start.call_args.kwargs["use_exec"] is False
+
+
 @pytest.fixture
 def supervisor_builder(mocker, session):
     def builder(job=None):
diff --git a/task-sdk/tests/task_sdk/execution_time/test_callback_supervisor.py 
b/task-sdk/tests/task_sdk/execution_time/test_callback_supervisor.py
index 7ccbb8c0dfa..8acd3feeddf 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_callback_supervisor.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_callback_supervisor.py
@@ -48,6 +48,8 @@ from airflow.sdk.execution_time.comms import (
     _RequestFrame,
 )
 
+from tests_common.test_utils.config import conf_vars
+
 
 def callback_no_args():
     """A simple callback that takes no arguments."""
@@ -553,6 +555,17 @@ class TestCallbackSubprocessStart:
 
         assert exc_info.value.code == 1
 
+    @pytest.mark.parametrize("option_value", ["True", "False"])
+    def test_start_keeps_bare_fork_regardless_of_exec_option(self, 
base_start_kwargs, option_value):
+        """
+        ``[core] execute_tasks_new_python_interpreter`` is a task-process 
opt-in and must not reach the
+        callback child: its target is a closure, which only a bare fork can 
run.
+        """
+        with conf_vars({("core", "execute_tasks_new_python_interpreter"): 
option_value}):
+            CallbackSubprocess.start(**base_start_kwargs)
+
+        assert self.mock_super_start.call_args.kwargs.get("use_exec", False) 
is False
+
 
 class TestSuperviseCallbackExchangesTokenFirst:
     """No callback code may run until the single-use token has been 
exchanged."""

Reply via email to