Skip to content

feat: support Annotated dependencies in dependency extraction#346

Closed
omikader wants to merge 3 commits intochrisguidry:mainfrom
omikader:main
Closed

feat: support Annotated dependencies in dependency extraction#346
omikader wants to merge 3 commits intochrisguidry:mainfrom
omikader:main

Conversation

@omikader
Copy link
Contributor

Summary

Extract dependencies from Annotated[T, Depends(fn)] metadata in addition to the existing default-value style (x: T = Depends(fn)).

Details

Add annotated_dependency() helper that finds the first Dependency instance in a parameter's Annotated metadata. Used by get_dependency_parameters() for DI resolution and compact_signature() for startup log display.

Note 1: while the runtime extraction works fully, Annotated-style deps interact poorly with pyright because docket.add() preserves the task's ParamSpec — pyright sees Annotated deps as required parameters with no default. See #334 for discussion. The default-value style remains the recommended approach for now.

Note 2: does not handle nested dependencies, e.g.

async def high_five_customer(
    customer: Annotated[int, ConcurrencyLimit(1)], 
    db: Annotated[Connection, Depends(get_connection), ConcurrencyLimit(4)]
):
   customer = db.query(...., customer)
   high_five(customer)

Though this can be a fast-follow

Miscellaneous

Updated the .pre-commit-config.yaml to no longer use the legacy alias

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: a1a7d8e792

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +76 to +77
metadata = getattr(param.annotation, "__metadata__", ())
return next((item for item in metadata if isinstance(item, Dependency)), None)

Choose a reason for hiding this comment

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

P1 Badge Resolve postponed annotations before scanning metadata

When tasks are defined in modules using from __future__ import annotations (common on Python 3.10), inspect.signature() exposes param.annotation as a string, so __metadata__ is missing and this helper returns None. That means Annotated[..., Depends(...)] parameters are silently skipped during dependency extraction, and workers later invoke the task without required injected args, raising runtime TypeError for missing parameters instead of resolving dependencies.

Useful? React with 👍 / 👎.

@codecov-commenter
Copy link

codecov-commenter commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (c51bde1) to head (e30e944).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #346   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           99       100    +1     
  Lines         3098      3144   +46     
  Branches        27        27           
=========================================
+ Hits          3098      3144   +46     
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 98.02% <100.00%> (+0.03%) ⬆️
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.94% <100.00%> (+0.03%) ⬆️
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/_functional.py 100.00% <ø> (ø)
src/docket/execution.py 100.00% <ø> (ø)
tests/fundamentals/test_annotated_dependencies.py 100.00% <100.00%> (ø)
tests/test_execution.py 100.00% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@read-the-docs-community
Copy link

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

Documentation build overview

📚 docket | 🛠️ Build #31502552 | 📁 Comparing e30e944 against latest (c51bde1)


🔍 Preview build

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

@chrisguidry
Copy link
Owner

chrisguidry commented Feb 24, 2026

Hey @omikader did you have any thoughts about the idea of using Annotated just for dependencies where you don't actually need the return value? #334 (comment)

@omikader
Copy link
Contributor Author

Hi @chrisguidry. I explored this a bit and the implementation is doable — a provides_value flag on the Dependency base class that defaults to True, with ConcurrencyLimit opting out. But looking at the existing dependencies, ConcurrencyLimit is really the only one where Annotated makes natural sense, since it's the only behavioral dep that's tied to a specific parameter. The others (Retry, Timeout, Perpetual, Cron) are all task-level concerns that don't really belong on any particular parameter.

I'm a bit wary that supporting Annotated for some dependency types but not others could be confusing -- many Python developers will reach for the FastAPI-style Annotated[str, Depends(fn)] pattern first and be surprised when it doesn't work. The existing default-value pattern is uniform and works the same way for every dependency type, which I think is worth preserving.

If there's broader demand for Annotated across all dependency types down the road, it could be designed more holistically then. What do you think?

@chrisguidry
Copy link
Owner

Sounds good, thank you for taking a swing at this, I think you're right, we should skip this until there's a clearer use case. It would also likely migrate over to the http://github.com/chrisguidry/uncalled-for library too (where I look forward to your first contribution!). I'll close this and the issue for now.

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.

3 participants