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
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 79 additions & 0 deletions docs/en/docs/advanced/annotated.md
@@ -0,0 +1,79 @@
# Other annotations
Python 3.9 introduced the `Annotated` syntax for type annotations. This may also be used in older versions of Python by using the `Annotated` from the `typing_extensions` package.

In FastAPI, this allows us to specify annotations without using default values, and allows us to avoid repeating the type annotation for a dependency.

Let's look at the example from the dependecies tutorial:
nzig marked this conversation as resolved.
Show resolved Hide resolved
=== "Python 3.6 and above"
Before Python 3.9, we have to import `Annotated` from `typing_extensions` rather than `typing`.

``` Python hl_lines="13 17"
{!> ../../../docs_src/annotated/tutorial001.py!}
```
=== "Python 3.9 and above"

``` Python hl_lines="12 16"
{!> ../../../docs_src/annotated/tutorial001_py39.py!}
```

We first define a _type alias_ for our dependency, called `CommonParamsDepends`. This will allow us to re-use the combined type annotatation (the `dict` part), and the FastAPI annotation (the `Depends` part).

We then use this as the annotation for the `commons` parameter. This will tell FastAPI that the `commons` parameter is a dependency, just like if we had written

```Python
async def read_items(commons = Depends(common_parameters)):
```

At the same time, it also tells your IDE and type-checker that `commons` is a `dict`.

This saves you from having to write `param: dict = Depends(common_parameters)` everytime you use the `common_parameters` dependency. Instead we just defined an alias once, and can write `param: CommonParamsDepends` every time we use it.

## Class dependencies
`Annotated` also works with class dependencies.
=== "Python 3.6 and above"

``` Python hl_lines="9-16"
{!> ../../../docs_src/annotated/tutorial002.py!}
```
=== "Python 3.9 and above"

``` Python hl_lines="8-15"
{!> ../../../docs_src/annotated/tutorial002_py39.py!}
```

## Other Parameters
You may also use `Annotated` with other parameters, like `Path` and `Query`.
=== "Python 3.6 and above"

``` Python hl_lines="9"
{!> ../../../docs_src/annotated/tutorial003.py!}
```
=== "Python 3.9 and above"

``` Python hl_lines="10"
{!> ../../../docs_src/annotated/tutorial003_py39.py!}
```

When using `Annotated`, you specify the default value as you would normally in Python, because the default value is no longer taken up by the annotation.

=== "Python 3.6 and above"

``` Python hl_lines="14"
{!> ../../../docs_src/annotated/tutorial003.py!}
```
=== "Python 3.9 and above"

``` Python hl_lines="15"
{!> ../../../docs_src/annotated/tutorial003_py39.py!}
```

Note how we write `= "me"` and not `Query("me", min_length=1)`.

## `Annotated` is optional
Using `Annotated` is optional. You can use it where you want, and continue to use default values in other places.

You can mix both kinds of annotations, as long as you don't use them together for the same parameter.

## Version

This is available since FastAPI version `0.X.0`. 🔖
1 change: 1 addition & 0 deletions docs/en/mkdocs.yml
Expand Up @@ -145,6 +145,7 @@ nav:
- advanced/openapi-callbacks.md
- advanced/wsgi.md
- advanced/generate-clients.md
- advanced/annotated.md
- async.md
- Deployment:
- deployment/index.md
Expand Down
18 changes: 18 additions & 0 deletions docs_src/annotated/tutorial001.py
@@ -0,0 +1,18 @@
from typing import Optional

from fastapi import Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}


CommonParamsDepends = Annotated[dict, Depends(common_parameters)]


@app.get("/items/")
async def read_items(commons: CommonParamsDepends):
return commons
17 changes: 17 additions & 0 deletions docs_src/annotated/tutorial001_py39.py
@@ -0,0 +1,17 @@
from typing import Annotated, Optional

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}


CommonParamsDepends = Annotated[dict, Depends(common_parameters)]


@app.get("/items/")
async def read_items(commons: CommonParamsDepends):
return commons
21 changes: 21 additions & 0 deletions docs_src/annotated/tutorial002.py
@@ -0,0 +1,21 @@
from typing import Optional

from fastapi import Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


class CommonQueryParams:
def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit


CommonQueryParamsDepends = Annotated[CommonQueryParams, Depends()]


@app.get("/items/")
async def read_items(commons: CommonQueryParamsDepends):
return commons
20 changes: 20 additions & 0 deletions docs_src/annotated/tutorial002_py39.py
@@ -0,0 +1,20 @@
from typing import Annotated, Optional

from fastapi import Depends, FastAPI

app = FastAPI()


class CommonQueryParams:
def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit


CommonQueryParamsDepends = Annotated[CommonQueryParams, Depends()]


@app.get("/items/")
async def read_items(commons: CommonQueryParamsDepends):
return commons
15 changes: 15 additions & 0 deletions docs_src/annotated/tutorial003.py
@@ -0,0 +1,15 @@
from fastapi import FastAPI, Path
from fastapi.param_functions import Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(gt=0)]):
return {"item_id": item_id}


@app.get("/users")
async def read_users(user_id: Annotated[str, Query(min_length=1)] = "me"):
return {"user_id": user_id}
16 changes: 16 additions & 0 deletions docs_src/annotated/tutorial003_py39.py
@@ -0,0 +1,16 @@
from typing import Annotated

from fastapi import FastAPI, Path
from fastapi.param_functions import Query

app = FastAPI()


@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(gt=0)]):
return {"item_id": item_id}


@app.get("/users")
async def read_users(user_id: Annotated[str, Query(min_length=1)] = "me"):
return {"user_id": user_id}