Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨Add support for PEP-593 Annotated for specifying dependencies and parameters #4871

Merged
merged 19 commits into from Mar 17, 2023

Conversation

nzig
Copy link
Contributor

@nzig nzig commented May 7, 2022

What this does

This PR adds support for PEP-593 Annotated for specifying dependencies and parameters.
Fixes #3323.

This would allow you to write

async def handler(dep: Annotated[MyClass, Depends(my_dependency)]):
    ...

instead of

async def handler(dep: MyClass = Depends(my_dependency)):
    ...

It would also allow you to define

MyDependecy: TypeAlias = Annotated[MyClass, Depends(my_dependency)]

so that the handler could be written simply as

async def handler(dep: MyDependecy):
    ...

Which saves you from having to write both the dependency and the type annotation, all while ensuring that your IDE and type-checker understand the type.

This will also solve the issue mentioned in the docs of parameter ordering.

I am also quite fond of dual-use handlers, where the same function is used both as a FastAPI handler and also as a regular callable function. This currently works okay, except that because Depends is a defualt value,
you don't get an error if you forget to pass a parameter. Using Annotated avoids this problem as well.

Other changes

This change required changing some of how FastAPI handles parameters.

  • default is no longer required for Param. This is because I think it should be possible to write a param as my_header: Annotated[str, Header()] if it required, and my_header: Annotated[str, Header()] = '' for a param with a default value. Update: This was already done by @tiangolo in ✨ Add support for not needing ... as default value in required Query(), Path(), Header(), etc. #4906.
    • When used with Annotated, the default value may not be specified as my_header: Annotated[str, Header('default')] but only as my_header: Annotated[str, Header()] = 'default'.
    • When used without Annotated, we check at runtime that default is set. This is like the previous behavior, except that it is now checked by FastAPI rather than Python erroring on a missing required parameter.
  • Ambiguous annotations are now an error. Specifying something like r: Request = Header(...) would be ambiguous, in this case choosing to use the Request annotation and igonoring the Header annotation. Now this will be an error. I think this is preferable, as the ambiguity likely indicates a mistake by the developer.
  • Specifying anything other than a Path annotation for a path parameter name is now an error.
  • A default value can not be specified for an explicit Path annotation. It is currently ignored anyway because a path parameter is always specified when the handler is called.

Implementation

The main change, as expected, is to stop treating param.default as the FastAPI annotation. Instead, analyze_param will analyze the param's annotation and default value, handling both Annotated and default value FastAPI annotations. It returns the plain type_annotation (with Annotated stripped off), a depends if there is a Depends annotation, and a field like get_param_field used to.

Docs

I took a conservative approach to the docs, and only added this feature as an advanced guide, without introducing it in any of the earlier tutorials.

If it is wanted, this can also be added to the "Python Types Intro", and also introduced earlier in the tutorial series.

@github-actions
Copy link
Contributor

github-actions bot commented May 7, 2022

📝 Docs preview for commit 23df7c8 at: https://627692161bdbbd0fbc829b52--fastapi.netlify.app

@nzig nzig changed the title ✨ Add support for PEP-593 Annotated for specifying dependencies and parameters ✨Add support for PEP-593 Annotated for specifying dependencies and parameters May 7, 2022
@codecov
Copy link

codecov bot commented May 7, 2022

Codecov Report

Patch coverage: 100.00% and no project coverage change.

Comparison is base (cf73051) 100.00% compared to head (76cd76a) 100.00%.

❗ Current head 76cd76a differs from pull request most recent head 371c006. Consider uploading reports for the commit 371c006 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##            master     #4871    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files          540       554    +14     
  Lines        13969     14147   +178     
==========================================
+ Hits         13969     14147   +178     
Impacted Files Coverage Δ
fastapi/param_functions.py 100.00% <ø> (ø)
tests/test_application.py 100.00% <ø> (ø)
tests/test_path.py 100.00% <ø> (ø)
docs_src/annotated/tutorial001.py 100.00% <100.00%> (ø)
docs_src/annotated/tutorial001_py39.py 100.00% <100.00%> (ø)
docs_src/annotated/tutorial002.py 100.00% <100.00%> (ø)
docs_src/annotated/tutorial002_py39.py 100.00% <100.00%> (ø)
docs_src/annotated/tutorial003.py 100.00% <100.00%> (ø)
docs_src/annotated/tutorial003_py39.py 100.00% <100.00%> (ø)
fastapi/dependencies/utils.py 100.00% <100.00%> (ø)
... and 12 more

... and 3 files with indirect coverage changes

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@github-actions
Copy link
Contributor

github-actions bot commented May 7, 2022

📝 Docs preview for commit 2632a3f at: https://62769653281d781346a5704e--fastapi.netlify.app

@github-actions
Copy link
Contributor

github-actions bot commented May 7, 2022

📝 Docs preview for commit 8ea5f3a at: https://6276982e75651d17587d33c2--fastapi.netlify.app

@github-actions
Copy link
Contributor

github-actions bot commented May 7, 2022

📝 Docs preview for commit b8a9fcc at: https://627699c0281d781622a5708a--fastapi.netlify.app

Copy link
Contributor

@yezz123 yezz123 left a comment

Choose a reason for hiding this comment

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

LGTM! This PR Looks really Clean and with the fully support for PEP-593 ✨

@github-actions
Copy link
Contributor

📝 Docs preview for commit dc602ed at: https://627cf57fbc8f8168f6285cb1--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 123e94a at: https://62876d807a8f973b876017a1--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 03babf8 at: https://62d14418678e3700afca7ef7--fastapi.netlify.app

@github-actions
Copy link
Contributor

github-actions bot commented Sep 5, 2022

📝 Docs preview for commit e04f226 at: https://63164047892a1527636384b2--fastapi.netlify.app

@NowanIlfideme
Copy link

Honestly, with modern Python, this is the thing that fixes what feels "old" with FastAPI (and also the default value problem). I wonder why it's not yet merged? Docs?

@github-actions
Copy link
Contributor

📝 Docs preview for commit 7f860fd at: https://634591c4c1cbd800937b9768--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 76cd76a at: https://63464dfccb69a319e9de7f29--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit aaebd5b at: https://637a3a2a850b2c45c40cc5c1--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 608992a at: https://639cd68ba7d0431b3b04061e--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 863f161 at: https://640e0614add6c019e2deae47--fastapi.netlify.app

@github-actions
Copy link
Contributor

📝 Docs preview for commit 371c006 at: https://6414cef6fb143a320a02da86--fastapi.netlify.app

@tiangolo tiangolo merged commit 375513f into tiangolo:master Mar 17, 2023
8 checks passed
@tiangolo
Copy link
Owner

You did an amazing job here @nzig, it took me several days to properly review it all with all the attention, and it's an impeccable implementation. Thanks a lot! 🙇 🍰 🚀

This is the biggest feature in FastAPI in a long while. 😎

Also, you took the right approach by being conservative about the docs, that was perfect.

...in fact, I think Annotated is the right approach, and I'm making it the default recommendation from now on.

I'm rewriting ALL the documentation to use it, with examples for all Python versions, new docs in the respective sections, new tests, etc. That's why I removed the Annotated-specific docs in this PR, but again, that was the right approach.

I actually wanted to do this for a long while (even before I noticed your PR for the first time 😅) but I needed all the consecutive time dedicated full-time to this to get it done (I've been re-writing docs for 2 weeks 😂).

Here's the PR adding the new docs: #9268

This will be available in the next release, in some hours. FastAPI 0.95.0. 🎉


Thanks for the reviews @MrRawbin and @yezz123 ! ☕ 🍰

@nzig
Copy link
Contributor Author

nzig commented Mar 18, 2023

Thanks @tiangolo ! I'm glad I'll be able to use this in FastAPI 🥳 .

@JoelPendleton
Copy link

JoelPendleton commented Mar 18, 2023

Hi guys, I experienced difficulties when using this new Annotated specification when following this section of the docs. In particular, I get the following error when using form_data: Annotated[OAuth2PasswordRequestForm, Depends()]: raise fastapi.exceptions.FastAPIError( fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[fastapi.security.oauth2.OAuth2PasswordRequestForm, Depends(NoneType)] is a valid Pydantic field type. If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None. Read more: https://fastapi.tiangolo.com/tutorial/response-model/. Moreover, when checking my endpoints at http://127.0.0.1:8000/docs the padlock is not present on the "/users/me" endpoint and it treats the current_user argument as an input and not a dependency: Screenshot 2023-03-18 at 21 14 20

I hope you can resolve this :)

Everything works as expected with the old approach.

@srdjus
Copy link

srdjus commented Mar 18, 2023

@JoelPendleton

I got the same error, Python 3.10.9, FastAPI 0.94.1.

EDIT: Well, I updated FastAPI to 0.95.0, and the error disappeared.

@phillipuniverse
Copy link

phillipuniverse commented Apr 5, 2023

@tiangolo @nzig just FYI this change had the unfortunate side effect of Union request bodies no longer working in 0.95, see #9287 (comment)

@9en9i
Copy link

9en9i commented Apr 26, 2023

@nzig

Hi, i tried using

T = TypeVar("T")

Inject = Annotated[T, Depends()]

and it doesn't seem to work. Should we expect it to ever work?

@nzig
Copy link
Contributor Author

nzig commented Apr 27, 2023

@9en9i What are you expecting to happen? A more complete example might be helpful.
It's probably better to open it as a discussion instead of discussing it on the PR.

@9en9i
Copy link

9en9i commented Apr 27, 2023

@nzig I created a discussion #9450 and added a code example with a description of the problem there. I would be grateful if you could take a look.

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 PEP 593 Annotated for specifying dependencies and parameters
9 participants