Skip to content

Static, definition-time type-annotation checking for asset-to-asset dependency edges #34009

Description

@pedrovgp

What

Dagster wires asset dependencies together by matching a parameter name to an upstream AssetKey string, not by an actual Python reference/call:

@asset
def my_int() -> int:
    return 1

@asset
def sum_one(my_int: str) -> int:  # bug: should be int/float, not str
    return len(my_int)

mypy/pyright see two unrelated functions here and report nothing wrong — they have no way to know sum_one's my_int parameter is actually fed by my_int()'s return value. Today the only thing that catches this is Dagster's runtime DagsterType.type_check_fn, which fires during a run, after materialization has already started.

Proposal

Add an opt-out, definition-time validation pass that walks every resolved asset dependency edge and checks the producer's output-type annotation against the consumer's input-type annotation, using the same PEP 484 assignability rules a type checker applies (numeric tower, Optional/Union, generics, subclassing, Any), and respecting whatever strict_optional (mypy) / strictParameterNoneValue (pyright) setting the project already has configured — so it doesn't introduce a third, conflicting set of typing rules. Incompatible edges raise DagsterInvalidDefinitionError as soon as the asset graph is resolved, instead of at runtime.

Why not just rely on mypy/pyright directly?

Because they're blind to this specific class of bug by construction — the connection is name-based, not reference-based, so nothing in the source lets a type checker see across the edge.

Related

#7073 requests strong static typing for resources — same underlying gap (Dagster's dynamic wiring being invisible to static type checkers), different edge of the graph (asset I/O vs. resource injection).

Status

I have a working prototype (toggleable via env var, fails open on ambiguous/unmodelable cases, benchmarked at low-single-digit ms overhead even at thousands of assets, full existing asset-defs test suite passing): pedrovgp#1. Opening this issue first, per the contributing guide's ask to discuss core feature ideas with maintainers before investing further implementation time, and would like feedback on the design before polishing it into a mergeable PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions