Compare commits

...

15 Commits

Author SHA1 Message Date
Sebastián Ramírez
7ddcca6b9a 🔥 Remove unused files from source examples 2026-02-12 11:24:06 +01:00
Sebastián Ramírez
af77d9b718 🔥 Remove test for removed source example 2026-02-12 11:07:43 +01:00
Sebastián Ramírez
400ccb04aa Remove tests for removed files 2026-02-12 11:04:43 +01:00
Sebastián Ramírez
fbfc1722e5 📝 Update source examples and docs from Python 3.9 to 3.10 2026-02-12 00:28:41 +01:00
github-actions[bot]
d06ab3f5c7 📝 Update release notes
[skip ci]
2026-02-11 18:41:46 +00:00
Sebastián Ramírez
3da206c06d 🎨 Update internal types for Python 3.10 (#14898) 2026-02-11 18:41:21 +00:00
github-actions[bot]
cc903bd440 📝 Update release notes
[skip ci]
2026-02-11 18:32:39 +00:00
Sebastián Ramírez
ad4e8e0060 Drop support for Python 3.9 (#14897)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-11 19:32:12 +01:00
Sebastián Ramírez
bdd20051c4 🔖 Release version 0.128.8 2026-02-11 16:16:34 +01:00
github-actions[bot]
1ed9bd4923 📝 Update release notes
[skip ci]
2026-02-11 13:37:37 +00:00
Sebastián Ramírez
aac30fd707 🔨 Tweak PDM hook script (#14895) 2026-02-11 13:37:09 +00:00
github-actions[bot]
417f1ee078 📝 Update release notes
[skip ci]
2026-02-11 12:34:12 +00:00
Sebastián Ramírez
ffb8965260 ♻️ Update build setup for fastapi-slim, deprecate it, and make it only depend on fastapi (#14894) 2026-02-11 12:33:49 +00:00
github-actions[bot]
93fa935fb8 📝 Update release notes
[skip ci]
2026-02-10 12:26:38 +00:00
Sanjana S
f0f3e7a113 📝 Fix grammar in docs/en/docs/tutorial/first-steps.md (#14708) 2026-02-10 13:26:10 +01:00
551 changed files with 5043 additions and 8808 deletions

View File

@@ -12,11 +12,6 @@ on:
jobs:
test-redistribute:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- fastapi
- fastapi-slim
steps:
- name: Dump GitHub context
env:
@@ -30,8 +25,6 @@ jobs:
- name: Install build dependencies
run: pip install build
- name: Build source distribution
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
run: python -m build --sdist
- name: Decompress source distribution
run: |
@@ -41,8 +34,6 @@ jobs:
run: |
cd dist/fastapi*/
pip install --group tests --editable .[all]
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
- name: Run source distribution tests
run: |
cd dist/fastapi*/

View File

@@ -56,14 +56,10 @@ jobs:
- starlette-pypi
- starlette-git
include:
- os: ubuntu-latest
python-version: "3.9"
coverage: coverage
uv-resolution: lowest-direct
- os: macos-latest
python-version: "3.10"
coverage: coverage
uv-resolution: highest
uv-resolution: lowest-direct
- os: windows-latest
python-version: "3.12"
coverage: coverage

View File

@@ -26,7 +26,7 @@ Each of those response `dict`s can have a key `model`, containing a Pydantic mod
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note
@@ -203,7 +203,7 @@ For example, you can declare a response with a status code `404` that uses a Pyd
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
It will all be combined and included in your OpenAPI, and shown in the API docs:

View File

@@ -18,7 +18,7 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
@@ -26,7 +26,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
@@ -34,7 +34,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
We could create an instance of this class with:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
@@ -50,7 +50,7 @@ checker(q="somequery")
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip

View File

@@ -0,0 +1,61 @@
# Advanced Python Types { #advanced-python-types }
Here are some additional ideas that might be useful when working with Python types.
## Using `Union` or `Optional` { #using-union-or-optional }
If your code for some reason can't use `|`, for example if it's not in a type annotation but in something like `response_model=`, instead of using the vertical bar (`|`) you can use `Union` from `typing`.
For example, you could declare that something could be a `str` or `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` also has a shortcut to declare that something could be `None`, with `Optional`.
Here's a tip from my very **subjective** point of view:
* 🚨 Avoid using `Optional[SomeType]`
* Instead ✨ **use `Union[SomeType, None]`** ✨.
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
I think `Union[SomeType, None]` is more explicit about what it means.
It's just about the words and names. But those words can affect how you and your teammates think about the code.
As an example, let's take this function:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
The good news is, in most cases, you will be able to simply use `|` to define unions of types:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
So, normally you don't have to worry about names like `Optional` and `Union`. 😎

View File

@@ -32,11 +32,11 @@ For a simple example, let's consider a file structure similar to the one describ
The file `main.py` would have:
{* ../../docs_src/async_tests/app_a_py39/main.py *}
{* ../../docs_src/async_tests/app_a_py310/main.py *}
The file `test_main.py` would have the tests for `main.py`, it could look like this now:
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Run it { #run-it }
@@ -56,7 +56,7 @@ $ pytest
The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously:
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip
@@ -66,7 +66,7 @@ Note that the test function is now `async def` instead of just `def` as before w
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
This is the equivalent to:

View File

@@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
For example, let's say you define a *path operation* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
If the client tries to go to `/items`, by default, it would be redirected to `/items/`.
@@ -115,7 +115,7 @@ In this case, the original path `/app` would actually be served at `/api/v1/app`
Even though all your code is written assuming there's just `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to the app server (probably Uvicorn via FastAPI CLI), keeping your application convinced that it is being served at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`.
@@ -193,7 +193,7 @@ You can get the current `root_path` used by your application for each request, i
Here we are including it in the message just for demonstration purposes.
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Then, if you start Uvicorn with:
@@ -220,7 +220,7 @@ The response would be something like:
Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app:
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn.
@@ -400,7 +400,7 @@ If you pass a custom list of `servers` and there's a `root_path` (because your A
For example:
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Will generate an OpenAPI schema like:
@@ -455,7 +455,7 @@ If you don't specify the `servers` parameter and `root_path` is equal to `/`, th
If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
and then it won't include it in the OpenAPI schema.

View File

@@ -30,7 +30,7 @@ This is because by default, FastAPI will inspect every item inside and make sure
But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class.
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
/// info
@@ -55,7 +55,7 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
* Import `HTMLResponse`.
* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*.
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info
@@ -73,7 +73,7 @@ As seen in [Return a Response directly](response-directly.md){.internal-link tar
The same example from above, returning an `HTMLResponse`, could look like:
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning
@@ -97,7 +97,7 @@ The `response_class` will then be used only to document the OpenAPI *path operat
For example, it could be something like:
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
@@ -136,7 +136,7 @@ It accepts the following parameters:
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the `media_type` and appending a charset for text types.
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
@@ -146,7 +146,7 @@ Takes some text or bytes and returns an HTML response, as you read above.
Takes some text or bytes and returns a plain text response.
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
@@ -180,7 +180,7 @@ This requires installing `ujson` for example with `pip install ujson`.
///
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
/// tip
@@ -194,14 +194,14 @@ Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default
You can return a `RedirectResponse` directly:
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Or you can use it in the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
If you do that, then you can return the URL directly from your *path operation* function.
@@ -211,13 +211,13 @@ In this case, the `status_code` used will be the default one for the `RedirectRe
You can also use the `status_code` parameter combined with the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Takes an async generator or a normal generator/iterator and streams the response body.
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }
@@ -227,7 +227,7 @@ That way, you don't have to read it all first in memory, and you can pass that g
This includes many libraries to interact with cloud storage, video processing, and others.
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
1. This is the generator function. It's a "generator function" because it contains `yield` statements inside.
2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
@@ -256,11 +256,11 @@ Takes a different set of arguments to instantiate than the other response types:
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
You can also use the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
In this case, you can return the file path directly from your *path operation* function.
@@ -274,7 +274,7 @@ Let's say you want it to return indented and formatted JSON, so you want to use
You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Now instead of returning:
@@ -300,7 +300,7 @@ The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip

View File

@@ -30,7 +30,7 @@ Let's start with an example and then see it in detail.
We create an async function `lifespan()` with `yield` like this:
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*.
@@ -48,7 +48,7 @@ Maybe you need to start a new version, or you just got tired of running it. 🤷
The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`.
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
The first part of the function, before the `yield`, will be executed **before** the application starts.
@@ -60,7 +60,7 @@ If you check, the function is decorated with an `@asynccontextmanager`.
That converts the function into something called an "**async context manager**".
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager:
@@ -82,7 +82,7 @@ In our code example above, we don't use it directly, but we pass it to FastAPI f
The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it.
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Alternative Events (deprecated) { #alternative-events-deprecated }
@@ -104,7 +104,7 @@ These functions can be declared with `async def` or normal `def`.
To add a function that should be run before the application starts, declare it with the event `"startup"`:
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
@@ -116,7 +116,7 @@ And your application won't start receiving requests until all the `startup` even
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.

View File

@@ -40,7 +40,7 @@ Some of these solutions may also be open source or offer free tiers, so you can
Let's start with a simple FastAPI application:
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`.
@@ -98,7 +98,7 @@ In many cases, your FastAPI app will be bigger, and you will probably use tags t
For example, you could have a section for **items** and another section for **users**, and they could be separated by tags:
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Generate a TypeScript Client with Tags { #generate-a-typescript-client-with-tags }
@@ -145,7 +145,7 @@ For example, here it is using the first tag (you will probably have only one tag
You can then pass that custom function to **FastAPI** as the `generate_unique_id_function` parameter:
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Generate a TypeScript Client with Custom Operation IDs { #generate-a-typescript-client-with-custom-operation-ids }
@@ -167,7 +167,7 @@ But for the generated client, we could **modify** the OpenAPI operation IDs righ
We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this:
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js

View File

@@ -57,13 +57,13 @@ Enforces that all incoming requests must either be `https` or `wss`.
Any incoming request to `http` or `ws` will be redirected to the secure scheme instead.
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
The following arguments are supported:
@@ -78,7 +78,7 @@ Handles GZip responses for any request that includes `"gzip"` in the `Accept-Enc
The middleware will handle both standard and streaming responses.
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
The following arguments are supported:

View File

@@ -32,7 +32,7 @@ Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0`
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:13,36:53] *}
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.

View File

@@ -12,7 +12,7 @@ You can set the OpenAPI `operationId` to be used in your *path operation* with t
You would have to make sure that it is unique for each operation.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Using the *path operation function* name as the operationId { #using-the-path-operation-function-name-as-the-operationid }
@@ -20,7 +20,7 @@ If you want to use your APIs' function names as `operationId`s, you can iterate
You should do it after adding all your *path operations*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip
@@ -40,7 +40,7 @@ Even if they are in different modules (Python files).
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Advanced description from docstring { #advanced-description-from-docstring }
@@ -92,7 +92,7 @@ You can extend the OpenAPI schema for a *path operation* using the parameter `op
This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
@@ -139,7 +139,7 @@ For example, you could decide to read and validate the request with your own cod
You could do that with `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
In this example, we didn't declare any Pydantic model. In fact, the request body is not even <dfn title="converted from some plain format, like bytes, into Python objects">parsed</dfn> as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
@@ -153,7 +153,7 @@ And you could do this even if the data type in the request is not JSON.
For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML.
@@ -161,7 +161,7 @@ Then we use the request directly, and extract the body as `bytes`. This means th
And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip

View File

@@ -20,7 +20,7 @@ You can declare a parameter of type `Response` in your *path operation function*
And then you can set the `status_code` in that *temporal* response object.
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).

View File

@@ -6,7 +6,7 @@ You can declare a parameter of type `Response` in your *path operation function*
And then you can set cookies in that *temporal* response object.
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
@@ -24,7 +24,7 @@ To do that, you can create a response as described in [Return a Response Directl
Then set Cookies in it, and then return it:
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip

View File

@@ -54,7 +54,7 @@ Let's say that you want to return an <a href="https://en.wikipedia.org/wiki/XML"
You could put your XML content in a string, put that in a `Response`, and return it:
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Notes { #notes }

View File

@@ -6,7 +6,7 @@ You can declare a parameter of type `Response` in your *path operation function*
And then you can set headers in that *temporal* response object.
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
@@ -22,7 +22,7 @@ You can also add headers when you return a `Response` directly.
Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter:
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Technical Details

View File

@@ -20,7 +20,7 @@ Then, when you type that username and password, the browser sends them in the he
* It returns an object of type `HTTPBasicCredentials`:
* It contains the `username` and `password` sent.
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
@@ -40,7 +40,7 @@ To handle that, we first convert the `username` and `password` to `bytes` encodi
Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
This would be similar to:
@@ -104,4 +104,4 @@ That way, using `secrets.compare_digest()` in your application code, it will be
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}

View File

@@ -54,7 +54,7 @@ The same way as with Pydantic models, you declare class attributes with type ann
You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`.
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip
@@ -70,7 +70,7 @@ Next it will convert and validate the data. So, when you use that `settings` obj
Then you can use the new `settings` object in your application:
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Run the server { #run-the-server }
@@ -104,11 +104,11 @@ You could put those settings in another module file as you saw in [Bigger Applic
For example, you could have a file `config.py` with:
{* ../../docs_src/settings/app01_py39/config.py *}
{* ../../docs_src/settings/app01_py310/config.py *}
And then use it in a file `main.py`:
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip
@@ -126,7 +126,7 @@ This could be especially useful during testing, as it's very easy to override a
Coming from the previous example, your `config.py` file could look like:
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Notice that now we don't create a default instance `settings = Settings()`.
@@ -134,7 +134,7 @@ Notice that now we don't create a default instance `settings = Settings()`.
Now we create a dependency that returns a new `config.Settings()`.
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip
@@ -146,13 +146,13 @@ For now you can assume `get_settings()` is a normal function.
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Settings and testing { #settings-and-testing }
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object.
@@ -193,7 +193,7 @@ APP_NAME="ChimichangApp"
And then update your `config.py` with:
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip
@@ -226,7 +226,7 @@ we would create that object for each request, and we would be reading the `.env`
But as we are using the `@lru_cache` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Then for any subsequent call of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.

View File

@@ -10,7 +10,7 @@ If you need to have two independent FastAPI applications, with their own indepen
First, create the main, top-level, **FastAPI** application, and its *path operations*:
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sub-application { #sub-application }
@@ -18,7 +18,7 @@ Then, create your sub-application, and its *path operations*.
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Mount the sub-application { #mount-the-sub-application }
@@ -26,7 +26,7 @@ In your top-level application, `app`, mount the sub-application, `subapi`.
In this case, it will be mounted at the path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Check the automatic API docs { #check-the-automatic-api-docs }

View File

@@ -27,7 +27,7 @@ $ pip install jinja2
* Declare a `Request` parameter in the *path operation* that will return a template.
* Use the `templates` you created to render and return a `TemplateResponse`, pass the name of the template, the request object, and a "context" dictionary with key-value pairs to be used inside of the Jinja2 template.
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
/// note

View File

@@ -2,11 +2,11 @@
When you need `lifespan` to run in your tests, you can use the `TestClient` with a `with` statement:
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
You can read more details about the ["Running lifespan in tests in the official Starlette documentation site."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
For the deprecated `startup` and `shutdown` events, you can use the `TestClient` as follows:
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}

View File

@@ -4,7 +4,7 @@ You can use the same `TestClient` to test WebSockets.
For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket:
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
/// note

View File

@@ -29,7 +29,7 @@ Let's imagine you want to get the client's IP address/host inside of your *path
For that you need to access the request directly.
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter.

View File

@@ -38,13 +38,13 @@ In production you would have one of the options above.
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
## Create a `websocket` { #create-a-websocket }
In your **FastAPI** application, create a `websocket`:
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
/// note | Technical Details
@@ -58,7 +58,7 @@ You could also use `from starlette.websockets import WebSocket`.
In your WebSocket route you can `await` for messages and send messages.
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
You can receive and send binary, text, and JSON data.
@@ -154,7 +154,7 @@ With that you can connect the WebSocket and then send and receive messages:
When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example.
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
To try it out:

View File

@@ -18,7 +18,7 @@ Then wrap the WSGI (e.g. Flask) app with the middleware.
And then mount that under a path.
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
/// note

View File

@@ -14,7 +14,7 @@ In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#b
<summary>Dockerfile Preview 👀</summary>
```Dockerfile
FROM python:3.9
FROM python:3.14
WORKDIR /code
@@ -166,7 +166,7 @@ Now in the same project directory create a file `Dockerfile` with:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.9
FROM python:3.14
# (2)!
WORKDIR /code
@@ -390,7 +390,7 @@ If your FastAPI is a single file, for example, `main.py` without an `./app` dire
Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.9
FROM python:3.14
WORKDIR /code
@@ -499,7 +499,7 @@ Of course, there are **special cases** where you could want to have **a containe
In those cases, you can use the `--workers` command line option to set the number of workers that you want to run:
```{ .dockerfile .annotate }
FROM python:3.9
FROM python:3.14
WORKDIR /code

View File

@@ -8,7 +8,7 @@ But if for some reason your clients depend on the old behavior, you can revert t
For example, you can create a subclass of `HTTPBearer` that returns a `403 Forbidden` error instead of the default `401 Unauthorized` error:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip

View File

@@ -29,7 +29,7 @@ You can easily use the same Pydantic settings to configure your generated OpenAP
For example:
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`.

View File

@@ -18,7 +18,7 @@ Without changing the settings, syntax highlighting is enabled by default:
But you can disable it by setting `syntaxHighlight` to `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...and then Swagger UI won't show the syntax highlighting anymore:
@@ -28,7 +28,7 @@ But you can disable it by setting `syntaxHighlight` to `False`:
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
That configuration would change the syntax highlighting color theme:
@@ -46,7 +46,7 @@ You can override any of them by setting a different value in the argument `swagg
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Other Swagger UI Parameters { #other-swagger-ui-parameters }

View File

@@ -18,7 +18,7 @@ The first step is to disable the automatic docs, as by default, those use the de
To disable them, set their URLs to `None` when creating your `FastAPI` app:
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Include the custom docs { #include-the-custom-docs }
@@ -34,7 +34,7 @@ You can reuse FastAPI's internal functions to create the HTML pages for the docs
And similarly for ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip
@@ -50,7 +50,7 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
Now, to be able to test that everything works, create a *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Test it { #test-it }
@@ -118,7 +118,7 @@ After that, your file structure could look like:
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Test the static files { #test-the-static-files }
@@ -144,7 +144,7 @@ The same as when using a custom CDN, the first step is to disable the automatic
To disable them, set their URLs to `None` when creating your `FastAPI` app:
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Include the custom docs for static files { #include-the-custom-docs-for-static-files }
@@ -160,7 +160,7 @@ Again, you can reuse FastAPI's internal functions to create the HTML pages for t
And similarly for ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip
@@ -176,7 +176,7 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
Now, to be able to test that everything works, create a *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Test Static Files UI { #test-static-files-ui }

View File

@@ -43,19 +43,19 @@ For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/doc
First, write all your **FastAPI** application as normally:
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Generate the OpenAPI schema { #generate-the-openapi-schema }
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modify the OpenAPI schema { #modify-the-openapi-schema }
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Cache the OpenAPI schema { #cache-the-openapi-schema }
@@ -65,13 +65,13 @@ That way, your application won't have to generate the schema every time a user o
It will be generated only once, and then the same cached schema will be used for the next requests.
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Override the method { #override-the-method }
Now you can replace the `.openapi()` method with your new function.
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Check it { #check-it }

View File

@@ -35,7 +35,7 @@ Depending on your use case, you might prefer to use a different library, but if
Here's a small preview of how you could integrate Strawberry with FastAPI:
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.

View File

@@ -22,7 +22,7 @@ If you are a Python expert, and you already know everything about type hints, sk
Let's start with a simple example:
{* ../../docs_src/python_types/tutorial001_py39.py *}
{* ../../docs_src/python_types/tutorial001_py310.py *}
Calling this program outputs:
@@ -36,7 +36,7 @@ The function does the following:
* Converts the first letter of each one to upper case with `title()`.
* <dfn title="Puts them together, as one. With the contents of one after the other.">Concatenates</dfn> them with a space in the middle.
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Edit it { #edit-it }
@@ -78,7 +78,7 @@ That's it.
Those are the "type hints":
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
That is not the same as declaring default values like would be with:
@@ -106,7 +106,7 @@ With that, you can scroll, seeing the options, until you find the one that "ring
Check this function, it already has type hints:
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
@@ -114,7 +114,7 @@ Because the editor knows the types of the variables, you don't only get completi
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Declaring types { #declaring-types }
@@ -133,29 +133,32 @@ You can use, for example:
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Generic types with type parameters { #generic-types-with-type-parameters }
### `typing` module { #typing-module }
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
For some additional use cases, you might need to import some things from the standard library `typing` module, for example when you want to declare that something has "any type", you can use `Any` from `typing`:
These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types.
```python
from typing import Any
To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints.
#### Newer versions of Python { #newer-versions-of-python }
def some_function(data: Any):
print(data)
```
The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc.
### Generic types { #generic-types }
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
Some types can take "type parameters" in square brackets, to define their internal types, for example a "list of strings" would be declared `list[str]`.
If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity.
These types that can take type parameters are called **Generic types** or **Generics**.
In all the docs there are examples compatible with each version of Python (when there's a difference).
You can use the same builtin types as generics (with square brackets and types inside):
For example "**Python 3.6+**" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And "**Python 3.9+**" means it's compatible with Python 3.9 or above (including 3.10, etc).
If you can use the **latest versions of Python**, use the examples for the latest version, those will have the **best and simplest syntax**, for example, "**Python 3.10+**".
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
@@ -167,7 +170,7 @@ As the type, put `list`.
As the list is a type that contains some internal types, you put them in square brackets:
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info
@@ -193,7 +196,7 @@ And still, the editor knows it is a `str`, and provides support for that.
You would do the same to declare `tuple`s and `set`s:
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
This means:
@@ -208,7 +211,7 @@ The first type parameter is for the keys of the `dict`.
The second type parameter is for the values of the `dict`:
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
This means:
@@ -220,44 +223,20 @@ This means:
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
To define it you use the <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn> to separate both types.
In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn>.
//// tab | Python 3.10+
This is called a "union", because the variable can be anything in the union of those two sets of types.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
In both cases this means that `item` could be an `int` or a `str`.
This means that `item` could be an `int` or a `str`.
#### Possibly `None` { #possibly-none }
You can declare that a value could have a type, like `str`, but that it could also be `None`.
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
```Python hl_lines="1 4"
{!../../docs_src/python_types/tutorial009_py39.py!}
```
Using `Optional[str]` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
This also means that in Python 3.10, you can use `Something | None`:
//// tab | Python 3.10+
```Python hl_lines="1"
@@ -266,96 +245,7 @@ This also means that in Python 3.10, you can use `Something | None`:
////
//// tab | Python 3.9+
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
#### Using `Union` or `Optional` { #using-union-or-optional }
If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view:
* 🚨 Avoid using `Optional[SomeType]`
* Instead ✨ **use `Union[SomeType, None]`** ✨.
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
I think `Union[SomeType, None]` is more explicit about what it means.
It's just about the words and names. But those words can affect how you and your teammates think about the code.
As an example, let's take this function:
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
The good news is, once you are on Python 3.10 you won't have to worry about that, as you will be able to simply use `|` to define unions of types:
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
And then you won't have to worry about names like `Optional` and `Union`. 😎
#### Generic types { #generic-types }
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
//// tab | Python 3.10+
You can use the same builtin types as generics (with square brackets and types inside):
* `list`
* `tuple`
* `set`
* `dict`
And the same as with previous Python versions, from the `typing` module:
* `Union`
* `Optional`
* ...and others.
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn> to declare unions of types, that's a lot better and simpler.
////
//// tab | Python 3.9+
You can use the same builtin types as generics (with square brackets and types inside):
* `list`
* `tuple`
* `set`
* `dict`
And generics from the `typing` module:
* `Union`
* `Optional`
* ...and others.
////
Using `str | None` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
### Classes as types { #classes-as-types }
@@ -363,11 +253,11 @@ You can also declare a class as the type of a variable.
Let's say you have a class `Person`, with a name:
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Then you can declare a variable to be of type `Person`:
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
And then, again, you get all the editor support:
@@ -403,19 +293,13 @@ To learn more about <a href="https://docs.pydantic.dev/" class="external-link" t
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
/// tip
Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
///
## Type Hints with Metadata Annotations { #type-hints-with-metadata-annotations }
Python also has a feature that allows putting **additional <dfn title="Data about the data, in this case, information about the type, e.g. a description.">metadata</dfn>** in these type hints using `Annotated`.
Since Python 3.9, `Annotated` is a part of the standard library, so you can import it from `typing`.
You can import `Annotated` from `typing`.
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.

View File

@@ -7,6 +7,25 @@ hide:
## Latest Changes
### Breaking Changes
* Drop support for Python 3.9. PR [#14897](https://github.com/fastapi/fastapi/pull/14897) by [@tiangolo](https://github.com/tiangolo).
### Refactors
* 🎨 Update internal types for Python 3.10. PR [#14898](https://github.com/fastapi/fastapi/pull/14898) by [@tiangolo](https://github.com/tiangolo).
## 0.128.8
### Docs
* 📝 Fix grammar in `docs/en/docs/tutorial/first-steps.md`. PR [#14708](https://github.com/fastapi/fastapi/pull/14708) by [@SanjanaS10](https://github.com/SanjanaS10).
### Internal
* 🔨 Tweak PDM hook script. PR [#14895](https://github.com/fastapi/fastapi/pull/14895) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Update build setup for `fastapi-slim`, deprecate it, and make it only depend on `fastapi`. PR [#14894](https://github.com/fastapi/fastapi/pull/14894) by [@tiangolo](https://github.com/tiangolo).
## 0.128.7
### Features

View File

@@ -15,7 +15,7 @@ This includes, for example:
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter.
@@ -31,13 +31,13 @@ In this case, the task function will write to a file (simulating sending an emai
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Add the background task { #add-the-background-task }
Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` receives as arguments:

View File

@@ -85,7 +85,7 @@ You can create the *path operations* for that module using `APIRouter`.
You import it and create an "instance" the same way you would with the class `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Path operations* with `APIRouter` { #path-operations-with-apirouter }
@@ -93,7 +93,7 @@ And then you use it to declare your *path operations*.
Use it the same way you would use the `FastAPI` class:
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
You can think of `APIRouter` as a "mini `FastAPI`" class.
@@ -117,7 +117,7 @@ So we put them in their own `dependencies` module (`app/dependencies.py`).
We will now use a simple dependency to read a custom `X-Token` header:
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip
@@ -149,7 +149,7 @@ We know all the *path operations* in this module have the same:
So, instead of adding all that to each *path operation*, we can add it to the `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
As the path of each *path operation* has to start with `/`, like in:
@@ -208,7 +208,7 @@ And we need to get the dependency function from the module `app.dependencies`, t
So we use a relative import with `..` for the dependencies:
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### How relative imports work { #how-relative-imports-work }
@@ -279,7 +279,7 @@ We are not adding the prefix `/items` nor the `tags=["items"]` to each *path ope
But we can still add _more_ `tags` that will be applied to a specific *path operation*, and also some extra `responses` specific to that *path operation*:
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip
@@ -305,13 +305,13 @@ You import and create a `FastAPI` class as normally.
And we can even declare [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank} that will be combined with the dependencies for each `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Import the `APIRouter` { #import-the-apirouter }
Now we import the other submodules that have `APIRouter`s:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
As the files `app/routers/users.py` and `app/routers/items.py` are submodules that are part of the same Python package `app`, we can use a single dot `.` to import them using "relative imports".
@@ -374,13 +374,13 @@ the `router` from `users` would overwrite the one from `items` and we wouldn't b
So, to be able to use both of them in the same file, we import the submodules directly:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Include the `APIRouter`s for `users` and `items` { #include-the-apirouters-for-users-and-items }
Now, let's include the `router`s from the submodules `users` and `items`:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info
@@ -420,13 +420,13 @@ It contains an `APIRouter` with some admin *path operations* that your organizat
For this example it will be super simple. But let's say that because it is shared with other projects in the organization, we cannot modify it and add a `prefix`, `dependencies`, `tags`, etc. directly to the `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
But we still want to set a custom `prefix` when including the `APIRouter` so that all its *path operations* start with `/admin`, we want to secure it with the `dependencies` we already have for this project, and we want to include `tags` and `responses`.
We can declare all that without having to modify the original `APIRouter` by passing those parameters to `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
That way, the original `APIRouter` will stay unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization.
@@ -447,7 +447,7 @@ We can also add *path operations* directly to the `FastAPI` app.
Here we do it... just to show that we can 🤷:
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
and it will work correctly, together with all the other *path operations* added with `app.include_router()`.

View File

@@ -106,13 +106,6 @@ As, by default, singular values are interpreted as query parameters, you don't h
q: str | None = None
```
Or in Python 3.9:
```Python
q: Union[str, None] = None
```
For example:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}

View File

@@ -164,7 +164,7 @@ images: list[Image]
as in:
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Editor support everywhere { #editor-support-everywhere }
@@ -194,7 +194,7 @@ That's what we are going to see here.
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip

View File

@@ -155,7 +155,7 @@ The function parameters will be recognized as follows:
FastAPI will know that the value of `q` is not required because of the default value `= None`.
The `str | None` (Python 3.10+) or `Union` in `Union[str, None]` (Python 3.9+) is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
The `str | None` is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
But adding the type annotations will allow your editor to give you better support and detect errors.

View File

@@ -46,7 +46,7 @@ You can also specify whether your backend allows:
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
* Specific HTTP headers or all of them with the wildcard `"*"`.
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

View File

@@ -6,7 +6,7 @@ You can connect the debugger in your editor, for example with Visual Studio Code
In your FastAPI application, import and run `uvicorn` directly:
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### About `__name__ == "__main__"` { #about-name-main }

View File

@@ -101,7 +101,7 @@ Now you can declare your dependency using this class.
Notice how we write `CommonQueryParams` twice in the above code:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
@@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip
@@ -137,7 +137,7 @@ It is from this one that FastAPI will extract the declared parameters and that i
In this case, the first `CommonQueryParams`, in:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
@@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip
@@ -163,7 +163,7 @@ commons: CommonQueryParams ...
You could actually write just:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
@@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip
@@ -197,7 +197,7 @@ But declaring the type is encouraged as that way your editor will know what will
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
@@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip
@@ -225,7 +225,7 @@ For those specific cases, you can do the following:
Instead of writing:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
@@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip
@@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
...you write:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
@@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip

View File

@@ -14,7 +14,7 @@ The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
These dependencies will be executed/solved the same way as normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
@@ -44,13 +44,13 @@ You can use the same dependency *functions* you use normally.
They can declare request requirements (like headers) or other sub-dependencies:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Raise exceptions { #raise-exceptions }
These dependencies can `raise` exceptions, the same as normal dependencies:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Return values { #return-values }
@@ -58,7 +58,7 @@ And they can return values or not, the values won't be used.
So, you can reuse a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Dependencies for a group of *path operations* { #dependencies-for-a-group-of-path-operations }

View File

@@ -29,15 +29,15 @@ For example, you could use this to create a database session and close it after
Only the code prior to and including the `yield` statement is executed before creating a response:
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
The yielded value is what is injected into *path operations* and other dependencies:
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
The code following the `yield` statement is executed after the response:
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip
@@ -57,7 +57,7 @@ So, you can look for that specific exception inside the dependency with `except
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Sub-dependencies with `yield` { #sub-dependencies-with-yield }
@@ -67,7 +67,7 @@ You can have sub-dependencies and "trees" of sub-dependencies of any size and sh
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
And all of them can use `yield`.
@@ -75,7 +75,7 @@ In this case `dependency_c`, to execute its exit code, needs the value from `dep
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
The same way, you could have some dependencies with `yield` and some other dependencies with `return`, and have some of those depend on some of the others.
@@ -109,7 +109,7 @@ But it's there for you if you need it. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
If you want to catch exceptions and create a custom response based on that, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
@@ -117,7 +117,7 @@ If you want to catch exceptions and create a custom response based on that, crea
If you catch an exception using `except` in a dependency with `yield` and you don't raise it again (or raise a new exception), FastAPI won't be able to notice there was an exception, the same way that would happen with regular Python:
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
In this case, the client will see an *HTTP 500 Internal Server Error* response as it should, given that we are not raising an `HTTPException` or similar, but the server will **not have any logs** or any other indication of what was the error. 😱
@@ -127,7 +127,7 @@ If you catch an exception in a dependency with `yield`, unless you are raising a
You can re-raise the same exception using `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Now the client will get the same *HTTP 500 Internal Server Error* response, but the server will have our custom `InternalError` in the logs. 😎
@@ -190,7 +190,7 @@ Normally the exit code of dependencies with `yield` is executed **after the resp
But if you know that you won't need to use the dependency after returning from the *path operation function*, you can use `Depends(scope="function")` to tell FastAPI that it should close the dependency after the *path operation function* returns, but **before** the **response is sent**.
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` receives a `scope` parameter that can be:
@@ -269,7 +269,7 @@ In Python, you can create Context Managers by <a href="https://docs.python.org/3
You can also use them inside of **FastAPI** dependencies with `yield` by using
`with` or `async with` statements inside of the dependency function:
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip

View File

@@ -6,7 +6,7 @@ Similar to the way you can [add `dependencies` to the *path operation decorators
In that case, they will be applied to all the *path operations* in the application:
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app.

View File

@@ -62,7 +62,7 @@ And it will save the returned value in a <dfn title="A utility/system to store c
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
//// tab | Python 3.9+
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
@@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
////
//// tab | Python 3.9+ non-Annotated
//// tab | Python 3.10+ non-Annotated
/// tip

View File

@@ -190,9 +190,9 @@ But if we put that in the assignment `response_model=PlaneItem | CarItem` we wou
The same way, you can declare responses of lists of objects.
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
For that, use the standard Python `list`:
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Response with arbitrary `dict` { #response-with-arbitrary-dict }
@@ -200,9 +200,9 @@ You can also declare a response using a plain arbitrary `dict`, declaring just t
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
In this case, you can use `dict`:
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Recap { #recap }

View File

@@ -2,7 +2,7 @@
The simplest FastAPI file could look like this:
{* ../../docs_src/first_steps/tutorial001_py39.py *}
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copy that to a file `main.py`.
@@ -54,7 +54,7 @@ In the output, there's a line with something like:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
That line shows the URL where your app is being served, in your local machine.
That line shows the URL where your app is being served on your local machine.
### Check it { #check-it }
@@ -183,7 +183,7 @@ That's it! Now you can access your app at that URL. ✨
### Step 1: import `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` is a Python class that provides all the functionality for your API.
@@ -197,7 +197,7 @@ You can use all the <a href="https://www.starlette.dev/" class="external-link" t
### Step 2: create a `FastAPI` "instance" { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Here the `app` variable will be an "instance" of the class `FastAPI`.
@@ -266,7 +266,7 @@ We are going to call them "**operations**" too.
#### Define a *path operation decorator* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
@@ -320,7 +320,7 @@ This is our "**path operation function**":
* **operation**: is `get`.
* **function**: is the function below the "decorator" (below `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
This is a Python function.
@@ -332,7 +332,7 @@ In this case, it is an `async` function.
You could also define it as a normal function instead of `async def`:
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note
@@ -342,7 +342,7 @@ If you don't know the difference, check the [Async: *"In a hurry?"*](../async.md
### Step 5: return the content { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
You can return a `dict`, `list`, singular values as `str`, `int`, etc.

View File

@@ -25,7 +25,7 @@ To return HTTP responses with errors to the client you use `HTTPException`.
### Import `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Raise an `HTTPException` in your code { #raise-an-httpexception-in-your-code }
@@ -39,7 +39,7 @@ The benefit of raising an exception over returning a value will be more evident
In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`:
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### The resulting response { #the-resulting-response }
@@ -77,7 +77,7 @@ You probably won't need to use it directly in your code.
But in case you needed it for an advanced scenario, you can add custom headers:
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Install custom exception handlers { #install-custom-exception-handlers }
@@ -89,7 +89,7 @@ And you want to handle this exception globally with FastAPI.
You could add a custom exception handler with `@app.exception_handler()`:
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`.
@@ -127,7 +127,7 @@ To override it, import the `RequestValidationError` and use it with `@app.except
The exception handler will receive a `Request` and the exception.
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Now, if you go to `/items/foo`, instead of getting the default JSON error with:
@@ -159,7 +159,7 @@ The same way, you can override the `HTTPException` handler.
For example, you could want to return a plain text response instead of JSON for these errors:
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Technical Details
@@ -183,7 +183,7 @@ The `RequestValidationError` contains the `body` it received with invalid data.
You could use it while developing your app to log the body and debug it, return it to the user, etc.
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Now try sending an invalid item like:
@@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
If you want to use the exception along with the same default exception handlers from **FastAPI**, you can import and reuse the default exception handlers from `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
In this example you are just printing the error with a very expressive message, but you get the idea. You can use the exception and then just reuse the default exception handlers.

View File

@@ -18,7 +18,7 @@ You can set the following fields that are used in the OpenAPI specification and
You can set them as follows:
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip
@@ -36,7 +36,7 @@ Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the `license_info` with
For example:
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Metadata for tags { #metadata-for-tags }
@@ -58,7 +58,7 @@ Let's try that in an example with tags for `users` and `items`.
Create metadata for your tags and pass it to the `openapi_tags` parameter:
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
@@ -72,7 +72,7 @@ You don't have to add metadata for all the tags that you use.
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info
@@ -100,7 +100,7 @@ But you can configure it with the parameter `openapi_url`.
For example, to set it to be served at `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it.
@@ -117,4 +117,4 @@ You can configure the two documentation user interfaces included:
For example, to set Swagger UI to be served at `/documentation` and disable ReDoc:
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}

View File

@@ -31,7 +31,7 @@ The middleware function receives:
* Then it returns the `response` generated by the corresponding *path operation*.
* You can then further modify the `response` before returning it.
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip
@@ -57,7 +57,7 @@ And also after the `response` is generated, before returning it.
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip

View File

@@ -46,7 +46,7 @@ In these cases, it could make sense to store the tags in an `Enum`.
**FastAPI** supports that the same way as with plain strings:
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Summary and description { #summary-and-description }
@@ -92,7 +92,7 @@ So, if you don't provide one, **FastAPI** will automatically generate one of "Su
If you need to mark a *path operation* as <dfn title="obsolete, recommended not to use it">deprecated</dfn>, but without removing it, pass the parameter `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
It will be clearly marked as deprecated in the interactive docs:

View File

@@ -54,11 +54,11 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
So, you can declare your function as:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
But keep in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Order the parameters as you need, tricks { #order-the-parameters-as-you-need-tricks }
@@ -83,13 +83,13 @@ Pass `*`, as the first parameter of the function.
Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Even if they don't have a default value.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Better with `Annotated` { #better-with-annotated }
Keep in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and you probably won't need to use `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Number validations: greater than or equal { #number-validations-greater-than-or-equal }
@@ -97,7 +97,7 @@ With `Query` and `Path` (and others you'll see later) you can declare number con
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Number validations: greater than and less than or equal { #number-validations-greater-than-and-less-than-or-equal }
@@ -106,7 +106,7 @@ The same applies for:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Number validations: floats, greater than and less than { #number-validations-floats-greater-than-and-less-than }
@@ -118,7 +118,7 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
And the same for <abbr title="less than"><code>lt</code></abbr>.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Recap { #recap }

View File

@@ -2,7 +2,7 @@
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
@@ -16,7 +16,7 @@ So, if you run this example and go to <a href="http://127.0.0.1:8000/items/foo"
You can declare the type of a path parameter in the function, using standard Python type annotations:
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
In this case, `item_id` is declared to be an `int`.
@@ -118,13 +118,13 @@ And then you can also have a path `/users/{user_id}` to get data about a specifi
Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`.
Similarly, you cannot redefine a path operation:
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
The first one will always be used since the path matches first.
@@ -140,7 +140,7 @@ By inheriting from `str` the API docs will be able to know that the values must
Then create class attributes with fixed values, which will be the available valid values:
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip
@@ -152,7 +152,7 @@ If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine
Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Check the docs { #check-the-docs }
@@ -168,13 +168,13 @@ The value of the *path parameter* will be an *enumeration member*.
You can compare it with the *enumeration member* in your created enum `ModelName`:
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Get the *enumeration value* { #get-the-enumeration-value }
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip
@@ -188,7 +188,7 @@ You can return *enum members* from your *path operation*, even nested in a JSON
They will be converted to their corresponding values (strings in this case) before returning them to the client:
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
In your client you will get a JSON response like:
@@ -227,7 +227,7 @@ In this case, the name of the parameter is `file_path`, and the last part, `:pat
So, you can use it with:
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip

View File

@@ -47,40 +47,16 @@ Now it's the time to use it with FastAPI. 🚀
We had this type annotation:
//// tab | Python 3.10+
```Python
q: str | None = None
```
////
//// tab | Python 3.9+
```Python
q: Union[str, None] = None
```
////
What we will do is wrap that with `Annotated`, so it becomes:
//// tab | Python 3.10+
```Python
q: Annotated[str | None] = None
```
////
//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
```
////
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
Now let's jump to the fun stuff. 🎉
@@ -212,7 +188,7 @@ You can, of course, use default values other than `None`.
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note
@@ -242,7 +218,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
So, when you need to declare a value as required while using `Query`, you can simply not declare a default value:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Required, can be `None` { #required-can-be-none }
@@ -293,7 +269,7 @@ The interactive API docs will update accordingly, to allow multiple values:
You can also define a default `list` of values if none are provided:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
If you go to:
@@ -316,7 +292,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
You can also use `list` directly instead of `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note

View File

@@ -2,7 +2,7 @@
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
@@ -128,7 +128,7 @@ If you don't want to add a specific value but just make it optional, set the def
But when you want to make a query parameter required, you can just not declare any default value:
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Here the query parameter `needy` is a required query parameter of type `str`.

View File

@@ -20,13 +20,13 @@ This is because uploaded files are sent as "form data".
Import `File` and `UploadFile` from `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Define `File` Parameters { #define-file-parameters }
Create file parameters the same way you would for `Body` or `Form`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info
@@ -54,7 +54,7 @@ But there are several cases in which you might benefit from using `UploadFile`.
Define a file parameter with a type of `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Using `UploadFile` has several advantages over `bytes`:
@@ -143,7 +143,7 @@ You can make a file optional by using standard type annotations and setting a de
You can also use `File()` with `UploadFile`, for example, to set additional metadata:
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Multiple File Uploads { #multiple-file-uploads }
@@ -153,7 +153,7 @@ They would be associated to the same "form field" sent using "form data".
To use that, declare a list of `bytes` or `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
@@ -169,7 +169,7 @@ You could also use `from starlette.responses import HTMLResponse`.
And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Recap { #recap }

View File

@@ -24,7 +24,7 @@ This is supported since FastAPI version `0.113.0`. 🤓
You just need to declare a **Pydantic model** with the fields you want to receive as **form fields**, and then declare the parameter as `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** will **extract** the data for **each field** from the **form data** in the request and give you the Pydantic model you defined.
@@ -48,7 +48,7 @@ This is supported since FastAPI version `0.114.0`. 🤓
You can use Pydantic's model configuration to `forbid` any `extra` fields:
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
If a client tries to send some extra data, they will receive an **error** response.

View File

@@ -16,13 +16,13 @@ $ pip install python-multipart
## Import `File` and `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Define `File` and `Form` parameters { #define-file-and-form-parameters }
Create file and form parameters the same way you would for `Body` or `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
The files and form fields will be uploaded as form data and you will receive the files and form fields.

View File

@@ -18,13 +18,13 @@ $ pip install python-multipart
Import `Form` from `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Define `Form` parameters { #define-form-parameters }
Create form parameters the same way you would for `Body` or `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.

View File

@@ -183,7 +183,7 @@ There might be cases where you return something that is not a valid Pydantic fie
The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}.
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass of) `Response`.
@@ -193,7 +193,7 @@ And tools will also be happy because both `RedirectResponse` and `JSONResponse`
You can also use a subclass of `Response` in the type annotation:
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case.

View File

@@ -8,7 +8,7 @@ The same way you can specify a response model, you can also declare the HTTP sta
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note
@@ -74,7 +74,7 @@ To know more about each status code and which code is for what, check the <a hre
Let's see the previous example again:
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` is the status code for "Created".
@@ -82,7 +82,7 @@ But you don't have to memorize what each of these codes mean.
You can use the convenience variables from `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them:

View File

@@ -20,7 +20,7 @@ Let's first just use the code and see how it works, and then we'll come back to
Copy the example in a file `main.py`:
{* ../../docs_src/security/tutorial001_an_py39.py *}
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Run it { #run-it }
@@ -132,7 +132,7 @@ In that case, **FastAPI** also provides you with the tools to build it.
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip
@@ -170,7 +170,7 @@ So, it can be used with `Depends`.
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.

View File

@@ -2,7 +2,7 @@
In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`:
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
But that is still not that useful.

View File

@@ -7,7 +7,7 @@ You can serve static files automatically from a directory using `StaticFiles`.
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Technical Details

View File

@@ -30,7 +30,7 @@ Use the `TestClient` object the same way as you do with `httpx`.
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip
@@ -76,7 +76,7 @@ Let's say you have a file structure as described in [Bigger Applications](bigger
In the file `main.py` you have your **FastAPI** app:
{* ../../docs_src/app_testing/app_a_py39/main.py *}
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Testing file { #testing-file }
@@ -92,7 +92,7 @@ Then you could have a file `test_main.py` with your tests. It could live on the
Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`):
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...and have the code for the tests just like before.

View File

@@ -191,6 +191,7 @@ nav:
- advanced/openapi-webhooks.md
- advanced/wsgi.md
- advanced/generate-clients.md
- advanced/advanced-python-types.md
- fastapi-cli.md
- Deployment:
- deployment/index.md

View File

@@ -0,0 +1,22 @@
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
return JSONResponse(status_code=404, content={"message": "Item not found"})

View File

@@ -1,30 +0,0 @@
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
200: {
"content": {"image/png": {}},
"description": "Return the JSON item or an image.",
}
},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}

View File

@@ -0,0 +1,37 @@
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
404: {"model": Message, "description": "The item was not found"},
200: {
"description": "Item requested by ID",
"content": {
"application/json": {
"example": {"id": "bar", "value": "The bar tenders"}
}
},
},
},
)
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})

View File

@@ -1,32 +0,0 @@
from typing import Union
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
responses = {
404: {"description": "Item not found"},
302: {"description": "The item was moved"},
403: {"description": "Not enough privileges"},
}
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: Union[bool, None] = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}

View File

@@ -1,25 +0,0 @@
from typing import Annotated, Union
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[Union[str, None], Body()] = None,
size: Annotated[Union[int, None], Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

View File

@@ -1,25 +0,0 @@
from typing import Union
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Union[str, None] = Body(default=None),
size: Union[int, None] = Body(default=None),
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)

View File

@@ -0,0 +1,11 @@
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
@app.get("/")
async def main():
return {"message": "Hello World"}

View File

@@ -0,0 +1,13 @@
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
@app.get("/")
async def main():
return {"message": "Hello World"}

View File

@@ -0,0 +1,11 @@
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
@app.get("/")
async def main():
return "somebigcontent"

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}

View File

@@ -0,0 +1,11 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

View File

@@ -1,38 +0,0 @@
from typing import Annotated, Union
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: Union[str, None] = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/")
async def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item:
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=409, detail="Item already exists")
fake_db[item.id] = item.model_dump()
return item

View File

@@ -1,65 +0,0 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_nonexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 409
assert response.json() == {"detail": "Item already exists"}

View File

@@ -1,38 +0,0 @@
from typing import Union
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: Union[str, None] = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: str = Header()):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/")
async def create_item(item: Item, x_token: str = Header()) -> Item:
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=409, detail="Item already exists")
fake_db[item.id] = item.model_dump()
return item

View File

@@ -1,65 +0,0 @@
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_nonexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 409
assert response.json() == {"detail": "Item already exists"}

View File

@@ -0,0 +1,18 @@
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

View File

@@ -0,0 +1,31 @@
from fastapi import FastAPI
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocket
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
@app.websocket("/ws")
async def websocket(websocket: WebSocket):
await websocket.accept()
await websocket.send_json({"msg": "Hello WebSocket"})
await websocket.close()
def test_read_main():
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
def test_websocket():
client = TestClient(app)
with client.websocket_connect("/ws") as websocket:
data = websocket.receive_json()
assert data == {"msg": "Hello WebSocket"}

View File

@@ -0,0 +1,24 @@
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
def test_read_items():
with TestClient(app) as client:
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Fighters"}

View File

@@ -0,0 +1,43 @@
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.testclient import TestClient
items = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
yield
# clean up items
items.clear()
app = FastAPI(lifespan=lifespan)
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
def test_read_items():
# Before the lifespan starts, "items" is still empty
assert items == {}
with TestClient(app) as client:
# Inside the "with TestClient" block, the lifespan starts and items added
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Fighters"}
# After the requests is done, the items are still there
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
# The end of the "with TestClient" block simulates terminating the app, so
# the lifespan ends and items are cleaned up
assert items == {}

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Tomato"}

View File

@@ -0,0 +1,14 @@
import pytest
from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
response = await ac.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Tomato"}

View File

@@ -0,0 +1,21 @@
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
app = FastAPI()
class HTTPBearer403(HTTPBearer):
def make_not_authenticated_error(self) -> HTTPException:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated"
)
CredentialsDep = Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer403())]
@app.get("/me")
def read_me(credentials: CredentialsDep):
return {"message": "You are authenticated", "token": credentials.credentials}

View File

@@ -0,0 +1,15 @@
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification(email: str, message=""):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}

View File

@@ -1,26 +0,0 @@
from typing import Annotated, Union
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

View File

@@ -1,26 +0,0 @@
from typing import Union
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
def read_items():
return ["plumbus", "portal gun"]

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI, Request
app = FastAPI(root_path="/api/v1")
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

View File

@@ -0,0 +1,14 @@
from fastapi import FastAPI, Request
app = FastAPI(
servers=[
{"url": "https://stag.example.com", "description": "Staging environment"},
{"url": "https://prod.example.com", "description": "Production environment"},
],
root_path="/api/v1",
)
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

View File

@@ -0,0 +1,15 @@
from fastapi import FastAPI, Request
app = FastAPI(
servers=[
{"url": "https://stag.example.com", "description": "Staging environment"},
{"url": "https://prod.example.com", "description": "Production environment"},
],
root_path="/api/v1",
root_path_in_servers=False,
)
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}

Some files were not shown because too many files have changed in this diff Show More