The loop would not work as-is (since it’d create multiple tasks with the same
id). But as currently designed, you CAN set multiple result tasks on a dag. The
result is always a dict keyed by tsk_id. So this slightly modified example
@dag
def my_dag():
@task
def t(x):
return x
@result
@task
def my_task(num):
return num*2
for i in range(4):
my_task.override(task_id=f"my_task_{i}")(t(i))
Would have the dag result
{
"my_task_0": 0,
"my_task_1": 2,
"my_task_2": 4,
"my_task_3": 6,
}
> On 16 Jun 2026, at 17:34, Ephraim Anierobi <[email protected]> wrote:
>
> Hi TP,
>
> Thanks for bringing up this discussion.
>
> I feel like `@result @task` is clean, however, it won't be clear what the
> Dag's result is if the task is invoked multiple times in a dag.
> Take for example:
>
> @dag
> def my_dag():
> @task
> def t(x):
> return x
> @result
> @task
> def my_task(num):
> return num*2
> for i in range(4):
> my_task(t(i))
>
> Unless I'm not understanding the @result well, but I feel like this means,
> every invocation of `my_task` is a result of the dag.
>
> If result is intended to be singular, I will prefer value inference from the
> dag:
>
> @dag
> def my_dag():
> @task
> def my_task():
> return 1
> return my_task()
>
> AND
>
> with DAG(...) as dag:
> output = f()
> dag.add_result(output)
>
> Thanks
> - Ephraim
>
> On Tue, 16 Jun 2026 at 08:37, Tzu-ping Chung via dev <[email protected]>
> wrote:
> Hi all,
>
> I’m currently working on the [Synchronous Dag Execution] feature and trying
> to gather opinions on how the Taskflow API should work when we want to mark a
> task as the dag’s “result task” (i.e. “the return value is a final output of
> the dag, not an intermediate value”).
>
> [Synchronous Dag Execution]: https://github.com/apache/airflow/issues/51711
>
> ## Prior art (kind of)
>
> We currently have the setup/teardown Taskflow API like this:
>
> @setup
> def f1(): ...
>
> @task
> def f2(): ...
>
> setup1 = f1() # This is a setup task.
>
> t2 = f2() # This is a normal task.
> setup2 = t2.as_setup() # This is a setup task.
>
> A teardown variant also exists for both cases.
>
> ## The decorator syntax
>
> The most straightforward syntax would be to have a @result decorator on a
> plain Python function. However, I don’t like this since a result task still
> has all the same arguments as a non-result task. Setup and teardown tasks
> don’t accept most task arguments. If @result needs to work on a plain
> function, it would need to duplicate and forward all the arguments on @task.
> I feel we can avoid this redundancy by requiring @result to be used ON TOP OF
> @task instead:
>
> @result
> @task(put your arguments here...)
> def f(): ...
>
> We COULD also make using @result without @task a shorthand to argument-less
> calls (which is probably common?)
>
> # This...
> @result
> def f(): ...
>
> # Is equivalent to...
> @result
> @task
> def f(): ...
>
> Alternatively, we could use a fluent interface:
>
> @task(arguments here...).result
> def f(): ...
>
> Pro: avoids needing a top-level name. Con: Not a common pattern in Airflow.
>
> ## The method syntax
>
> I don’t think adding a method similar to as_setup/teardown makes sense here.
> It makes sense for setup/teardown because it allows the same body of code to
> be BOTH a setup/teardown task AND a normal task at the same time, as shown
> above. This does not make sense for a result task—a task either returns the
> result, or it doesn’t. If we want a method-based syntax, it makes more sense
> to have a method on the dag:
>
> with DAG(...) as dag:
> @task
> def f():
>
> t = f()
> dag.add_result(t)
>
> ## For @dag decorator
>
> One more syntax that only makes sense here is we can automatically detect the
> return value of an @dag-decorated function:
>
> @dag
> def my_dag():
> @task
> def f1(): ...
>
> @task
> def f2(v): ...
>
> result = f2(f1())
>
> return result # Marks f2 as the result task!
>
> ---------------
>
> Looking forward to hearing thoughts on the above, and more ideas on possible
> syntaxes.
>
> TP
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]