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"