mirror of
https://github.com/fastapi/fastapi.git
synced 2026-02-06 04:11:14 -05:00
Compare commits
1 Commits
0.128.2
...
translate-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6135c77139 |
6
.github/workflows/translate.yml
vendored
6
.github/workflows/translate.yml
vendored
@@ -35,11 +35,6 @@ on:
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
max:
|
||||
description: Maximum number of items to translate (e.g. 10)
|
||||
type: number
|
||||
required: false
|
||||
default: 10
|
||||
|
||||
jobs:
|
||||
langs:
|
||||
@@ -120,4 +115,3 @@ jobs:
|
||||
EN_PATH: ${{ github.event.inputs.en_path }}
|
||||
COMMAND: ${{ matrix.command }}
|
||||
COMMIT_IN_PLACE: ${{ github.event.inputs.commit_in_place }}
|
||||
MAX: ${{ github.event.inputs.max }}
|
||||
|
||||
@@ -6,29 +6,13 @@ Dazu können Sie die `WSGIMiddleware` verwenden und damit Ihre WSGI-Anwendung wr
|
||||
|
||||
## `WSGIMiddleware` verwenden { #using-wsgimiddleware }
|
||||
|
||||
/// info | Info
|
||||
|
||||
Dafür muss `a2wsgi` installiert sein, z. B. mit `pip install a2wsgi`.
|
||||
|
||||
///
|
||||
|
||||
Sie müssen `WSGIMiddleware` aus `a2wsgi` importieren.
|
||||
Sie müssen `WSGIMiddleware` importieren.
|
||||
|
||||
Wrappen Sie dann die WSGI-Anwendung (z. B. Flask) mit der Middleware.
|
||||
|
||||
Und dann mounten Sie das auf einem Pfad.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
Früher wurde empfohlen, `WSGIMiddleware` aus `fastapi.middleware.wsgi` zu verwenden, dies ist jetzt deprecatet.
|
||||
|
||||
Stattdessen wird empfohlen, das Paket `a2wsgi` zu verwenden. Die Nutzung bleibt gleich.
|
||||
|
||||
Stellen Sie lediglich sicher, dass das Paket `a2wsgi` installiert ist und importieren Sie `WSGIMiddleware` korrekt aus `a2wsgi`.
|
||||
|
||||
///
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Es testen { #check-it }
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ Es gibt andere Formate und Tools zum Definieren und Installieren von Paketabhän
|
||||
* Erstellen Sie eine `main.py`-Datei mit:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -156,7 +158,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ $ pip install "fastapi[standard]"
|
||||
Erstellen Sie eine Datei `main.py` mit:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -181,7 +183,9 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
Wenn Ihr Code `async` / `await` verwendet, benutzen Sie `async def`:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,7 +197,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -284,7 +288,9 @@ Sie sehen die alternative automatische Dokumentation (bereitgestellt von <a href
|
||||
|
||||
Deklarieren Sie den Body mit Standard-Python-Typen, dank Pydantic.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -294,7 +300,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool | None = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -303,7 +309,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Body – Mehrere Parameter { #body-multiple-parameters }
|
||||
|
||||
Nun, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von <abbr title="Requestbody">Requestbody</abbr>-Deklarationen an.
|
||||
Nun, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von <abbr title="Anfragekörper">Requestbody</abbr>-Deklarationen an.
|
||||
|
||||
## `Path`-, `Query`- und Body-Parameter vermischen { #mix-path-query-and-body-parameters }
|
||||
|
||||
@@ -101,13 +101,13 @@ Natürlich können Sie auch, wann immer Sie das brauchen, weitere Query-Paramete
|
||||
Da einfache Werte standardmäßig als Query-Parameter interpretiert werden, müssen Sie `Query` nicht explizit hinzufügen, Sie können einfach schreiben:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Oder in Python 3.9:
|
||||
Oder in Python 3.10 und darüber:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Zum Beispiel:
|
||||
|
||||
@@ -52,7 +52,7 @@ In diesem Fall macht es Sinn, die Tags in einem `Enum` zu speichern.
|
||||
|
||||
Sie können eine <abbr title="Zusammenfassung">`summary`</abbr> und eine <abbr title="Beschreibung">`description`</abbr> hinzufügen:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
|
||||
## Beschreibung mittels Docstring { #description-from-docstring }
|
||||
|
||||
@@ -70,7 +70,7 @@ Es wird in der interaktiven Dokumentation verwendet:
|
||||
|
||||
Sie können die Response mit dem Parameter `response_description` beschreiben:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
|
||||
|
||||
/// info | Info
|
||||
|
||||
|
||||
@@ -7,17 +7,6 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.128.2
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Add support for PEP695 `TypeAliasType`. PR [#13920](https://github.com/fastapi/fastapi/pull/13920) by [@cstruct](https://github.com/cstruct).
|
||||
* ✨ Allow `Response` type hint as dependency annotation. PR [#14794](https://github.com/fastapi/fastapi/pull/14794) by [@jonathan-fulton](https://github.com/jonathan-fulton).
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix using `Json[list[str]]` type (issue #10997). PR [#14616](https://github.com/fastapi/fastapi/pull/14616) by [@mkanetsuna](https://github.com/mkanetsuna).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Update docs for translations. PR [#14830](https://github.com/fastapi/fastapi/pull/14830) by [@tiangolo](https://github.com/tiangolo).
|
||||
@@ -25,24 +14,12 @@ hide:
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Enable Traditional Chinese translations. PR [#14842](https://github.com/fastapi/fastapi/pull/14842) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Enable French docs translations. PR [#14841](https://github.com/fastapi/fastapi/pull/14841) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for fr (translate-page). PR [#14837](https://github.com/fastapi/fastapi/pull/14837) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for de (update-outdated). PR [#14836](https://github.com/fastapi/fastapi/pull/14836) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for pt (update-outdated). PR [#14833](https://github.com/fastapi/fastapi/pull/14833) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for ko (update-outdated). PR [#14835](https://github.com/fastapi/fastapi/pull/14835) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for es (update-outdated). PR [#14832](https://github.com/fastapi/fastapi/pull/14832) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for tr (update-outdated). PR [#14831](https://github.com/fastapi/fastapi/pull/14831) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for tr (add-missing). PR [#14790](https://github.com/fastapi/fastapi/pull/14790) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for fr (update-outdated). PR [#14826](https://github.com/fastapi/fastapi/pull/14826) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for zh-hant (update-outdated). PR [#14825](https://github.com/fastapi/fastapi/pull/14825) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🌐 Update translations for uk (update-outdated). PR [#14822](https://github.com/fastapi/fastapi/pull/14822) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔨 Update docs and translations scripts, enable Turkish. PR [#14824](https://github.com/fastapi/fastapi/pull/14824) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔨 Add max pages to translate to configs. PR [#14840](https://github.com/fastapi/fastapi/pull/14840) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.128.1
|
||||
|
||||
### Features
|
||||
|
||||
@@ -317,8 +317,6 @@ extra:
|
||||
name: de - Deutsch
|
||||
- link: /es/
|
||||
name: es - español
|
||||
- link: /fr/
|
||||
name: fr - français
|
||||
- link: /ja/
|
||||
name: ja - 日本語
|
||||
- link: /ko/
|
||||
@@ -331,8 +329,6 @@ extra:
|
||||
name: tr - Türkçe
|
||||
- link: /uk/
|
||||
name: uk - українська мова
|
||||
- link: /zh-hant/
|
||||
name: zh-hant - 繁體中文
|
||||
extra_css:
|
||||
- css/termynal.css
|
||||
- css/custom.css
|
||||
|
||||
@@ -2,33 +2,17 @@
|
||||
|
||||
Puedes montar aplicaciones WSGI como viste con [Sub Aplicaciones - Mounts](sub-applications.md){.internal-link target=_blank}, [Detrás de un Proxy](behind-a-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
Para eso, puedes usar el `WSGIMiddleware` y usarlo para envolver tu aplicación WSGI, por ejemplo, Flask, Django, etc.
|
||||
Para eso, puedes usar `WSGIMiddleware` y usarlo para envolver tu aplicación WSGI, por ejemplo, Flask, Django, etc.
|
||||
|
||||
## Usando `WSGIMiddleware` { #using-wsgimiddleware }
|
||||
|
||||
/// info | Información
|
||||
|
||||
Esto requiere instalar `a2wsgi`, por ejemplo con `pip install a2wsgi`.
|
||||
|
||||
///
|
||||
|
||||
Necesitas importar `WSGIMiddleware` de `a2wsgi`.
|
||||
Necesitas importar `WSGIMiddleware`.
|
||||
|
||||
Luego envuelve la aplicación WSGI (p. ej., Flask) con el middleware.
|
||||
|
||||
Y luego móntala bajo un path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Anteriormente, se recomendaba usar `WSGIMiddleware` de `fastapi.middleware.wsgi`, pero ahora está deprecado.
|
||||
|
||||
Se aconseja usar el paquete `a2wsgi` en su lugar. El uso sigue siendo el mismo.
|
||||
|
||||
Solo asegúrate de tener instalado el paquete `a2wsgi` e importar `WSGIMiddleware` correctamente desde `a2wsgi`.
|
||||
|
||||
///
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Revisa { #check-it }
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ Existen otros formatos y herramientas para definir e instalar dependencias de pa
|
||||
* Crea un archivo `main.py` con:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -156,7 +158,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -570,7 +572,7 @@ Si tienes una configuración simple, con un **contenedor único** que luego inic
|
||||
|
||||
### Imagen Base de Docker { #base-docker-image }
|
||||
|
||||
Solía haber una imagen official de Docker de FastAPI: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Pero ahora está obsoleta. ⛔️
|
||||
Solía haber una imagen official de Docker de FastAPI: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi-docker</a>. Pero ahora está obsoleta. ⛔️
|
||||
|
||||
Probablemente **no** deberías usar esta imagen base de Docker (o cualquier otra similar).
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ $ pip install "fastapi[standard]"
|
||||
Crea un archivo `main.py` con:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -181,7 +183,9 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
Si tu código usa `async` / `await`, usa `async def`:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,7 +197,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -284,7 +288,9 @@ Ahora modifica el archivo `main.py` para recibir un body desde un request `PUT`.
|
||||
|
||||
Declara el body usando tipos estándar de Python, gracias a Pydantic.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -294,7 +300,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool | None = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -303,7 +309,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
@@ -101,13 +101,13 @@ Por supuesto, también puedes declarar parámetros adicionales de query siempre
|
||||
Como, por defecto, los valores singulares se interpretan como parámetros de query, no tienes que añadir explícitamente un `Query`, solo puedes hacer:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
O en Python 3.9:
|
||||
O en Python 3.10 y superior:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
@@ -52,7 +52,7 @@ En estos casos, podría tener sentido almacenar las tags en un `Enum`.
|
||||
|
||||
Puedes añadir un `summary` y `description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
|
||||
## Descripción desde docstring { #description-from-docstring }
|
||||
|
||||
@@ -70,7 +70,7 @@ Será usado en la documentación interactiva:
|
||||
|
||||
Puedes especificar la descripción del response con el parámetro `response_description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
|
||||
@@ -1,82 +1,33 @@
|
||||
# Exécuter un serveur manuellement { #run-a-server-manually }
|
||||
# Exécuter un serveur manuellement - Uvicorn
|
||||
|
||||
## Utiliser la commande `fastapi run` { #use-the-fastapi-run-command }
|
||||
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** sur une machine serveur distante est un programme serveur ASGI tel que **Uvicorn**.
|
||||
|
||||
En bref, utilisez `fastapi run` pour servir votre application FastAPI :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
|
||||
|
||||
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
|
||||
|
||||
Searching for package file structure from directories
|
||||
with <font color="#3465A4">__init__.py</font> files
|
||||
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
|
||||
the following code:
|
||||
|
||||
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
|
||||
|
||||
Logs:
|
||||
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
|
||||
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
|
||||
to quit<b>)</b>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Cela fonctionnerait pour la plupart des cas. 😎
|
||||
|
||||
Vous pourriez utiliser cette commande par exemple pour démarrer votre application **FastAPI** dans un conteneur, sur un serveur, etc.
|
||||
|
||||
## Serveurs ASGI { #asgi-servers }
|
||||
|
||||
Allons un peu plus en détail.
|
||||
|
||||
FastAPI utilise un standard pour construire des frameworks web Python et des serveurs appelé <abbr title="Asynchronous Server Gateway Interface - Interface passerelle serveur asynchrone">ASGI</abbr>. FastAPI est un framework web ASGI.
|
||||
|
||||
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** (ou toute autre application ASGI) sur une machine serveur distante est un programme serveur ASGI comme **Uvicorn**, c'est celui utilisé par défaut par la commande `fastapi`.
|
||||
|
||||
Il existe plusieurs alternatives, notamment :
|
||||
Il existe 3 principales alternatives :
|
||||
|
||||
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a> : un serveur ASGI haute performance.
|
||||
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a> : un serveur ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a> : le serveur ASGI conçu pour Django Channels.
|
||||
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a> : un serveur HTTP Rust pour les applications Python.
|
||||
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a> : NGINX Unit est un environnement d'exécution d'applications web léger et polyvalent.
|
||||
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a> : un serveur
|
||||
ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
|
||||
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a> : le serveur ASGI
|
||||
conçu pour Django Channels.
|
||||
|
||||
## Machine serveur et programme serveur { #server-machine-and-server-program }
|
||||
## Machine serveur et programme serveur
|
||||
|
||||
Il y a un petit détail sur les noms à garder à l'esprit. 💡
|
||||
|
||||
Le mot « serveur » est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
|
||||
Le mot "**serveur**" est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
|
||||
|
||||
Gardez cela à l'esprit lorsque vous lisez « serveur » en général, cela pourrait faire référence à l'une de ces deux choses.
|
||||
Gardez cela à l'esprit lorsque vous lisez "serveur" en général, cela pourrait faire référence à l'une de ces deux choses.
|
||||
|
||||
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant normalement Linux, sur laquelle vous exécutez des programmes.
|
||||
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant Linux, en règle générale, sur laquelle vous exécutez des programmes.
|
||||
|
||||
## Installer le programme serveur { #install-the-server-program }
|
||||
|
||||
Lorsque vous installez FastAPI, il est fourni avec un serveur de production, Uvicorn, et vous pouvez le démarrer avec la commande `fastapi run`.
|
||||
## Installer le programme serveur
|
||||
|
||||
Mais vous pouvez également installer un serveur ASGI manuellement.
|
||||
Vous pouvez installer un serveur compatible ASGI avec :
|
||||
|
||||
Vous devez créer un [environnement virtuel](../virtual-environments.md){.internal-link target=_blank}, l'activer, puis vous pouvez installer l'application serveur.
|
||||
//// tab | Uvicorn
|
||||
|
||||
Par exemple, pour installer Uvicorn :
|
||||
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>, un serveur ASGI rapide comme l'éclair, basé sur uvloop et httptools.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -88,21 +39,39 @@ $ pip install "uvicorn[standard]"
|
||||
|
||||
</div>
|
||||
|
||||
Un processus similaire s'appliquerait à tout autre programme de serveur ASGI.
|
||||
|
||||
/// tip | Astuce
|
||||
|
||||
En ajoutant `standard`, Uvicorn va installer et utiliser quelques dépendances supplémentaires recommandées.
|
||||
|
||||
Cela inclut `uvloop`, le remplaçant hautes performances de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
|
||||
|
||||
Lorsque vous installez FastAPI avec quelque chose comme `pip install "fastapi[standard]"`, vous obtenez déjà `uvicorn[standard]` aussi.
|
||||
Cela inclut `uvloop`, le remplaçant performant de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
|
||||
|
||||
///
|
||||
|
||||
## Exécuter le programme serveur { #run-the-server-program }
|
||||
////
|
||||
|
||||
Si vous avez installé un serveur ASGI manuellement, vous devrez normalement passer une chaîne d'import dans un format spécial pour qu'il importe votre application FastAPI :
|
||||
//// tab | Hypercorn
|
||||
|
||||
* <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, un serveur ASGI également compatible avec HTTP/2.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install hypercorn
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
...ou tout autre serveur ASGI.
|
||||
|
||||
////
|
||||
|
||||
## Exécutez le programme serveur
|
||||
|
||||
Vous pouvez ensuite exécuter votre application de la même manière que vous l'avez fait dans les tutoriels, mais sans l'option `--reload`, par exemple :
|
||||
|
||||
//// tab | Uvicorn
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -114,44 +83,85 @@ $ uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
</div>
|
||||
|
||||
/// note | Remarque
|
||||
////
|
||||
|
||||
La commande `uvicorn main:app` fait référence à :
|
||||
//// tab | Hypercorn
|
||||
|
||||
* `main` : le fichier `main.py` (le « module » Python).
|
||||
* `app` : l'objet créé dans `main.py` avec la ligne `app = FastAPI()`.
|
||||
<div class="termy">
|
||||
|
||||
C'est équivalent à :
|
||||
```console
|
||||
$ hypercorn main:app --bind 0.0.0.0:80
|
||||
|
||||
```Python
|
||||
from main import app
|
||||
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
|
||||
```
|
||||
|
||||
///
|
||||
</div>
|
||||
|
||||
Chaque programme de serveur ASGI alternatif aurait une commande similaire, vous pouvez en lire plus dans leur documentation respective.
|
||||
////
|
||||
|
||||
/// warning | Alertes
|
||||
/// warning
|
||||
|
||||
Uvicorn et d'autres serveurs prennent en charge une option `--reload` utile pendant le développement.
|
||||
N'oubliez pas de supprimer l'option `--reload` si vous l'utilisiez.
|
||||
|
||||
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
|
||||
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
|
||||
|
||||
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
|
||||
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
|
||||
|
||||
///
|
||||
|
||||
## Concepts de déploiement { #deployment-concepts }
|
||||
## Hypercorn avec Trio
|
||||
|
||||
Ces exemples exécutent le programme serveur (par exemple Uvicorn), en démarrant **un seul processus**, à l'écoute sur toutes les IP (`0.0.0.0`) sur un port prédéfini (par exemple `80`).
|
||||
Starlette et **FastAPI** sont basés sur
|
||||
<a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, qui les rend
|
||||
compatibles avec <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a>, de la bibliothèque standard Python et
|
||||
<a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
|
||||
|
||||
C'est l'idée de base. Mais vous voudrez probablement vous occuper de certaines choses supplémentaires, comme :
|
||||
Néanmoins, Uvicorn n'est actuellement compatible qu'avec asyncio, et il utilise normalement <a href="https://github.
|
||||
com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a >, le remplaçant hautes performances de `asyncio`.
|
||||
|
||||
* Sécurité - HTTPS
|
||||
* Exécution au démarrage
|
||||
* Redémarrages
|
||||
* Réplication (le nombre de processus en cours d'exécution)
|
||||
* Mémoire
|
||||
* Étapes précédant le démarrage
|
||||
Mais si vous souhaitez utiliser directement **Trio**, vous pouvez utiliser **Hypercorn** car il le prend en charge. ✨
|
||||
|
||||
Je vous en dirai plus sur chacun de ces concepts, sur la manière d'y réfléchir, et donnerai quelques exemples concrets avec des stratégies pour les gérer dans les prochains chapitres. 🚀
|
||||
### Installer Hypercorn avec Trio
|
||||
|
||||
Vous devez d'abord installer Hypercorn avec le support Trio :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install "hypercorn[trio]"
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Exécuter avec Trio
|
||||
|
||||
Ensuite, vous pouvez passer l'option de ligne de commande `--worker-class` avec la valeur `trio` :
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ hypercorn main:app --worker-class trio
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Et cela démarrera Hypercorn avec votre application en utilisant Trio comme backend.
|
||||
|
||||
Vous pouvez désormais utiliser Trio en interne dans votre application. Ou mieux encore, vous pouvez utiliser AnyIO pour que votre code reste compatible avec Trio et asyncio. 🎉
|
||||
|
||||
## Concepts de déploiement
|
||||
|
||||
Ces exemples lancent le programme serveur (e.g. Uvicorn), démarrant **un seul processus**, sur toutes les IPs (`0.0.
|
||||
0.0`) sur un port prédéfini (par example, `80`).
|
||||
|
||||
C'est l'idée de base. Mais vous vous préoccuperez probablement de certains concepts supplémentaires, tels que ... :
|
||||
|
||||
* la sécurité - HTTPS
|
||||
* l'exécution au démarrage
|
||||
* les redémarrages
|
||||
* la réplication (le nombre de processus en cours d'exécution)
|
||||
* la mémoire
|
||||
* les étapes précédant le démarrage
|
||||
|
||||
Je vous en dirai plus sur chacun de ces concepts, sur la façon de les aborder, et donnerai quelques exemples concrets avec des stratégies pour les traiter dans les prochains chapitres. 🚀
|
||||
|
||||
@@ -79,13 +79,13 @@ checker(q="somequery")
|
||||
|
||||
### `yield`와 `scope`가 있는 의존성 { #dependencies-with-yield-and-scope }
|
||||
|
||||
0.121.0 버전에서 FastAPI는 `Depends(scope="function")` 지원을 추가했습니다.
|
||||
0.121.0 버전에서 FastAPI는 `yield`가 있는 의존성에 대해 `Depends(scope="function")` 지원을 추가했습니다.
|
||||
|
||||
`Depends(scope="function")`를 사용하면, `yield` 이후의 종료 코드는 *경로 처리 함수*가 끝난 직후(클라이언트에 응답이 반환되기 전)에 실행됩니다.
|
||||
|
||||
그리고 `Depends(scope="request")`(기본값)를 사용하면, `yield` 이후의 종료 코드는 응답이 전송된 후에 실행됩니다.
|
||||
|
||||
자세한 내용은 [`yield`가 있는 의존성 - 조기 종료와 `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 문서를 참고하세요.
|
||||
자세한 내용은 [Dependencies with `yield` - Early exit and `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 문서를 참고하세요.
|
||||
|
||||
### `yield`가 있는 의존성과 `StreamingResponse`, 기술 세부사항 { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
@@ -133,7 +133,7 @@ SQLModel(또는 SQLAlchemy)을 사용하면서 이런 특정 사용 사례가
|
||||
|
||||
그러면 세션이 데이터베이스 연결을 해제하여, 다른 요청들이 이를 사용할 수 있게 됩니다.
|
||||
|
||||
`yield`가 있는 의존성에서 조기 종료가 필요한 다른 사용 사례가 있다면, 여러분의 구체적인 사용 사례와 `yield`가 있는 의존성에 대한 조기 종료가 어떤 점에서 이득이 되는지를 포함해 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions 질문</a>을 생성해 주세요.
|
||||
`yield`가 있는 의존성에서 조기 종료가 필요한 다른 사용 사례가 있다면, 여러분의 구체적인 사용 사례와 `yield`가 있는 의존성에 대한 조기 종료가 어떤 점에서 이득이 되는지를 포함해 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussion Question</a>을 생성해 주세요.
|
||||
|
||||
`yield`가 있는 의존성에서 조기 종료에 대한 설득력 있는 사용 사례가 있다면, 조기 종료를 선택적으로 활성화할 수 있는 새로운 방법을 추가하는 것을 고려하겠습니다.
|
||||
|
||||
@@ -145,7 +145,7 @@ FastAPI 0.110.0 이전에는 `yield`가 있는 의존성을 사용한 다음 그
|
||||
|
||||
### 백그라운드 태스크와 `yield`가 있는 의존성, 기술 세부사항 { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성의 종료 코드는 응답이 전송된 *후에* 실행되었기 때문에, [예외 핸들러](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 뒤였습니다.
|
||||
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성의 종료 코드는 응답이 전송된 *후에* 실행되었기 때문에, [Exception Handlers](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}가 이미 실행된 뒤였습니다.
|
||||
|
||||
이는 주로 백그라운드 태스크 안에서 의존성이 "yield"한 동일한 객체들을 사용할 수 있게 하기 위한 설계였습니다. 백그라운드 태스크가 끝난 뒤에 종료 코드가 실행되었기 때문입니다.
|
||||
|
||||
|
||||
@@ -1,48 +1,32 @@
|
||||
# WSGI 포함하기 - Flask, Django 등 { #including-wsgi-flask-django-others }
|
||||
# WSGI 포함하기 - Flask, Django 그 외 { #including-wsgi-flask-django-others }
|
||||
|
||||
[서브 애플리케이션 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤에서](behind-a-proxy.md){.internal-link target=_blank}에서 본 것처럼 WSGI 애플리케이션을 마운트할 수 있습니다.
|
||||
[서브 응용 프로그램 - 마운트](sub-applications.md){.internal-link target=_blank}, [프록시 뒤편에서](behind-a-proxy.md){.internal-link target=_blank}에서 보았듯이 WSGI 응용 프로그램들을 마운트 할 수 있습니다.
|
||||
|
||||
이를 위해 `WSGIMiddleware`를 사용해 WSGI 애플리케이션(예: Flask, Django 등)을 감쌀 수 있습니다.
|
||||
이를 위해 `WSGIMiddleware`를 사용해 WSGI 응용 프로그램(예: Flask, Django 등)을 감쌀 수 있습니다.
|
||||
|
||||
## `WSGIMiddleware` 사용하기 { #using-wsgimiddleware }
|
||||
|
||||
/// info | 정보
|
||||
`WSGIMiddleware`를 불러와야 합니다.
|
||||
|
||||
이를 사용하려면 `a2wsgi`를 설치해야 합니다. 예: `pip install a2wsgi`
|
||||
그런 다음, WSGI(예: Flask) 응용 프로그램을 미들웨어로 포장합니다.
|
||||
|
||||
///
|
||||
그 후, 해당 경로에 마운트합니다.
|
||||
|
||||
`a2wsgi`에서 `WSGIMiddleware`를 import 해야 합니다.
|
||||
|
||||
그런 다음, WSGI(예: Flask) 애플리케이션을 미들웨어로 감쌉니다.
|
||||
|
||||
그리고 해당 경로에 마운트합니다.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | 참고
|
||||
|
||||
이전에 `fastapi.middleware.wsgi`의 `WSGIMiddleware` 사용을 권장했지만 이제는 더 이상 권장되지 않습니다.
|
||||
|
||||
대신 `a2wsgi` 패키지 사용을 권장합니다. 사용 방법은 동일합니다.
|
||||
|
||||
단, `a2wsgi` 패키지가 설치되어 있고 `a2wsgi`에서 `WSGIMiddleware`를 올바르게 import 하는지만 확인하세요.
|
||||
|
||||
///
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## 확인하기 { #check-it }
|
||||
|
||||
이제 `/v1/` 경로에 있는 모든 요청은 Flask 애플리케이션에서 처리됩니다.
|
||||
이제 `/v1/` 경로에 있는 모든 요청은 Flask 응용 프로그램에서 처리됩니다.
|
||||
|
||||
그리고 나머지는 **FastAPI**에 의해 처리됩니다.
|
||||
|
||||
실행하고 <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>로 이동하면 Flask의 응답을 볼 수 있습니다:
|
||||
실행하면 <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a>으로 이동해서 Flask의 응답을 볼 수 있습니다:
|
||||
|
||||
```txt
|
||||
Hello, World from Flask!
|
||||
```
|
||||
|
||||
그리고 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a>로 이동하면 **FastAPI**의 응답을 볼 수 있습니다:
|
||||
그리고 다음으로 이동하면 <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> **FastAPI**의 응답을 볼 수 있습니다:
|
||||
|
||||
```JSON
|
||||
{
|
||||
|
||||
@@ -145,6 +145,8 @@ Successfully installed fastapi pydantic
|
||||
* 다음 내용으로 `main.py` 파일을 만듭니다:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -156,7 +158,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -243,14 +245,14 @@ Docker 지시어 <a href="https://docs.docker.com/reference/dockerfile/#cmd" cla
|
||||
✅ **Exec** form:
|
||||
|
||||
```Dockerfile
|
||||
# ✅ 이렇게 하세요
|
||||
# ✅ Do this
|
||||
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
||||
```
|
||||
|
||||
⛔️ **Shell** form:
|
||||
|
||||
```Dockerfile
|
||||
# ⛔️ 이렇게 하지 마세요
|
||||
# ⛔️ Don't do this
|
||||
CMD fastapi run app/main.py --port 80
|
||||
```
|
||||
|
||||
@@ -454,7 +456,7 @@ Traefik은 Docker, Kubernetes 등과 통합되어 있어, 이를 사용해 컨
|
||||
|
||||
## 복제 - 프로세스 개수 { #replication-number-of-processes }
|
||||
|
||||
**Kubernetes**, Docker Swarm Mode, Nomad 등의 복잡한 시스템으로 여러 머신에 분산된 컨테이너를 관리하는 <abbr title="어떤 방식으로 연결되어 함께 동작하도록 구성된 머신의 그룹">클러스터</abbr>를 사용한다면, 각 컨테이너에서(**워커를 사용하는 Uvicorn** 같은) **프로세스 매니저**를 쓰는 대신, **클러스터 레벨**에서 **복제를 처리**하고 싶을 가능성이 큽니다.
|
||||
**Kubernetes**, Docker Swarm Mode, Nomad 등의 복잡한 시스템으로 여러 머신에 분산된 컨테이너를 관리하는 <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr>를 사용한다면, 각 컨테이너에서(**워커를 사용하는 Uvicorn** 같은) **프로세스 매니저**를 쓰는 대신, **클러스터 레벨**에서 **복제를 처리**하고 싶을 가능성이 큽니다.
|
||||
|
||||
Kubernetes 같은 분산 컨테이너 관리 시스템은 보통 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서도, **컨테이너 복제**를 처리하는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서요.
|
||||
|
||||
@@ -578,7 +580,7 @@ Kubernetes를 사용한다면, 이는 아마도 <a href="https://kubernetes.io/d
|
||||
|
||||
그리고 여러 워커가 필요하다면, `--workers` 커맨드 라인 옵션을 간단히 사용하면 됩니다.
|
||||
|
||||
/// note | 기술 세부사항
|
||||
/// note Technical Details | 기술 세부사항
|
||||
|
||||
이 Docker 이미지는 Uvicorn이 죽은 워커를 관리하고 재시작하는 기능을 지원하지 않던 시기에 만들어졌습니다. 그래서 Gunicorn과 Uvicorn을 함께 사용해야 했고, Gunicorn이 Uvicorn 워커 프로세스를 관리하고 재시작하도록 하기 위해 상당한 복잡성이 추가되었습니다.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
|
||||
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#performance).
|
||||
* **빠른 코드 작성**: 약 200%에서 300%까지 기능 개발 속도 증가. *
|
||||
* **적은 버그**: 사람(개발자)에 의한 에러 약 40% 감소. *
|
||||
* **직관적**: 훌륭한 편집기 지원. 모든 곳에서 <abbr title="다른 말로는: auto-complete, autocompletion, IntelliSense">자동완성</abbr>. 적은 디버깅 시간.
|
||||
* **직관적**: 훌륭한 편집기 지원. 모든 곳에서 <abbr title="also known as auto-complete, autocompletion, IntelliSense">자동완성</abbr>. 적은 디버깅 시간.
|
||||
* **쉬움**: 쉽게 사용하고 배우도록 설계. 적은 문서 읽기 시간.
|
||||
* **짧음**: 코드 중복 최소화. 각 매개변수 선언의 여러 기능. 적은 버그.
|
||||
* **견고함**: 준비된 프로덕션 용 코드를 얻으십시오. 자동 대화형 문서와 함께.
|
||||
@@ -127,7 +127,7 @@ FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
|
||||
웹 API 대신 터미널에서 사용할 <abbr title="Command Line Interface - 커맨드 라인 인터페이스">CLI</abbr> 앱을 만들고 있다면, <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>를 확인해 보십시오.
|
||||
웹 API 대신 터미널에서 사용할 <abbr title="Command Line Interface">CLI</abbr> 앱을 만들고 있다면, <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>를 확인해 보십시오.
|
||||
|
||||
**Typer**는 FastAPI의 동생입니다. 그리고 **CLI를 위한 FastAPI**가 되기 위해 생겼습니다. ⌨️ 🚀
|
||||
|
||||
@@ -161,6 +161,8 @@ $ pip install "fastapi[standard]"
|
||||
다음 내용으로 `main.py` 파일을 만드십시오:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -181,7 +183,9 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
여러분의 코드가 `async` / `await`을 사용한다면, `async def`를 사용하십시오:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,7 +197,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -284,7 +288,9 @@ INFO: Application startup complete.
|
||||
|
||||
Pydantic 덕분에 표준 Python 타입을 사용해 본문을 선언합니다.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -294,7 +300,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool | None = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -303,7 +309,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@@ -368,7 +374,7 @@ item: Item
|
||||
* 데이터 검증:
|
||||
* 데이터가 유효하지 않을 때 자동으로 생성하는 명확한 에러.
|
||||
* 깊이 중첩된 JSON 객체에 대한 유효성 검사.
|
||||
* 입력 데이터 <abbr title="다른 말로는: serialization, parsing, marshalling">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
|
||||
* 입력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
|
||||
* JSON.
|
||||
* 경로 매개변수.
|
||||
* 쿼리 매개변수.
|
||||
@@ -376,7 +382,7 @@ item: Item
|
||||
* 헤더.
|
||||
* 폼(Forms).
|
||||
* 파일.
|
||||
* 출력 데이터 <abbr title="다른 말로는: serialization, parsing, marshalling">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
|
||||
* 출력 데이터 <abbr title="also known as: serialization, parsing, marshalling">변환</abbr>: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
|
||||
* 파이썬 타입 변환 (`str`, `int`, `float`, `bool`, `list`, 등).
|
||||
* `datetime` 객체.
|
||||
* `UUID` 객체.
|
||||
@@ -439,7 +445,7 @@ item: Item
|
||||
|
||||
* 서로 다른 장소에서 **매개변수** 선언: **헤더**, **쿠키**, **폼 필드** 그리고 **파일**.
|
||||
* `maximum_length` 또는 `regex`처럼 **유효성 제약**하는 방법.
|
||||
* 강력하고 사용하기 쉬운 **<abbr title="다른 말로는: components, resources, providers, services, injectables">의존성 주입</abbr>** 시스템.
|
||||
* 강력하고 사용하기 쉬운 **<abbr title="also known as components, resources, providers, services, injectables">의존성 주입</abbr>** 시스템.
|
||||
* **OAuth2** 지원을 포함한 **JWT tokens** 및 **HTTP Basic**을 갖는 보안과 인증.
|
||||
* (Pydantic 덕분에) **깊은 중첩 JSON 모델**을 선언하는데 더 진보한 (하지만 마찬가지로 쉬운) 기술.
|
||||
* <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> 및 기타 라이브러리와의 **GraphQL** 통합.
|
||||
|
||||
@@ -102,16 +102,15 @@
|
||||
|
||||
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요 없이 이렇게 하면 됩니다:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
또는 Python 3.9에서는:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
또는 Python 3.10 이상에서는:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
예를 들어:
|
||||
|
||||
|
||||
@@ -52,11 +52,11 @@
|
||||
|
||||
`summary`와 `description`을 추가할 수 있습니다:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
|
||||
## 독스트링으로 만든 설명 { #description-from-docstring }
|
||||
|
||||
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 처리* 설명을 함수 <abbr title="문서화에 사용되는 함수 내부 첫 표현식의 여러 줄 문자열(어떤 변수에도 할당되지 않음)">docstring</abbr>에 선언할 수 있으며, **FastAPI**는 그곳에서 이를 읽습니다.
|
||||
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 처리* 설명을 함수 <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation – 문서화에 사용되는 함수 내부 첫 표현식의 여러 줄 문자열(어떤 변수에도 할당되지 않음)">docstring</abbr>에 선언할 수 있으며, **FastAPI**는 그곳에서 이를 읽습니다.
|
||||
|
||||
독스트링에는 <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a>을 작성할 수 있으며, (독스트링의 들여쓰기를 고려하여) 올바르게 해석되고 표시됩니다.
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
`response_description` 매개변수로 응답에 관한 설명을 명시할 수 있습니다:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
|
||||
|
||||
/// info | 정보
|
||||
|
||||
@@ -90,7 +90,7 @@ OpenAPI는 각 *경로 처리*가 응답에 관한 설명을 요구할 것을
|
||||
|
||||
## *경로 처리* 지원중단하기 { #deprecate-a-path-operation }
|
||||
|
||||
*경로 처리*를 제거하지 않고 <abbr title="구식이며 사용하지 않는 것이 권장됨">deprecated</abbr>로 표시해야 한다면, `deprecated` 매개변수를 전달하면 됩니다:
|
||||
*경로 처리*를 제거하지 않고 <abbr title="obsolete, recommended not to use it – 구식이며 사용하지 않는 것이 권장됨">deprecated</abbr>로 표시해야 한다면, `deprecated` 매개변수를 전달하면 됩니다:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ Antes do FastAPI 0.106.0, lançar exceções após o `yield` não era possível,
|
||||
|
||||
Isso foi projetado assim principalmente para permitir o uso dos mesmos objetos "yielded" por dependências dentro de tarefas em segundo plano, porque o código de saída seria executado depois que as tarefas em segundo plano fossem concluídas.
|
||||
|
||||
Isso foi alterado no FastAPI 0.106.0 com a intenção de não manter recursos enquanto se espera a response percorrer a rede.
|
||||
Isso foi alterado no FastAPI 0.106.0 com a intenção de não manter recursos enquanto se espera a resposta percorrer a rede.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
|
||||
@@ -6,29 +6,13 @@ Para isso, você pode utilizar o `WSGIMiddleware` para encapsular a sua aplicaç
|
||||
|
||||
## Usando `WSGIMiddleware` { #using-wsgimiddleware }
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Isso requer instalar `a2wsgi`, por exemplo com `pip install a2wsgi`.
|
||||
|
||||
///
|
||||
|
||||
Você precisa importar o `WSGIMiddleware` de `a2wsgi`.
|
||||
Você precisa importar o `WSGIMiddleware`.
|
||||
|
||||
Em seguida, encapsule a aplicação WSGI (e.g. Flask) com o middleware.
|
||||
|
||||
E então monte isso sob um path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Anteriormente, recomendava-se usar `WSGIMiddleware` de `fastapi.middleware.wsgi`, mas agora está descontinuado.
|
||||
|
||||
É aconselhável usar o pacote `a2wsgi` em seu lugar. O uso permanece o mesmo.
|
||||
|
||||
Apenas certifique-se de que o pacote `a2wsgi` está instalado e importe `WSGIMiddleware` corretamente de `a2wsgi`.
|
||||
|
||||
///
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Confira { #check-it }
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ Há outros formatos e ferramentas para definir e instalar dependências de pacot
|
||||
* Crie um arquivo `main.py` com:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -156,7 +158,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -570,7 +572,7 @@ Se você tiver uma configuração simples, com um **único contêiner** que ent
|
||||
|
||||
### Imagem Docker base { #base-docker-image }
|
||||
|
||||
Antes havia uma imagem oficial do FastAPI para Docker: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi-docker</a>. Mas agora ela está descontinuada. ⛔️
|
||||
Antes havia uma imagem oficial do FastAPI para Docker: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Mas agora ela está descontinuada. ⛔️
|
||||
|
||||
Você provavelmente **não** deve usar essa imagem base do Docker (ou qualquer outra semelhante).
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
---
|
||||
|
||||
FastAPI é um moderno e rápido (alta performance) framework web para construção de APIs com Python, baseado nos type hints padrões do Python.
|
||||
FastAPI é um moderno e rápido (alta performance) _framework web_ para construção de APIs com Python, baseado nos _type hints_ padrões do Python.
|
||||
|
||||
Os recursos chave são:
|
||||
|
||||
@@ -161,6 +161,8 @@ $ pip install "fastapi[standard]"
|
||||
Crie um arquivo `main.py` com:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -181,7 +183,9 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
Se seu código utiliza `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,7 +197,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -284,7 +288,9 @@ Agora modifique o arquivo `main.py` para receber um corpo de uma requisição `P
|
||||
|
||||
Declare o corpo utilizando tipos padrão Python, graças ao Pydantic.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -294,7 +300,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool | None = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -303,7 +309,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@@ -405,7 +411,7 @@ Voltando ao código do exemplo anterior, **FastAPI** irá:
|
||||
* Documentar tudo com OpenAPI, que poderá ser usado por:
|
||||
* Sistemas de documentação interativos.
|
||||
* Sistemas de clientes de geração de código automáticos, para muitas linguagens.
|
||||
* Fornecer diretamente 2 interfaces web de documentação interativa.
|
||||
* Fornecer diretamente 2 interfaces _web_ de documentação interativa.
|
||||
|
||||
---
|
||||
|
||||
@@ -504,7 +510,7 @@ Siga os tutoriais do seu provedor de nuvem para implantar aplicações FastAPI c
|
||||
|
||||
## Performance { #performance }
|
||||
|
||||
Testes de performance da Independent TechEmpower mostram aplicações **FastAPI** rodando sob Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">um dos frameworks Python mais rápidos disponíveis</a>, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*)
|
||||
Testes de performance da _Independent TechEmpower_ mostram aplicações **FastAPI** rodando sob Uvicorn como <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">um dos _frameworks_ Python mais rápidos disponíveis</a>, somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*)
|
||||
|
||||
Para entender mais sobre isso, veja a seção <a href="https://fastapi.tiangolo.com/pt/benchmarks/" class="internal-link" target="_blank">Comparações</a>.
|
||||
|
||||
@@ -524,7 +530,7 @@ Utilizado pelo Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Obrigatório caso você queira utilizar o `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Obrigatório se você quer utilizar a configuração padrão de templates.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obrigatório se você deseja suporte a <abbr title="convertendo a string que vem de uma requisição HTTP em dados Python">"parsing"</abbr> de formulário, com `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Obrigatório se você deseja suporte a <abbr title="converting the string that comes from an HTTP request into Python data - convertendo a string que vem de uma requisição HTTP em dados Python">"parsing"</abbr> de formulário, com `request.form()`.
|
||||
|
||||
Utilizado pelo FastAPI:
|
||||
|
||||
|
||||
@@ -101,13 +101,13 @@ Obviamente, você também pode declarar parâmetros de consulta assim que você
|
||||
Dado que, por padrão, valores singulares são interpretados como parâmetros de consulta, você não precisa explicitamente adicionar uma `Query`, você pode somente:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Ou em Python 3.9:
|
||||
Ou como em Python 3.10 e versões superiores:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Por exemplo:
|
||||
|
||||
@@ -52,7 +52,7 @@ Nestes casos, pode fazer sentido armazenar as tags em um `Enum`.
|
||||
|
||||
Você pode adicionar um `summary` e uma `description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
|
||||
## Descrição do docstring { #description-from-docstring }
|
||||
|
||||
@@ -70,7 +70,7 @@ Ela será usada nas documentações interativas:
|
||||
|
||||
Você pode especificar a descrição da resposta com o parâmetro `response_description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
|
||||
|
||||
/// info | Informação
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
…и передаст возвращённое значение как значение зависимости в нашу *функцию-обработчике пути* в параметр `fixed_content_included`:
|
||||
…и передаст возвращённое значение как значение зависимости в параметр `fixed_content_included` нашей *функции-обработчика пути*:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
|
||||
|
||||
@@ -6,13 +6,29 @@
|
||||
|
||||
## Использование `WSGIMiddleware` { #using-wsgimiddleware }
|
||||
|
||||
Нужно импортировать `WSGIMiddleware`.
|
||||
/// info | Информация
|
||||
|
||||
Для этого требуется установить `a2wsgi`, например с помощью `pip install a2wsgi`.
|
||||
|
||||
///
|
||||
|
||||
Нужно импортировать `WSGIMiddleware` из `a2wsgi`.
|
||||
|
||||
Затем оберните WSGI‑приложение (например, Flask) в middleware (Промежуточный слой).
|
||||
|
||||
После этого смонтируйте его на путь.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
Ранее рекомендовалось использовать `WSGIMiddleware` из `fastapi.middleware.wsgi`, но теперь он помечен как устаревший.
|
||||
|
||||
Вместо него рекомендуется использовать пакет `a2wsgi`. Использование остаётся таким же.
|
||||
|
||||
Просто убедитесь, что пакет `a2wsgi` установлен, и импортируйте `WSGIMiddleware` из `a2wsgi`.
|
||||
|
||||
///
|
||||
|
||||
## Проверьте { #check-it }
|
||||
|
||||
|
||||
@@ -145,8 +145,6 @@ Successfully installed fastapi pydantic
|
||||
* Создайте файл `main.py` со следующим содержимым:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -158,7 +156,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
|
||||
@@ -161,8 +161,6 @@ $ pip install "fastapi[standard]"
|
||||
Создайте файл `main.py` со следующим содержимым:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -174,7 +172,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -183,9 +181,7 @@ def read_item(item_id: int, q: Union[str, None] = None):
|
||||
|
||||
Если ваш код использует `async` / `await`, используйте `async def`:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -197,7 +193,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -288,9 +284,7 @@ INFO: Application startup complete.
|
||||
|
||||
Объявите тело запроса, используя стандартные типы Python, спасибо Pydantic.
|
||||
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -300,7 +294,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: Union[bool, None] = None
|
||||
is_offer: bool | None = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -309,7 +303,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
@@ -101,13 +101,13 @@
|
||||
Поскольку по умолчанию, отдельные значения интерпретируются как query-параметры, вам не нужно явно добавлять `Query`, вы можете просто сделать так:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Или в Python 3.10 и выше:
|
||||
Или в Python 3.9:
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
Например:
|
||||
@@ -116,7 +116,7 @@ q: str | None = None
|
||||
|
||||
/// info | Информация
|
||||
|
||||
`Body` также имеет все те же дополнительные параметры валидации и метаданных, как у `Query`,`Path` и других, которые вы увидите позже.
|
||||
`Body` также имеет все те же дополнительные параметры валидации и метаданных, как у `Query`, `Path` и других, которые вы увидите позже.
|
||||
|
||||
///
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
Вы можете добавить параметры `summary` и `description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[18:19] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
|
||||
|
||||
## Описание из строк документации { #description-from-docstring }
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
Вы можете указать описание ответа с помощью параметра `response_description`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[19] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
///
|
||||
|
||||
/// check
|
||||
/// check | Проверка
|
||||
|
||||
OpenAPI указывает, что каждой *операции пути* необходимо описание ответа.
|
||||
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
# WSGI'yi Dahil Etme - Flask, Django ve Diğerleri { #including-wsgi-flask-django-others }
|
||||
|
||||
WSGI uygulamalarını [Alt Uygulamalar - Mount Etme](sub-applications.md){.internal-link target=_blank}, [Bir Proxy Arkasında](behind-a-proxy.md){.internal-link target=_blank} bölümlerinde gördüğünüz gibi mount edebilirsiniz.
|
||||
WSGI uygulamalarını [Sub Applications - Mounts](sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](behind-a-proxy.md){.internal-link target=_blank} bölümlerinde gördüğünüz gibi mount edebilirsiniz.
|
||||
|
||||
Bunun için `WSGIMiddleware`'ı kullanabilir ve bunu WSGI uygulamanızı (örneğin Flask, Django vb.) sarmalamak için kullanabilirsiniz.
|
||||
|
||||
## `WSGIMiddleware` Kullanımı { #using-wsgimiddleware }
|
||||
|
||||
/// info | Bilgi
|
||||
|
||||
Bunun için `a2wsgi` kurulmalıdır; örneğin `pip install a2wsgi` ile.
|
||||
|
||||
///
|
||||
|
||||
`WSGIMiddleware`'ı `a2wsgi` paketinden import etmeniz gerekir.
|
||||
`WSGIMiddleware`'ı import etmeniz gerekir.
|
||||
|
||||
Ardından WSGI (örn. Flask) uygulamasını middleware ile sarmalayın.
|
||||
|
||||
Ve sonra bunu bir path'in altına mount edin.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
|
||||
/// note | Not
|
||||
|
||||
Önceden, `fastapi.middleware.wsgi` içindeki `WSGIMiddleware`'ın kullanılması öneriliyordu, ancak artık kullanımdan kaldırıldı.
|
||||
|
||||
Bunun yerine `a2wsgi` paketini kullanmanız önerilir. Kullanım aynıdır.
|
||||
|
||||
Sadece `a2wsgi` paketinin kurulu olduğundan emin olun ve `WSGIMiddleware`'ı `a2wsgi` içinden doğru şekilde import edin.
|
||||
|
||||
///
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Kontrol Edelim { #check-it }
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ $ pip install "fastapi[standard]"
|
||||
Şu içerikle `main.py` adında bir dosya oluşturalım:
|
||||
|
||||
```Python
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -181,7 +183,9 @@ def read_item(item_id: int, q: str | None = None):
|
||||
|
||||
Eğer kodunuz `async` / `await` kullanıyorsa, `async def` kullanın:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -193,7 +197,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str | None = None):
|
||||
async def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -284,7 +288,9 @@ Alternatif otomatik dokümantasyonu göreceksiniz (<a href="https://github.com/R
|
||||
|
||||
Body'yi Pydantic sayesinde standart Python tiplerini kullanarak tanımlayalım.
|
||||
|
||||
```Python hl_lines="2 7-10 23-25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -294,7 +300,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool | None = None
|
||||
is_offer: Union[bool, None] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -303,7 +309,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str | None = None):
|
||||
def read_item(item_id: int, q: Union[str, None] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.128.2"
|
||||
__version__ = "0.128.1"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ from fastapi.logger import logger
|
||||
from fastapi.security.oauth2 import SecurityScopes
|
||||
from fastapi.types import DependencyCacheKey
|
||||
from fastapi.utils import create_model_field, get_path_param_names
|
||||
from pydantic import BaseModel, Json
|
||||
from pydantic import BaseModel
|
||||
from pydantic.fields import FieldInfo
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
@@ -66,7 +66,6 @@ from starlette.requests import HTTPConnection, Request
|
||||
from starlette.responses import Response
|
||||
from starlette.websockets import WebSocket
|
||||
from typing_extensions import Literal, get_args, get_origin
|
||||
from typing_inspection.typing_objects import is_typealiastype
|
||||
|
||||
multipart_not_installed_error = (
|
||||
'Form data requires "python-multipart" to be installed. \n'
|
||||
@@ -371,9 +370,6 @@ def analyze_param(
|
||||
depends = None
|
||||
type_annotation: Any = Any
|
||||
use_annotation: Any = Any
|
||||
if is_typealiastype(annotation):
|
||||
# unpack in case PEP 695 type syntax is used
|
||||
annotation = annotation.__value__
|
||||
if annotation is not inspect.Signature.empty:
|
||||
use_annotation = annotation
|
||||
type_annotation = annotation
|
||||
@@ -453,9 +449,7 @@ def analyze_param(
|
||||
depends = dataclasses.replace(depends, dependency=type_annotation)
|
||||
|
||||
# Handle non-param type annotations like Request
|
||||
# Only apply special handling when there's no explicit Depends - if there's a Depends,
|
||||
# the dependency will be called and its return value used instead of the special injection
|
||||
if depends is None and lenient_issubclass(
|
||||
if lenient_issubclass(
|
||||
type_annotation,
|
||||
(
|
||||
Request,
|
||||
@@ -466,6 +460,7 @@ def analyze_param(
|
||||
SecurityScopes,
|
||||
),
|
||||
):
|
||||
assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
|
||||
assert field_info is None, (
|
||||
f"Cannot specify FastAPI annotation for type {type_annotation!r}"
|
||||
)
|
||||
@@ -726,19 +721,11 @@ def _validate_value_with_model_field(
|
||||
return v_, []
|
||||
|
||||
|
||||
def _is_json_field(field: ModelField) -> bool:
|
||||
return any(type(item) is Json for item in field.field_info.metadata)
|
||||
|
||||
|
||||
def _get_multidict_value(
|
||||
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
|
||||
) -> Any:
|
||||
alias = alias or get_validation_alias(field)
|
||||
if (
|
||||
(not _is_json_field(field))
|
||||
and is_sequence_field(field)
|
||||
and isinstance(values, (ImmutableMultiDict, Headers))
|
||||
):
|
||||
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
|
||||
value = values.getlist(alias)
|
||||
else:
|
||||
value = values.get(alias, None)
|
||||
|
||||
@@ -46,7 +46,6 @@ dependencies = [
|
||||
"starlette>=0.40.0,<0.51.0",
|
||||
"pydantic>=2.7.0",
|
||||
"typing-extensions>=4.8.0",
|
||||
"typing-inspection>=0.4.2",
|
||||
"annotated-doc>=0.0.2",
|
||||
]
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ SUPPORTED_LANGS = {
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"fr",
|
||||
# "fr",
|
||||
"ja",
|
||||
"ko",
|
||||
"pt",
|
||||
@@ -31,7 +31,7 @@ SUPPORTED_LANGS = {
|
||||
"tr",
|
||||
"uk",
|
||||
# "zh",
|
||||
"zh-hant",
|
||||
# "zh-hant",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -347,12 +347,9 @@ def list_outdated(language: str) -> list[Path]:
|
||||
|
||||
|
||||
@app.command()
|
||||
def update_outdated(
|
||||
language: Annotated[str, typer.Option(envvar="LANGUAGE")],
|
||||
max: Annotated[int, typer.Option(envvar="MAX")] = 10,
|
||||
) -> None:
|
||||
def update_outdated(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
|
||||
outdated_paths = list_outdated(language)
|
||||
for path in outdated_paths[:max]:
|
||||
for path in outdated_paths:
|
||||
print(f"Updating lang: {language} path: {path}")
|
||||
translate_page(language=language, en_path=path)
|
||||
print(f"Done updating: {path}")
|
||||
@@ -360,12 +357,9 @@ def update_outdated(
|
||||
|
||||
|
||||
@app.command()
|
||||
def add_missing(
|
||||
language: Annotated[str, typer.Option(envvar="LANGUAGE")],
|
||||
max: Annotated[int, typer.Option(envvar="MAX")] = 10,
|
||||
) -> None:
|
||||
def add_missing(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
|
||||
missing_paths = list_missing(language)
|
||||
for path in missing_paths[:max]:
|
||||
for path in missing_paths:
|
||||
print(f"Adding lang: {language} path: {path}")
|
||||
translate_page(language=language, en_path=path)
|
||||
print(f"Done adding: {path}")
|
||||
@@ -373,14 +367,11 @@ def add_missing(
|
||||
|
||||
|
||||
@app.command()
|
||||
def update_and_add(
|
||||
language: Annotated[str, typer.Option(envvar="LANGUAGE")],
|
||||
max: Annotated[int, typer.Option(envvar="MAX")] = 10,
|
||||
) -> None:
|
||||
def update_and_add(language: Annotated[str, typer.Option(envvar="LANGUAGE")]) -> None:
|
||||
print(f"Updating outdated translations for {language}")
|
||||
update_outdated(language=language, max=max)
|
||||
update_outdated(language=language)
|
||||
print(f"Adding missing translations for {language}")
|
||||
add_missing(language=language, max=max)
|
||||
add_missing(language=language)
|
||||
print(f"Done updating and adding for {language}")
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from typing_extensions import TypeAliasType
|
||||
|
||||
|
||||
async def some_value() -> int:
|
||||
return 123
|
||||
|
||||
|
||||
DependedValue = TypeAliasType(
|
||||
"DependedValue", Annotated[int, Depends(some_value)], type_params=()
|
||||
)
|
||||
|
||||
|
||||
def test_pep695_type_dependencies():
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/")
|
||||
async def get_with_dep(value: DependedValue) -> str: # noqa
|
||||
return f"value: {value}"
|
||||
|
||||
client = TestClient(app)
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.text == '"value: 123"'
|
||||
@@ -1,63 +0,0 @@
|
||||
import json
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Cookie, FastAPI, Form, Header, Query
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import Json
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/form-json-list")
|
||||
def form_json_list(items: Annotated[Json[list[str]], Form()]) -> list[str]:
|
||||
return items
|
||||
|
||||
|
||||
@app.get("/query-json-list")
|
||||
def query_json_list(items: Annotated[Json[list[str]], Query()]) -> list[str]:
|
||||
return items
|
||||
|
||||
|
||||
@app.get("/header-json-list")
|
||||
def header_json_list(x_items: Annotated[Json[list[str]], Header()]) -> list[str]:
|
||||
return x_items
|
||||
|
||||
|
||||
@app.get("/cookie-json-list")
|
||||
def cookie_json_list(items: Annotated[Json[list[str]], Cookie()]) -> list[str]:
|
||||
return items
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_form_json_list():
|
||||
response = client.post(
|
||||
"/form-json-list", data={"items": json.dumps(["abc", "def"])}
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == ["abc", "def"]
|
||||
|
||||
|
||||
def test_query_json_list():
|
||||
response = client.get(
|
||||
"/query-json-list", params={"items": json.dumps(["abc", "def"])}
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == ["abc", "def"]
|
||||
|
||||
|
||||
def test_header_json_list():
|
||||
response = client.get(
|
||||
"/header-json-list", headers={"x-items": json.dumps(["abc", "def"])}
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == ["abc", "def"]
|
||||
|
||||
|
||||
def test_cookie_json_list():
|
||||
client.cookies.set("items", json.dumps(["abc", "def"]))
|
||||
response = client.get("/cookie-json-list")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == ["abc", "def"]
|
||||
client.cookies.clear()
|
||||
@@ -1,173 +0,0 @@
|
||||
"""Test using special types (Response, Request, BackgroundTasks) as dependency annotations.
|
||||
|
||||
These tests verify that special FastAPI types can be used with Depends() annotations
|
||||
and that the dependency injection system properly handles them.
|
||||
"""
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import BackgroundTasks, Depends, FastAPI, Request, Response
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_response_with_depends_annotated():
|
||||
"""Response type hint should work with Annotated[Response, Depends(...)]."""
|
||||
app = FastAPI()
|
||||
|
||||
def modify_response(response: Response) -> Response:
|
||||
response.headers["X-Custom"] = "modified"
|
||||
return response
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(response: Annotated[Response, Depends(modify_response)]):
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"status": "ok"}
|
||||
assert resp.headers.get("X-Custom") == "modified"
|
||||
|
||||
|
||||
def test_response_with_depends_default():
|
||||
"""Response type hint should work with Response = Depends(...)."""
|
||||
app = FastAPI()
|
||||
|
||||
def modify_response(response: Response) -> Response:
|
||||
response.headers["X-Custom"] = "modified"
|
||||
return response
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(response: Response = Depends(modify_response)):
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"status": "ok"}
|
||||
assert resp.headers.get("X-Custom") == "modified"
|
||||
|
||||
|
||||
def test_response_without_depends():
|
||||
"""Regular Response injection should still work."""
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(response: Response):
|
||||
response.headers["X-Direct"] = "set"
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"status": "ok"}
|
||||
assert resp.headers.get("X-Direct") == "set"
|
||||
|
||||
|
||||
def test_response_dependency_chain():
|
||||
"""Response dependency should work in a chain of dependencies."""
|
||||
app = FastAPI()
|
||||
|
||||
def first_modifier(response: Response) -> Response:
|
||||
response.headers["X-First"] = "1"
|
||||
return response
|
||||
|
||||
def second_modifier(
|
||||
response: Annotated[Response, Depends(first_modifier)],
|
||||
) -> Response:
|
||||
response.headers["X-Second"] = "2"
|
||||
return response
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(response: Annotated[Response, Depends(second_modifier)]):
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.headers.get("X-First") == "1"
|
||||
assert resp.headers.get("X-Second") == "2"
|
||||
|
||||
|
||||
def test_response_dependency_returns_different_response_instance():
|
||||
"""Dependency that returns a different Response instance should work.
|
||||
|
||||
When a dependency returns a new Response object (e.g., JSONResponse) instead
|
||||
of modifying the injected one, the returned response should be used and any
|
||||
modifications to it in the endpoint should be preserved.
|
||||
"""
|
||||
app = FastAPI()
|
||||
|
||||
def default_response() -> Response:
|
||||
response = JSONResponse(content={"status": "ok"})
|
||||
response.headers["X-Custom"] = "initial"
|
||||
return response
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(response: Annotated[Response, Depends(default_response)]):
|
||||
response.headers["X-Custom"] = "modified"
|
||||
return response
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"status": "ok"}
|
||||
assert resp.headers.get("X-Custom") == "modified"
|
||||
|
||||
|
||||
# Tests for Request type hint with Depends
|
||||
def test_request_with_depends_annotated():
|
||||
"""Request type hint should work in dependency chain."""
|
||||
app = FastAPI()
|
||||
|
||||
def extract_request_info(request: Request) -> dict:
|
||||
return {
|
||||
"path": request.url.path,
|
||||
"user_agent": request.headers.get("user-agent", "unknown"),
|
||||
}
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(
|
||||
info: Annotated[dict, Depends(extract_request_info)],
|
||||
):
|
||||
return info
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/", headers={"user-agent": "test-agent"})
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"path": "/", "user_agent": "test-agent"}
|
||||
|
||||
|
||||
# Tests for BackgroundTasks type hint with Depends
|
||||
def test_background_tasks_with_depends_annotated():
|
||||
"""BackgroundTasks type hint should work with Annotated[BackgroundTasks, Depends(...)]."""
|
||||
app = FastAPI()
|
||||
task_results = []
|
||||
|
||||
def background_task(message: str):
|
||||
task_results.append(message)
|
||||
|
||||
def add_background_task(background_tasks: BackgroundTasks) -> BackgroundTasks:
|
||||
background_tasks.add_task(background_task, "from dependency")
|
||||
return background_tasks
|
||||
|
||||
@app.get("/")
|
||||
def endpoint(
|
||||
background_tasks: Annotated[BackgroundTasks, Depends(add_background_task)],
|
||||
):
|
||||
background_tasks.add_task(background_task, "from endpoint")
|
||||
return {"status": "ok"}
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert "from dependency" in task_results
|
||||
assert "from endpoint" in task_results
|
||||
2
uv.lock
generated
2
uv.lock
generated
@@ -1021,7 +1021,6 @@ dependencies = [
|
||||
{ name = "starlette", version = "0.49.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "starlette", version = "0.50.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -1203,7 +1202,6 @@ requires-dist = [
|
||||
{ name = "pyyaml", marker = "extra == 'all'", specifier = ">=5.3.1" },
|
||||
{ name = "starlette", specifier = ">=0.40.0,<0.51.0" },
|
||||
{ name = "typing-extensions", specifier = ">=4.8.0" },
|
||||
{ name = "typing-inspection", specifier = ">=0.4.2" },
|
||||
{ name = "ujson", marker = "extra == 'all'", specifier = ">=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0" },
|
||||
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.12.0" },
|
||||
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.12.0" },
|
||||
|
||||
Reference in New Issue
Block a user