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

bbovenzi 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 ad2ef0634ec Dedent task docstrings used as doc_md (#74436)
ad2ef0634ec is described below

commit ad2ef0634ec0e6e63a6dc5481d43fe234ec7c8c3
Author: Yuseok Jo <[email protected]>
AuthorDate: Sat Oct 10 05:27:00 2026 +0900

    Dedent task docstrings used as doc_md (#74436)
---
 task-sdk/src/airflow/sdk/bases/decorator.py     |  4 +++-
 task-sdk/tests/task_sdk/bases/test_decorator.py | 12 ++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/task-sdk/src/airflow/sdk/bases/decorator.py 
b/task-sdk/src/airflow/sdk/bases/decorator.py
index c4d8ad7b5a6..ebad069534a 100644
--- a/task-sdk/src/airflow/sdk/bases/decorator.py
+++ b/task-sdk/src/airflow/sdk/bases/decorator.py
@@ -565,8 +565,10 @@ class _TaskDecorator(ExpandableFactory, Generic[FParams, 
FReturn, OperatorSubcla
         op.returns_dag_result = self.returns_dag_result
         op_doc_attrs = [op.doc, op.doc_json, op.doc_md, op.doc_rst, 
op.doc_yaml]
         # Set the task's doc_md to the function's docstring if it exists and 
no other doc* args are set.
+        # cleandoc strips the leading indentation the docstring inherits from 
the nested function so it
+        # renders as Markdown rather than an indented code block.
         if self.function.__doc__ and not any(op_doc_attrs):
-            op.doc_md = self.function.__doc__
+            op.doc_md = inspect.cleandoc(self.function.__doc__)
         return XComArg(op)
 
     def _validate_arg_names(self, func: ValidationSource, kwargs: dict[str, 
Any]):
diff --git a/task-sdk/tests/task_sdk/bases/test_decorator.py 
b/task-sdk/tests/task_sdk/bases/test_decorator.py
index b3dfd1630c5..8a8c7ce960d 100644
--- a/task-sdk/tests/task_sdk/bases/test_decorator.py
+++ b/task-sdk/tests/task_sdk/bases/test_decorator.py
@@ -68,6 +68,18 @@ class TestBaseDecorator:
         ast.parse(cleaned)
         assert cleaned.lstrip().splitlines()[0].startswith("def a_task")
 
+    def test_docstring_used_as_doc_md_is_dedented(self):
+        @task
+        def my_task():
+            """Summary line.
+
+            Body line indented under the function.
+            """
+
+        op = my_task().operator
+
+        assert op.doc_md == "Summary line.\n\nBody line indented under the 
function."
+
 
 class DummyDecoratedOperator(DecoratedOperator):
     custom_operator_name = "@task.dummy"

Reply via email to