On this page
Airflow Task Dependency Syntax
The `>>` operator in Airflow sets task dependencies and returns the downstream
I wrote task1 >> task2 >> task3 in an Airflow DAG and my linter flagged it: “Statement seems to have no effect.” The code worked fine — dependencies were set correctly — but the linter was technically right. The >> operator returns a value that I was ignoring. Understanding how this operator actually works explains both the linter warning and the idiomatic fix.
How >> Works
Airflow overloads Python’s right-shift operator (>>) on BaseOperator. It calls set_downstream() as a side effect and returns the downstream task:
# >> calls set_downstream() and returns the downstream task
result = task1 >> task2 # result is task2
# The dependency is set as a side effect
# The return value is often unused The dependency between task1 and task2 is established immediately as a side effect. The return value (the downstream task) enables chaining: task1 >> task2 >> task3 works because task1 >> task2 returns task2, and then task2 >> task3 sets the second dependency.
The Linter Warning
When you write dependencies as a standalone expression, linters see an unused return value:
# Linter warning: "Statement seems to have no effect"
task1 >> task2 >> task3 The statement does have an effect (setting dependencies), but the linter doesn’t know about Airflow’s operator overloading. It sees a bitshift expression whose result is discarded.
The _ = Pattern
The idiomatic fix is to assign to _, which signals “I want the side effect, not the return value”:
# Suppresses linter warning
# Shows intent: we want the side effect, not the return value
_ = task1 >> task2 >> task3 This is a common Python convention. The underscore variable means “intentionally discarded.” It satisfies the linter and communicates your intent to other developers.
Alternative: The chain() Function
Airflow provides a chain() function that’s more explicit about setting dependencies:
from airflow.models.baseoperator import chain
# More explicit, no return value
chain(task1, task2, task3) chain() doesn’t return a value, so there’s no linter warning. It’s also more readable for complex dependency graphs. For simple linear chains, _ = task1 >> task2 is more concise.
Real-World Example
validate = PythonOperator(task_id='validate', ...)
extract = DockerOperator(task_id='extract', ...)
load = PythonOperator(task_id='load', ...)
# Clear intent: setting dependencies, discarding return
_ = validate >> extract >> load Key Points
>>is syntactic sugar forset_downstream()- The dependency is set immediately (side effect)
- The return value is the rightmost task in the chain
_ =communicates “intentionally discarding the return value”- Both
_ =andchain()are valid;_ =is more concise,chain()is more explicit
Takeaway
When Airflow’s >> operator triggers a linter warning, use _ = task1 >> task2 >> task3 to suppress it. The underscore assignment signals that you’re using >> for its side effect (setting dependencies), not its return value. For complex dependency graphs, consider chain() from airflow.models.baseoperator as a more explicit alternative.