Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[airflow] Implement airflow-xcom-pull-in-template-string (AIR004)#23583

Open
Dev-iL wants to merge 1 commit intoastral-sh:mainfrom
Dev-iL:2602/airflow/xcom_template
Open

[airflow] Implement airflow-xcom-pull-in-template-string (AIR004)#23583
Dev-iL wants to merge 1 commit intoastral-sh:mainfrom
Dev-iL:2602/airflow/xcom_template

Conversation

@Dev-iL
Copy link
Contributor

@Dev-iL Dev-iL commented Feb 26, 2026

Summary

Implements rule AIR004 (airflow-xcom-pull-in-template-string) that detects Airflow operator/sensor keyword arguments using a Jinja template string containing a single xcom_pull call (e.g., "{{ ti.xcom_pull(task_ids='some_task') }}") and suggests replacing it with the .output attribute on the task object (e.g., some_task.output).

Using .output instead of xcom_pull template strings:

  • Makes task dependencies explicit and visible to the DAG parser
  • Provides better IDE support (autocompletion, go-to-definition)
  • Is the recommended pattern for both traditional operators and TaskFlow API (@task-decorated functions)

What the rule flags

from airflow.operators.python import PythonOperator
from airflow.operators.bash import BashOperator

task_1 = PythonOperator(task_id="task_1", python_callable=my_func)
task_2 = BashOperator(
    task_id="task_2",
    bash_command="{{ ti.xcom_pull(task_ids='task_1') }}",  # AIR004
)

Suggested fix

task_2 = BashOperator(
    task_id="task_2",
    bash_command=task_1.output,
)

Template patterns detected

  • {{ ti.xcom_pull(task_ids='...') }} and {{ task_instance.xcom_pull(task_ids='...') }}
  • Positional argument: {{ ti.xcom_pull('...') }}
  • Both task_id= and task_ids= keyword forms
  • Various whitespace and quote styles

What it allows (no false positives)

  • Mixed content strings: "echo {{ ti.xcom_pull(task_ids='task_1') }}"
  • Non-default key arguments: "{{ ti.xcom_pull(task_ids='task_1', key='my_key') }}"
  • Already using .output: task_1.output
  • List of task_ids: "{{ ti.xcom_pull(task_ids=['a', 'b']) }}"
  • Non-operator/sensor calls (e.g., DAG(...))

Unsafe fix

When the referenced task_id matches a variable in scope (either an operator assignment or a @task-decorated function), an unsafe fix is provided that replaces the template string with <variable>.output. When no matching variable is found, the diagnostic is still reported but without an auto-fix.

Test Plan

  • Snapshot tests in AIR004.py covering:
    • Violations with fixes: ti.xcom_pull, task_instance.xcom_pull, positional args, double quotes, no-space braces, singular task_id keyword, sensors, provider operators, and @task-decorated function sources.
    • Violations without fixes: referencing a task_id not visible as a variable in the current scope.
    • Non-violations: mixed content strings, extra keyword arguments, already using .output, regular strings, non-string arguments, list task_ids, and non-operator calls.
  • Unit tests for the parse_xcom_pull_template parser covering all template variants and rejection cases.
  • Clippy passes with no warnings.

related: apache/airflow#43176

@astral-sh-bot
Copy link

astral-sh-bot bot commented Feb 26, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+18 -0 violations, +0 -0 fixes in 1 projects; 55 projects unchanged)

apache/airflow (+18 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ providers/amazon/tests/system/amazon/aws/example_dms_serverless.py:329:32: AIR004 Use the `.output` attribute on the task object for "create_replication_config" instead of `xcom_pull` in a template string
+ providers/amazon/tests/system/amazon/aws/example_ssm.py:214:49: AIR004 Use the `.output` attribute on the task object for "run_command" instead of `xcom_pull` in a template string
+ providers/amazon/tests/system/amazon/aws/example_ssm.py:221:20: AIR004 Use the `.output` attribute on the task object for "run_command" instead of `xcom_pull` in a template string
+ providers/amazon/tests/system/amazon/aws/example_ssm.py:240:20: AIR004 Use the `.output` attribute on the task object for "run_command_async" instead of `xcom_pull` in a template string
+ providers/amazon/tests/system/amazon/aws/example_ssm.py:261:20: AIR004 Use the `.output` attribute on the task object for "run_command_async" instead of `xcom_pull` in a template string
+ providers/amazon/tests/system/amazon/aws/example_ssm.py:295:20: AIR004 Use the `.output` attribute on the task object for "run_command_traditional" instead of `xcom_pull` in a template string
+ providers/databricks/tests/system/databricks/example_databricks.py:119:35: AIR004 Use the `.output` attribute on the task object for "jobs_create_named" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/dataplex/example_dataplex_dp.py:243:16: AIR004 Use the `.output` attribute on the task object for "run_data_scan_async" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/dataplex/example_dataplex_dq.py:267:16: AIR004 Use the `.output` attribute on the task object for "run_data_scan_async" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vertex_ai/example_vertex_ai_feature_store.py:174:32: AIR004 Use the `.output` attribute on the task object for "sync_task" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vertex_ai/example_vertex_ai_feature_store.py:185:32: AIR004 Use the `.output` attribute on the task object for "sync_task" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:184:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:192:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:201:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:210:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:221:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:233:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string
+ providers/google/tests/system/google/cloud/vision/example_vision_autogenerated.py:244:20: AIR004 Use the `.output` attribute on the task object for "product_create" instead of `xcom_pull` in a template string

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
AIR004 18 18 0 0 0

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Feb 26, 2026
Dev-iL added a commit to Dev-iL/airflow that referenced this pull request Feb 26, 2026
Dev-iL added a commit to Dev-iL/airflow that referenced this pull request Feb 26, 2026
@Dev-iL Dev-iL force-pushed the 2602/airflow/xcom_template branch from c0c2205 to fb18572 Compare February 26, 2026 17:20
Dev-iL added a commit to Dev-iL/airflow that referenced this pull request Feb 26, 2026
@amyreese
Copy link
Member

cc @sjyangkevin for review?

Comment on lines +172 to +182
#[cfg(test)]
mod tests {
use super::parse_xcom_pull_template;

#[test]
fn test_basic_ti_xcom_pull() {
assert_eq!(
parse_xcom_pull_template("{{ ti.xcom_pull(task_ids='my_task') }}"),
Some("my_task".to_string())
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious about what are these tests 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude can explain this best:

This is a unit test for the parse_xcom_pull_template helper function. It verifies the happy path: given a standard Jinja template string {{ ti.xcom_pull(task_ids='my_task') }}, the parser correctly extracts and returns the task ID "my_task".

This matters because parse_xcom_pull_template is a pure string-parsing function (no AST, no checker context), so it can be tested in isolation without spinning up the full linter infrastructure. The broader test module (lines 172–268) covers the full matrix of the parser's behavior:

  • Positive cases: both receiver names (ti vs task_instance), positional vs keyword argument, task_id vs task_ids, single vs double quotes, varying whitespace.
  • Negative cases: mixed content around the template, additional keyword arguments (key=...), empty task IDs, non-template strings, list-valued task_ids.

Together they ensure the hand-rolled parser is robust without needing snapshot-based integration tests for every string variation. The integration-level behavior (detecting operator calls, emitting diagnostics, generating fixes) is covered separately by the snapshot fixtures in crates/ruff_linter/resources/test/fixtures/airflow/.

@sjyangkevin
Copy link
Contributor

cc @sjyangkevin for review?

would be great to also have @Lee-W to have a look when he is available.

Dev-iL added a commit to Dev-iL/airflow that referenced this pull request Feb 27, 2026
@Dev-iL Dev-iL force-pushed the 2602/airflow/xcom_template branch from fb18572 to 438534f Compare March 1, 2026 11:58
@Dev-iL Dev-iL force-pushed the 2602/airflow/xcom_template branch from 438534f to ca33d94 Compare March 1, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants