Skip to content

Add async generator support for dynamic deps in build system#84

Open
andhus wants to merge 5 commits intomainfrom
async-dynamic-deps-build
Open

Add async generator support for dynamic deps in build system#84
andhus wants to merge 5 commits intomainfrom
async-dynamic-deps-build

Conversation

@andhus
Copy link
Collaborator

@andhus andhus commented Mar 4, 2026

Summary

  • Build sequential and concurrent executors now handle async generators from run_aio() for dynamic dependencies (class-based Task API)
  • @task decorator rejects generator functions with a clear error message pointing users to the class-based API
  • Modal _run function now supports async-only tasks via asyncio.run(run_aio())
  • New AsyncDynamicDiamondTask test helper and async dynamic deps tests for both sequential and concurrent build paths

Context

The build system previously only handled sync generators (__next__) for dynamic dependencies. When a class-based task's run_aio() is an async generator (yielding TaskStruct deps), the build system would fail because await doesn't work on async generators. This PR adds inspect.isasyncgenfunction() detection and async for iteration alongside the existing sync generator handling.

The @task decorator API is intentionally kept simple — dynamic dependencies are complex and better served by the class-based API where type annotations and generator patterns are more natural.

Changes

File Change
build/_sequential.py Detect async generator run_aio, iterate with async for
build/_concurrent.py _handle_result (now async), _handle_generator_aio, _resume_generator_aio, updated typing
_core/decorator.py Reject generator/async generator functions with TypeError
integration/modal/_app.py _run detects async-only tasks, uses asyncio.run(run_aio())
utils/testing/helper_tasks.py New AsyncDynamicDiamondTask class
tests/test__core/test_decorator.py Generator rejection tests
tests/test_build/test_sequential.py Async dynamic diamond tests
tests/test_build/test_concurrent.py Async dynamic diamond tests

Test plan

  • All 71 existing + new tests pass (test_decorator.py, test_sequential.py, test_concurrent.py)
  • Pyright passes with 0 errors
  • Modal integration test (requires Modal test setup — tracked separately)

🤖 Generated with Claude Code

andhus and others added 3 commits March 5, 2026 00:35
- Support async functions with @task decorator (run_aio, call_aio)
- Add LoadableTask.load_aio with load/load_aio mutual fallback (mirrors run/run_aio)
- Add Task.load_aio override using target().load_aio()
- Add _open_aio to InMemoryFileSystemTarget for complete async interface
- Add _FuncReturnT TypeVar for decorator overload clarity
- Guard call() and call_aio() against sync/async mismatch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Build sequential and concurrent executors now handle async generators
  from run_aio() (detect via isasyncgenfunction, iterate with async for)
- @task decorator rejects generator functions with clear error message
  pointing to class-based API for dynamic dependencies
- Modal _run supports async-only tasks via asyncio.run(run_aio())
- Add AsyncDynamicDiamondTask helper and tests for both build paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@andhus andhus force-pushed the async-dynamic-deps-build branch from 34874f4 to 5facc34 Compare March 4, 2026 23:36
andhus and others added 2 commits March 5, 2026 00:47
- Extract _iter_dynamic_deps() async helper to deduplicate sync/async
  generator handling in _run_task_sequential_aio
- Rewrite Modal _run to handle dynamic deps via idempotent re-execution
  (mirrors _run_task_in_process): drive generator forward while deps are
  complete, return TaskStruct of incomplete deps for ModalTaskExecutor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant