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]

Reply via email to