Skip to content

Commit ebd339f

Browse files
authored
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.
1 parent 60bde11 commit ebd339f

3 files changed

Lines changed: 55 additions & 0 deletions

File tree

‎airflow-core/tests/unit/dag_processing/test_processor.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,32 @@ def test_start_opts_into_fork_exec(monkeypatch, mocker, platform_uses_exec, targ
512512
assert base_start.call_args.kwargs["use_exec"] is expected_use_exec
513513

514514

515+
@pytest.mark.parametrize("option_value", ["True", "False"])
516+
def test_start_ignores_execute_tasks_new_python_interpreter(mocker, option_value):
517+
"""
518+
``[core] execute_tasks_new_python_interpreter`` is a task-process opt-in and must not reach the
519+
parsing child, which only follows the platform gate (pinned to bare fork by ``_force_bare_fork``).
520+
"""
521+
base_start = mocker.patch(
522+
"airflow.sdk.execution_time.supervisor.WatchedSubprocess.start", return_value=MagicMock()
523+
)
524+
mocker.patch("airflow.dag_processing.processor._pre_import_airflow_modules")
525+
526+
with conf_vars({("core", "execute_tasks_new_python_interpreter"): option_value}):
527+
DagFileProcessorProcess.start(
528+
path="some_dag.py",
529+
bundle_path=pathlib.Path("/tmp/bundle"),
530+
bundle_name="testing",
531+
dag_file_rel_path="some_dag.py",
532+
callbacks=[],
533+
client=MagicMock(spec=Client),
534+
target=_parse_file_entrypoint,
535+
logger=MagicMock(),
536+
)
537+
538+
assert base_start.call_args.kwargs["use_exec"] is False
539+
540+
515541
def write_dag_in_a_fn_to_file(fn: Callable[[], None], folder: pathlib.Path) -> pathlib.Path:
516542
# Create the dag in a fn, and use inspect.getsource to write it to a file so that
517543
# a) the test dag is directly viewable here in the tests

‎airflow-core/tests/unit/jobs/test_triggerer_job.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,22 @@ def test_start_opts_into_fork_exec(monkeypatch, mocker, platform_uses_exec):
298298
assert base_start.call_args.kwargs["target"] == TriggerRunnerSupervisor.run_in_process
299299

300300

301+
@pytest.mark.parametrize("option_value", ["True", "False"])
302+
def test_start_ignores_execute_tasks_new_python_interpreter(mocker, option_value):
303+
"""
304+
``[core] execute_tasks_new_python_interpreter`` is a task-process opt-in and must not reach the
305+
runner child, which only follows the platform gate (pinned to bare fork by ``_force_bare_fork``).
306+
"""
307+
base_start = mocker.patch(
308+
"airflow.sdk.execution_time.supervisor.WatchedSubprocess.start", return_value=MagicMock()
309+
)
310+
311+
with conf_vars({("core", "execute_tasks_new_python_interpreter"): option_value}):
312+
TriggerRunnerSupervisor.start(job=Job(id=999), capacity=10)
313+
314+
assert base_start.call_args.kwargs["use_exec"] is False
315+
316+
301317
@pytest.fixture
302318
def supervisor_builder(mocker, session):
303319
def builder(job=None):

‎task-sdk/tests/task_sdk/execution_time/test_callback_supervisor.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
_RequestFrame,
4949
)
5050

51+
from tests_common.test_utils.config import conf_vars
52+
5153

5254
def callback_no_args():
5355
"""A simple callback that takes no arguments."""
@@ -553,6 +555,17 @@ def test_callback_supervisor_should_exit_on_error(self, base_start_kwargs):
553555

554556
assert exc_info.value.code == 1
555557

558+
@pytest.mark.parametrize("option_value", ["True", "False"])
559+
def test_start_keeps_bare_fork_regardless_of_exec_option(self, base_start_kwargs, option_value):
560+
"""
561+
``[core] execute_tasks_new_python_interpreter`` is a task-process opt-in and must not reach the
562+
callback child: its target is a closure, which only a bare fork can run.
563+
"""
564+
with conf_vars({("core", "execute_tasks_new_python_interpreter"): option_value}):
565+
CallbackSubprocess.start(**base_start_kwargs)
566+
567+
assert self.mock_super_start.call_args.kwargs.get("use_exec", False) is False
568+
556569

557570
class TestSuperviseCallbackExchangesTokenFirst:
558571
"""No callback code may run until the single-use token has been exchanged."""

0 commit comments

Comments
 (0)