This is an automated email from the ASF dual-hosted git repository.
potiuk 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 3de732ab9b2 Fix BashOperator script detection after templating (#70369)
3de732ab9b2 is described below
commit 3de732ab9b20a63b368d660c553633fe7830f4c7
Author: Vincent Hsiao <[email protected]>
AuthorDate: Thu Jul 30 17:51:36 2026 +0800
Fix BashOperator script detection after templating (#70369)
BashOperator template fields are rendered after construction, so script
detection based on the raw constructor argument can choose the wrong execution
path for templated commands.
---
.../src/airflow/providers/standard/decorators/bash.py | 1 -
.../src/airflow/providers/standard/operators/bash.py | 5 ++---
.../standard/tests/unit/standard/operators/test_bash.py | 17 +++++++++++++++++
scripts/ci/prek/validate_operators_init_exemptions.txt | 1 -
4 files changed, 19 insertions(+), 5 deletions(-)
diff --git
a/providers/standard/src/airflow/providers/standard/decorators/bash.py
b/providers/standard/src/airflow/providers/standard/decorators/bash.py
index 169a8dea58a..8cf704b9f84 100644
--- a/providers/standard/src/airflow/providers/standard/decorators/bash.py
+++ b/providers/standard/src/airflow/providers/standard/decorators/bash.py
@@ -88,7 +88,6 @@ class _BashDecoratedOperator(DecoratedOperator, BashOperator):
if not isinstance(self.bash_command, str) or self.bash_command.strip()
== "":
raise TypeError("The returned value from the TaskFlow callable
must be a non-empty string.")
- self._is_inline_cmd =
self._is_inline_command(bash_command=self.bash_command)
self.render_template_fields(context)
return super().execute(context)
diff --git
a/providers/standard/src/airflow/providers/standard/operators/bash.py
b/providers/standard/src/airflow/providers/standard/operators/bash.py
index 8f5205c11bd..c0949287360 100644
--- a/providers/standard/src/airflow/providers/standard/operators/bash.py
+++ b/providers/standard/src/airflow/providers/standard/operators/bash.py
@@ -178,9 +178,7 @@ class BashOperator(BaseOperator):
self.cwd = cwd
self.append_env = append_env
self.output_processor = output_processor
- self._is_inline_cmd = None
- if isinstance(bash_command, str):
- self._is_inline_cmd =
self._is_inline_command(bash_command=bash_command)
+ self._is_inline_cmd: bool | None = None
@cached_property
def subprocess_hook(self):
@@ -215,6 +213,7 @@ class BashOperator(BaseOperator):
raise AirflowException(f"The cwd {self.cwd} must be a
directory")
env = self.get_env(context)
+ self._is_inline_cmd = self._is_inline_command(bash_command=cast("str",
self.bash_command))
if self._is_inline_cmd:
result = self._run_inline_command(bash_path=bash_path, env=env)
else:
diff --git a/providers/standard/tests/unit/standard/operators/test_bash.py
b/providers/standard/tests/unit/standard/operators/test_bash.py
index 1a8aad37981..b38a53d9d07 100644
--- a/providers/standard/tests/unit/standard/operators/test_bash.py
+++ b/providers/standard/tests/unit/standard/operators/test_bash.py
@@ -33,6 +33,7 @@ from airflow.providers.common.compat.sdk import (
AirflowTaskTimeout,
timezone,
)
+from airflow.providers.standard.hooks.subprocess import SubprocessResult
from airflow.providers.standard.operators.bash import BashOperator
from airflow.utils.state import State
from airflow.utils.types import DagRunType
@@ -286,6 +287,22 @@ class TestBashOperator:
assert task.bash_command == 'echo "test_templated_fields_dag"'
assert task.cwd == Path(__file__).absolute().parent.as_posix()
+ @mock.patch.object(BashOperator, "_run_inline_command")
+ @mock.patch.object(
+ BashOperator, "_run_rendered_script_file",
return_value=SubprocessResult(exit_code=0, output="ok")
+ )
+ def test_execute_detects_script_after_bash_command_is_rendered(
+ self, mock_run_rendered_script_file, mock_run_inline_command, context
+ ):
+ op = BashOperator(task_id="abc", bash_command="{{ bash_script }}")
+ op.bash_command = "sample.sh"
+
+ result = op.execute(context)
+
+ assert result == "ok"
+ mock_run_rendered_script_file.assert_called_once()
+ mock_run_inline_command.assert_not_called()
+
@pytest.mark.db_test
def test_templated_bash_script(self, dag_maker,
create_task_instance_of_operator, tmp_path, session):
"""
diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt
b/scripts/ci/prek/validate_operators_init_exemptions.txt
index c993ca2a163..4cab009b46b 100644
--- a/scripts/ci/prek/validate_operators_init_exemptions.txt
+++ b/scripts/ci/prek/validate_operators_init_exemptions.txt
@@ -28,4 +28,3 @@
providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py::GCS
providers/google/src/airflow/providers/google/marketing_platform/operators/campaign_manager.py::GoogleCampaignManagerDeleteReportOperator
providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/gcs_to_wasb.py::GCSToAzureBlobStorageOperator
providers/microsoft/psrp/src/airflow/providers/microsoft/psrp/operators/psrp.py::PsrpOperator
-providers/standard/src/airflow/providers/standard/operators/bash.py::BashOperator