kaxil commented on code in PR #67207:
URL: https://github.com/apache/airflow/pull/67207#discussion_r3307234176


##########
task-sdk/src/airflow/sdk/definitions/decorators/task_group.py:
##########
@@ -94,8 +94,11 @@ def __call__(self, *args: FParams.args, **kwargs: 
FParams.kwargs) -> DAGNode:
 
     def _create_task_group(self, tg_factory: Callable[..., TaskGroup], *args: 
Any, **kwargs: Any) -> DAGNode:
         with tg_factory(add_suffix_on_collision=True, **self.tg_kwargs) as 
task_group:
-            if self.function.__doc__ and not task_group.tooltip:
-                task_group.tooltip = self.function.__doc__
+            if self.function.__doc__:
+                if not task_group.tooltip:
+                    task_group.tooltip = self.function.__doc__
+                if not task_group.doc_md:
+                    task_group.doc_md = inspect.cleandoc(self.function.__doc__)

Review Comment:
   Same docstring, two normalizations: `tooltip` gets the raw `__doc__`, 
`doc_md` gets `inspect.cleandoc(__doc__)`. Either both should cleandoc or 
neither should (and the cleandoc happens at the API response layer, matching 
how DAG `doc_md` works today). Worth picking one rule and applying it 
consistently:
   
   ```python
   if doc := self.function.__doc__:
       cleaned = inspect.cleandoc(doc)
       if not task_group.tooltip:
           task_group.tooltip = cleaned
       if not task_group.doc_md:
           task_group.doc_md = cleaned
   ```
   
   Also, `task_group.doc_md = ...` runs the attrs converter on setattr. If the 
cleandoc'd docstring happens to end with `.md` (e.g., last line is "see 
README.md"), `_convert_doc_md` will try to open it as a file. Inherited from 
DAG, but worth knowing.



##########
airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/common.py:
##########
@@ -67,6 +67,7 @@ class GridNodeResponse(BaseModel):
     children: list[GridNodeResponse] | None = None
     is_mapped: bool | None
     setup_teardown_type: Literal["setup", "teardown"] | None = None
+    doc_md: str | None = None

Review Comment:
   The DAG `doc_md` response runs `inspect.cleandoc` via a `field_validator` 
(see `datamodels/dags.py:218-224`). This one doesn't, so a literal 
`TaskGroup(doc_md=""" \n    # Heading\n    - item\n """)` ships to the UI with 
its 4-space indent intact, and ReactMarkdown renders it as an indented code 
block instead of a heading + list. The `@task_group` decorator path runs 
cleandoc manually, but anyone using the class constructor directly hits this 
footgun.
   
   Adding a `@field_validator("doc_md", mode="before")` here, mirroring the DAG 
one, would handle both cases at the same layer and let you drop the cleandoc 
call from the decorator.



##########
airflow-core/src/airflow/api_fastapi/core_api/services/ui/task_group.py:
##########
@@ -110,9 +112,13 @@ def task_group_to_dict_grid(task_item_or_group, 
parent_group_is_mapped=False):
         for x in task_group_sort(task_group)
     ]
 
-    return {
+    node = {
         "id": task_group.group_id,
         "label": task_group.group_display_name or task_group.label,
         "is_mapped": mapped or None,
         "children": children or None,
     }
+    doc_md = getattr(task_group, "doc_md", None)

Review Comment:
   `getattr(task_group, "doc_md", None)` with a default isn't needed -- 
`SerializedTaskGroup` declares `doc_md` as an attrs field with `default=None`, 
so the attribute always exists on every code path that reaches here. Direct 
access (`task_group.doc_md`) is what the rest of this function does for 
`tooltip`, `group_id`, etc. If access ever fails, an `AttributeError` is a real 
bug worth surfacing rather than papering over.



##########
airflow-core/src/airflow/ui/public/i18n/locales/en/common.json:
##########
@@ -270,6 +270,7 @@
   "taskGroup": "Task Group",
   "taskGroup_one": "Task Group",
   "taskGroup_other": "Task Groups",
+  "taskGroupDocumentation": "Task Group Documentation",

Review Comment:
   Two small things:
   
   1. Existing pattern is namespaced (`task.documentation`, 
`dagDetails.documentation`). This adds a top-level `taskGroupDocumentation`. A 
`taskGroup.documentation` nested key would match the existing convention.
   2. Only `en/common.json` is updated. The other locale dirs (`de`, `es`, 
`fr`, `ko`, ...) keep the English fallback working, but the convention here is 
to seed the English string into every locale's `common.json` so missing-key 
fallback is consistent (see how #67540 handled the recent ko addition).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to