Skip to content

Support Annotated-style dependency injection#354

Merged
chrisguidry merged 2 commits intomainfrom
annotation-deps
Feb 27, 2026
Merged

Support Annotated-style dependency injection#354
chrisguidry merged 2 commits intomainfrom
annotation-deps

Conversation

@chrisguidry
Copy link
Owner

Summary

Dependencies can now be attached as Annotated type-hint metadata instead
of only as default parameter values:

async def process(customer_id: Annotated[int, ConcurrencyLimit(1)]): ...

The parameter keeps its real value; the dependency runs as a side-effect.
This is especially nice for ConcurrencyLimit where the old default-param
style required a separate dummy parameter just to carry the dependency.

Changes:

  • resolved_dependencies() now calls get_annotation_dependencies() from
    uncalled-for and enters each annotation dep via bind_to_parameter()
  • ConcurrencyLimit gains bind_to_parameter() to auto-infer the
    argument name from the annotated parameter
  • ConcurrencyLimit(1) shorthand: passing an int as the first positional
    arg sets max_concurrent (convenient for the Annotated style)
  • get_single_dependency_parameter_of_type() now searches annotations too
  • Bumps uncalled-for to >=0.2.0 for the annotation extraction API

Includes contract tests for the uncalled-for behaviors we depend on, and
integration tests covering concurrency limits, Depends side-effects, mixed
styles, type aliases, and validation.

Closes #334, closes #163.

🤖 Generated with Claude Code

Dependencies can now be attached as `Annotated` type-hint metadata instead
of only as default parameter values:

    async def process(customer_id: Annotated[int, ConcurrencyLimit(1)]): ...

The parameter keeps its real value; the dependency runs as a side-effect.
This is especially nice for ConcurrencyLimit where the old default-param
style required a separate dummy parameter just to carry the dependency.

Changes:
- `resolved_dependencies()` now calls `get_annotation_dependencies()` from
  uncalled-for and enters each annotation dep via `bind_to_parameter()`
- `ConcurrencyLimit` gains `bind_to_parameter()` to auto-infer the
  argument name from the annotated parameter
- `ConcurrencyLimit(1)` shorthand: passing an int as the first positional
  arg sets `max_concurrent` (convenient for the Annotated style)
- `get_single_dependency_parameter_of_type()` now searches annotations too
- Bumps uncalled-for to >=0.2.0 for the annotation extraction API

Includes contract tests for the uncalled-for behaviors we depend on, and
integration tests covering concurrency limits, Depends side-effects, mixed
styles, type aliases, and validation.

Closes #334, closes #163.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@read-the-docs-community
Copy link

read-the-docs-community bot commented Feb 27, 2026

Documentation build overview

📚 docket | 🛠️ Build #31593263 | 📁 Comparing 7afac78 against latest (3484ea9)


🔍 Preview build

Show files changed (1 files in total): 📝 1 modified | ➕ 0 added | ➖ 0 deleted
File Status
api-reference/index.html 📝 modified

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8af600a9d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 93 to 95
bound = dep.bind_to_parameter(param_name, value)
try:
await stack.enter_async_context(bound)

Choose a reason for hiding this comment

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

P1 Badge Preserve annotated Runtime/Completion dependencies

This loop enters annotated dependencies but drops the object returned by __aenter__, so those dependencies never appear in the dependencies map yielded by resolved_dependencies. In worker._execute, runtime and completion behavior is discovered only via get_single_dependency_of_type(dependencies, Runtime) / CompletionHandler, so Annotated[..., Timeout(...)] or Annotated[..., Perpetual(...)] will silently not enforce timeouts or run completion hooks even though they are declared on the task.

Useful? React with 👍 / 👎.


annotation_deps = get_annotation_dependencies(execution.function)
for param_name, deps in annotation_deps.items():
value = execution.kwargs.get(param_name, arguments.get(param_name))

Choose a reason for hiding this comment

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

P2 Badge Bind annotated dependencies using bound arg values

The value passed to bind_to_parameter is taken only from execution.kwargs (or previously resolved dependency args), so annotated dependencies on positional parameters receive None instead of the real argument value when tasks are scheduled positionally (e.g. add(task)(42)). This breaks any dependency that relies on bind_to_parameter(name, value) for per-value behavior; it should use bound argument resolution (e.g. execution.get_argument) rather than raw kwargs lookup.

Useful? React with 👍 / 👎.

@codecov-commenter
Copy link

codecov-commenter commented Feb 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (3484ea9) to head (7afac78).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #354   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           99       102    +3     
  Lines         2969      3046   +77     
  Branches        26        26           
=========================================
+ Hits          2969      3046   +77     
Flag Coverage Δ
cli-python-3.10 100.00% <ø> (ø)
cli-python-3.11 100.00% <ø> (ø)
cli-python-3.12 100.00% <ø> (ø)
cli-python-3.13 100.00% <ø> (ø)
cli-python-3.14 100.00% <ø> (ø)
python-3.10 100.00% <100.00%> (ø)
python-3.11 97.99% <100.00%> (+0.05%) ⬆️
python-3.12 100.00% <100.00%> (ø)
python-3.13 100.00% <100.00%> (ø)
python-3.14 100.00% <100.00%> (ø)
windows-python-3.10 100.00% <100.00%> (ø)
windows-python-3.11 97.86% <100.00%> (?)
windows-python-3.12 100.00% <100.00%> (ø)
windows-python-3.13 100.00% <100.00%> (ø)
windows-python-3.14 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/docket/dependencies/__init__.py 100.00% <ø> (ø)
src/docket/dependencies/_concurrency.py 100.00% <100.00%> (ø)
src/docket/dependencies/_functional.py 100.00% <ø> (ø)
src/docket/dependencies/_resolution.py 100.00% <ø> (ø)
tests/concurrency_limits/test_annotated.py 100.00% <100.00%> (ø)
tests/test_annotated_dependencies.py 100.00% <100.00%> (ø)
tests/test_uncalled_for_contract.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Add @overload signatures to ConcurrencyLimit.__init__ to clarify the
  three calling conventions (int shorthand, str argument name, keyword-only)
- Rename abbreviated variables (deps, param_name, dep) to full names
- Rename test helper to my_side_effect for clarity
- Move concurrency-specific tests into tests/concurrency_limits/test_annotated.py
  alongside the existing concurrency test suite
- Remove section header comments from tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chrisguidry chrisguidry merged commit 7180b79 into main Feb 27, 2026
50 checks passed
@chrisguidry chrisguidry deleted the annotation-deps branch February 27, 2026 19:50
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.

Support for Annotated dependencies Allow Controlling Concurrency Limit Strategies

2 participants