Somewhere I thought we had a list of BaseOperator init kwargs that cannot be
mapped — for instance you cannot map over the queue or pool slots arguments
today?
But the rest of what TP says I agree with. If you want the result of an
operator to be marked as the dag result, you can swap to use the @dag decorator
syntax:
@dag
def my_dag():
task = EmptyOperator(taks_id=“xyz”)
return task
I think that is sufficient?
-a
> On 17 Jun 2026, at 04:19, Tzu-ping Chung via dev <[email protected]>
> wrote:
>
> Using an operator argument such as
>
> task = EmptyOperator(task_id="xxx", result=True)
>
> would hint that you might be able to do
>
> task = (
> EmptyOperator
> .partial(task_id="xxx")
> .expand(result=[False, True])
> )
>
> which I don’t think we should allow. (No, I don’t think this makes sense in
> the first place, but someone somewhere might.)
>
> Of course, we could add checks so this emits an error when the dag is parsed,
> but this is additional mental context (for both users and maintainers) that
> could be entirely avoided in the first place. When AIP-52 proposed the
> setup/teardown syntax (which I want to be consistent with, as mentioned
> previously) also did not propose MyOperator(..., setup=True).
>
> Personally I don’t like
>
> @dag(return_task="my_task_id")
>
> since it would be two places to edit if you want to change the task id in the
> future. With the add_task() or return syntax mentioned previously, the handle
> used is a Python variable, so an incorrect name would be easy to catch with
> standard linters. The error emitted would also contain better context (line
> numbers etc).
>
>
> TP
>
>
>> On 16 Jun 2026, at 21:17, Vincent Beck <[email protected]> wrote:
>>
>> What about operators? Do we want to support only tasks using Taskflow API?
>> If not, a new parameter would work for both use cases (e.g. `result=True`).
>>
>> ```
>> @task(result=True)
>> def my_task(num):
>> return num*2
>> ```
>>
>> ```
>> task = EmptyOperator(task_id="xxx", result=True)
>> ```
>>
>> Or a new parameter in the Dag constructor:
>>
>> ```
>> @dag(return_task="my_task_id")
>> ```
>>
>> The inconvenience of the latter is you limit one task to be a Dag task
>> result (and it seems we want to enable having multiple task results per Dag).
>>
>> On 2026/06/16 10:20:44 Ash Berlin-Taylor wrote:
>>> TaskFlow automatically suffixes pretty close to this out of the box — I
>>> think without the override we’d end up with my_task, my_task__1,
>>> my_task__2, my_task__3 etc.
>>> https://github.com/apache/airflow/blob/376cecdb9f258fdb6f81f264c48f281c1cd2aeb5/task-sdk/src/airflow/sdk/bases/decorator.py#L111-L150
>>>
>>> -a
>>>
>>>> On 16 Jun 2026, at 10:59, Tzu-ping Chung via dev <[email protected]>
>>>> wrote:
>>>>
>>>> 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]
>>>>
>>>
>>>
>>
>> ---------------------------------------------------------------------
>> 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]
>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]