Skip to content

TS SDK: bind TaskFlow call arguments by folding names on both sides - #73189

Merged
jason810496 merged 1 commit into
apache:mainfrom
jason810496:feature/ts-sdk/taskflow-arg-folding
Sep 18, 2026
Merged

jason810496 merged 1 commit into
apache:mainfrom
jason810496:feature/ts-sdk/taskflow-arg-folding

Conversation

@jason810496

@jason810496 jason810496 commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Why

We need to support binding the literal defined on @task.stub to the TS task handler argument.

How

  • Consume ti_context.arg_bindings msgpack and hand it to the handler as its parameter.
  • Names bind by folding on both sides, lowercased with underscores removed.
    Same as the Go SDK's rule (strings.ToLower(strings.ReplaceAll(name, "_", "")))
    So one Python signature binds identically in either SDK, and no need to explicitly rename snake_case by default.
  • The bound object is a Proxy so folding happens per read when task handler start.
  • An unmatched name logs rather than throws -- respecting the keyword only binding sementic.
  • Two Python argument names that fold to same name fail the task before the handler runs.

The XCom binding will be support in the next PR instead of current one.


Was generative AI tooling used to co-author this PR?

A Python Dag declares a task that runs in TypeScript with `@task.stub` and
calls it TaskFlow-style, but the handler could not see those arguments: it was
called with nothing, so the call site was decoration and its values had to be
hardcoded in the handler or re-fetched from XCom. Airflow already delivers that
call site as an ordered, named spec in the task's run context.

The names are the problem worth solving. Python spells a parameter
`region_code` and TypeScript wants to read it as `regionCode`, and making an
author declare that for every ordinary snake_case parameter would be a tax on
the common case. So names bind by folding on both sides, lowercased with
underscores removed, which is exactly the Go SDK's rule. One Python signature
then binds identically in either SDK with nothing declared.

Folding on read rather than up front is why the bound object is a `Proxy`: the
SDK sees Python's names and cannot know which spelling a handler will
destructure, so no guess at a TypeScript name is ever materialized. `in` folds
like a read, and `Object.keys` and rest destructuring yield Python's names. The
object has a null prototype, so a Python argument named `toString` binds like
any other and one that was not passed misses rather than resolving to a
function.

An unmatched name logs rather than throws, since a destructuring default is a
legitimate miss and nothing can tell one from a typo. The warning names both
the requested name and what the call delivered, and a failing task reports the
same list, because a handler that destructured an argument under a name nothing
folds to gets no error of its own.

Two Python names that fold to one token do fail the task, before the handler
runs and naming both: neither could be reached, and picking either silently
would hand the handler the wrong value. An XCom-backed binding fails the same
way for now, with the getXCom call to write instead. Anything else the SDK
cannot honour fails rather than being dropped, because an unbound argument
reaches the handler as `undefined` and corrupts its output instead of stopping
it.
@jason810496
jason810496 force-pushed the feature/ts-sdk/taskflow-arg-folding branch from 47e4cba to e57a0f9 Compare September 18, 2026 03:16

@pierrejeambrun pierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cool! LGTM

@jason810496
jason810496 merged commit 9556dcf into apache:main Sep 18, 2026
91 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants