diff --git a/docs/en/docs/tutorial/sql-databases.md b/docs/en/docs/tutorial/sql-databases.md
index 0645cc9f1..56971bf9d 100644
--- a/docs/en/docs/tutorial/sql-databases.md
+++ b/docs/en/docs/tutorial/sql-databases.md
@@ -101,7 +101,9 @@ Now let's see what each file/module does.
## Install `SQLAlchemy`
-First you need to install `SQLAlchemy`:
+First you need to install `SQLAlchemy`.
+
+Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example:
diff --git a/docs/en/docs/tutorial/testing.md b/docs/en/docs/tutorial/testing.md
index 95c8c5bef..06a87e92e 100644
--- a/docs/en/docs/tutorial/testing.md
+++ b/docs/en/docs/tutorial/testing.md
@@ -12,7 +12,11 @@ With it, you can use
`httpx`.
-E.g. `pip install httpx`.
+Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example:
+
+```console
+$ pip install httpx
+```
///
@@ -206,7 +210,9 @@ If you have a Pydantic model in your test and you want to send its data to the a
## Run it
-After that, you just need to install `pytest`:
+After that, you just need to install `pytest`.
+
+Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example:
diff --git a/docs/en/docs/virtual-environments.md b/docs/en/docs/virtual-environments.md
new file mode 100644
index 000000000..fcc72fbe7
--- /dev/null
+++ b/docs/en/docs/virtual-environments.md
@@ -0,0 +1,844 @@
+# Virtual Environments
+
+When you work in Python projects you probably should use a **virtual environment** (or a similar mechanism) to isolate the packages you install for each project.
+
+/// info
+
+If you already know about virtual environments, how to create them and use them, you might want to skip this section. 🤓
+
+///
+
+/// tip
+
+A **virtual environment** is different than an **environment variable**.
+
+An **environment variable** is a variable in the system that can be used by programs.
+
+A **virtual environment** is a directory with some files in it.
+
+///
+
+/// info
+
+This page will teach you how to use **virtual environments** and how they work.
+
+If you are ready to adopt a **tool that manages everything** for you (including installing Python), try
uv.
+
+///
+
+## Create a Project
+
+First, create a directory for your project.
+
+What I normally do is that I create a directory named `code` inside my home/user directory.
+
+And inside of that I create one directory per project.
+
+
+
+```console
+// Go to the home directory
+$ cd
+// Create a directory for all your code projects
+$ mkdir code
+// Enter into that code directory
+$ cd code
+// Create a directory for this project
+$ mkdir awesome-project
+// Enter into that project directory
+$ cd awesome-project
+```
+
+
+
+## Create a Virtual Environment
+
+When you start working on a Python project **for the first time**, create a virtual environment **
inside your project**.
+
+/// tip
+
+You only need to do this **once per project**, not every time you work.
+
+///
+
+//// tab | `venv`
+
+To create a virtual environment, you can use the `venv` module that comes with Python.
+
+
+
+```console
+$ python -m venv .venv
+```
+
+
+
+/// details | What that command means
+
+* `python`: use the program called `python`
+* `-m`: call a module as a script, we'll tell it which module next
+* `venv`: use the module called `venv` that normally comes installed with Python
+* `.venv`: create the virtual environment in the new directory `.venv`
+
+///
+
+////
+
+//// tab | `uv`
+
+If you have
`uv` installed, you can use it to create a virtual environment.
+
+
+
+```console
+$ uv venv
+```
+
+
+
+/// tip
+
+By default, `uv` will create a virtual environment in a directory called `.venv`.
+
+But you could customize it passing an additional argument with the directory name.
+
+///
+
+////
+
+That command creates a new virtual environment in a directory called `.venv`.
+
+/// details | `.venv` or other name
+
+You could create the virtual environment in a different directory, but there's a convention of calling it `.venv`.
+
+///
+
+## Activate the Virtual Environment
+
+Activate the new virtual environment so that any Python command you run or package you install uses it.
+
+/// tip
+
+Do this **every time** you start a **new terminal session** to work on the project.
+
+///
+
+//// tab | Linux, macOS
+
+
+
+```console
+$ source .venv/bin/activate
+```
+
+
+
+////
+
+//// tab | Windows PowerShell
+
+
+
+```console
+$ .venv\Scripts\Activate.ps1
+```
+
+
+
+////
+
+//// tab | Windows Bash
+
+Or if you use Bash for Windows (e.g.
Git Bash):
+
+
+
+```console
+$ source .venv/Scripts/activate
+```
+
+
+
+////
+
+/// tip
+
+Every time you install a **new package** in that environment, **activate** the environment again.
+
+This makes sure that if you use a **terminal (
CLI) program** installed by that package, you use the one from your virtual environment and not any other that could be installed globally, probably with a different version than what you need.
+
+///
+
+## Check the Virtual Environment is Active
+
+Check that the virtual environment is active (the previous command worked).
+
+/// tip
+
+This is **optional**, but it's a good way to **check** that everything is working as expected and you are using the virtual environment you intended.
+
+///
+
+//// tab | Linux, macOS, Windows Bash
+
+
+
+```console
+$ which python
+
+/home/user/code/awesome-project/.venv/bin/python
+```
+
+
+
+If it shows the `python` binary at `.venv/bin/python`, inside of your project (in this case `awesome-project`), then it worked. 🎉
+
+////
+
+//// tab | Windows PowerShell
+
+
+
+```console
+$ Get-Command python
+
+C:\Users\user\code\awesome-project\.venv\Scripts\python
+```
+
+
+
+If it shows the `python` binary at `.venv\Scripts\python`, inside of your project (in this case `awesome-project`), then it worked. 🎉
+
+////
+
+## Upgrade `pip`
+
+/// tip
+
+If you use
`uv` you would use it to install things instead of `pip`, so you don't need to upgrade `pip`. 😎
+
+///
+
+If you are using `pip` to install packages (it comes by default with Python), you should **upgrade** it to the latest version.
+
+Many exotic errors while installing a package are solved by just upgrading `pip` first.
+
+/// tip
+
+You would normally do this **once**, right after you create the virtual environment.
+
+///
+
+Make sure the virtual environment is active (with the command above) and then run:
+
+
+
+```console
+$ python -m pip install --upgrade pip
+
+---> 100%
+```
+
+
+
+## Add `.gitignore`
+
+If you are using **Git** (you should), add a `.gitignore` file to exclude everything in your `.venv` from Git.
+
+/// tip
+
+If you used
`uv` to create the virtual environment, it already did this for you, you can skip this step. 😎
+
+///
+
+/// tip
+
+Do this **once**, right after you create the virtual environment.
+
+///
+
+
+
+```console
+$ echo "*" > .venv/.gitignore
+```
+
+
+
+/// details | What that command means
+
+* `echo "*"`: will "print" the text `*` in the terminal (the next part changes that a bit)
+* `>`: anything printed to the terminal by the command to the left of `>` should not be printed but instead written to the file that goes to the right of `>`
+* `.gitignore`: the name of the file where the text should be written
+
+And `*` for Git means "everything". So, it will ignore everything in the `.venv` directory.
+
+That command will create a file `.gitignore` with the content:
+
+```gitignore
+*
+```
+
+///
+
+## Install Packages
+
+After activating the environment, you can install packages in it.
+
+/// tip
+
+Do this **once** when installing or upgrading the packages your project needs.
+
+If you need to upgrade a version or add a new package you would **do this again**.
+
+///
+
+### Install Packages Directly
+
+If you're in a hurry and don't want to use a file to declare your project's package requirements, you can install them directly.
+
+/// tip
+
+It's a (very) good idea to put the packages and versions your program needs in a file (for example `requirements.txt` or `pyproject.toml`).
+
+///
+
+//// tab | `pip`
+
+
+
+```console
+$ pip install "fastapi[standard]"
+
+---> 100%
+```
+
+
+
+////
+
+//// tab | `uv`
+
+If you have
`uv`:
+
+
+
+```console
+$ uv pip install "fastapi[standard]"
+---> 100%
+```
+
+
+
+////
+
+### Install from `requirements.txt`
+
+If you have a `requirements.txt`, you can now use it to install its packages.
+
+//// tab | `pip`
+
+
+
+```console
+$ pip install -r requirements.txt
+---> 100%
+```
+
+
+
+////
+
+//// tab | `uv`
+
+If you have
`uv`:
+
+
+
+```console
+$ uv pip install -r requirements.txt
+---> 100%
+```
+
+
+
+////
+
+/// details | `requirements.txt`
+
+A `requirements.txt` with some packages could look like:
+
+```requirements.txt
+fastapi[standard]==0.113.0
+pydantic==2.8.0
+```
+
+///
+
+## Run Your Program
+
+After you activated the virtual environment, you can run your program, and it will use the Python inside of your virtual environment with the packages you installed there.
+
+
+
+```console
+$ python main.py
+
+Hello World
+```
+
+
+
+## Configure Your Editor
+
+You would probably use an editor, make sure you configure it to use the same virtual environment you created (it will probably autodetect it) so that you can get autocompletion and inline errors.
+
+For example:
+
+*
VS Code
+*
PyCharm
+
+/// tip
+
+You normally have to do this only **once**, when you create the virtual environment.
+
+///
+
+## Deactivate the Virtual Environment
+
+Once you are done working on your project you can **deactivate** the virtual environment.
+
+
+
+```console
+$ deactivate
+```
+
+
+
+This way, when you run `python` it won't try to run it from that virtual environment with the packages installed there.
+
+## Ready to Work
+
+Now you're ready to start working on your project.
+
+
+
+/// tip
+
+Do you want to understand what's all that above?
+
+Continue reading. 👇🤓
+
+///
+
+## Why Virtual Environments
+
+To work with FastAPI you need to install
Python.
+
+After that, you would need to **install** FastAPI and any other **packages** you want to use.
+
+To install packages you would normally use the `pip` command that comes with Python (or similar alternatives).
+
+Nevertheless, if you just use `pip` directly, the packages would be installed in your **global Python environment** (the global installation of Python).
+
+### The Problem
+
+So, what's the problem with installing packages in the global Python environment?
+
+At some point, you will probably end up writing many different programs that depend on **different packages**. And some of these projects you work on will depend on **different versions** of the same package. 😱
+
+For example, you could create a project called `philosophers-stone`, this program depends on another package called **`harry`, using the version `1`**. So, you need to install `harry`.
+
+```mermaid
+flowchart LR
+ stone(philosophers-stone) -->|requires| harry-1[harry v1]
+```
+
+Then, at some point later, you create another project called `prisoner-of-azkaban`, and this project also depends on `harry`, but this project needs **`harry` version `3`**.
+
+```mermaid
+flowchart LR
+ azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
+```
+
+But now the problem is, if you install the packages globally (in the global environment) instead of in a local **virtual environment**, you will have to choose which version of `harry` to install.
+
+If you want to run `philosophers-stone` you will need to first install `harry` version `1`, for example with:
+
+
+
+```console
+$ pip install "harry==1"
+```
+
+
+
+And then you would end up with `harry` version `1` installed in your global Python environment.
+
+```mermaid
+flowchart LR
+ subgraph global[global env]
+ harry-1[harry v1]
+ end
+ subgraph stone-project[philosophers-stone project]
+ stone(philosophers-stone) -->|requires| harry-1
+ end
+```
+
+But then if you want to run `prisoner-of-azkaban`, you will need to uninstall `harry` version `1` and install `harry` version `3` (or just installing version `3` would automatically uninstall version `1`).
+
+
+
+```console
+$ pip install "harry==3"
+```
+
+
+
+And then you would end up with `harry` version `3` installed in your global Python environment.
+
+And if you try to run `philosophers-stone` again, there's a chance it would **not work** because it needs `harry` version `1`.
+
+```mermaid
+flowchart LR
+ subgraph global[global env]
+ harry-1[
harry v1]
+ style harry-1 fill:#ccc,stroke-dasharray: 5 5
+ harry-3[harry v3]
+ end
+ subgraph stone-project[philosophers-stone project]
+ stone(philosophers-stone) -.-x|⛔️| harry-1
+ end
+ subgraph azkaban-project[prisoner-of-azkaban project]
+ azkaban(prisoner-of-azkaban) --> |requires| harry-3
+ end
+```
+
+/// tip
+
+It's very common in Python packages to try the best to **avoid breaking changes** in **new versions**, but it's better to be safe, and install newer versions intentionally and when you can run the tests to check everything is working correctly.
+
+///
+
+Now, imagine that with **many** other **packages** that all your **projects depend on**. That's very difficult to manage. And you would probably end up running some projects with some **incompatible versions** of the packages, and not knowing why something isn't working.
+
+Also, depending on your operating system (e.g. Linux, Windows, macOS), it could have come with Python already installed. And in that case it probably had some packages pre-installed with some specific versions **needed by your system**. If you install packages in the global Python environment, you could end up **breaking** some of the programs that came with your operating system.
+
+## Where are Packages Installed
+
+When you install Python, it creates some directories with some files in your computer.
+
+Some of these directories are the ones in charge of having all the packages you install.
+
+When you run:
+
+
+
+```console
+// Don't run this now, it's just an example 🤓
+$ pip install "fastapi[standard]"
+---> 100%
+```
+
+
+
+That will download a compressed file with the FastAPI code, normally from
PyPI.
+
+It will also **download** files for other packages that FastAPI depends on.
+
+Then it will **extract** all those files and put them in a directory in your computer.
+
+By default, it will put those files downloaded and extracted in the directory that comes with your Python installation, that's the **global environment**.
+
+## What are Virtual Environments
+
+The solution to the problems of having all the packages in the global environment is to use a **virtual environment for each project** you work on.
+
+A virtual environment is a **directory**, very similar to the global one, where you can install the packages for a project.
+
+This way, each project will have its own virtual environment (`.venv` directory) with its own packages.
+
+```mermaid
+flowchart TB
+ subgraph stone-project[philosophers-stone project]
+ stone(philosophers-stone) --->|requires| harry-1
+ subgraph venv1[.venv]
+ harry-1[harry v1]
+ end
+ end
+ subgraph azkaban-project[prisoner-of-azkaban project]
+ azkaban(prisoner-of-azkaban) --->|requires| harry-3
+ subgraph venv2[.venv]
+ harry-3[harry v3]
+ end
+ end
+ stone-project ~~~ azkaban-project
+```
+
+## What Does Activating a Virtual Environment Mean
+
+When you activate a virtual environment, for example with:
+
+//// tab | Linux, macOS
+
+
+
+```console
+$ source .venv/bin/activate
+```
+
+
+
+////
+
+//// tab | Windows PowerShell
+
+
+
+```console
+$ .venv\Scripts\Activate.ps1
+```
+
+
+
+////
+
+//// tab | Windows Bash
+
+Or if you use Bash for Windows (e.g.
Git Bash):
+
+
+
+```console
+$ source .venv/Scripts/activate
+```
+
+
+
+////
+
+That command will create or modify some [environment variables](environment-variables.md){.internal-link target=_blank} that will be available for the next commands.
+
+One of those variables is the `PATH` variable.
+
+/// tip
+
+You can learn more about the `PATH` environment variable in the [Environment Variables](environment-variables.md#path-environment-variable){.internal-link target=_blank} section.
+
+///
+
+Activating a virtual environment adds its path `.venv/bin` (on Linux and macOS) or `.venv\Scripts` (on Windows) to the `PATH` environment variable.
+
+Let's say that before activating the environment, the `PATH` variable looked like this:
+
+//// tab | Linux, macOS
+
+```plaintext
+/usr/bin:/bin:/usr/sbin:/sbin
+```
+
+That means that the system would look for programs in:
+
+* `/usr/bin`
+* `/bin`
+* `/usr/sbin`
+* `/sbin`
+
+////
+
+//// tab | Windows
+
+```plaintext
+C:\Windows\System32
+```
+
+That means that the system would look for programs in:
+
+* `C:\Windows\System32`
+
+////
+
+After activating the virtual environment, the `PATH` variable would look something like this:
+
+//// tab | Linux, macOS
+
+```plaintext
+/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
+```
+
+That means that the system will now start looking first look for programs in:
+
+```plaintext
+/home/user/code/awesome-project/.venv/bin
+```
+
+before looking in the other directories.
+
+So, when you type `python` in the terminal, the system will find the Python program in
+
+```plaintext
+/home/user/code/awesome-project/.venv/bin/python
+```
+
+and use that one.
+
+////
+
+//// tab | Windows
+
+```plaintext
+C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
+```
+
+That means that the system will now start looking first look for programs in:
+
+```plaintext
+C:\Users\user\code\awesome-project\.venv\Scripts
+```
+
+before looking in the other directories.
+
+So, when you type `python` in the terminal, the system will find the Python program in
+
+```plaintext
+C:\Users\user\code\awesome-project\.venv\Scripts\python
+```
+
+and use that one.
+
+////
+
+An important detail is that it will put the virtual environment path at the **beginning** of the `PATH` variable. The system will find it **before** finding any other Python available. This way, when you run `python`, it will use the Python **from the virtual environment** instead of any other `python` (for example, a `python` from a global environment).
+
+Activating a virtual environment also changes a couple of other things, but this is one of the most important things it does.
+
+## Checking a Virtual Environment
+
+When you check if a virtual environment is active, for example with:
+
+//// tab | Linux, macOS, Windows Bash
+
+
+
+```console
+$ which python
+
+/home/user/code/awesome-project/.venv/bin/python
+```
+
+
+
+////
+
+//// tab | Windows PowerShell
+
+
+
+```console
+$ Get-Command python
+
+C:\Users\user\code\awesome-project\.venv\Scripts\python
+```
+
+
+
+////
+
+That means that the `python` program that will be used is the one **in the virtual environment**.
+
+you use `which` in Linux and macOS and `Get-Command` in Windows PowerShell.
+
+The way that command works is that it will go and check in the `PATH` environment variable, going through **each path in order**, looking for the program called `python`. Once it finds it, it will **show you the path** to that program.
+
+The most important part is that when you call `python`, that is the exact "`python`" that will be executed.
+
+So, you can confirm if you are in the correct virtual environment.
+
+/// tip
+
+It's easy to activate one virtual environment, get one Python, and then **go to another project**.
+
+And the second project **wouldn't work** because you are using the **incorrect Python**, from a virtual environment for another project.
+
+It's useful being able to check what `python` is being used. 🤓
+
+///
+
+## Why Deactivate a Virtual Environment
+
+For example, you could be working on a project `philosophers-stone`, **activate that virtual environment**, install packages and work with that environment.
+
+And then you want to work on **another project** `prisoner-of-azkaban`.
+
+You go to that project:
+
+
+
+```console
+$ cd ~/code/prisoner-of-azkaban
+```
+
+
+
+If you don't deactivate the virtual environment for `philosophers-stone`, when you run `python` in the terminal, it will try to use the Python from `philosophers-stone`.
+
+
+
+```console
+$ cd ~/code/prisoner-of-azkaban
+
+$ python main.py
+
+// Error importing sirius, it's not installed 😱
+Traceback (most recent call last):
+ File "main.py", line 1, in
+ import sirius
+```
+
+
+
+But if you deactivate the virtual environment and activate the new one for `prisoner-of-askaban` then when you run `python` it will use the Python from the virtual environment in `prisoner-of-azkaban`.
+
+
+
+```console
+$ cd ~/code/prisoner-of-azkaban
+
+// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
+$ deactivate
+
+// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
+$ source .venv/bin/activate
+
+// Now when you run python, it will find the package sirius installed in this virtual environment ✨
+$ python main.py
+
+I solemnly swear 🐺
+```
+
+
+
+## Alternatives
+
+This is a simple guide to get you started and teach you how everything works **underneath**.
+
+There are many **alternatives** to managing virtual environments, package dependencies (requirements), projects.
+
+Once you are ready and want to use a tool to **manage the entire project**, packages dependencies, virtual environments, etc. I would suggest you try
uv.
+
+`uv` can do a lot of things, it can:
+
+* **Install Python** for you, including different versions
+* Manage the **virtual environment** for your projects
+* Install **packages**
+* Manage package **dependencies and versions** for your project
+* Make sure you have an **exact** set of packages and versions to install, including their dependencies, so that you can be sure that you can run your project in production exactly the same as in your computer while developing, this is called **locking**
+* And many other things
+
+## Conclusion
+
+If you read and understood all this, now **you know much more** about virtual environments than many developers out there. 🤓
+
+Knowing these details will most probably be useful in a future time when you are debugging something that seems complex, but you will know **how it all works underneath**. 😎
diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml
index d23ddd42f..528c80b8e 100644
--- a/docs/en/mkdocs.yml
+++ b/docs/en/mkdocs.yml
@@ -102,13 +102,14 @@ plugins:
show_symbol_type_toc: true
nav:
-- FastAPI:
- - index.md
- - features.md
+- FastAPI: index.md
+- features.md
- Learn:
- learn/index.md
- python-types.md
- async.md
+ - environment-variables.md
+ - virtual-environments.md
- Tutorial - User Guide:
- tutorial/index.md
- tutorial/first-steps.md
@@ -362,6 +363,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
+ - link: /nl/
+ name: nl - Nederlands
- link: /pl/
name: pl - Polski
- link: /pt/
diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html
index 229cbca71..47e46c4bf 100644
--- a/docs/en/overrides/main.html
+++ b/docs/en/overrides/main.html
@@ -59,7 +59,7 @@
-
diff --git a/docs/es/docs/advanced/additional-status-codes.md b/docs/es/docs/advanced/additional-status-codes.md
index f40fad03c..664604c59 100644
--- a/docs/es/docs/advanced/additional-status-codes.md
+++ b/docs/es/docs/advanced/additional-status-codes.md
@@ -18,7 +18,7 @@ Para conseguir esto importa `JSONResponse` y devuelve ahí directamente tu conte
{!../../../docs_src/additional_status_codes/tutorial001.py!}
```
-/// warning | "Advertencia"
+/// warning | Advertencia
Cuando devuelves directamente una `Response`, como en los ejemplos anteriores, será devuelta directamente.
@@ -28,7 +28,7 @@ Asegúrate de que la respuesta tenga los datos que quieras, y que los valores se
///
-/// note | "Detalles Técnicos"
+/// note | Detalles Técnicos
También podrías utilizar `from starlette.responses import JSONResponse`.
diff --git a/docs/es/docs/advanced/index.md b/docs/es/docs/advanced/index.md
index 88ef8e19f..10a1ff0d5 100644
--- a/docs/es/docs/advanced/index.md
+++ b/docs/es/docs/advanced/index.md
@@ -6,7 +6,7 @@ El [Tutorial - Guía de Usuario](../tutorial/index.md){.internal-link target=_bl
En las secciones siguientes verás otras opciones, configuraciones, y características adicionales.
-/// tip
+/// tip | Consejo
Las próximas secciones **no son necesariamente "avanzadas"**.
diff --git a/docs/es/docs/advanced/path-operation-advanced-configuration.md b/docs/es/docs/advanced/path-operation-advanced-configuration.md
index 9e8714fe4..18f96213f 100644
--- a/docs/es/docs/advanced/path-operation-advanced-configuration.md
+++ b/docs/es/docs/advanced/path-operation-advanced-configuration.md
@@ -26,13 +26,13 @@ Deberías hacerlo después de adicionar todas tus *operaciones de path*.
{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
```
-/// tip | "Consejo"
+/// tip | Consejo
Si llamas manualmente a `app.openapi()`, debes actualizar el `operationId`s antes de hacerlo.
///
-/// warning | "Advertencia"
+/// warning | Advertencia
Si haces esto, debes asegurarte de que cada una de tus *funciones de las operaciones de path* tenga un nombre único.
diff --git a/docs/es/docs/advanced/response-directly.md b/docs/es/docs/advanced/response-directly.md
index 7ce5bddca..4eeab3fd0 100644
--- a/docs/es/docs/advanced/response-directly.md
+++ b/docs/es/docs/advanced/response-directly.md
@@ -14,7 +14,7 @@ Esto puede ser útil, por ejemplo, para devolver cookies o headers personalizado
De hecho, puedes devolver cualquier `Response` o cualquier subclase de la misma.
-/// tip | "Consejo"
+/// tip | Consejo
`JSONResponse` en sí misma es una subclase de `Response`.
@@ -38,7 +38,7 @@ Para esos casos, puedes usar el `jsonable_encoder` para convertir tus datos ante
{!../../../docs_src/response_directly/tutorial001.py!}
```
-/// note | "Detalles Técnicos"
+/// note | Detalles Técnicos
También puedes usar `from starlette.responses import JSONResponse`.
diff --git a/docs/es/docs/advanced/response-headers.md b/docs/es/docs/advanced/response-headers.md
index 414b145fc..c3aa20993 100644
--- a/docs/es/docs/advanced/response-headers.md
+++ b/docs/es/docs/advanced/response-headers.md
@@ -29,7 +29,7 @@ Crea un response tal como se describe en [Retornar una respuesta directamente](r
{!../../../docs_src/response_headers/tutorial001.py!}
```
-/// note | "Detalles Técnicos"
+/// note | Detalles Técnicos
También podrías utilizar `from starlette.responses import Response` o `from starlette.responses import JSONResponse`.
diff --git a/docs/es/docs/advanced/security/index.md b/docs/es/docs/advanced/security/index.md
index 7fa8047e9..92de67d6a 100644
--- a/docs/es/docs/advanced/security/index.md
+++ b/docs/es/docs/advanced/security/index.md
@@ -4,7 +4,7 @@
Hay algunas características adicionales para manejar la seguridad además de las que se tratan en el [Tutorial - Guía de Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank}.
-/// tip
+/// tip | Consejo
Las siguientes secciones **no necesariamente son "avanzadas"**.
diff --git a/docs/es/docs/async.md b/docs/es/docs/async.md
index 193d24270..5ab2ff9a4 100644
--- a/docs/es/docs/async.md
+++ b/docs/es/docs/async.md
@@ -21,7 +21,7 @@ async def read_results():
return results
```
-/// note | "Nota"
+/// note | Nota
Solo puedes usar `await` dentro de funciones creadas con `async def`.
@@ -138,7 +138,7 @@ Tú y esa persona 😍 se comen las hamburguesas 🍔 y la pasan genial ✨.
-/// info
+/// info | Información
Las ilustraciones fueron creados por Ketrina Thompson. 🎨
@@ -204,7 +204,7 @@ Sólo las comes y listo 🍔 ⏹.
No has hablado ni coqueteado mucho, ya que has pasado la mayor parte del tiempo esperando 🕙 frente al mostrador 😞.
-/// info
+/// info | Información
Las ilustraciones fueron creados por
Ketrina Thompson. 🎨
@@ -396,7 +396,7 @@ Todo eso es lo que impulsa FastAPI (a través de Starlette) y lo que hace que te
## Detalles muy técnicos
-/// warning | "Advertencia"
+/// warning | Advertencia
Probablemente puedas saltarte esto.
diff --git a/docs/es/docs/deployment/versions.md b/docs/es/docs/deployment/versions.md
index 7d09a2739..74243da89 100644
--- a/docs/es/docs/deployment/versions.md
+++ b/docs/es/docs/deployment/versions.md
@@ -42,7 +42,7 @@ Siguiendo las convenciones de *Semantic Versioning*, cualquier versión por deba
FastAPI también sigue la convención de que cualquier cambio hecho en una
"PATCH" version es para solucionar errores y
*non-breaking changes*.
-/// tip
+/// tip | Consejo
El
"PATCH" es el último número, por ejemplo, en `0.2.3`, la
PATCH version es `3`.
@@ -56,7 +56,7 @@ fastapi>=0.45.0,<0.46.0
En versiones
"MINOR" son añadidas nuevas características y posibles
breaking changes.
-/// tip
+/// tip | Consejo
La versión "MINOR" es el número en el medio, por ejemplo, en `0.2.3`, la
"MINOR" version es `2`.
diff --git a/docs/es/docs/features.md b/docs/es/docs/features.md
index 3c610f8f1..b75918dff 100644
--- a/docs/es/docs/features.md
+++ b/docs/es/docs/features.md
@@ -63,7 +63,7 @@ second_user_data = {
my_second_user: User = User(**second_user_data)
```
-/// info
+/// info | Información
`**second_user_data` significa:
diff --git a/docs/es/docs/how-to/graphql.md b/docs/es/docs/how-to/graphql.md
index d75af7d81..590c2e828 100644
--- a/docs/es/docs/how-to/graphql.md
+++ b/docs/es/docs/how-to/graphql.md
@@ -4,7 +4,7 @@ Como **FastAPI** está basado en el estándar **ASGI**, es muy fácil integrar c
Puedes combinar *operaciones de path* regulares de la library de FastAPI con GraphQL en la misma aplicación.
-/// tip
+/// tip | Consejo
**GraphQL** resuelve algunos casos de uso específicos.
@@ -49,7 +49,7 @@ Versiones anteriores de Starlette incluyen la clase `GraphQLApp` para integrarlo
Esto fue marcado como obsoleto en Starlette, pero si aún tienes código que lo usa, puedes fácilmente **migrar** a
starlette-graphene3, la cual cubre el mismo caso de uso y tiene una **interfaz casi idéntica.**
-/// tip
+/// tip | Consejo
Si necesitas GraphQL, te recomendaría revisar
Strawberry, que es basada en anotaciones de tipo en vez de clases y tipos personalizados.
diff --git a/docs/es/docs/python-types.md b/docs/es/docs/python-types.md
index fce434483..4015dbb05 100644
--- a/docs/es/docs/python-types.md
+++ b/docs/es/docs/python-types.md
@@ -12,7 +12,7 @@ Todo **FastAPI** está basado en estos type hints, lo que le da muchas ventajas
Pero, así nunca uses **FastAPI** te beneficiarás de aprender un poco sobre los type hints.
-/// note | "Nota"
+/// note | Nota
Si eres un experto en Python y ya lo sabes todo sobre los type hints, salta al siguiente capítulo.
@@ -256,7 +256,7 @@ Tomado de la documentación oficial de Pydantic:
{!../../../docs_src/python_types/tutorial010.py!}
```
-/// info | "Información"
+/// info | Información
Para aprender más sobre
Pydantic mira su documentación.
@@ -288,7 +288,7 @@ Puede que todo esto suene abstracto. Pero no te preocupes que todo lo verás en
Lo importante es que usando los tipos de Python estándar en un único lugar (en vez de añadir más clases, decorator, etc.) **FastAPI** hará mucho del trabajo por ti.
-/// info | "Información"
+/// info | Información
Si ya pasaste por todo el tutorial y volviste a la sección de los tipos, una buena referencia es
la "cheat sheet" de `mypy`.
diff --git a/docs/es/docs/tutorial/cookie-params.md b/docs/es/docs/tutorial/cookie-params.md
index 27ba8ed57..3eaea31f9 100644
--- a/docs/es/docs/tutorial/cookie-params.md
+++ b/docs/es/docs/tutorial/cookie-params.md
@@ -32,9 +32,9 @@ Primero importa `Cookie`:
//// tab | Python 3.10+ non-Annotated
-/// tip
+/// tip | Consejo
-Prefer to use the `Annotated` version if possible.
+Es preferible utilizar la versión `Annotated` si es posible.
///
@@ -46,9 +46,9 @@ Prefer to use the `Annotated` version if possible.
//// tab | Python 3.8+ non-Annotated
-/// tip
+/// tip | Consejo
-Prefer to use the `Annotated` version if possible.
+Es preferible utilizar la versión `Annotated` si es posible.
///
@@ -90,9 +90,9 @@ El primer valor es el valor por defecto, puedes pasar todos los parámetros adic
//// tab | Python 3.10+ non-Annotated
-/// tip
+/// tip | Consejo
-Prefer to use the `Annotated` version if possible.
+Es preferible utilizar la versión `Annotated` si es posible.
///
@@ -104,9 +104,9 @@ Prefer to use the `Annotated` version if possible.
//// tab | Python 3.8+ non-Annotated
-/// tip
+/// tip | Consejo
-Prefer to use the `Annotated` version if possible.
+Es preferible utilizar la versión `Annotated` si es posible.
///
diff --git a/docs/es/docs/tutorial/first-steps.md b/docs/es/docs/tutorial/first-steps.md
index affdfebff..8d8909b97 100644
--- a/docs/es/docs/tutorial/first-steps.md
+++ b/docs/es/docs/tutorial/first-steps.md
@@ -24,7 +24,7 @@ $ uvicorn main:app --reload
-/// note | "Nota"
+/// note | Nota
El comando `uvicorn main:app` se refiere a:
@@ -139,7 +139,7 @@ También podrías usarlo para generar código automáticamente, para los cliente
`FastAPI` es una clase de Python que provee toda la funcionalidad para tu API.
-/// note | "Detalles Técnicos"
+/// note | Detalles Técnicos
`FastAPI` es una clase que hereda directamente de `Starlette`.
@@ -205,7 +205,7 @@ https://example.com/items/foo
/items/foo
```
-/// info | "Información"
+/// info | Información
Un "path" también se conoce habitualmente como "endpoint", "route" o "ruta".
@@ -259,7 +259,7 @@ El `@app.get("/")` le dice a **FastAPI** que la función que tiene justo debajo
* el path `/`
* usando una
operación get
-/// info | "Información sobre `@decorator`"
+/// info | Información sobre `@decorator`
Esa sintaxis `@algo` se llama un "decorador" en Python.
@@ -286,7 +286,7 @@ y las más exóticas:
* `@app.patch()`
* `@app.trace()`
-/// tip | "Consejo"
+/// tip | Consejo
Tienes la libertad de usar cada operación (método de HTTP) como quieras.
@@ -324,7 +324,7 @@ También podrías definirla como una función estándar en lugar de `async def`:
{!../../../docs_src/first_steps/tutorial003.py!}
```
-/// note | "Nota"
+/// note | Nota
Si no sabes la diferencia, revisa el [Async: *"¿Tienes prisa?"*](../async.md#tienes-prisa){.internal-link target=_blank}.
diff --git a/docs/es/docs/tutorial/path-params.md b/docs/es/docs/tutorial/path-params.md
index 73bd586f1..e09e0381f 100644
--- a/docs/es/docs/tutorial/path-params.md
+++ b/docs/es/docs/tutorial/path-params.md
@@ -24,7 +24,7 @@ Puedes declarar el tipo de un parámetro de path en la función usando las anota
En este caso, `item_id` es declarado como un `int`.
-/// check | "Revisa"
+/// check | Revisa
Esto te dará soporte en el editor dentro de tu función, con chequeo de errores, auto-completado, etc.
@@ -38,7 +38,7 @@ Si corres este ejemplo y abres tu navegador en
http://127.0.0.1:8000/items/4.2
-/// check | "Revisa"
+/// check | Revisa
Así, con la misma declaración de tipo de Python, **FastAPI** te da validación de datos.
@@ -85,7 +85,7 @@ Cuando abras tu navegador en
-/// check | "Revisa"
+/// check | Revisa
Nuevamente, con la misma declaración de tipo de Python, **FastAPI** te da documentación automática e interactiva (integrándose con Swagger UI)
@@ -143,13 +143,13 @@ Luego crea atributos de clase con valores fijos, que serán los valores disponib
{!../../../docs_src/path_params/tutorial005.py!}
```
-/// info | "Información"
+/// info | Información
Las Enumerations (o enums) están disponibles en Python desde la versión 3.4.
///
-/// tip | "Consejo"
+/// tip | Consejo
Si lo estás dudando, "AlexNet", "ResNet", y "LeNet" son solo nombres de
modelos de Machine Learning.
@@ -189,7 +189,7 @@ Puedes obtener el valor exacto (un `str` en este caso) usando `model_name.value`
{!../../../docs_src/path_params/tutorial005.py!}
```
-/// tip | "Consejo"
+/// tip | Consejo
También podrías obtener el valor `"lenet"` con `ModelName.lenet.value`.
@@ -246,7 +246,7 @@ Entonces lo puedes usar con:
{!../../../docs_src/path_params/tutorial004.py!}
```
-/// tip | "Consejo"
+/// tip | Consejo
Podrías necesitar que el parámetro contenga `/home/johndoe/myfile.txt` con un slash inicial (`/`).
diff --git a/docs/es/docs/tutorial/query-params.md b/docs/es/docs/tutorial/query-params.md
index 52a3e66a4..6f88fd617 100644
--- a/docs/es/docs/tutorial/query-params.md
+++ b/docs/es/docs/tutorial/query-params.md
@@ -69,13 +69,13 @@ Del mismo modo puedes declarar parámetros de query opcionales definiendo el val
En este caso el parámetro de la función `q` será opcional y será `None` por defecto.
-/// check | "Revisa"
+/// check | Revisa
También puedes notar que **FastAPI** es lo suficientemente inteligente para darse cuenta de que el parámetro de path `item_id` es un parámetro de path y que `q` no lo es, y por lo tanto es un parámetro de query.
///
-/// note | "Nota"
+/// note | Nota
FastAPI sabrá que `q` es opcional por el `= None`.
@@ -199,7 +199,7 @@ En este caso hay 3 parámetros de query:
* `skip`, un `int` con un valor por defecto de `0`.
* `limit`, un `int` opcional.
-/// tip | "Consejo"
+/// tip | Consejo
También podrías usar los `Enum`s de la misma manera que con los [Parámetros de path](path-params.md#valores-predefinidos){.internal-link target=_blank}.
diff --git a/docs/fr/docs/fastapi-people.md b/docs/fr/docs/fastapi-people.md
deleted file mode 100644
index 52a79032a..000000000
--- a/docs/fr/docs/fastapi-people.md
+++ /dev/null
@@ -1,180 +0,0 @@
----
-hide:
- - navigation
----
-
-# La communauté FastAPI
-
-FastAPI a une communauté extraordinaire qui accueille des personnes de tous horizons.
-
-## Créateur - Mainteneur
-
-Salut! 👋
-
-C'est moi :
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Je suis le créateur et le responsable de **FastAPI**. Vous pouvez en lire plus à ce sujet dans [Aide FastAPI - Obtenir de l'aide - Se rapprocher de l'auteur](help-fastapi.md#se-rapprocher-de-lauteur){.internal-link target=_blank}.
-
-...Mais ici, je veux vous montrer la communauté.
-
----
-
-**FastAPI** reçoit beaucoup de soutien de la part de la communauté. Et je tiens à souligner leurs contributions.
-
-Ce sont ces personnes qui :
-
-* [Aident les autres à résoudre des problèmes (questions) dans GitHub](help-fastapi.md#aider-les-autres-a-resoudre-les-problemes-dans-github){.internal-link target=_blank}.
-* [Créent des Pull Requests](help-fastapi.md#creer-une-pull-request){.internal-link target=_blank}.
-* Review les Pull Requests, [particulièrement important pour les traductions](contributing.md#traductions){.internal-link target=_blank}.
-
-Une salve d'applaudissements pour eux. 👏 🙇
-
-## Utilisateurs les plus actifs le mois dernier
-
-Ce sont les utilisateurs qui ont [aidé le plus les autres avec des problèmes (questions) dans GitHub](help-fastapi.md#aider-les-autres-a-resoudre-les-problemes-dans-github){.internal-link target=_blank} au cours du dernier mois. ☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Experts
-
-Voici les **Experts FastAPI**. 🤓
-
-Ce sont les utilisateurs qui ont [aidé le plus les autres avec des problèmes (questions) dans GitHub](help-fastapi.md#aider-les-autres-a-resoudre-les-problemes-dans-github){.internal-link target=_blank} depuis *toujours*.
-
-Ils ont prouvé qu'ils étaient des experts en aidant beaucoup d'autres personnes. ✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Principaux contributeurs
-
-Ces utilisateurs sont les **Principaux contributeurs**. 👷
-
-Ces utilisateurs ont [créé le plus grand nombre de demandes Pull Request](help-fastapi.md#creer-une-pull-request){.internal-link target=_blank} qui ont été *merged*.
-
-Ils ont contribué au code source, à la documentation, aux traductions, etc. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Il existe de nombreux autres contributeurs (plus d'une centaine), vous pouvez les voir tous dans la
Page des contributeurs de FastAPI GitHub. 👷
-
-## Principaux Reviewers
-
-Ces utilisateurs sont les **Principaux Reviewers**. 🕵️
-
-### Reviewers des traductions
-
-Je ne parle que quelques langues (et pas très bien 😅). Ainsi, les reviewers sont ceux qui ont le [**pouvoir d'approuver les traductions**](contributing.md#traductions){.internal-link target=_blank} de la documentation. Sans eux, il n'y aurait pas de documentation dans plusieurs autres langues.
-
----
-
-Les **Principaux Reviewers** 🕵️ ont examiné le plus grand nombre de demandes Pull Request des autres, assurant la qualité du code, de la documentation, et surtout, des **traductions**.
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Sponsors
-
-Ce sont les **Sponsors**. 😎
-
-Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec
GitHub Sponsors.
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Gold Sponsors
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Silver Sponsors
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Bronze Sponsors
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-### Individual Sponsors
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## À propos des données - détails techniques
-
-L'intention de cette page est de souligner l'effort de la communauté pour aider les autres.
-
-Notamment en incluant des efforts qui sont normalement moins visibles, et, dans de nombreux cas, plus difficile, comme aider d'autres personnes à résoudre des problèmes et examiner les Pull Requests de traduction.
-
-Les données sont calculées chaque mois, vous pouvez lire le
code source ici.
-
-Je me réserve également le droit de mettre à jour l'algorithme, les sections, les seuils, etc. (juste au cas où 🤷).
diff --git a/docs/ja/docs/fastapi-people.md b/docs/ja/docs/fastapi-people.md
deleted file mode 100644
index aaf76ba21..000000000
--- a/docs/ja/docs/fastapi-people.md
+++ /dev/null
@@ -1,184 +0,0 @@
----
-hide:
- - navigation
----
-
-# FastAPI People
-
-FastAPIには、様々なバックグラウンドの人々を歓迎する素晴らしいコミュニティがあります。
-
-## Creator - Maintainer
-
-こんにちは! 👋
-
-これが私です:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-
-{% endif %}
-
-私は **FastAPI** の作成者および Maintainer です。詳しくは [FastAPIを応援 - ヘルプの入手 - 開発者とつながる](help-fastapi.md#_1){.internal-link target=_blank} に記載しています。
-
-...ところで、ここではコミュニティを紹介したいと思います。
-
----
-
-**FastAPI** は、コミュニティから多くのサポートを受けています。そこで、彼らの貢献にスポットライトを当てたいと思います。
-
-紹介するのは次のような人々です:
-
-* [GitHub issuesで他の人を助ける](help-fastapi.md#github-issues){.internal-link target=_blank}。
-* [プルリクエストをする](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。
-* プルリクエストのレビューをする ([特に翻訳に重要](contributing.md#_8){.internal-link target=_blank})。
-
-彼らに大きな拍手を。👏 🙇
-
-## 先月最もアクティブだったユーザー
-
-彼らは、先月の[GitHub issuesで最も多くの人を助けた](help-fastapi.md#github-issues){.internal-link target=_blank}ユーザーです。☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Experts
-
-**FastAPI experts** を紹介します。🤓
-
-彼らは、*これまでに* [GitHub issuesで最も多くの人を助けた](help-fastapi.md#github-issues){.internal-link target=_blank}ユーザーです。
-
-多くの人を助けることでexpertsであると示されています。✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Top Contributors
-
-**Top Contributors** を紹介します。👷
-
-彼らは、*マージされた* [最も多くのプルリクエストを作成した](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}ユーザーです。
-
-ソースコード、ドキュメント、翻訳などに貢献してくれました。📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-他にもたくさん (100人以上) の contributors がいます。
FastAPI GitHub Contributors ページですべての contributors を確認できます。👷
-
-## Top Reviewers
-
-以下のユーザーは **Top Reviewers** です。🕵️
-
-### 翻訳のレビュー
-
-私は少しの言語しか話せません (もしくはあまり上手ではありません😅)。したがって、reviewers は、ドキュメントの[**翻訳を承認する権限**](contributing.md#_8){.internal-link target=_blank}を持っています。それらがなければ、いくつかの言語のドキュメントはなかったでしょう。
-
----
-
-**Top Reviewers** 🕵️は、他の人からのプルリクエストのほとんどをレビューし、コード、ドキュメント、特に**翻訳**の品質を保証しています。
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Sponsors
-
-**Sponsors** を紹介します。😎
-
-彼らは、
GitHub Sponsors を介して私の **FastAPI** などに関する活動を支援してくれています。
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Gold Sponsors
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Silver Sponsors
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Bronze Sponsors
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### Individual Sponsors
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## データについて - 技術詳細
-
-このページの目的は、他の人を助けるためのコミュニティの努力にスポットライトを当てるためです。
-
-特に、他の人の issues を支援したり、翻訳のプルリクエストを確認したりするなど、通常は目立たず、多くの場合、より困難な作業を含みます。
-
-データは毎月集計されます。
ソースコードはこちらで確認できます。
-
-ここでは、スポンサーの貢献も強調しています。
-
-アルゴリズム、セクション、閾値などは更新されるかもしれません (念のために 🤷)。
diff --git a/docs/ja/docs/learn/index.md b/docs/ja/docs/learn/index.md
new file mode 100644
index 000000000..2f24c670a
--- /dev/null
+++ b/docs/ja/docs/learn/index.md
@@ -0,0 +1,5 @@
+# 学習
+
+ここでは、**FastAPI** を学習するための入門セクションとチュートリアルを紹介します。
+
+これは、FastAPIを学習するにあたっての**書籍**や**コース**であり、**公式**かつ推奨される方法とみなすことができます 😎
diff --git a/docs/nl/docs/features.md b/docs/nl/docs/features.md
new file mode 100644
index 000000000..848b155ec
--- /dev/null
+++ b/docs/nl/docs/features.md
@@ -0,0 +1,201 @@
+# Functionaliteit
+
+## FastAPI functionaliteit
+
+**FastAPI** biedt je het volgende:
+
+### Gebaseerd op open standaarden
+
+*
OpenAPI voor het maken van API's, inclusief declaraties van
padbewerkingen, parameters, request bodies, beveiliging, enz.
+* Automatische datamodel documentatie met
JSON Schema (aangezien OpenAPI zelf is gebaseerd op JSON Schema).
+* Ontworpen op basis van deze standaarden, na zorgvuldig onderzoek. In plaats van achteraf deze laag er bovenop te bouwen.
+* Dit maakt het ook mogelijk om automatisch **clientcode te genereren** in verschillende programmeertalen.
+
+### Automatische documentatie
+
+Interactieve API-documentatie en verkenning van webgebruikersinterfaces. Aangezien dit framework is gebaseerd op OpenAPI, zijn er meerdere documentatie opties mogelijk, waarvan er standaard 2 zijn inbegrepen.
+
+*
Swagger UI, met interactieve interface, maakt het mogelijk je API rechtstreeks vanuit de browser aan te roepen en te testen.
+
+
+
+* Alternatieve API-documentatie met
ReDoc.
+
+
+
+### Gewoon Moderne Python
+
+Het is allemaal gebaseerd op standaard **Python type** declaraties (dankzij Pydantic). Je hoeft dus geen nieuwe syntax te leren. Het is gewoon standaard moderne Python.
+
+Als je een opfriscursus van 2 minuten nodig hebt over het gebruik van Python types (zelfs als je FastAPI niet gebruikt), bekijk dan deze korte tutorial: [Python Types](python-types.md){.internal-link target=_blank}.
+
+Je schrijft gewoon standaard Python met types:
+
+```Python
+from datetime import date
+
+from pydantic import BaseModel
+
+# Declareer een variabele als een str
+# en krijg editorondersteuning in de functie
+def main(user_id: str):
+ return user_id
+
+
+# Een Pydantic model
+class User(BaseModel):
+ id: int
+ name: str
+ joined: date
+```
+
+Vervolgens kan je het op deze manier gebruiken:
+
+```Python
+my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
+
+second_user_data = {
+ "id": 4,
+ "name": "Mary",
+ "joined": "2018-11-30",
+}
+
+my_second_user: User = User(**second_user_data)
+```
+
+/// info
+
+`**second_user_data` betekent:
+
+Geef de sleutels (keys) en waarden (values) van de `second_user_data` dict direct door als sleutel-waarden argumenten, gelijk aan: `User(id=4, name=“Mary”, joined=“2018-11-30”)`
+
+///
+
+### Editor-ondersteuning
+
+Het gehele framework is ontworpen om eenvoudig en intuïtief te zijn in gebruik. Alle beslissingen zijn getest op meerdere code-editors nog voordat het daadwerkelijke ontwikkelen begon, om zo de beste ontwikkelervaring te garanderen.
+
+Uit enquêtes onder Python ontwikkelaars blijkt maar al te duidelijk dat "(automatische) code aanvulling"
een van de meest gebruikte functionaliteiten is.
+
+Het hele **FastAPI** framework is daarop gebaseerd. Automatische code aanvulling werkt overal.
+
+Je hoeft zelden terug te vallen op de documentatie.
+
+Zo kan je editor je helpen:
+
+* in
Visual Studio Code:
+
+
+
+* in
PyCharm:
+
+
+
+Je krijgt autocomletion die je voorheen misschien zelfs voor onmogelijk had gehouden. Zoals bijvoorbeeld de `price` key in een JSON body (die genest had kunnen zijn) die afkomstig is van een request.
+
+Je hoeft niet langer de verkeerde keys in te typen, op en neer te gaan tussen de documentatie, of heen en weer te scrollen om te checken of je `username` of toch `user_name` had gebruikt.
+
+### Kort
+
+Dit framework heeft voor alles verstandige **standaardinstellingen**, met overal optionele configuraties. Alle parameters kunnen worden verfijnd zodat het past bij wat je nodig hebt, om zo de API te kunnen definiëren die jij nodig hebt.
+
+Maar standaard werkt alles **“gewoon”**.
+
+### Validatie
+
+* Validatie voor de meeste (of misschien wel alle?) Python **datatypes**, inclusief:
+ * JSON objecten (`dict`).
+ * JSON array (`list`) die itemtypes definiëren.
+ * String (`str`) velden, die min en max lengtes hebben.
+ * Getallen (`int`, `float`) met min en max waarden, enz.
+
+* Validatie voor meer exotische typen, zoals:
+ * URL.
+ * E-mail.
+ * UUID.
+ * ...en anderen.
+
+Alle validatie wordt uitgevoerd door het beproefde en robuuste **Pydantic**.
+
+### Beveiliging en authenticatie
+
+Beveiliging en authenticatie is geïntegreerd. Zonder compromissen te doen naar databases of datamodellen.
+
+Alle beveiligingsschema's gedefinieerd in OpenAPI, inclusief:
+
+* HTTP Basic.
+* **OAuth2** (ook met **JWT tokens**). Bekijk de tutorial over [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}.
+* API keys in:
+ * Headers.
+ * Query parameters.
+ * Cookies, enz.
+
+Plus alle beveiligingsfuncties van Starlette (inclusief **sessiecookies**).
+
+Gebouwd als een herbruikbare tool met componenten die makkelijk te integreren zijn in en met je systemen, datastores, relationele en NoSQL databases, enz.
+
+### Dependency Injection
+
+FastAPI bevat een uiterst eenvoudig, maar uiterst krachtig
Dependency Injection systeem.
+
+* Zelfs dependencies kunnen dependencies hebben, waardoor een hiërarchie of **“graph” van dependencies** ontstaat.
+* Allemaal **automatisch afgehandeld** door het framework.
+* Alle dependencies kunnen data nodig hebben van request, de vereiste **padoperaties veranderen** en automatische documentatie verstrekken.
+* **Automatische validatie** zelfs voor *padoperatie* parameters gedefinieerd in dependencies.
+* Ondersteuning voor complexe gebruikersauthenticatiesystemen, **databaseverbindingen**, enz.
+* **Geen compromisen** met databases, gebruikersinterfaces, enz. Maar eenvoudige integratie met ze allemaal.
+
+### Ongelimiteerde "plug-ins"
+
+Of anders gezegd, je hebt ze niet nodig, importeer en gebruik de code die je nodig hebt.
+
+Elke integratie is ontworpen om eenvoudig te gebruiken (met afhankelijkheden), zodat je een “plug-in" kunt maken in 2 regels code, met dezelfde structuur en syntax die wordt gebruikt voor je *padbewerkingen*.
+
+### Getest
+
+* 100%
van de code is getest.
+* 100%
type geannoteerde codebase.
+* Wordt gebruikt in productietoepassingen.
+
+## Starlette functies
+
+**FastAPI** is volledig verenigbaar met (en gebaseerd op)
Starlette.
+
+`FastAPI` is eigenlijk een subklasse van `Starlette`. Dus als je Starlette al kent of gebruikt, zal de meeste functionaliteit op dezelfde manier werken.
+
+Met **FastAPI** krijg je alle functies van **Starlette** (FastAPI is gewoon Starlette op steroïden):
+
+* Zeer indrukwekkende prestaties. Het is
een van de snelste Python frameworks, vergelijkbaar met **NodeJS** en **Go**.
+* **WebSocket** ondersteuning.
+* Taken in de achtergrond tijdens het proces.
+* Opstart- en afsluit events.
+* Test client gebouwd op HTTPX.
+* **CORS**, GZip, Statische bestanden, Streaming reacties.
+* **Sessie en Cookie** ondersteuning.
+* 100% van de code is getest.
+* 100% type geannoteerde codebase.
+
+## Pydantic functionaliteit
+
+**FastAPI** is volledig verenigbaar met (en gebaseerd op) Pydantic. Dus alle extra
Pydantic code die je nog hebt werkt ook.
+
+Inclusief externe pakketten die ook gebaseerd zijn op Pydantic, zoals
ORMs,
ODMs voor databases.
+
+Dit betekent ook dat je in veel gevallen het object dat je van een request krijgt **direct naar je database** kunt sturen, omdat alles automatisch wordt gevalideerd.
+
+Hetzelfde geldt ook andersom, in veel gevallen kun je dus het object dat je krijgt van de database **direct doorgeven aan de client**.
+
+Met **FastAPI** krijg je alle functionaliteit van **Pydantic** (omdat FastAPI is gebaseerd op Pydantic voor alle dataverwerking):
+
+* **Geen brainfucks**:
+ * Je hoeft geen nieuwe microtaal voor schemadefinities te leren.
+ * Als je bekend bent Python types, weet je hoe je Pydantic moet gebruiken.
+* Werkt goed samen met je **
IDE/
linter/hersenen**:
+ * Doordat pydantic's datastructuren enkel instanties zijn van klassen, die je definieert, werkt automatische aanvulling, linting, mypy en je intuïtie allemaal goed met je gevalideerde data.
+* Valideer **complexe structuren**:
+ * Gebruik van hiërarchische Pydantic modellen, Python `typing`'s `List` en `Dict`, enz.
+ * Met validators kunnen complexe dataschema's duidelijk en eenvoudig worden gedefinieerd, gecontroleerd en gedocumenteerd als JSON Schema.
+ * Je kunt diep **geneste JSON** objecten laten valideren en annoteren.
+* **Uitbreidbaar**:
+ * Met Pydantic kunnen op maat gemaakte datatypen worden gedefinieerd of je kunt validatie uitbreiden met methoden op een model dat is ingericht met de decorator validator.
+* 100% van de code is getest.
diff --git a/docs/nl/docs/index.md b/docs/nl/docs/index.md
new file mode 100644
index 000000000..8edc3ba0c
--- /dev/null
+++ b/docs/nl/docs/index.md
@@ -0,0 +1,494 @@
+# FastAPI
+
+
+
+
+
+
+
+ FastAPI framework, zeer goede prestaties, eenvoudig te leren, snel te programmeren, klaar voor productie
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+**Documentatie**:
https://fastapi.tiangolo.com
+
+**Broncode**:
https://github.com/tiangolo/fastapi
+
+---
+
+FastAPI is een modern, snel (zeer goede prestaties), web framework voor het bouwen van API's in Python, gebruikmakend van standaard Python type-hints.
+
+De belangrijkste kenmerken zijn:
+
+* **Snel**: Zeer goede prestaties, vergelijkbaar met **NodeJS** en **Go** (dankzij Starlette en Pydantic). [Een van de snelste beschikbare Python frameworks](#prestaties).
+* **Snel te programmeren**: Verhoog de snelheid om functionaliteit te ontwikkelen met ongeveer 200% tot 300%. *
+* **Minder bugs**: Verminder ongeveer 40% van de door mensen (ontwikkelaars) veroorzaakte fouten. *
+* **Intuïtief**: Buitengewoon goede ondersteuning voor editors.
Overal automische code aanvulling. Minder tijd kwijt aan debuggen.
+* **Eenvoudig**: Ontworpen om gemakkelijk te gebruiken en te leren. Minder tijd nodig om documentatie te lezen.
+* **Kort**: Minimaliseer codeduplicatie. Elke parameterdeclaratie ondersteunt meerdere functionaliteiten. Minder bugs.
+* **Robust**: Code gereed voor productie. Met automatische interactieve documentatie.
+* **Standards-based**: Gebaseerd op (en volledig verenigbaar met) open standaarden voor API's:
OpenAPI (voorheen bekend als Swagger) en
JSON Schema.
+
+
* schatting op basis van testen met een intern ontwikkelteam en bouwen van productieapplicaties.
+
+## Sponsors
+
+
+
+{% if sponsors %}
+{% for sponsor in sponsors.gold -%}
+

+{% endfor -%}
+{%- for sponsor in sponsors.silver -%}
+

+{% endfor %}
+{% endif %}
+
+
+
+
Overige sponsoren
+
+## Meningen
+
+"_[...] Ik gebruik **FastAPI** heel vaak tegenwoordig. [...] Ik ben van plan om het te gebruiken voor alle **ML-services van mijn team bij Microsoft**. Sommige van deze worden geïntegreerd in het kernproduct van **Windows** en sommige **Office**-producten._"
+
+
Kabir Khan -
Microsoft (ref)
+
+---
+
+"_We hebben de **FastAPI** library gebruikt om een **REST** server te maken die bevraagd kan worden om **voorspellingen** te maken. [voor Ludwig]_"
+
+
Piero Molino, Yaroslav Dudin en Sai Sumanth Miryala -
Uber (ref)
+
+---
+
+"_**Netflix** is verheugd om een open-source release aan te kondigen van ons **crisismanagement**-orkestratieframework: **Dispatch**! [gebouwd met **FastAPI**]_"
+
+
Kevin Glisson, Marc Vilanova, Forest Monsen -
Netflix (ref)
+
+---
+
+"_Ik ben super enthousiast over **FastAPI**. Het is zo leuk!_"
+
+
+
+---
+
+"_Wat je hebt gebouwd ziet er echt super solide en gepolijst uit. In veel opzichten is het wat ik wilde dat **Hug** kon zijn - het is echt inspirerend om iemand dit te zien bouwen._"
+
+
+
+---
+
+"Wie geïnteresseerd is in een **modern framework** voor het bouwen van REST API's, bekijkt best eens **FastAPI** [...] Het is snel, gebruiksvriendelijk en gemakkelijk te leren [...]_"
+
+"_We zijn overgestapt naar **FastAPI** voor onze **API's** [...] Het gaat jou vast ook bevallen [...]_"
+
+
+
+---
+
+"_Wie een Python API wil bouwen voor productie, kan ik ten stelligste **FastAPI** aanraden. Het is **prachtig ontworpen**, **eenvoudig te gebruiken** en **gemakkelijk schaalbaar**, het is een **cruciale component** geworden in onze strategie om API's centraal te zetten, en het vereenvoudigt automatisering en diensten zoals onze Virtual TAC Engineer._"
+
+
Deon Pillsbury -
Cisco (ref)
+
+---
+
+## **Typer**, de FastAPI van CLIs
+
+

+
+Als je een
CLI-app bouwt die in de terminal moet worden gebruikt in plaats van een web-API, gebruik dan
**Typer**.
+
+**Typer** is het kleine broertje van FastAPI. En het is bedoeld als de **FastAPI van CLI's**. ️
+
+## Vereisten
+
+FastAPI staat op de schouders van reuzen:
+
+*
Starlette voor de webonderdelen.
+*
Pydantic voor de datadelen.
+
+## Installatie
+
+
+
+```console
+$ pip install "fastapi[standard]"
+
+---> 100%
+```
+
+
+
+**Opmerking**: Zet `"fastapi[standard]"` tussen aanhalingstekens om ervoor te zorgen dat het werkt in alle terminals.
+
+## Voorbeeld
+
+### Creëer het
+
+* Maak het bestand `main.py` aan met daarin:
+
+```Python
+from typing import Union
+
+from fastapi import FastAPI
+
+app = FastAPI()
+
+
+@app.get("/")
+def read_root():
+ return {"Hello": "World"}
+
+
+@app.get("/items/{item_id}")
+def read_item(item_id: int, q: Union[str, None] = None):
+ return {"item_id": item_id, "q": q}
+```
+
+
+Of maak gebruik van async def
...
+
+Als je code gebruik maakt van `async` / `await`, gebruik dan `async def`:
+
+```Python hl_lines="9 14"
+from typing import Union
+
+from fastapi import FastAPI
+
+app = FastAPI()
+
+
+@app.get("/")
+async def read_root():
+ return {"Hello": "World"}
+
+
+@app.get("/items/{item_id}")
+async def read_item(item_id: int, q: Union[str, None] = None):
+ return {"item_id": item_id, "q": q}
+```
+
+**Opmerking**:
+
+Als je het niet weet, kijk dan in het gedeelte _"Heb je haast?"_ over `async` en `await` in de documentatie.
+
+
+
+### Voer het uit
+
+Run de server met:
+
+
+
+```console
+$ fastapi dev main.py
+
+ ╭────────── FastAPI CLI - Development mode ───────────╮
+ │ │
+ │ Serving at: http://127.0.0.1:8000 │
+ │ │
+ │ API docs: http://127.0.0.1:8000/docs │
+ │ │
+ │ Running in development mode, for production use: │
+ │ │
+ │ fastapi run │
+ │ │
+ ╰─────────────────────────────────────────────────────╯
+
+INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
+INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+INFO: Started reloader process [2248755] using WatchFiles
+INFO: Started server process [2248757]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+
+
+
+Over het commando fastapi dev main.py
...
+
+Het commando `fastapi dev` leest het `main.py` bestand, detecteert de **FastAPI** app, en start een server met Uvicorn.
+
+Standaard zal dit commando `fastapi dev` starten met "auto-reload" geactiveerd voor ontwikkeling op het lokale systeem.
+
+Je kan hier meer over lezen in de FastAPI CLI documentatie.
+
+
+
+### Controleer het
+
+Open je browser op
http://127.0.0.1:8000/items/5?q=somequery.
+
+Je zult een JSON response zien:
+
+```JSON
+{"item_id": 5, "q": "somequery"}
+```
+
+Je hebt een API gemaakt die:
+
+* HTTP verzoeken kan ontvangen op de _paden_ `/` en `/items/{item_id}`.
+* Beide _paden_ hebben `GET`
operaties (ook bekend als HTTP _methoden_).
+* Het _pad_ `/items/{item_id}` heeft een _pad parameter_ `item_id` dat een `int` moet zijn.
+* Het _pad_ `/items/{item_id}` heeft een optionele `str` _query parameter_ `q`.
+
+### Interactieve API documentatie
+
+Ga naar
http://127.0.0.1:8000/docs.
+
+Je ziet de automatische interactieve API documentatie (verstrekt door
Swagger UI):
+
+
+
+### Alternatieve API documentatie
+
+Ga vervolgens naar
http://127.0.0.1:8000/redoc.
+
+Je ziet de automatische interactieve API documentatie (verstrekt door
ReDoc):
+
+
+
+## Voorbeeld upgrade
+
+Pas nu het bestand `main.py` aan om de body van een `PUT` request te ontvangen.
+
+Dankzij Pydantic kunnen we de body declareren met standaard Python types.
+
+```Python hl_lines="4 9-12 25-27"
+from typing import Union
+
+from fastapi import FastAPI
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class Item(BaseModel):
+ name: str
+ price: float
+ is_offer: Union[bool, None] = None
+
+
+@app.get("/")
+def read_root():
+ return {"Hello": "World"}
+
+
+@app.get("/items/{item_id}")
+def read_item(item_id: int, q: Union[str, None] = None):
+ return {"item_id": item_id, "q": q}
+
+
+@app.put("/items/{item_id}")
+def update_item(item_id: int, item: Item):
+ return {"item_name": item.name, "item_id": item_id}
+```
+
+De `fastapi dev` server zou automatisch moeten herladen.
+
+### Interactieve API documentatie upgrade
+
+Ga nu naar
http://127.0.0.1:8000/docs.
+
+* De interactieve API-documentatie wordt automatisch bijgewerkt, inclusief de nieuwe body:
+
+
+
+* Klik op de knop "Try it out", hiermee kan je de parameters invullen en direct met de API interacteren:
+
+
+
+* Klik vervolgens op de knop "Execute", de gebruikersinterface zal communiceren met jouw API, de parameters verzenden, de resultaten ophalen en deze op het scherm tonen:
+
+
+
+### Alternatieve API documentatie upgrade
+
+Ga vervolgens naar
http://127.0.0.1:8000/redoc.
+
+* De alternatieve documentatie zal ook de nieuwe queryparameter en body weergeven:
+
+
+
+### Samenvatting
+
+Samengevat declareer je **eenmalig** de types van parameters, body, etc. als functieparameters.
+
+Dat doe je met standaard moderne Python types.
+
+Je hoeft geen nieuwe syntax te leren, de methods of klassen van een specifieke bibliotheek, etc.
+
+Gewoon standaard **Python**.
+
+Bijvoorbeeld, voor een `int`:
+
+```Python
+item_id: int
+```
+
+of voor een complexer `Item` model:
+
+```Python
+item: Item
+```
+
+...en met die ene verklaring krijg je:
+
+* Editor ondersteuning, inclusief:
+ * Code aanvulling.
+ * Type validatie.
+* Validatie van data:
+ * Automatische en duidelijke foutboodschappen wanneer de data ongeldig is.
+ * Validatie zelfs voor diep geneste JSON objecten.
+*
Conversie van invoergegevens: afkomstig van het netwerk naar Python-data en -types. Zoals:
+ * JSON.
+ * Pad parameters.
+ * Query parameters.
+ * Cookies.
+ * Headers.
+ * Formulieren.
+ * Bestanden.
+*
Conversie van uitvoergegevens: converstie van Python-data en -types naar netwerkgegevens (zoals JSON):
+ * Converteer Python types (`str`, `int`, `float`, `bool`, `list`, etc).
+ * `datetime` objecten.
+ * `UUID` objecten.
+ * Database modellen.
+ * ...en nog veel meer.
+* Automatische interactieve API-documentatie, inclusief 2 alternatieve gebruikersinterfaces:
+ * Swagger UI.
+ * ReDoc.
+
+---
+
+Terugkomend op het vorige code voorbeeld, **FastAPI** zal:
+
+* Valideren dat er een `item_id` bestaat in het pad voor `GET` en `PUT` verzoeken.
+* Valideren dat het `item_id` van het type `int` is voor `GET` en `PUT` verzoeken.
+ * Wanneer dat niet het geval is, krijgt de cliënt een nuttige, duidelijke foutmelding.
+* Controleren of er een optionele query parameter is met de naam `q` (zoals in `http://127.0.0.1:8000/items/foo?q=somequery`) voor `GET` verzoeken.
+ * Aangezien de `q` parameter werd gedeclareerd met `= None`, is deze optioneel.
+ * Zonder de `None` declaratie zou deze verplicht zijn (net als bij de body in het geval met `PUT`).
+* Voor `PUT` verzoeken naar `/items/{item_id}`, lees de body als JSON:
+ * Controleer of het een verplicht attribuut `naam` heeft en dat dat een `str` is.
+ * Controleer of het een verplicht attribuut `price` heeft en dat dat een`float` is.
+ * Controleer of het een optioneel attribuut `is_offer` heeft, dat een `bool` is wanneer het aanwezig is.
+ * Dit alles werkt ook voor diep geneste JSON objecten.
+* Converteer automatisch van en naar JSON.
+* Documenteer alles met OpenAPI, dat gebruikt kan worden door:
+ * Interactieve documentatiesystemen.
+ * Automatische client code generatie systemen, voor vele talen.
+* Biedt 2 interactieve documentatie-webinterfaces aan.
+
+---
+
+Dit was nog maar een snel overzicht, maar je zou nu toch al een idee moeten hebben over hoe het allemaal werkt.
+
+Probeer deze regel te veranderen:
+
+```Python
+ return {"item_name": item.name, "item_id": item_id}
+```
+
+...van:
+
+```Python
+ ... "item_name": item.name ...
+```
+
+...naar:
+
+```Python
+ ... "item_price": item.price ...
+```
+
+...en zie hoe je editor de attributen automatisch invult en hun types herkent:
+
+
+
+Voor een vollediger voorbeeld met meer mogelijkheden, zie de
Tutorial - Gebruikershandleiding.
+
+**Spoiler alert**: de tutorial - gebruikershandleiding bevat:
+
+* Declaratie van **parameters** op andere plaatsen zoals: **headers**, **cookies**, **formuliervelden** en **bestanden**.
+* Hoe stel je **validatie restricties** in zoals `maximum_length` of een `regex`.
+* Een zeer krachtig en eenvoudig te gebruiken **
Dependency Injection** systeem.
+* Beveiliging en authenticatie, inclusief ondersteuning voor **OAuth2** met **JWT-tokens** en **HTTP Basic** auth.
+* Meer geavanceerde (maar even eenvoudige) technieken voor het declareren van **diep geneste JSON modellen** (dankzij Pydantic).
+* **GraphQL** integratie met
Strawberry en andere packages.
+* Veel extra functies (dankzij Starlette) zoals:
+ * **WebSockets**
+ * uiterst gemakkelijke tests gebaseerd op HTTPX en `pytest`
+ * **CORS**
+ * **Cookie Sessions**
+ * ...en meer.
+
+## Prestaties
+
+Onafhankelijke TechEmpower benchmarks tonen **FastAPI** applicaties draaiend onder Uvicorn aan als
een van de snelste Python frameworks beschikbaar, alleen onder Starlette en Uvicorn zelf (intern gebruikt door FastAPI). (*)
+
+Zie de sectie
Benchmarks om hier meer over te lezen.
+
+## Afhankelijkheden
+
+FastAPI maakt gebruik van Pydantic en Starlette.
+
+### `standard` Afhankelijkheden
+
+Wanneer je FastAPI installeert met `pip install "fastapi[standard]"`, worden de volgende `standard` optionele afhankelijkheden geïnstalleerd:
+
+Gebruikt door Pydantic:
+
+*
email_validator
- voor email validatie.
+
+Gebruikt door Starlette:
+
+*
httpx
- Vereist indien je de `TestClient` wil gebruiken.
+*
jinja2
- Vereist als je de standaard templateconfiguratie wil gebruiken.
+*
python-multipart
- Vereist indien je
"parsen" van formulieren wil ondersteunen met `requests.form()`.
+
+Gebruikt door FastAPI / Starlette:
+
+*
uvicorn
- voor de server die jouw applicatie laadt en bedient.
+* `fastapi-cli` - om het `fastapi` commando te voorzien.
+
+### Zonder `standard` Afhankelijkheden
+
+Indien je de optionele `standard` afhankelijkheden niet wenst te installeren, kan je installeren met `pip install fastapi` in plaats van `pip install "fastapi[standard]"`.
+
+### Bijkomende Optionele Afhankelijkheden
+
+Er zijn nog een aantal bijkomende afhankelijkheden die je eventueel kan installeren.
+
+Bijkomende optionele afhankelijkheden voor Pydantic:
+
+*
pydantic-settings
- voor het beheren van settings.
+*
pydantic-extra-types
- voor extra data types die gebruikt kunnen worden met Pydantic.
+
+Bijkomende optionele afhankelijkheden voor FastAPI:
+
+*
orjson
- Vereist indien je `ORJSONResponse` wil gebruiken.
+*
ujson
- Vereist indien je `UJSONResponse` wil gebruiken.
+
+## Licentie
+
+Dit project is gelicenseerd onder de voorwaarden van de MIT licentie.
diff --git a/docs/nl/mkdocs.yml b/docs/nl/mkdocs.yml
new file mode 100644
index 000000000..de18856f4
--- /dev/null
+++ b/docs/nl/mkdocs.yml
@@ -0,0 +1 @@
+INHERIT: ../en/mkdocs.yml
diff --git a/docs/pl/docs/fastapi-people.md b/docs/pl/docs/fastapi-people.md
deleted file mode 100644
index 6c431b401..000000000
--- a/docs/pl/docs/fastapi-people.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# Ludzie FastAPI
-
-FastAPI posiada wspaniałą społeczność, która jest otwarta dla ludzi z każdego środowiska.
-
-## Twórca - Opienik
-
-Cześć! 👋
-
-To ja:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Jestem twórcą i opiekunem **FastAPI**. Możesz przeczytać więcej na ten temat w [Pomoc FastAPI - Uzyskaj pomoc - Skontaktuj się z autorem](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}.
-
-...Ale tutaj chcę pokazać Ci społeczność.
-
----
-
-**FastAPI** otrzymuje wiele wsparcia od społeczności. Chciałbym podkreślić ich wkład.
-
-To są ludzie, którzy:
-
-* [Pomagają innym z pytaniami na GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}.
-* [Tworzą Pull Requesty](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}.
-* Oceniają Pull Requesty, [to szczególnie ważne dla tłumaczeń](contributing.md#translations){.internal-link target=_blank}.
-
-Proszę o brawa dla nich. 👏 🙇
-
-## Najaktywniejsi użytkownicy w zeszłym miesiącu
-
-Oto niektórzy użytkownicy, którzy [pomagali innym w największej liczbie pytań na GitHubie](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} podczas ostatniego miesiąca. ☕
-
-{% if people %}
-
-{% for user in people.last_month_active %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Eksperci
-
-Oto **eksperci FastAPI**. 🤓
-
-To użytkownicy, którzy [pomogli innym z największa liczbą pytań na GitHubie](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} od *samego początku*.
-
-Poprzez pomoc wielu innym, udowodnili, że są ekspertami. ✨
-
-{% if people %}
-
-{% for user in people.experts %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Najlepsi Kontrybutorzy
-
-Oto **Najlepsi Kontrybutorzy**. 👷
-
-Ci użytkownicy [stworzyli najwięcej Pull Requestów](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}, które zostały *wcalone*.
-
-Współtworzyli kod źródłowy, dokumentację, tłumaczenia itp. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Jest wielu więcej kontrybutorów (ponad setka), możesz zobaczyć ich wszystkich na stronie
Kontrybutorzy FastAPI na GitHub. 👷
-
-## Najlepsi Oceniajacy
-
-Ci uzytkownicy są **Najlepszymi oceniającymi**. 🕵️
-
-### Oceny Tłumaczeń
-
-Ja mówię tylko kilkoma językami (i to niezbyt dobrze 😅). Zatem oceniający są tymi, którzy mają [**moc zatwierdzania tłumaczeń**](contributing.md#translations){.internal-link target=_blank} dokumentacji. Bez nich nie byłoby dokumentacji w kilku innych językach.
-
----
-
-**Najlepsi Oceniający** 🕵️ przejrzeli więcej Pull Requestów, niż inni, zapewniając jakość kodu, dokumentacji, a zwłaszcza **tłumaczeń**.
-
-{% if people %}
-
-{% for user in people.top_reviewers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Sponsorzy
-
-Oto **Sponsorzy**. 😎
-
-Wspierają moją pracę nad **FastAPI** (i innymi), głównie poprzez
GitHub Sponsors.
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Złoci Sponsorzy
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Srebrni Sponsorzy
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Brązowi Sponsorzy
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### Indywidualni Sponsorzy
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## Techniczne szczegóły danych
-
-Głównym celem tej strony jest podkreślenie wysiłku społeczności w pomaganiu innym.
-
-Szczególnie włączając wysiłki, które są zwykle mniej widoczne, a w wielu przypadkach bardziej żmudne, tak jak pomaganie innym z pytaniami i ocenianie Pull Requestów z tłumaczeniami.
-
-Dane są obliczane każdego miesiąca, możesz przeczytać
kod źródłowy tutaj.
-
-Tutaj również podkreślam wkład od sponsorów.
-
-Zastrzegam sobie prawo do aktualizacji algorytmu, sekcji, progów itp. (na wszelki wypadek 🤷).
diff --git a/docs/pt/docs/advanced/async-tests.md b/docs/pt/docs/advanced/async-tests.md
index ab5bfa648..7cac26262 100644
--- a/docs/pt/docs/advanced/async-tests.md
+++ b/docs/pt/docs/advanced/async-tests.md
@@ -72,7 +72,7 @@ Note que a função de teste é `async def` agora, no lugar de apenas `def` como
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
-```Python hl_lines="9-10"
+```Python hl_lines="9-12"
{!../../../docs_src/async_tests/test_main.py!}
```
diff --git a/docs/pt/docs/advanced/security/index.md b/docs/pt/docs/advanced/security/index.md
new file mode 100644
index 000000000..ae63f1c96
--- /dev/null
+++ b/docs/pt/docs/advanced/security/index.md
@@ -0,0 +1,19 @@
+# Segurança Avançada
+
+## Funcionalidades Adicionais
+
+Existem algumas funcionalidades adicionais para lidar com segurança além das cobertas em [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+/// tip | "Dica"
+
+As próximas seções **não são necessariamente "avançadas"**.
+
+E é possível que para o seu caso de uso, a solução está em uma delas.
+
+///
+
+## Leia o Tutorial primeiro
+
+As próximas seções pressupõem que você já leu o principal [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md){.internal-link target=_blank}.
+
+Todas elas são baseadas nos mesmos conceitos, mas permitem algumas funcionalidades extras.
diff --git a/docs/pt/docs/advanced/testing-events.md b/docs/pt/docs/advanced/testing-events.md
new file mode 100644
index 000000000..392fb741c
--- /dev/null
+++ b/docs/pt/docs/advanced/testing-events.md
@@ -0,0 +1,7 @@
+# Testando Eventos: inicialização - encerramento
+
+Quando você precisa que os seus manipuladores de eventos (`startup` e `shutdown`) sejam executados em seus testes, você pode utilizar o `TestClient` usando a instrução `with`:
+
+```Python hl_lines="9-12 20-24"
+{!../../../docs_src/app_testing/tutorial003.py!}
+```
diff --git a/docs/pt/docs/fastapi-people.md b/docs/pt/docs/fastapi-people.md
deleted file mode 100644
index d67ae0d33..000000000
--- a/docs/pt/docs/fastapi-people.md
+++ /dev/null
@@ -1,183 +0,0 @@
----
-hide:
- - navigation
----
-
-# Pessoas do FastAPI
-
-FastAPI possue uma comunidade incrível que recebe pessoas de todos os níveis.
-
-## Criador - Mantenedor
-
-Ei! 👋
-
-Este sou eu:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Eu sou o criador e mantenedor do **FastAPI**. Você pode ler mais sobre isso em [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#conect-se-com-o-autor){.internal-link target=_blank}.
-
-...Mas aqui eu quero mostrar a você a comunidade.
-
----
-
-**FastAPI** recebe muito suporte da comunidade. E quero destacar suas contribuições.
-
-Estas são as pessoas que:
-
-* [Help others with issues (questions) in GitHub](help-fastapi.md#responda-perguntas-no-github){.internal-link target=_blank}.
-* [Create Pull Requests](help-fastapi.md#crie-um-pull-request){.internal-link target=_blank}.
-* Revisar Pull Requests, [especially important for translations](contributing.md#traducoes){.internal-link target=_blank}.
-
-Uma salva de palmas para eles. 👏 🙇
-
-## Usuários mais ativos do ultimo mês
-
-Estes são os usuários que estão [helping others the most with issues (questions) in GitHub](help-fastapi.md#responda-perguntas-no-github){.internal-link target=_blank} durante o ultimo mês. ☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Especialistas
-
-Aqui está os **Especialistas do FastAPI**. 🤓
-
-
-Estes são os usuários que [helped others the most with issues (questions) in GitHub](help-fastapi.md#responda-perguntas-no-github){.internal-link target=_blank} em *todo o tempo*.
-
-Eles provaram ser especialistas ajudando muitos outros. ✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Top Contribuidores
-
-Aqui está os **Top Contribuidores**. 👷
-
-Esses usuários têm [created the most Pull Requests](help-fastapi.md#crie-um-pull-request){.internal-link target=_blank} que tem sido *mergeado*.
-
-Eles contribuíram com o código-fonte, documentação, traduções, etc. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Existem muitos outros contribuidores (mais de uma centena), você pode ver todos eles em
Página de Contribuidores do FastAPI no GitHub. 👷
-
-## Top Revisores
-
-Esses usuários são os **Top Revisores**. 🕵️
-
-### Revisões para Traduções
-
-Eu só falo algumas línguas (e não muito bem 😅). Então, os revisores são aqueles que têm o [**poder de aprovar traduções**](contributing.md#traducoes){.internal-link target=_blank} da documentação. Sem eles, não haveria documentação em vários outros idiomas.
-
----
-
-Os **Top Revisores** 🕵️ revisaram a maior parte de Pull Requests de outros, garantindo a qualidade do código, documentação, e especialmente, as **traduções**.
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Patrocinadores
-
-Esses são os **Patrocinadores**. 😎
-
-Eles estão apoiando meu trabalho **FastAPI** (e outros), principalmente através de
GitHub Sponsors.
-
-{% if sponsors %}
-{% if sponsors.gold %}
-
-### Patrocinadores Ouro
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Patrocinadores Prata
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Patrocinadores Bronze
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-### Patrocinadores Individuais
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-## Sobre os dados - detalhes técnicos
-
-A principal intenção desta página é destacar o esforço da comunidade para ajudar os outros.
-
-Especialmente incluindo esforços que normalmente são menos visíveis, e em muitos casos mais árduo, como ajudar os outros com issues e revisando Pull Requests com traduções.
-
-Os dados são calculados todo mês, você pode ler o
código fonte aqui.
-
-Aqui também estou destacando contribuições de patrocinadores.
-
-Eu também me reservo o direito de atualizar o algoritmo, seções, limites, etc (só para prevenir 🤷).
diff --git a/docs/pt/docs/tutorial/request_files.md b/docs/pt/docs/tutorial/request_files.md
new file mode 100644
index 000000000..60e4ecb26
--- /dev/null
+++ b/docs/pt/docs/tutorial/request_files.md
@@ -0,0 +1,418 @@
+# Arquivos de Requisição
+
+Você pode definir arquivos para serem enviados para o cliente utilizando `File`.
+
+/// info
+
+Para receber arquivos compartilhados, primeiro instale
`python-multipart`.
+
+E.g. `pip install python-multipart`.
+
+Isso se deve por que arquivos enviados são enviados como "dados de formulário".
+
+///
+
+## Importe `File`
+
+Importe `File` e `UploadFile` do `fastapi`:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="3"
+{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="1"
+{!> ../../../docs_src/request_files/tutorial001_an.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível.
+
+///
+
+```Python hl_lines="1"
+{!> ../../../docs_src/request_files/tutorial001.py!}
+```
+
+////
+
+## Defina os parâmetros de `File`
+
+Cria os parâmetros do arquivo da mesma forma que você faria para `Body` ou `Form`:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="9"
+{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="8"
+{!> ../../../docs_src/request_files/tutorial001_an.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível.
+
+///
+
+```Python hl_lines="7"
+{!> ../../../docs_src/request_files/tutorial001.py!}
+```
+
+////
+
+/// info | Informação
+
+`File` é uma classe que herda diretamente de `Form`.
+
+Mas lembre-se que quando você importa `Query`,`Path`, `File`, entre outros, do `fastapi`, essas são na verdade funções que retornam classes especiais.
+
+///
+
+/// tip | Dica
+
+Para declarar o corpo de arquivos, você precisa utilizar `File`, do contrário os parâmetros seriam interpretados como parâmetros de consulta ou corpo (JSON) da requisição.
+
+///
+
+Os arquivos serão enviados como "form data".
+
+Se você declarar o tipo do seu parâmetro na sua *função de operação de rota* como `bytes`, o **FastAPI** irá ler o arquivo para você e você receberá o conteúdo como `bytes`.
+
+Lembre-se que isso significa que o conteúdo inteiro será armazenado em memória. Isso funciona bem para arquivos pequenos.
+
+Mas existem vários casos em que você pode se beneficiar ao usar `UploadFile`.
+
+## Parâmetros de arquivo com `UploadFile`
+
+Defina um parâmetro de arquivo com o tipo `UploadFile`
+
+//// tab | Python 3.9+
+
+```Python hl_lines="14"
+{!> ../../../docs_src/request_files/tutorial001_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="13"
+{!> ../../../docs_src/request_files/tutorial001_an.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível.
+
+///
+
+```Python hl_lines="12"
+{!> ../../../docs_src/request_files/tutorial001.py!}
+```
+
+////
+
+Utilizando `UploadFile` tem várias vantagens sobre `bytes`:
+
+* Você não precisa utilizar `File()` como o valor padrão do parâmetro.
+* A classe utiliza um arquivo em "spool":
+ * Um arquivo guardado em memória até um tamanho máximo, depois desse limite ele é guardado em disco.
+* Isso significa que a classe funciona bem com arquivos grandes como imagens, vídeos, binários extensos, etc. Sem consumir toda a memória.
+* Você pode obter metadados do arquivo enviado.
+* Ela possui uma interface
semelhante a arquivos `async`.
+* Ela expõe um objeto python
`SpooledTemporaryFile` que você pode repassar para bibliotecas que esperam um objeto com comportamento de arquivo.
+
+### `UploadFile`
+
+`UploadFile` tem os seguintes atributos:
+
+* `filename`: Uma string (`str`) com o nome original do arquivo enviado (e.g. `myimage.jpg`).
+* `content-type`: Uma `str` com o tipo do conteúdo (tipo MIME / media) (e.g. `image/jpeg`).
+* `file`: Um objeto do tipo
`SpooledTemporaryFile` (um objeto
file-like). O arquivo propriamente dito que você pode passar diretamente para outras funções ou bibliotecas que esperam um objeto "file-like".
+
+`UploadFile` tem os seguintes métodos `async`. Todos eles chamam os métodos de arquivos por baixo dos panos (usando o objeto `SpooledTemporaryFile` interno).
+
+* `write(data)`: escreve dados (`data`) em `str` ou `bytes` no arquivo.
+* `read(size)`: Lê um número de bytes/caracteres de acordo com a quantidade `size` (`int`).
+* `seek(offset)`: Navega para o byte na posição `offset` (`int`) do arquivo.
+ * E.g., `await myfile.seek(0)` navegaria para o ínicio do arquivo.
+ * Isso é especialmente útil se você executar `await myfile.read()` uma vez e depois precisar ler os conteúdos do arquivo de novo.
+* `close()`: Fecha o arquivo.
+
+Como todos esses métodos são assíncronos (`async`) você precisa esperar ("await") por eles.
+
+Por exemplo, dentro de uma *função de operação de rota* assíncrona você pode obter os conteúdos com:
+
+```Python
+contents = await myfile.read()
+```
+
+Se você estiver dentro de uma *função de operação de rota* definida normalmente com `def`, você pode acessar `UploadFile.file` diretamente, por exemplo:
+
+```Python
+contents = myfile.file.read()
+```
+
+/// note | Detalhes técnicos do `async`
+
+Quando você utiliza métodos assíncronos, o **FastAPI** executa os métodos do arquivo em uma threadpool e espera por eles.
+
+///
+
+/// note | Detalhes técnicos do Starlette
+
+O `UploadFile` do **FastAPI** herda diretamente do `UploadFile` do **Starlette**, mas adiciona algumas funcionalidades necessárias para ser compatível com o **Pydantic**
+
+///
+
+## O que é "Form Data"
+
+A forma como formulários HTML(`
`) enviam dados para o servidor normalmente utilizam uma codificação "especial" para esses dados, que é diferente do JSON.
+
+O **FastAPI** garante que os dados serão lidos da forma correta, em vez do JSON.
+
+/// note | Detalhes Técnicos
+
+Dados vindos de formulários geralmente tem a codificação com o "media type" `application/x-www-form-urlencoded` quando estes não incluem arquivos.
+
+Mas quando os dados incluem arquivos, eles são codificados como `multipart/form-data`. Se você utilizar `File`, **FastAPI** saberá que deve receber os arquivos da parte correta do corpo da requisição.
+
+Se você quer ler mais sobre essas codificações e campos de formulário, veja a documentação online da
MDN sobre POST
.
+
+///
+
+/// warning | Aviso
+
+Você pode declarar múltiplos parâmetros `File` e `Form` em uma *operação de rota*, mas você não pode declarar campos `Body`que seriam recebidos como JSON junto desses parâmetros, por que a codificação do corpo da requisição será `multipart/form-data` em vez de `application/json`.
+
+Isso não é uma limitação do **FastAPI**, é uma parte do protocolo HTTP.
+
+///
+
+## Arquivo de upload opcional
+
+Você pode definir um arquivo como opcional utilizando as anotações de tipo padrão e definindo o valor padrão como `None`:
+
+//// tab | Python 3.10+
+
+```Python hl_lines="9 17"
+{!> ../../../docs_src/request_files/tutorial001_02_an_py310.py!}
+```
+
+////
+
+//// tab | Python 3.9+
+
+```Python hl_lines="9 17"
+{!> ../../../docs_src/request_files/tutorial001_02_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="10 18"
+{!> ../../../docs_src/request_files/tutorial001_02_an.py!}
+```
+
+////
+
+//// tab | Python 3.10+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated`, se possível
+
+///
+
+```Python hl_lines="7 15"
+{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated`, se possível
+
+///
+
+```Python hl_lines="9 17"
+{!> ../../../docs_src/request_files/tutorial001_02.py!}
+```
+
+////
+
+## `UploadFile` com Metadados Adicionais
+
+Você também pode utilizar `File()` com `UploadFile`, por exemplo, para definir metadados adicionais:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="9 15"
+{!> ../../../docs_src/request_files/tutorial001_03_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="8 14"
+{!> ../../../docs_src/request_files/tutorial001_03_an.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível
+
+///
+
+```Python hl_lines="7 13"
+{!> ../../../docs_src/request_files/tutorial001_03.py!}
+```
+
+////
+
+## Envio de Múltiplos Arquivos
+
+É possível enviar múltiplos arquivos ao mesmo tmepo.
+
+Ele ficam associados ao mesmo "campo do formulário" enviado com "form data".
+
+Para usar isso, declare uma lista de `bytes` ou `UploadFile`:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="10 15"
+{!> ../../../docs_src/request_files/tutorial002_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="11 16"
+{!> ../../../docs_src/request_files/tutorial002_an.py!}
+```
+
+////
+
+//// tab | Python 3.9+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível
+
+///
+
+```Python hl_lines="8 13"
+{!> ../../../docs_src/request_files/tutorial002_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível
+
+///
+
+```Python hl_lines="10 15"
+{!> ../../../docs_src/request_files/tutorial002.py!}
+```
+
+////
+
+Você irá receber, como delcarado uma lista (`list`) de `bytes` ou `UploadFile`s,
+
+/// note | Detalhes Técnicos
+
+Você também poderia utilizar `from starlette.responses import HTMLResponse`.
+
+O **FastAPI** fornece as mesmas `starlette.responses` como `fastapi.responses` apenas como um facilitador para você, desenvolvedor. Mas a maior parte das respostas vem diretamente do Starlette.
+
+///
+
+### Enviando Múltiplos Arquivos com Metadados Adicionais
+
+E da mesma forma que antes, você pode utilizar `File()` para definir parâmetros adicionais, até mesmo para `UploadFile`:
+
+//// tab | Python 3.9+
+
+```Python hl_lines="11 18-20"
+{!> ../../../docs_src/request_files/tutorial003_an_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+
+
+```Python hl_lines="12 19-21"
+{!> ../../../docs_src/request_files/tutorial003_an.py!}
+```
+
+////
+
+//// tab | Python 3.9+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível.
+
+///
+
+```Python hl_lines="9 16"
+{!> ../../../docs_src/request_files/tutorial003_py39.py!}
+```
+
+////
+
+//// tab | Python 3.8+ non-Annotated
+
+/// tip | Dica
+
+Utilize a versão com `Annotated` se possível.
+
+///
+
+```Python hl_lines="11 18"
+{!> ../../../docs_src/request_files/tutorial003.py!}
+```
+
+////
+
+## Recapitulando
+
+Use `File`, `bytes` e `UploadFile` para declarar arquivos que serão enviados na requisição, enviados como dados do formulário.
diff --git a/docs/ru/docs/fastapi-people.md b/docs/ru/docs/fastapi-people.md
deleted file mode 100644
index 31bb2798e..000000000
--- a/docs/ru/docs/fastapi-people.md
+++ /dev/null
@@ -1,184 +0,0 @@
----
-hide:
- - navigation
----
-
-# Люди, поддерживающие FastAPI
-
-У FastAPI замечательное сообщество, которое доброжелательно к людям с любым уровнем знаний.
-
-## Создатель и хранитель
-
-Хай! 👋
-
-Это я:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Я создал и продолжаю поддерживать **FastAPI**. Узнать обо мне больше можно тут [Помочь FastAPI - Получить помощь - Связаться с автором](help-fastapi.md#_2){.internal-link target=_blank}.
-
-... но на этой странице я хочу показать вам наше сообщество.
-
----
-
-**FastAPI** получает огромную поддержку от своего сообщества. И я хочу отметить вклад его участников.
-
-Это люди, которые:
-
-* [Помогают другим с их проблемами (вопросами) на GitHub](help-fastapi.md#github_1){.internal-link target=_blank}.
-* [Создают пул-реквесты](help-fastapi.md#-_1){.internal-link target=_blank}.
-* Делают ревью пул-реквестов, [что особенно важно для переводов на другие языки](contributing.md#_8){.internal-link target=_blank}.
-
-Поаплодируем им! 👏 🙇
-
-## Самые активные участники за прошедший месяц
-
-Эти участники [оказали наибольшую помощь другим с решением их проблем (вопросов) на GitHub](help-fastapi.md#github_1){.internal-link target=_blank} в течение последнего месяца. ☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Эксперты
-
-Здесь представлены **Эксперты FastAPI**. 🤓
-
-Эти участники [оказали наибольшую помощь другим с решением их проблем (вопросов) на GitHub](help-fastapi.md#github_1){.internal-link target=_blank} за *всё время*.
-
-Оказывая помощь многим другим, они подтвердили свой уровень знаний. ✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Рейтинг участников, внёсших вклад в код
-
-Здесь представлен **Рейтинг участников, внёсших вклад в код**. 👷
-
-Эти люди [сделали наибольшее количество пул-реквестов](help-fastapi.md#-_1){.internal-link target=_blank}, *включённых в основной код*.
-
-Они сделали наибольший вклад в исходный код, документацию, переводы и т.п. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-На самом деле таких людей довольно много (более сотни), вы можете увидеть всех на этой странице
FastAPI GitHub Contributors page. 👷
-
-## Рейтинг ревьюеров
-
-Здесь представлен **Рейтинг ревьюеров**. 🕵️
-
-### Проверки переводов на другие языки
-
-Я знаю не очень много языков (и не очень хорошо 😅).
-Итак, ревьюеры - это люди, которые могут [**подтвердить предложенный вами перевод** документации](contributing.md#_8){.internal-link target=_blank}. Без них не было бы документации на многих языках.
-
----
-
-В **Рейтинге ревьюеров** 🕵️ представлены те, кто проверил наибольшее количество пул-реквестов других участников, обеспечивая качество кода, документации и, особенно, **переводов на другие языки**.
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Спонсоры
-
-Здесь представлены **Спонсоры**. 😎
-
-Спонсоры поддерживают мою работу над **FastAPI** (и другими проектами) главным образом через
GitHub Sponsors.
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Золотые спонсоры
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Серебрянные спонсоры
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Бронзовые спонсоры
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### Индивидуальные спонсоры
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## О данных - технические детали
-
-Основная цель этой страницы - подчеркнуть усилия сообщества по оказанию помощи другим.
-
-Особенно это касается усилий, которые обычно менее заметны и во многих случаях более трудоемки, таких как помощь другим в решении проблем и проверка пул-реквестов с переводами.
-
-Данные рейтинги подсчитываются каждый месяц, ознакомиться с тем, как это работает можно
тут.
-
-Кроме того, я также подчеркиваю вклад спонсоров.
-
-И я оставляю за собой право обновлять алгоритмы подсчёта, виды рейтингов, пороговые значения и т.д. (так, на всякий случай 🤷).
diff --git a/docs/tr/docs/fastapi-people.md b/docs/tr/docs/fastapi-people.md
deleted file mode 100644
index de62c57c4..000000000
--- a/docs/tr/docs/fastapi-people.md
+++ /dev/null
@@ -1,183 +0,0 @@
----
-hide:
- - navigation
----
-
-# FastAPI Topluluğu
-
-FastAPI, her kökenden insanı ağırlayan harika bir topluluğa sahip.
-
-## Yazan - Geliştiren
-
-Merhaba! 👋
-
-İşte bu benim:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Ben **FastAPI**'ın geliştiricisiyim. Bununla ilgili daha fazla bilgiyi şurada okuyabilirsiniz: [FastAPI yardım - yardım al - benimle iletişime geç](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}.
-
-...burada size harika FastAPI topluluğunu göstermek istiyorum.
-
----
-
-**FastAPI**, topluluğundan çok destek alıyor. Ben de onların katkılarını vurgulamak istiyorum.
-
-Bu insanlar:
-
-* [GitHubdaki soruları cevaplayarak diğerlerine yardım ediyor](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}.
-* [Pull Request'ler oluşturuyor](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}.
-* Pull Request'leri gözden geçiriyorlar, [özellikle çeviriler için bu çok önemli](contributing.md#translations){.internal-link target=_blank}.
-
-Onları bir alkışlayalım. 👏 🙇
-
-## Geçen Ayın En Aktif Kullanıcıları
-
-Geçtiğimiz ay boyunca [GitHub'da diğerlerine en çok yardımcı olan](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} kullanıcılar. ☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Uzmanlar
-
-İşte **FastAPI Uzmanları**. 🤓
-
-Uzmanlarımız ise *tüm zamanlar boyunca* [GitHub'da insanların sorularına en çok yardımcı olan](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} insanlar.
-
-Bir çok kullanıcıya yardım ederek uzman olduklarını kanıtladılar! ✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## En Fazla Katkıda Bulunanlar
-
-Şimdi ise sıra **en fazla katkıda bulunanlar**da. 👷
-
-Bu kullanıcılar en fazla [kaynak koduyla birleştirilen Pull Request'lere](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} sahip!
-
-Kaynak koduna, dökümantasyona, çevirilere ve bir sürü şeye katkıda bulundular. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Bunlar dışında katkıda bulunan, yüzden fazla, bir sürü insan var. Hepsini
FastAPI GitHub Katkıda Bulunanlar sayfasında görebilirsin. 👷
-
-## En Fazla Değerlendirme Yapanlar
-
-İşte **en çok değerlendirme yapanlar**. 🕵️
-
-### Çeviri Değerlendirmeleri
-
-Yalnızca birkaç dil konuşabiliyorum (ve çok da iyi değilim 😅). Bu yüzden değerlendirme yapanların da döküman çevirilerini [**onaylama yetkisi**](contributing.md#translations){.internal-link target=_blank} var. Onlar olmasaydı çeşitli dillerde dökümantasyon da olmazdı.
-
----
-
-**En fazla değerlendirme yapanlar** 🕵️ kodun, dökümantasyonun ve özellikle **çevirilerin** Pull Request'lerini inceleyerek kalitesinden emin oldular.
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Sponsorlar
-
-işte **Sponsorlarımız**. 😎
-
-Çoğunlukla
GitHub Sponsorları aracılığıyla olmak üzere, **FastAPI** ve diğer projelerdeki çalışmalarımı destekliyorlar.
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Altın Sponsorlar
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Gümüş Sponsorlar
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Bronz Sponsorlar
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### Bireysel Sponsorlar
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## Veriler - Teknik detaylar
-
-Bu sayfanın temel amacı, topluluğun başkalarına yardım etme çabasını vurgulamaktır.
-
-Özellikle normalde daha az görünür olan ve çoğu durumda daha zahmetli olan, diğerlerine sorularında yardımcı olmak, çevirileri ve Pull Request'leri gözden geçirmek gibi çabalar dahil.
-
-Veriler ayda bir hesaplanır,
kaynak kodu buradan okuyabilirsin.
-
-Burada sponsorların katkılarını da vurguluyorum.
-
-Ayrıca algoritmayı, bölümleri, eşikleri vb. güncelleme hakkımı da saklı tutuyorum (her ihtimale karşı 🤷).
diff --git a/docs/uk/docs/fastapi-people.md b/docs/uk/docs/fastapi-people.md
deleted file mode 100644
index c6a6451d8..000000000
--- a/docs/uk/docs/fastapi-people.md
+++ /dev/null
@@ -1,183 +0,0 @@
----
-hide:
- - navigation
----
-
-# Люди FastAPI
-
-FastAPI має дивовижну спільноту, яка вітає людей різного походження.
-
-## Творець – Супроводжувач
-
-Привіт! 👋
-
-Це я:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Я - творець і супроводжувач **FastAPI**. Детальніше про це можна прочитати в [Довідка FastAPI - Отримати довідку - Зв'язатися з автором](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}.
-
-...Але тут я хочу показати вам спільноту.
-
----
-
-**FastAPI** отримує велику підтримку від спільноти. І я хочу відзначити їхній внесок.
-
-Це люди, які:
-
-* [Допомагають іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}.
-* [Створюють пул реквести](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}.
-* Переглядають пул реквести, [особливо важливо для перекладів](contributing.md#translations){.internal-link target=_blank}.
-
-Оплески їм. 👏 🙇
-
-## Найбільш активні користувачі минулого місяця
-
-Це користувачі, які [найбільше допомагали іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} протягом минулого місяця. ☕
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Експерти
-
-Ось **експерти FastAPI**. 🤓
-
-Це користувачі, які [найбільше допомагали іншим із проблемами (запитаннями) у GitHub](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank} протягом *всього часу*.
-
-Вони зарекомендували себе як експерти, допомагаючи багатьом іншим. ✨
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Найкращі контрибютори
-
-Ось **Найкращі контрибютори**. 👷
-
-Ці користувачі [створили найбільшу кількість пул реквестів](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} які були *змержені*.
-
-Вони надали програмний код, документацію, переклади тощо. 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-Є багато інших контрибюторів (більше сотні), їх усіх можна побачити на сторінці
FastAPI GitHub Contributors. 👷
-
-## Найкращі рецензенти
-
-Ці користувачі є **Найкращими рецензентами**. 🕵️
-
-### Рецензенти на переклади
-
-Я розмовляю лише кількома мовами (і не дуже добре 😅). Отже, рецензенти – це ті, хто має [**повноваження схвалювати переклади**](contributing.md#translations){.internal-link target=_blank} документації. Без них не було б документації кількома іншими мовами.
-
----
-
-**Найкращі рецензенти** 🕵️ переглянули більшість пул реквестів від інших, забезпечуючи якість коду, документації і особливо **перекладів**.
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## Спонсори
-
-Це **Спонсори**. 😎
-
-Вони підтримують мою роботу з **FastAPI** (та іншими), переважно через
GitHub Sponsors.
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### Золоті спонсори
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### Срібні спонсори
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### Бронзові спонсори
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### Індивідуальні спонсори
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## Про дані - технічні деталі
-
-Основна мета цієї сторінки – висвітлити зусилля спільноти, щоб допомогти іншим.
-
-Особливо враховуючи зусилля, які зазвичай менш помітні, а в багатьох випадках більш важкі, як-от допомога іншим із проблемами та перегляд пул реквестів перекладів.
-
-Дані розраховуються щомісяця, ви можете ознайомитися з
вихідним кодом тут.
-
-Тут я також підкреслюю внески спонсорів.
-
-Я також залишаю за собою право оновлювати алгоритми підрахунку, види рейтингів, порогові значення тощо (про всяк випадок 🤷).
diff --git a/docs/zh-hant/docs/fastapi-people.md b/docs/zh-hant/docs/fastapi-people.md
deleted file mode 100644
index 99277b419..000000000
--- a/docs/zh-hant/docs/fastapi-people.md
+++ /dev/null
@@ -1,239 +0,0 @@
----
-hide:
- - navigation
----
-
-# FastAPI 社群
-
-FastAPI 有一個非常棒的社群,歡迎來自不同背景的朋友參與。
-
-## 作者
-
-嘿! 👋
-
-關於我:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-我是 **FastAPI** 的作者。你可以在[幫助 FastAPI - 獲得幫助 - 與作者聯繫](help-fastapi.md#connect-with-the-author){.internal-link target=_blank} 中閱讀更多相關資訊。
-
-...但在這裡,我想向你介紹這個社群。
-
----
-
-**FastAPI** 獲得了許多社群的大力支持。我想特別表揚他們的貢獻。
-
-這些人包括:
-
-* [在 GitHub 中幫助他人解答問題](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}。
-* [建立 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。
-* 審查 Pull Requests,[尤其是翻譯方面的貢獻](contributing.md#translations){.internal-link target=_blank}。
-
-讓我們為他們熱烈鼓掌。 👏 🙇
-
-## FastAPI 專家
-
-這些是在 [GitHub 中幫助其他人解決問題最多的用戶](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}。 🙇
-
-他們透過幫助其他人,證明了自己是 **FastAPI 專家**。 ✨
-
-/// 提示
-
-你也可以成為官方的 FastAPI 專家!
-
-只需要在 [GitHub 中幫助他人解答問題](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}。 🤓
-
-///
-
-你可以查看這些期間的 **FastAPI 專家**:
-
-* [上個月](#fastapi-experts-last-month) 🤓
-* [過去 3 個月](#fastapi-experts-3-months) 😎
-* [過去 6 個月](#fastapi-experts-6-months) 🧐
-* [過去 1 年](#fastapi-experts-1-year) 🧑🔬
-* [**所有時間**](#fastapi-experts-all-time) 🧙
-
-### FastAPI 專家 - 上個月
-
-上個月在 [GitHub 中幫助他人解決問題最多的](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}用戶。 🤓
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 專家 - 過去 3 個月
-
-過去三個月在 [GitHub 中幫助他人解決問題最多的](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}用戶。 😎
-
-{% if people %}
-
-{% for user in people.three_months_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 專家 - 過去 6 個月
-
-過去六個月在 [GitHub 中幫助他人解決問題最多的](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}用戶。 🧐
-
-{% if people %}
-
-{% for user in people.six_months_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 專家 - 過去一年
-
-過去一年在 [GitHub 中幫助他人解決最多問題的](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}用戶。 🧑🔬
-
-{% if people %}
-
-{% for user in people.one_year_experts[:20] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 專家 - 全部時間
-
-以下是全部時間的 **FastAPI 專家**。 🤓🤯
-
-過去在 [GitHub 中幫助他人解決問題最多的](help-fastapi.md#help-others-with-questions-in-github){.internal-link target=_blank}用戶。 🧙
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## 主要貢獻者
-
-以下是**主要貢獻者**。 👷
-
-這些用戶[建立了最多已被**合併**的 Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}。
-
-他們貢獻了原始碼、文件和翻譯等。 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-還有許多其他的貢獻者(超過一百位),你可以在
FastAPI GitHub 貢獻者頁面查看。 👷
-
-## 主要翻譯審核者
-
-以下是 **主要翻譯審核者**。 🕵️
-
-我只會講幾種語言(而且不是很流利 😅),所以審核者[**擁有批准翻譯**](contributing.md#translations){.internal-link target=_blank}文件的權限。沒有他們,就不會有多語言版本的文件。
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## 贊助者
-
-以下是**贊助者**。 😎
-
-他們主要透過
GitHub Sponsors 支持我在 **FastAPI**(以及其他項目)上的工作。
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### 金牌贊助商
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### 銀牌贊助商
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### 銅牌贊助商
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### 個人贊助商
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## 關於數據 - 技術細節
-
-這個頁面的主要目的是突顯社群幫助他人所做的努力
-
-特別是那些通常不太顯眼但往往更加艱辛的工作,例如幫助他人解答問題和審查包含翻譯的 Pull Requests。
-
-這些數據每月計算一次,你可以在這查看
原始碼。
-
-此外,我也特別表揚贊助者的貢獻。
-
-我也保留更新演算法、章節、門檻值等的權利(以防萬一 🤷)。
diff --git a/docs/zh/docs/fastapi-people.md b/docs/zh/docs/fastapi-people.md
deleted file mode 100644
index 30a75240c..000000000
--- a/docs/zh/docs/fastapi-people.md
+++ /dev/null
@@ -1,239 +0,0 @@
----
-hide:
- - navigation
----
-
-# FastAPI 社区
-
-FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋友。
-
-## 创建者 & 维护者
-
-嘿! 👋
-
-这就是我:
-
-{% if people %}
-
-{% for user in people.maintainers %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-我是 **FastAPI** 的创建者和维护者. 你能在 [帮助 FastAPI - 获取帮助 - 与作者联系](help-fastapi.md#_2){.internal-link target=_blank} 阅读有关此内容的更多信息。
-
-...但是在这里我想向您展示社区。
-
----
-
-**FastAPI** 得到了社区的大力支持。因此我想突出他们的贡献。
-
-这些人:
-
-* [帮助他人解决 GitHub 上的问题](help-fastapi.md#github_1){.internal-link target=_blank}。
-* [创建 Pull Requests](help-fastapi.md#pr){.internal-link target=_blank}。
-* 审核 Pull Requests, 对于 [翻译](contributing.md#_8){.internal-link target=_blank} 尤为重要。
-
-向他们致以掌声。 👏 🙇
-
-## FastAPI 专家
-
-这些用户一直以来致力于 [帮助他人解决 GitHub 上的问题](help-fastapi.md#github_1){.internal-link target=_blank}。 🙇
-
-他们通过帮助许多人而被证明是 **FastAPI 专家**。 ✨
-
-/// 小提示
-
-你也可以成为认可的 FastAPI 专家!
-
-只需要 [帮助他人解决 GitHub 上的问题](help-fastapi.md#github_1){.internal-link target=_blank}。 🤓
-
-///
-
-你可以查看不同时期的 **FastAPI 专家**:
-
-* [上个月](#fastapi-experts-last-month) 🤓
-* [三个月](#fastapi-experts-3-months) 😎
-* [六个月](#fastapi-experts-6-months) 🧐
-* [一年](#fastapi-experts-1-year) 🧑🔬
-* [**全部时间**](#fastapi-experts-all-time) 🧙
-
-## FastAPI 专家 - 上个月
-
-这些是在过去一个月中 [在 GitHub 上帮助他人解答最多问题](help-fastapi.md#github_1){.internal-link target=_blank} 的用户。 🤓
-
-{% if people %}
-
-{% for user in people.last_month_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 专家 - 三个月
-
-这些是在过去三个月中 [在 GitHub 上帮助他人解答最多问题](help-fastapi.md#github_1){.internal-link target=_blank} 的用户。 😎
-
-{% if people %}
-
-{% for user in people.three_months_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 专家 - 六个月
-
-这些是在过去六个月中 [在 GitHub 上帮助他人解答最多问题](help-fastapi.md#github_1){.internal-link target=_blank} 的用户。 🧐
-
-{% if people %}
-
-{% for user in people.six_months_experts[:10] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-### FastAPI 专家 - 一年
-
-这些是在过去一年中 [在 GitHub 上帮助他人解答最多问题](help-fastapi.md#github_1){.internal-link target=_blank} 的用户。 🧑🔬
-
-{% if people %}
-
-{% for user in people.one_year_experts[:20] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## FastAPI 专家 - 全部时间
-
-以下是全部时间的 **FastAPI 专家**。 🤓🤯
-
-这些用户一直以来致力于 [帮助他人解决 GitHub 的 上的问题](help-fastapi.md#github_1){.internal-link target=_blank}。 🧙
-
-{% if people %}
-
-{% for user in people.experts[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## 杰出贡献者
-
-以下是 **杰出的贡献者**。 👷
-
-这些用户 [创建了最多已被合并的 Pull Requests](help-fastapi.md#pr){.internal-link target=_blank}。
-
-他们贡献了源代码,文档,翻译等。 📦
-
-{% if people %}
-
-{% for user in people.top_contributors[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-还有很多别的贡献者(超过100个),你可以在
FastAPI GitHub 贡献者页面 中看到他们。👷
-
-## 杰出翻译审核者
-
-以下用户是 **杰出的评审者**。 🕵️
-
-我只会说少数几种语言(而且还不是很流利 😅)。所以这些评审者们具备[能力去批准文档翻译](contributing.md#_8){.internal-link target=_blank}。如果没有他们,就不会有多语言文档。
-
-{% if people %}
-
-{% for user in people.top_translations_reviewers[:50] %}
-
-
-{% endfor %}
-
-
-{% endif %}
-
-## 赞助商
-
-以下是 **赞助商** 。😎
-
-他们主要通过
GitHub Sponsors支持我在 **FastAPI** (和其他项目)的工作。
-
-{% if sponsors %}
-
-{% if sponsors.gold %}
-
-### 金牌赞助商
-
-{% for sponsor in sponsors.gold -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.silver %}
-
-### 银牌赞助商
-
-{% for sponsor in sponsors.silver -%}
-

-{% endfor %}
-{% endif %}
-
-{% if sponsors.bronze %}
-
-### 铜牌赞助商
-
-{% for sponsor in sponsors.bronze -%}
-

-{% endfor %}
-{% endif %}
-
-{% endif %}
-
-### 个人赞助
-
-{% if github_sponsors %}
-{% for group in github_sponsors.sponsors %}
-
-
-
-{% for user in group %}
-{% if user.login not in sponsors_badge.logins %}
-
-
-
-{% endif %}
-{% endfor %}
-
-
-
-{% endfor %}
-{% endif %}
-
-## 关于数据 - 技术细节
-
-该页面的目的是突出社区为帮助他人而付出的努力。
-
-尤其是那些不引人注目且涉及更困难的任务,例如帮助他人解决问题或者评审翻译 Pull Requests。
-
-该数据每月计算一次,您可以阅读
源代码。
-
-这里也强调了赞助商的贡献。
-
-我也保留更新算法,栏目,统计阈值等的权利(以防万一🤷)。
diff --git a/docs/zh/docs/how-to/index.md b/docs/zh/docs/how-to/index.md
index 262dcfaee..ac097618b 100644
--- a/docs/zh/docs/how-to/index.md
+++ b/docs/zh/docs/how-to/index.md
@@ -6,7 +6,7 @@
如果某些内容看起来对你的项目有用,请继续查阅,否则请直接跳过它们。
-/// 小技巧
+/// tip | 小技巧
如果你想以系统的方式**学习 FastAPI**(推荐),请阅读 [教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank} 的每一章节。
diff --git a/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md
index beca95d45..6058f7878 100644
--- a/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md
+++ b/docs/zh/docs/tutorial/dependencies/dependencies-with-yield.md
@@ -1,12 +1,12 @@
# 使用yield的依赖项
-FastAPI支持在完成后执行一些
额外步骤的依赖项.
+FastAPI支持在完成后执行一些
额外步骤的依赖项.
-为此,请使用 `yield` 而不是 `return`,然后再编写额外的步骤(代码)。
+为此,你需要使用 `yield` 而不是 `return`,然后再编写这些额外的步骤(代码)。
-/// tip | "提示"
+/// tip | 提示
-确保只使用一次 `yield` 。
+确保在每个依赖中只使用一次 `yield`。
///
@@ -25,7 +25,7 @@ FastAPI支持在完成后执行一些
> tasks: Send background tasks
end
opt Raise other exception
- tasks -->> dep: Raise other exception
- end
- Note over dep: After yield
- opt Handle other exception
- dep -->> dep: Handle exception, can't change response. E.g. close DB session.
+ tasks -->> tasks: Handle exceptions in the background task code
end
```
-/// info
+/// info | 说明
-只会向客户端发送**一次响应**,可能是一个错误响应之一,也可能是来自*路径操作*的响应。
+只会向客户端发送 **一次响应** ,可能是一个错误响应,也可能是来自 *路由函数* 的响应。
在发送了其中一个响应之后,就无法再发送其他响应了。
///
-/// tip
+/// tip | 提示
+
+这个时序图展示了 `HTTPException`,除此之外你也可以抛出任何你在使用 `yield` 的依赖项中或者[自定义异常处理器](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}中捕获的异常。
+
+如果你引发任何异常,它将传递给使用 `yield` 的依赖项,包括 `HTTPException`。在大多数情况下你应当从使用 `yield` 的依赖项中重新抛出捕获的异常或者一个新的异常来确保它会被正确的处理。
+
+///
+
+## 包含 `yield`, `HTTPException`, `except` 的依赖项和后台任务
+
+/// warning | 注意
+
+你大概率不需要了解这些技术细节,可以跳过这一章节继续阅读后续的内容。
+
+如果你使用的FastAPI的版本早于0.106.0,并且在使用后台任务中使用了包含 `yield` 的依赖项中的资源,那么这些细节会对你有一些用处。
+
+///
+
+### 包含 `yield` 和 `except` 的依赖项的技术细节
+
+在FastAPI 0.110.0版本之前,如果使用了一个包含 `yield` 的依赖项,你在依赖项中使用 `except` 捕获了一个异常,但是你没有再次抛出该异常,这个异常会被自动抛出/转发到异常处理器或者内部服务错误处理器。
+
+### 后台任务和使用 `yield` 的依赖项的技术细节
+
+在FastAPI 0.106.0版本之前,在 `yield` 后面抛出异常是不可行的,因为 `yield` 之后的退出代码是在响应被发送之后再执行,这个时候异常处理器已经执行过了。
-这个图表展示了`HTTPException`,但你也可以引发任何其他你创建了[自定义异常处理程序](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}的异常。
+这样设计的目的主要是为了允许在后台任务中使用被依赖项`yield`的对象,因为退出代码会在后台任务结束后再执行。
-如果你引发任何异常,它将传递给带有`yield`的依赖,包括`HTTPException`,然后**再次**传递给异常处理程序。如果没有针对该异常的异常处理程序,那么它将被默认的内部`ServerErrorMiddleware`处理,返回500 HTTP状态码,告知客户端服务器发生了错误。
+然而这也意味着在等待响应通过网络传输的同时,非必要的持有一个 `yield` 依赖项中的资源(例如数据库连接),这一行为在FastAPI 0.106.0被改变了。
+
+/// tip | 提示
+
+除此之外,后台任务通常是一组独立的逻辑,应该被单独处理,并且使用它自己的资源(例如它自己的数据库连接)。
+
+这样也会让你的代码更加简洁。
///
+如果你之前依赖于这一行为,那么现在你应该在后台任务中创建并使用它自己的资源,不要在内部使用属于 `yield` 依赖项的资源。
+
+例如,你应该在后台任务中创建一个新的数据库会话用于查询数据,而不是使用相同的会话。你应该将对象的ID作为参数传递给后台任务函数,然后在该函数中重新获取该对象,而不是直接将数据库对象作为参数。
+
## 上下文管理器
-### 什么是“上下文管理器”
+### 什么是"上下文管理器"
-“上下文管理器”是您可以在`with`语句中使用的任何Python对象。
+"上下文管理器"是你可以在 `with` 语句中使用的任何Python对象。
-例如,您可以使用`with`读取文件:
+例如,你可以使用`with`读取文件:
```Python
with open("./somefile.txt") as f:
@@ -254,38 +379,38 @@ with open("./somefile.txt") as f:
print(contents)
```
-在底层,`open("./somefile.txt")`创建了一个被称为“上下文管理器”的对象。
+在底层,`open("./somefile.txt")`创建了一个被称为"上下文管理器"的对象。
-当`with`块结束时,它会确保关闭文件,即使发生了异常也是如此。
+当 `with` 代码块结束时,它会确保关闭文件,即使发生了异常也是如此。
-当你使用`yield`创建一个依赖项时,**FastAPI**会在内部将其转换为上下文管理器,并与其他相关工具结合使用。
+当你使用 `yield` 创建一个依赖项时,**FastAPI** 会在内部将其转换为上下文管理器,并与其他相关工具结合使用。
-### 在依赖项中使用带有`yield`的上下文管理器
+### 在使用 `yield` 的依赖项中使用上下文管理器
-/// warning
+/// warning | 注意
-这是一个更为“高级”的想法。
+这是一个更为"高级"的想法。
-如果您刚开始使用**FastAPI**,您可能暂时可以跳过它。
+如果你刚开始使用 **FastAPI** ,你可以暂时可以跳过它。
///
在Python中,你可以通过创建一个带有`__enter__()`和`__exit__()`方法的类来创建上下文管理器。
-你也可以在**FastAPI**的依赖项中使用带有`yield`的`with`或`async with`语句,通过在依赖函数内部使用它们。
+你也可以在 **FastAPI** 的 `yield` 依赖项中通过 `with` 或者 `async with` 语句来使用它们:
```Python hl_lines="1-9 13"
{!../../../docs_src/dependencies/tutorial010.py!}
```
-/// tip
+/// tip | 提示
另一种创建上下文管理器的方法是:
* `@contextlib.contextmanager`或者
* `@contextlib.asynccontextmanager`
-使用上下文管理器装饰一个只有单个`yield`的函数。这就是**FastAPI**在内部用于带有`yield`的依赖项的方式。
+使用它们装饰一个只有单个 `yield` 的函数。这就是 **FastAPI** 内部对于 `yield` 依赖项的处理方式。
但是你不需要为FastAPI的依赖项使用这些装饰器(而且也不应该)。FastAPI会在内部为你处理这些。
diff --git a/docs_src/async_tests/test_main.py b/docs_src/async_tests/test_main.py
index 9f1527d5f..a57a31f7d 100644
--- a/docs_src/async_tests/test_main.py
+++ b/docs_src/async_tests/test_main.py
@@ -1,12 +1,14 @@
import pytest
-from httpx import AsyncClient
+from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
- async with AsyncClient(app=app, base_url="http://test") as ac:
+ 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"}
diff --git a/docs_src/middleware/tutorial001.py b/docs_src/middleware/tutorial001.py
index 6bab3410a..e65a7dade 100644
--- a/docs_src/middleware/tutorial001.py
+++ b/docs_src/middleware/tutorial001.py
@@ -7,8 +7,8 @@ app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
- start_time = time.time()
+ start_time = time.perf_counter()
response = await call_next(request)
- process_time = time.time() - start_time
+ process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
diff --git a/docs_src/path_params_numeric_validations/tutorial006.py b/docs_src/path_params_numeric_validations/tutorial006.py
index 0ea32694a..f07629aa0 100644
--- a/docs_src/path_params_numeric_validations/tutorial006.py
+++ b/docs_src/path_params_numeric_validations/tutorial006.py
@@ -13,4 +13,6 @@ async def read_items(
results = {"item_id": item_id}
if q:
results.update({"q": q})
+ if size:
+ results.update({"size": size})
return results
diff --git a/docs_src/path_params_numeric_validations/tutorial006_an.py b/docs_src/path_params_numeric_validations/tutorial006_an.py
index 22a143623..ac4732573 100644
--- a/docs_src/path_params_numeric_validations/tutorial006_an.py
+++ b/docs_src/path_params_numeric_validations/tutorial006_an.py
@@ -14,4 +14,6 @@ async def read_items(
results = {"item_id": item_id}
if q:
results.update({"q": q})
+ if size:
+ results.update({"size": size})
return results
diff --git a/docs_src/path_params_numeric_validations/tutorial006_an_py39.py b/docs_src/path_params_numeric_validations/tutorial006_an_py39.py
index 804751893..426ec3776 100644
--- a/docs_src/path_params_numeric_validations/tutorial006_an_py39.py
+++ b/docs_src/path_params_numeric_validations/tutorial006_an_py39.py
@@ -15,4 +15,6 @@ async def read_items(
results = {"item_id": item_id}
if q:
results.update({"q": q})
+ if size:
+ results.update({"size": size})
return results
diff --git a/fastapi/__init__.py b/fastapi/__init__.py
index 0b79d45ef..ac2508d89 100644
--- a/fastapi/__init__.py
+++ b/fastapi/__init__.py
@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
-__version__ = "0.112.1"
+__version__ = "0.112.2"
from starlette import status as status
diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py
index 61ef00638..418c11725 100644
--- a/fastapi/dependencies/models.py
+++ b/fastapi/dependencies/models.py
@@ -1,58 +1,37 @@
-from typing import Any, Callable, List, Optional, Sequence
+from dataclasses import dataclass, field
+from typing import Any, Callable, List, Optional, Sequence, Tuple
from fastapi._compat import ModelField
from fastapi.security.base import SecurityBase
+@dataclass
class SecurityRequirement:
- def __init__(
- self, security_scheme: SecurityBase, scopes: Optional[Sequence[str]] = None
- ):
- self.security_scheme = security_scheme
- self.scopes = scopes
+ security_scheme: SecurityBase
+ scopes: Optional[Sequence[str]] = None
+@dataclass
class Dependant:
- def __init__(
- self,
- *,
- path_params: Optional[List[ModelField]] = None,
- query_params: Optional[List[ModelField]] = None,
- header_params: Optional[List[ModelField]] = None,
- cookie_params: Optional[List[ModelField]] = None,
- body_params: Optional[List[ModelField]] = None,
- dependencies: Optional[List["Dependant"]] = None,
- security_schemes: Optional[List[SecurityRequirement]] = None,
- name: Optional[str] = None,
- call: Optional[Callable[..., Any]] = None,
- request_param_name: Optional[str] = None,
- websocket_param_name: Optional[str] = None,
- http_connection_param_name: Optional[str] = None,
- response_param_name: Optional[str] = None,
- background_tasks_param_name: Optional[str] = None,
- security_scopes_param_name: Optional[str] = None,
- security_scopes: Optional[List[str]] = None,
- use_cache: bool = True,
- path: Optional[str] = None,
- ) -> None:
- self.path_params = path_params or []
- self.query_params = query_params or []
- self.header_params = header_params or []
- self.cookie_params = cookie_params or []
- self.body_params = body_params or []
- self.dependencies = dependencies or []
- self.security_requirements = security_schemes or []
- self.request_param_name = request_param_name
- self.websocket_param_name = websocket_param_name
- self.http_connection_param_name = http_connection_param_name
- self.response_param_name = response_param_name
- self.background_tasks_param_name = background_tasks_param_name
- self.security_scopes = security_scopes
- self.security_scopes_param_name = security_scopes_param_name
- self.name = name
- self.call = call
- self.use_cache = use_cache
- # Store the path to be able to re-generate a dependable from it in overrides
- self.path = path
- # Save the cache key at creation to optimize performance
+ path_params: List[ModelField] = field(default_factory=list)
+ query_params: List[ModelField] = field(default_factory=list)
+ header_params: List[ModelField] = field(default_factory=list)
+ cookie_params: List[ModelField] = field(default_factory=list)
+ body_params: List[ModelField] = field(default_factory=list)
+ dependencies: List["Dependant"] = field(default_factory=list)
+ security_requirements: List[SecurityRequirement] = field(default_factory=list)
+ name: Optional[str] = None
+ call: Optional[Callable[..., Any]] = None
+ request_param_name: Optional[str] = None
+ websocket_param_name: Optional[str] = None
+ http_connection_param_name: Optional[str] = None
+ response_param_name: Optional[str] = None
+ background_tasks_param_name: Optional[str] = None
+ security_scopes_param_name: Optional[str] = None
+ security_scopes: Optional[List[str]] = None
+ use_cache: bool = True
+ path: Optional[str] = None
+ cache_key: Tuple[Optional[Callable[..., Any]], Tuple[str, ...]] = field(init=False)
+
+ def __post_init__(self) -> None:
self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index 6c75ca6b2..453700cb7 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -1,6 +1,7 @@
import inspect
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
+from dataclasses import dataclass
from typing import (
Any,
Callable,
@@ -55,7 +56,7 @@ from fastapi.logger import logger
from fastapi.security.base import SecurityBase
from fastapi.security.oauth2 import OAuth2, SecurityScopes
from fastapi.security.open_id_connect_url import OpenIdConnect
-from fastapi.utils import create_response_field, get_path_param_names
+from fastapi.utils import create_model_field, get_path_param_names
from pydantic.fields import FieldInfo
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from starlette.concurrency import run_in_threadpool
@@ -80,25 +81,23 @@ multipart_incorrect_install_error = (
)
-def check_file_field(field: ModelField) -> None:
- field_info = field.field_info
- if isinstance(field_info, params.Form):
+def ensure_multipart_is_installed() -> None:
+ try:
+ # __version__ is available in both multiparts, and can be mocked
+ from multipart import __version__ # type: ignore
+
+ assert __version__
try:
- # __version__ is available in both multiparts, and can be mocked
- from multipart import __version__ # type: ignore
-
- assert __version__
- try:
- # parse_options_header is only available in the right multipart
- from multipart.multipart import parse_options_header # type: ignore
-
- assert parse_options_header
- except ImportError:
- logger.error(multipart_incorrect_install_error)
- raise RuntimeError(multipart_incorrect_install_error) from None
+ # parse_options_header is only available in the right multipart
+ from multipart.multipart import parse_options_header # type: ignore
+
+ assert parse_options_header
except ImportError:
- logger.error(multipart_not_installed_error)
- raise RuntimeError(multipart_not_installed_error) from None
+ logger.error(multipart_incorrect_install_error)
+ raise RuntimeError(multipart_incorrect_install_error) from None
+ except ImportError:
+ logger.error(multipart_not_installed_error)
+ raise RuntimeError(multipart_not_installed_error) from None
def get_param_sub_dependant(
@@ -176,7 +175,7 @@ def get_flat_dependant(
header_params=dependant.header_params.copy(),
cookie_params=dependant.cookie_params.copy(),
body_params=dependant.body_params.copy(),
- security_schemes=dependant.security_requirements.copy(),
+ security_requirements=dependant.security_requirements.copy(),
use_cache=dependant.use_cache,
path=dependant.path,
)
@@ -259,16 +258,16 @@ def get_dependant(
)
for param_name, param in signature_params.items():
is_path_param = param_name in path_param_names
- type_annotation, depends, param_field = analyze_param(
+ param_details = analyze_param(
param_name=param_name,
annotation=param.annotation,
value=param.default,
is_path_param=is_path_param,
)
- if depends is not None:
+ if param_details.depends is not None:
sub_dependant = get_param_sub_dependant(
param_name=param_name,
- depends=depends,
+ depends=param_details.depends,
path=path,
security_scopes=security_scopes,
)
@@ -276,18 +275,18 @@ def get_dependant(
continue
if add_non_field_param_to_dependency(
param_name=param_name,
- type_annotation=type_annotation,
+ type_annotation=param_details.type_annotation,
dependant=dependant,
):
assert (
- param_field is None
+ param_details.field is None
), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
continue
- assert param_field is not None
- if is_body_param(param_field=param_field, is_path_param=is_path_param):
- dependant.body_params.append(param_field)
+ assert param_details.field is not None
+ if is_body_param(param_field=param_details.field, is_path_param=is_path_param):
+ dependant.body_params.append(param_details.field)
else:
- add_param_to_fields(field=param_field, dependant=dependant)
+ add_param_to_fields(field=param_details.field, dependant=dependant)
return dependant
@@ -315,13 +314,20 @@ def add_non_field_param_to_dependency(
return None
+@dataclass
+class ParamDetails:
+ type_annotation: Any
+ depends: Optional[params.Depends]
+ field: Optional[ModelField]
+
+
def analyze_param(
*,
param_name: str,
annotation: Any,
value: Any,
is_path_param: bool,
-) -> Tuple[Any, Optional[params.Depends], Optional[ModelField]]:
+) -> ParamDetails:
field_info = None
depends = None
type_annotation: Any = Any
@@ -329,6 +335,7 @@ def analyze_param(
if annotation is not inspect.Signature.empty:
use_annotation = annotation
type_annotation = annotation
+ # Extract Annotated info
if get_origin(use_annotation) is Annotated:
annotated_args = get_args(annotation)
type_annotation = annotated_args[0]
@@ -348,6 +355,7 @@ def analyze_param(
)
else:
fastapi_annotation = None
+ # Set default for Annotated FieldInfo
if isinstance(fastapi_annotation, FieldInfo):
# Copy `field_info` because we mutate `field_info.default` below.
field_info = copy_field_info(
@@ -362,9 +370,10 @@ def analyze_param(
field_info.default = value
else:
field_info.default = Required
+ # Get Annotated Depends
elif isinstance(fastapi_annotation, params.Depends):
depends = fastapi_annotation
-
+ # Get Depends from default value
if isinstance(value, params.Depends):
assert depends is None, (
"Cannot specify `Depends` in `Annotated` and default value"
@@ -375,6 +384,7 @@ def analyze_param(
f" default value together for {param_name!r}"
)
depends = value
+ # Get FieldInfo from default value
elif isinstance(value, FieldInfo):
assert field_info is None, (
"Cannot specify FastAPI annotations in `Annotated` and default value"
@@ -384,11 +394,13 @@ def analyze_param(
if PYDANTIC_V2:
field_info.annotation = type_annotation
+ # Get Depends from type annotation
if depends is not None and depends.dependency is None:
# Copy `depends` before mutating it
depends = copy(depends)
depends.dependency = type_annotation
+ # Handle non-param type annotations like Request
if lenient_issubclass(
type_annotation,
(
@@ -404,6 +416,7 @@ def analyze_param(
assert (
field_info is None
), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
+ # Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
elif field_info is None and depends is None:
default_value = value if value is not inspect.Signature.empty else Required
if is_path_param:
@@ -421,7 +434,9 @@ def analyze_param(
field_info = params.Query(annotation=use_annotation, default=default_value)
field = None
+ # It's a field_info, not a dependency
if field_info is not None:
+ # Handle field_info.in_
if is_path_param:
assert isinstance(field_info, params.Path), (
f"Cannot use `{field_info.__class__.__name__}` for path param"
@@ -437,12 +452,14 @@ def analyze_param(
field_info,
param_name,
)
+ if isinstance(field_info, params.Form):
+ ensure_multipart_is_installed()
if not field_info.alias and getattr(field_info, "convert_underscores", None):
alias = param_name.replace("_", "-")
else:
alias = field_info.alias or param_name
field_info.alias = alias
- field = create_response_field(
+ field = create_model_field(
name=param_name,
type_=use_annotation_from_field_info,
default=field_info.default,
@@ -451,7 +468,7 @@ def analyze_param(
field_info=field_info,
)
- return type_annotation, depends, field
+ return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
@@ -522,6 +539,15 @@ async def solve_generator(
return await stack.enter_async_context(cm)
+@dataclass
+class SolvedDependency:
+ values: Dict[str, Any]
+ errors: List[Any]
+ background_tasks: Optional[StarletteBackgroundTasks]
+ response: Response
+ dependency_cache: Dict[Tuple[Callable[..., Any], Tuple[str]], Any]
+
+
async def solve_dependencies(
*,
request: Union[Request, WebSocket],
@@ -532,13 +558,7 @@ async def solve_dependencies(
dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
async_exit_stack: AsyncExitStack,
-) -> Tuple[
- Dict[str, Any],
- List[Any],
- Optional[StarletteBackgroundTasks],
- Response,
- Dict[Tuple[Callable[..., Any], Tuple[str]], Any],
-]:
+) -> SolvedDependency:
values: Dict[str, Any] = {}
errors: List[Any] = []
if response is None:
@@ -580,27 +600,21 @@ async def solve_dependencies(
dependency_cache=dependency_cache,
async_exit_stack=async_exit_stack,
)
- (
- sub_values,
- sub_errors,
- background_tasks,
- _, # the subdependency returns the same response we have
- sub_dependency_cache,
- ) = solved_result
- dependency_cache.update(sub_dependency_cache)
- if sub_errors:
- errors.extend(sub_errors)
+ background_tasks = solved_result.background_tasks
+ dependency_cache.update(solved_result.dependency_cache)
+ if solved_result.errors:
+ errors.extend(solved_result.errors)
continue
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
solved = dependency_cache[sub_dependant.cache_key]
elif is_gen_callable(call) or is_async_gen_callable(call):
solved = await solve_generator(
- call=call, stack=async_exit_stack, sub_values=sub_values
+ call=call, stack=async_exit_stack, sub_values=solved_result.values
)
elif is_coroutine_callable(call):
- solved = await call(**sub_values)
+ solved = await call(**solved_result.values)
else:
- solved = await run_in_threadpool(call, **sub_values)
+ solved = await run_in_threadpool(call, **solved_result.values)
if sub_dependant.name is not None:
values[sub_dependant.name] = solved
if sub_dependant.cache_key not in dependency_cache:
@@ -647,7 +661,13 @@ async def solve_dependencies(
values[dependant.security_scopes_param_name] = SecurityScopes(
scopes=dependant.security_scopes
)
- return values, errors, background_tasks, response, dependency_cache
+ return SolvedDependency(
+ values=values,
+ errors=errors,
+ background_tasks=background_tasks,
+ response=response,
+ dependency_cache=dependency_cache,
+ )
def request_params_to_args(
@@ -776,7 +796,6 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
embed = getattr(field_info, "embed", None)
body_param_names_set = {param.name for param in flat_dependant.body_params}
if len(body_param_names_set) == 1 and not embed:
- check_file_field(first_param)
return first_param
# If one field requires to embed, all have to be embedded
# in case a sub-dependency is evaluated with a single unique body field
@@ -808,14 +827,13 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
]
if len(set(body_param_media_types)) == 1:
BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
- final_field = create_response_field(
+ final_field = create_model_field(
name="body",
type_=BodyModel,
required=required,
alias="body",
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
- check_file_field(final_field)
return final_field
diff --git a/fastapi/params.py b/fastapi/params.py
index 860146531..cc2a5c13c 100644
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -91,7 +91,7 @@ class Param(FieldInfo):
max_length=max_length,
discriminator=discriminator,
multiple_of=multiple_of,
- allow_nan=allow_inf_nan,
+ allow_inf_nan=allow_inf_nan,
max_digits=max_digits,
decimal_places=decimal_places,
**extra,
@@ -547,7 +547,7 @@ class Body(FieldInfo):
max_length=max_length,
discriminator=discriminator,
multiple_of=multiple_of,
- allow_nan=allow_inf_nan,
+ allow_inf_nan=allow_inf_nan,
max_digits=max_digits,
decimal_places=decimal_places,
**extra,
diff --git a/fastapi/routing.py b/fastapi/routing.py
index 8b01a5c1e..9857fd406 100644
--- a/fastapi/routing.py
+++ b/fastapi/routing.py
@@ -3,14 +3,16 @@ import dataclasses
import email.message
import inspect
import json
-from contextlib import AsyncExitStack
+from contextlib import AsyncExitStack, asynccontextmanager
from enum import Enum, IntEnum
from typing import (
Any,
+ AsyncIterator,
Callable,
Coroutine,
Dict,
List,
+ Mapping,
Optional,
Sequence,
Set,
@@ -49,7 +51,7 @@ from fastapi.exceptions import (
from fastapi.types import DecoratedCallable, IncEx
from fastapi.utils import (
create_cloned_field,
- create_response_field,
+ create_model_field,
generate_unique_id,
get_value_or_default,
is_body_allowed_for_status_code,
@@ -69,7 +71,7 @@ from starlette.routing import (
websocket_session,
)
from starlette.routing import Mount as Mount # noqa
-from starlette.types import ASGIApp, Lifespan, Scope
+from starlette.types import AppType, ASGIApp, Lifespan, Scope
from starlette.websockets import WebSocket
from typing_extensions import Annotated, Doc, deprecated
@@ -121,6 +123,23 @@ def _prepare_response_content(
return res
+def _merge_lifespan_context(
+ original_context: Lifespan[Any], nested_context: Lifespan[Any]
+) -> Lifespan[Any]:
+ @asynccontextmanager
+ async def merged_lifespan(
+ app: AppType,
+ ) -> AsyncIterator[Optional[Mapping[str, Any]]]:
+ async with original_context(app) as maybe_original_state:
+ async with nested_context(app) as maybe_nested_state:
+ if maybe_nested_state is None and maybe_original_state is None:
+ yield None # old ASGI compatibility
+ else:
+ yield {**(maybe_nested_state or {}), **(maybe_original_state or {})}
+
+ return merged_lifespan # type: ignore[return-value]
+
+
async def serialize_response(
*,
field: Optional[ModelField] = None,
@@ -275,26 +294,34 @@ def get_request_handler(
dependency_overrides_provider=dependency_overrides_provider,
async_exit_stack=async_exit_stack,
)
- values, errors, background_tasks, sub_response, _ = solved_result
+ errors = solved_result.errors
if not errors:
raw_response = await run_endpoint_function(
- dependant=dependant, values=values, is_coroutine=is_coroutine
+ dependant=dependant,
+ values=solved_result.values,
+ is_coroutine=is_coroutine,
)
if isinstance(raw_response, Response):
if raw_response.background is None:
- raw_response.background = background_tasks
+ raw_response.background = solved_result.background_tasks
response = raw_response
else:
- response_args: Dict[str, Any] = {"background": background_tasks}
+ response_args: Dict[str, Any] = {
+ "background": solved_result.background_tasks
+ }
# If status_code was set, use it, otherwise use the default from the
# response class, in the case of redirect it's 307
current_status_code = (
- status_code if status_code else sub_response.status_code
+ status_code
+ if status_code
+ else solved_result.response.status_code
)
if current_status_code is not None:
response_args["status_code"] = current_status_code
- if sub_response.status_code:
- response_args["status_code"] = sub_response.status_code
+ if solved_result.response.status_code:
+ response_args["status_code"] = (
+ solved_result.response.status_code
+ )
content = await serialize_response(
field=response_field,
response_content=raw_response,
@@ -309,7 +336,7 @@ def get_request_handler(
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
- response.headers.raw.extend(sub_response.headers.raw)
+ response.headers.raw.extend(solved_result.response.headers.raw)
if errors:
validation_error = RequestValidationError(
_normalize_errors(errors), body=body
@@ -343,11 +370,12 @@ def get_websocket_app(
dependency_overrides_provider=dependency_overrides_provider,
async_exit_stack=async_exit_stack,
)
- values, errors, _, _2, _3 = solved_result
- if errors:
- raise WebSocketRequestValidationError(_normalize_errors(errors))
+ if solved_result.errors:
+ raise WebSocketRequestValidationError(
+ _normalize_errors(solved_result.errors)
+ )
assert dependant.call is not None, "dependant.call must be a function"
- await dependant.call(**values)
+ await dependant.call(**solved_result.values)
return app
@@ -471,7 +499,7 @@ class APIRoute(routing.Route):
status_code
), f"Status code {status_code} must not have a response body"
response_name = "Response_" + self.unique_id
- self.response_field = create_response_field(
+ self.response_field = create_model_field(
name=response_name,
type_=self.response_model,
mode="serialization",
@@ -504,7 +532,7 @@ class APIRoute(routing.Route):
additional_status_code
), f"Status code {additional_status_code} must not have a response body"
response_name = f"Response_{additional_status_code}_{self.unique_id}"
- response_field = create_response_field(name=response_name, type_=model)
+ response_field = create_model_field(name=response_name, type_=model)
response_fields[additional_status_code] = response_field
if response_fields:
self.response_fields: Dict[Union[int, str], ModelField] = response_fields
@@ -1323,6 +1351,10 @@ class APIRouter(routing.Router):
self.add_event_handler("startup", handler)
for handler in router.on_shutdown:
self.add_event_handler("shutdown", handler)
+ self.lifespan_context = _merge_lifespan_context(
+ self.lifespan_context,
+ router.lifespan_context,
+ )
def get(
self,
diff --git a/fastapi/utils.py b/fastapi/utils.py
index 5c2538fac..4c7350fea 100644
--- a/fastapi/utils.py
+++ b/fastapi/utils.py
@@ -60,9 +60,9 @@ def get_path_param_names(path: str) -> Set[str]:
return set(re.findall("{(.*?)}", path))
-def create_response_field(
+def create_model_field(
name: str,
- type_: Type[Any],
+ type_: Any,
class_validators: Optional[Dict[str, Validator]] = None,
default: Optional[Any] = Undefined,
required: Union[bool, UndefinedType] = Undefined,
@@ -71,9 +71,6 @@ def create_response_field(
alias: Optional[str] = None,
mode: Literal["validation", "serialization"] = "validation",
) -> ModelField:
- """
- Create a new response field. Raises if type_ is invalid.
- """
class_validators = class_validators or {}
if PYDANTIC_V2:
field_info = field_info or FieldInfo(
@@ -135,7 +132,7 @@ def create_cloned_field(
use_type.__fields__[f.name] = create_cloned_field(
f, cloned_types=cloned_types
)
- new_field = create_response_field(name=field.name, type_=use_type)
+ new_field = create_model_field(name=field.name, type_=use_type)
new_field.has_alias = field.has_alias # type: ignore[attr-defined]
new_field.alias = field.alias # type: ignore[misc]
new_field.class_validators = field.class_validators # type: ignore[attr-defined]
diff --git a/pyproject.toml b/pyproject.toml
index 8db2d2b2d..bb87be470 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -165,6 +165,7 @@ filterwarnings = [
[tool.coverage.run]
parallel = true
+data_file = "coverage/.coverage"
source = [
"docs_src",
"tests",
@@ -177,6 +178,13 @@ omit = [
"docs_src/response_model/tutorial003_04_py310.py",
]
+[tool.coverage.report]
+show_missing = true
+sort = "-Cover"
+
+[tool.coverage.html]
+show_contexts = true
+
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
diff --git a/requirements-docs.txt b/requirements-docs.txt
index ab2b0165b..332fd1857 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -8,7 +8,7 @@ pyyaml >=5.3.1,<7.0.0
# For Material for MkDocs, Chinese search
jieba==0.42.1
# For image processing by Material for MkDocs
-pillow==10.3.0
+pillow==10.4.0
# For image processing by Material for MkDocs
cairosvg==2.7.1
mkdocstrings[python]==0.25.1
diff --git a/scripts/docs.py b/scripts/docs.py
index 1dba4aa0a..f0c51f7a6 100644
--- a/scripts/docs.py
+++ b/scripts/docs.py
@@ -29,6 +29,7 @@ missing_translation_snippet = """
non_translated_sections = [
"reference/",
"release-notes.md",
+ "fastapi-people.md",
"external-links.md",
"newsletter.md",
"management-tasks.md",
diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py
index 10e8dc153..0bc4929a4 100644
--- a/scripts/mkdocs_hooks.py
+++ b/scripts/mkdocs_hooks.py
@@ -11,6 +11,7 @@ from mkdocs.structure.pages import Page
non_translated_sections = [
"reference/",
"release-notes.md",
+ "fastapi-people.md",
"external-links.md",
"newsletter.md",
"management-tasks.md",
diff --git a/scripts/test-cov-html.sh b/scripts/test-cov-html.sh
index 85aef9601..517ac6422 100755
--- a/scripts/test-cov-html.sh
+++ b/scripts/test-cov-html.sh
@@ -5,5 +5,5 @@ set -x
bash scripts/test.sh ${@}
coverage combine
-coverage report --show-missing
-coverage html --show-contexts
+coverage report
+coverage html
diff --git a/tests/test_allow_inf_nan_in_enforcing.py b/tests/test_allow_inf_nan_in_enforcing.py
new file mode 100644
index 000000000..9e855fdf8
--- /dev/null
+++ b/tests/test_allow_inf_nan_in_enforcing.py
@@ -0,0 +1,83 @@
+import pytest
+from fastapi import Body, FastAPI, Query
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+
+@app.post("/")
+async def get(
+ x: Annotated[float, Query(allow_inf_nan=True)] = 0,
+ y: Annotated[float, Query(allow_inf_nan=False)] = 0,
+ z: Annotated[float, Query()] = 0,
+ b: Annotated[float, Body(allow_inf_nan=False)] = 0,
+) -> str:
+ return "OK"
+
+
+client = TestClient(app)
+
+
+@pytest.mark.parametrize(
+ "value,code",
+ [
+ ("-1", 200),
+ ("inf", 200),
+ ("-inf", 200),
+ ("nan", 200),
+ ("0", 200),
+ ("342", 200),
+ ],
+)
+def test_allow_inf_nan_param_true(value: str, code: int):
+ response = client.post(f"/?x={value}")
+ assert response.status_code == code, response.text
+
+
+@pytest.mark.parametrize(
+ "value,code",
+ [
+ ("-1", 200),
+ ("inf", 422),
+ ("-inf", 422),
+ ("nan", 422),
+ ("0", 200),
+ ("342", 200),
+ ],
+)
+def test_allow_inf_nan_param_false(value: str, code: int):
+ response = client.post(f"/?y={value}")
+ assert response.status_code == code, response.text
+
+
+@pytest.mark.parametrize(
+ "value,code",
+ [
+ ("-1", 200),
+ ("inf", 200),
+ ("-inf", 200),
+ ("nan", 200),
+ ("0", 200),
+ ("342", 200),
+ ],
+)
+def test_allow_inf_nan_param_default(value: str, code: int):
+ response = client.post(f"/?z={value}")
+ assert response.status_code == code, response.text
+
+
+@pytest.mark.parametrize(
+ "value,code",
+ [
+ ("-1", 200),
+ ("inf", 422),
+ ("-inf", 422),
+ ("nan", 422),
+ ("0", 200),
+ ("342", 200),
+ ],
+)
+def test_allow_inf_nan_body(value: str, code: int):
+ response = client.post("/", json=value)
+ assert response.status_code == code, response.text
diff --git a/tests/test_router_events.py b/tests/test_router_events.py
index 1b9de18ae..dd7ff3314 100644
--- a/tests/test_router_events.py
+++ b/tests/test_router_events.py
@@ -1,8 +1,8 @@
from contextlib import asynccontextmanager
-from typing import AsyncGenerator, Dict
+from typing import AsyncGenerator, Dict, Union
import pytest
-from fastapi import APIRouter, FastAPI
+from fastapi import APIRouter, FastAPI, Request
from fastapi.testclient import TestClient
from pydantic import BaseModel
@@ -109,3 +109,134 @@ def test_app_lifespan_state(state: State) -> None:
assert response.json() == {"message": "Hello World"}
assert state.app_startup is True
assert state.app_shutdown is True
+
+
+def test_router_nested_lifespan_state(state: State) -> None:
+ @asynccontextmanager
+ async def lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]:
+ state.app_startup = True
+ yield {"app": True}
+ state.app_shutdown = True
+
+ @asynccontextmanager
+ async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]:
+ state.router_startup = True
+ yield {"router": True}
+ state.router_shutdown = True
+
+ @asynccontextmanager
+ async def subrouter_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]:
+ state.sub_router_startup = True
+ yield {"sub_router": True}
+ state.sub_router_shutdown = True
+
+ sub_router = APIRouter(lifespan=subrouter_lifespan)
+
+ router = APIRouter(lifespan=router_lifespan)
+ router.include_router(sub_router)
+
+ app = FastAPI(lifespan=lifespan)
+ app.include_router(router)
+
+ @app.get("/")
+ def main(request: Request) -> Dict[str, str]:
+ assert request.state.app
+ assert request.state.router
+ assert request.state.sub_router
+ return {"message": "Hello World"}
+
+ assert state.app_startup is False
+ assert state.router_startup is False
+ assert state.sub_router_startup is False
+ assert state.app_shutdown is False
+ assert state.router_shutdown is False
+ assert state.sub_router_shutdown is False
+
+ with TestClient(app) as client:
+ assert state.app_startup is True
+ assert state.router_startup is True
+ assert state.sub_router_startup is True
+ assert state.app_shutdown is False
+ assert state.router_shutdown is False
+ assert state.sub_router_shutdown is False
+ response = client.get("/")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Hello World"}
+
+ assert state.app_startup is True
+ assert state.router_startup is True
+ assert state.sub_router_startup is True
+ assert state.app_shutdown is True
+ assert state.router_shutdown is True
+ assert state.sub_router_shutdown is True
+
+
+def test_router_nested_lifespan_state_overriding_by_parent() -> None:
+ @asynccontextmanager
+ async def lifespan(
+ app: FastAPI,
+ ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]:
+ yield {
+ "app_specific": True,
+ "overridden": "app",
+ }
+
+ @asynccontextmanager
+ async def router_lifespan(
+ app: FastAPI,
+ ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]:
+ yield {
+ "router_specific": True,
+ "overridden": "router", # should override parent
+ }
+
+ router = APIRouter(lifespan=router_lifespan)
+ app = FastAPI(lifespan=lifespan)
+ app.include_router(router)
+
+ with TestClient(app) as client:
+ assert client.app_state == {
+ "app_specific": True,
+ "router_specific": True,
+ "overridden": "app",
+ }
+
+
+def test_merged_no_return_lifespans_return_none() -> None:
+ @asynccontextmanager
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
+ yield
+
+ @asynccontextmanager
+ async def router_lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
+ yield
+
+ router = APIRouter(lifespan=router_lifespan)
+ app = FastAPI(lifespan=lifespan)
+ app.include_router(router)
+
+ with TestClient(app) as client:
+ assert not client.app_state
+
+
+def test_merged_mixed_state_lifespans() -> None:
+ @asynccontextmanager
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
+ yield
+
+ @asynccontextmanager
+ async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]:
+ yield {"router": True}
+
+ @asynccontextmanager
+ async def sub_router_lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
+ yield
+
+ sub_router = APIRouter(lifespan=sub_router_lifespan)
+ router = APIRouter(lifespan=router_lifespan)
+ app = FastAPI(lifespan=lifespan)
+ router.include_router(sub_router)
+ app.include_router(router)
+
+ with TestClient(app) as client:
+ assert client.app_state == {"router": True}