@@ -461,7 +461,7 @@ Utilisées par Starlette :
Utilisées par FastAPI / Starlette :
-*
uvicorn - Pour le serveur qui charge et sert votre application.
+*
uvicorn - Pour le serveur qui charge et sert votre application.
*
orjson - Obligatoire si vous voulez utiliser `ORJSONResponse`.
*
ujson - Obligatoire si vous souhaitez utiliser `UJSONResponse`.
diff --git a/docs/fr/docs/tutorial/background-tasks.md b/docs/fr/docs/tutorial/background-tasks.md
index 2065ca58e..6efd16e07 100644
--- a/docs/fr/docs/tutorial/background-tasks.md
+++ b/docs/fr/docs/tutorial/background-tasks.md
@@ -61,7 +61,7 @@ Et ensuite une autre tâche d'arrière-plan (générée dans les paramètres de
## Détails techniques
-La classe `BackgroundTasks` provient directement de
`starlette.background`.
+La classe `BackgroundTasks` provient directement de
`starlette.background`.
Elle est importée/incluse directement dans **FastAPI** pour que vous puissiez l'importer depuis `fastapi` et éviter d'importer accidentellement `BackgroundTask` (sans `s` à la fin) depuis `starlette.background`.
@@ -69,7 +69,7 @@ En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possi
Il est tout de même possible d'utiliser `BackgroundTask` seul dans **FastAPI**, mais dans ce cas il faut créer l'objet dans le code et renvoyer une `Response` Starlette l'incluant.
-Plus de détails sont disponibles dans
la documentation officielle de Starlette sur les tâches d'arrière-plan (via leurs classes `BackgroundTasks`et `BackgroundTask`).
+Plus de détails sont disponibles dans
la documentation officielle de Starlette sur les tâches d'arrière-plan (via leurs classes `BackgroundTasks`et `BackgroundTask`).
## Avertissement
diff --git a/docs/fr/docs/tutorial/first-steps.md b/docs/fr/docs/tutorial/first-steps.md
index 758145362..96ea56e62 100644
--- a/docs/fr/docs/tutorial/first-steps.md
+++ b/docs/fr/docs/tutorial/first-steps.md
@@ -140,7 +140,7 @@ Vous pourriez aussi l'utiliser pour générer du code automatiquement, pour les
`FastAPI` est une classe héritant directement de `Starlette`.
-Vous pouvez donc aussi utiliser toutes les fonctionnalités de
Starlette depuis `FastAPI`.
+Vous pouvez donc aussi utiliser toutes les fonctionnalités de
Starlette depuis `FastAPI`.
///
diff --git a/docs/ja/docs/advanced/websockets.md b/docs/ja/docs/advanced/websockets.md
index 43009eba8..2517530ab 100644
--- a/docs/ja/docs/advanced/websockets.md
+++ b/docs/ja/docs/advanced/websockets.md
@@ -184,5 +184,5 @@ Client #1596980209979 left the chat
オプションの詳細については、Starletteのドキュメントを確認してください。
-*
`WebSocket` クラス
-*
クラスベースのWebSocket処理
+*
`WebSocket` クラス
+*
クラスベースのWebSocket処理
diff --git a/docs/ja/docs/alternatives.md b/docs/ja/docs/alternatives.md
index 8129a7002..9f5152c08 100644
--- a/docs/ja/docs/alternatives.md
+++ b/docs/ja/docs/alternatives.md
@@ -419,7 +419,7 @@ Marshmallowに匹敵しますが、ベンチマークではMarshmallowよりも
///
-###
Starlette
+###
Starlette
Starletteは、軽量な
ASGIフレームワーク/ツールキットで、高性能な非同期サービスの構築に最適です。
@@ -465,7 +465,7 @@ webに関するコアな部分を全て扱います。その上に機能を追
///
-###
Uvicorn
+###
Uvicorn
Uvicornは非常に高速なASGIサーバーで、uvloopとhttptoolsにより構成されています。
diff --git a/docs/ja/docs/deployment/manually.md b/docs/ja/docs/deployment/manually.md
index 4ea6bd8ff..da382a9c5 100644
--- a/docs/ja/docs/deployment/manually.md
+++ b/docs/ja/docs/deployment/manually.md
@@ -6,7 +6,7 @@
//// tab | Uvicorn
-*
Uvicorn, uvloopとhttptoolsを基にした高速なASGIサーバ。
+*
Uvicorn, uvloopとhttptoolsを基にした高速なASGIサーバ。
@@ -78,7 +78,7 @@ Running on 0.0.0.0:8080 over http (CTRL + C to quit)
停止した場合に自動的に再起動させるツールを設定したいかもしれません。
-さらに、
Gunicornをインストールして
Uvicornのマネージャーとして使用したり、複数のワーカーでHypercornを使用したいかもしれません。
+さらに、
Gunicornをインストールして
Uvicornのマネージャーとして使用したり、複数のワーカーでHypercornを使用したいかもしれません。
ワーカー数などの微調整も行いたいかもしれません。
diff --git a/docs/ja/docs/features.md b/docs/ja/docs/features.md
index 4024590cf..f78eab430 100644
--- a/docs/ja/docs/features.md
+++ b/docs/ja/docs/features.md
@@ -160,7 +160,7 @@ FastAPIには非常に使いやすく、非常に強力な
してカスタムの独自ヘッダーを追加できます。
-ただし、ブラウザのクライアントに表示させたいカスタムヘッダーがある場合は、StarletteのCORSドキュメントに記載されているパラメータ `expose_headers` を使用して、それらをCORS設定に追加する必要があります ([CORS (オリジン間リソース共有)](cors.md){.internal-link target=_blank})
+ただし、ブラウザのクライアントに表示させたいカスタムヘッダーがある場合は、StarletteのCORSドキュメントに記載されているパラメータ `expose_headers` を使用して、それらをCORS設定に追加する必要があります ([CORS (オリジン間リソース共有)](cors.md){.internal-link target=_blank})
///
diff --git a/docs/ja/docs/tutorial/static-files.md b/docs/ja/docs/tutorial/static-files.md
index f63f3f3b1..f910d7e36 100644
--- a/docs/ja/docs/tutorial/static-files.md
+++ b/docs/ja/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@
## より詳しい情報
-詳細とオプションについては、Starletteの静的ファイルに関するドキュメントを確認してください。
+詳細とオプションについては、Starletteの静的ファイルに関するドキュメントを確認してください。
diff --git a/docs/ja/docs/tutorial/testing.md b/docs/ja/docs/tutorial/testing.md
index fe6c8c6b4..4e8ad4f7c 100644
--- a/docs/ja/docs/tutorial/testing.md
+++ b/docs/ja/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# テスト
-Starlette のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
+Starlette のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
HTTPX がベースなので、非常に使いやすく直感的です。
diff --git a/docs/ko/docs/advanced/events.md b/docs/ko/docs/advanced/events.md
index 5f8fe0f1e..4318ada54 100644
--- a/docs/ko/docs/advanced/events.md
+++ b/docs/ko/docs/advanced/events.md
@@ -154,7 +154,7 @@ ASGI 기술 사양에 따르면, 이는 Starlette의 Lifespan 문서에서 확인할 수 있습니다.
+Starlette의 `lifespan` 핸들러에 대해 더 읽고 싶다면 Starlette의 Lifespan 문서에서 확인할 수 있습니다.
이 문서에는 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
diff --git a/docs/ko/docs/advanced/middlewares.md b/docs/ko/docs/advanced/middlewares.md
index c00aedeaf..5778528a8 100644
--- a/docs/ko/docs/advanced/middlewares.md
+++ b/docs/ko/docs/advanced/middlewares.md
@@ -93,4 +93,4 @@ HTTP 호스트 헤더 공격을 방지하기 위해 모든 수신 요청에 올
유비콘의 `ProxyHeadersMiddleware`>
MessagePack
-사용 가능한 다른 미들웨어를 확인하려면 스타렛의 미들웨어 문서 및 ASGI Awesome List를 참조하세요.
+사용 가능한 다른 미들웨어를 확인하려면 스타렛의 미들웨어 문서 및 ASGI Awesome List를 참조하세요.
diff --git a/docs/ko/docs/advanced/response-cookies.md b/docs/ko/docs/advanced/response-cookies.md
index 327f20afe..50da713fe 100644
--- a/docs/ko/docs/advanced/response-cookies.md
+++ b/docs/ko/docs/advanced/response-cookies.md
@@ -46,4 +46,4 @@
///
-사용 가능한 모든 매개변수와 옵션은 Starlette 문서에서 확인할 수 있습니다.
+사용 가능한 모든 매개변수와 옵션은 Starlette 문서에서 확인할 수 있습니다.
diff --git a/docs/ko/docs/advanced/response-headers.md b/docs/ko/docs/advanced/response-headers.md
index e8abe0be2..e4e022c9b 100644
--- a/docs/ko/docs/advanced/response-headers.md
+++ b/docs/ko/docs/advanced/response-headers.md
@@ -38,4 +38,4 @@
‘X-’ 접두어를 사용하여 커스텀 사설 헤더를 추가할 수 있습니다.
-하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). `expose_headers` 매개변수를 사용하여 Starlette의 CORS 설명서에 문서화된 대로 설정할 수 있습니다.
+하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}에서 자세히 알아보세요). `expose_headers` 매개변수를 사용하여 Starlette의 CORS 설명서에 문서화된 대로 설정할 수 있습니다.
diff --git a/docs/ko/docs/advanced/templates.md b/docs/ko/docs/advanced/templates.md
index 4cb4cbe0d..612635713 100644
--- a/docs/ko/docs/advanced/templates.md
+++ b/docs/ko/docs/advanced/templates.md
@@ -124,4 +124,4 @@ Item ID: 42
## 더 많은 세부 사항
-템플릿 테스트를 포함한 더 많은 세부 사항은 Starlette의 템플릿 문서를 확인하세요.
+템플릿 테스트를 포함한 더 많은 세부 사항은 Starlette의 템플릿 문서를 확인하세요.
diff --git a/docs/ko/docs/advanced/testing-websockets.md b/docs/ko/docs/advanced/testing-websockets.md
index 9f3b4a451..9b6782429 100644
--- a/docs/ko/docs/advanced/testing-websockets.md
+++ b/docs/ko/docs/advanced/testing-websockets.md
@@ -8,6 +8,6 @@
/// note | 참고
-자세한 내용은 Starlette의 WebSocket 테스트에 관한 설명서를 참고하시길 바랍니다.
+자세한 내용은 Starlette의 WebSocket 테스트에 관한 설명서를 참고하시길 바랍니다.
///
diff --git a/docs/ko/docs/advanced/using-request-directly.md b/docs/ko/docs/advanced/using-request-directly.md
index bfa4fa4db..b88a83bf4 100644
--- a/docs/ko/docs/advanced/using-request-directly.md
+++ b/docs/ko/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@
## `Request` 객체에 대한 세부 사항
-**FastAPI**는 실제로 내부에 **Starlette**을 사용하며, 그 위에 여러 도구를 덧붙인 구조입니다. 따라서 여러분이 필요할 때 Starlette의 `Request` 객체를 직접 사용할 수 있습니다.
+**FastAPI**는 실제로 내부에 **Starlette**을 사용하며, 그 위에 여러 도구를 덧붙인 구조입니다. 따라서 여러분이 필요할 때 Starlette의 `Request` 객체를 직접 사용할 수 있습니다.
`Request` 객체에서 데이터를 직접 가져오는 경우(예: 본문을 읽기)에는 FastAPI가 해당 데이터를 검증하거나 변환하지 않으며, 문서화(OpenAPI를 통한 문서 자동화(로 생성된) API 사용자 인터페이스)도 되지 않습니다.
@@ -45,7 +45,7 @@
## `Request` 설명서
-여러분은 `Request` 객체에 대한 더 자세한 내용을 공식 Starlette 설명서 사이트에서 읽어볼 수 있습니다.
+여러분은 `Request` 객체에 대한 더 자세한 내용을 공식 Starlette 설명서 사이트에서 읽어볼 수 있습니다.
/// note | 기술 세부사항
diff --git a/docs/ko/docs/advanced/websockets.md b/docs/ko/docs/advanced/websockets.md
index fa60a428b..d9d0dd95c 100644
--- a/docs/ko/docs/advanced/websockets.md
+++ b/docs/ko/docs/advanced/websockets.md
@@ -182,5 +182,5 @@ FastAPI와 쉽게 통합할 수 있으면서 더 견고하고 Redis, PostgreSQL
다음 옵션에 대한 자세한 내용을 보려면 Starlette의 문서를 확인하세요:
-* `WebSocket` 클래스.
-* 클래스 기반 WebSocket 처리.
+* `WebSocket` 클래스.
+* 클래스 기반 WebSocket 처리.
diff --git a/docs/ko/docs/fastapi-cli.md b/docs/ko/docs/fastapi-cli.md
index 3a976af36..a1160c71f 100644
--- a/docs/ko/docs/fastapi-cli.md
+++ b/docs/ko/docs/fastapi-cli.md
@@ -60,7 +60,7 @@ FastAPI CLI는 Python 프로그램의 경로(예: `main.py`)를 인수로 받아
프로덕션 환경에서는 `fastapi run` 명령어를 사용합니다. 🚀
-내부적으로, **FastAPI CLI**는 고성능의, 프로덕션에 적합한, ASGI 서버인 Uvicorn을 사용합니다. 😎
+내부적으로, **FastAPI CLI**는 고성능의, 프로덕션에 적합한, ASGI 서버인 Uvicorn을 사용합니다. 😎
## `fastapi dev`
diff --git a/docs/ko/docs/features.md b/docs/ko/docs/features.md
index 5e880c298..dfbf47999 100644
--- a/docs/ko/docs/features.md
+++ b/docs/ko/docs/features.md
@@ -159,7 +159,7 @@ FastAPI는 사용하기 매우 간편하지만, 엄청난 하여 추가할 수 있습니다.
-그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 Starlette CORS 문서에 명시된 `expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다.
+그러나 만약 클라이언트의 브라우저에서 볼 수 있는 사용자 정의 헤더를 가지고 있다면, 그것들을 CORS 설정([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank})에 Starlette CORS 문서에 명시된 `expose_headers` 매개변수를 이용하여 헤더들을 추가하여야합니다.
///
diff --git a/docs/ko/docs/tutorial/static-files.md b/docs/ko/docs/tutorial/static-files.md
index 9db5e1c67..4f3e3ab28 100644
--- a/docs/ko/docs/tutorial/static-files.md
+++ b/docs/ko/docs/tutorial/static-files.md
@@ -38,4 +38,4 @@
## 추가 정보
-자세한 내용과 선택 사항을 보려면 Starlette의 정적 파일에 관한 문서를 확인하십시오.
+자세한 내용과 선택 사항을 보려면 Starlette의 정적 파일에 관한 문서를 확인하십시오.
diff --git a/docs/ko/docs/tutorial/testing.md b/docs/ko/docs/tutorial/testing.md
index a483cbf00..915ff6d22 100644
--- a/docs/ko/docs/tutorial/testing.md
+++ b/docs/ko/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# 테스팅
-Starlette 덕분에 **FastAPI** 를 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
+Starlette 덕분에 **FastAPI** 를 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
Starlette는 HTTPX를 기반으로 하며, 이는 Requests를 기반으로 설계되었기 때문에 매우 친숙하고 직관적입니다.
diff --git a/docs/missing-translation.md b/docs/missing-translation.md
index c2882e90e..bfff84766 100644
--- a/docs/missing-translation.md
+++ b/docs/missing-translation.md
@@ -1,7 +1,9 @@
/// warning
-The current page still doesn't have a translation for this language.
+This page hasn’t been translated into your language yet. 🌍
-But you can help translating it: [Contributing](https://fastapi.tiangolo.com/contributing/){.internal-link target=_blank}.
+We’re currently switching to an automated translation system 🤖, which will help keep all translations complete and up to date.
+
+Learn more: [Contributing – Translations](https://fastapi.tiangolo.com/contributing/#translations){.internal-link target=_blank}
///
diff --git a/docs/pt/docs/advanced/events.md b/docs/pt/docs/advanced/events.md
index 504b6db57..2d38e0899 100644
--- a/docs/pt/docs/advanced/events.md
+++ b/docs/pt/docs/advanced/events.md
@@ -155,7 +155,7 @@ Por baixo, na especificação técnica ASGI, essa é a parte do Documentação do Lifespan Starlette.
+Você pode ler mais sobre o manipulador `lifespan` do Starlette na Documentação do Lifespan Starlette.
Incluindo como manipular estado do lifespan que pode ser usado em outras áreas do seu código.
diff --git a/docs/pt/docs/advanced/middleware.md b/docs/pt/docs/advanced/middleware.md
index 8167f7d27..7700939f0 100644
--- a/docs/pt/docs/advanced/middleware.md
+++ b/docs/pt/docs/advanced/middleware.md
@@ -93,4 +93,4 @@ Por exemplo:
* Uvicorn's `ProxyHeadersMiddleware`
* MessagePack
-Para checar outros middlewares disponíveis, confira Documentação de Middlewares do Starlette e a Lista Incrível do ASGI.
+Para checar outros middlewares disponíveis, confira Documentação de Middlewares do Starlette e a Lista Incrível do ASGI.
diff --git a/docs/pt/docs/advanced/response-cookies.md b/docs/pt/docs/advanced/response-cookies.md
index eed69f222..f005f0b9b 100644
--- a/docs/pt/docs/advanced/response-cookies.md
+++ b/docs/pt/docs/advanced/response-cookies.md
@@ -48,4 +48,4 @@ E como o `Response` pode ser usado frequentemente para definir cabeçalhos e coo
///
-Para ver todos os parâmetros e opções disponíveis, verifique a documentação no Starlette.
+Para ver todos os parâmetros e opções disponíveis, verifique a documentação no Starlette.
diff --git a/docs/pt/docs/advanced/response-headers.md b/docs/pt/docs/advanced/response-headers.md
index a8034a7a4..a1fc84cc0 100644
--- a/docs/pt/docs/advanced/response-headers.md
+++ b/docs/pt/docs/advanced/response-headers.md
@@ -38,4 +38,4 @@ E como a `Response` pode ser usada frequentemente para definir cabeçalhos e coo
Tenha em mente que cabeçalhos personalizados proprietários podem ser adicionados usando o prefixo 'X-'.
-Porém, se voce tiver cabeçalhos personalizados que deseja que um cliente no navegador possa ver, você precisa adicioná-los às suas configurações de CORS (saiba mais em [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando o parâmetro `expose_headers` descrito na documentação de CORS do Starlette.
+Porém, se voce tiver cabeçalhos personalizados que deseja que um cliente no navegador possa ver, você precisa adicioná-los às suas configurações de CORS (saiba mais em [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando o parâmetro `expose_headers` descrito na documentação de CORS do Starlette.
diff --git a/docs/pt/docs/advanced/templates.md b/docs/pt/docs/advanced/templates.md
index 4d22bfbbf..65ff89fae 100644
--- a/docs/pt/docs/advanced/templates.md
+++ b/docs/pt/docs/advanced/templates.md
@@ -121,4 +121,4 @@ E como você está usando `StaticFiles`, este arquivo CSS será automaticamente
## Mais detalhes
-Para obter mais detalhes, incluindo como testar templates, consulte a documentação da Starlette sobre templates.
+Para obter mais detalhes, incluindo como testar templates, consulte a documentação da Starlette sobre templates.
diff --git a/docs/pt/docs/advanced/testing-websockets.md b/docs/pt/docs/advanced/testing-websockets.md
index 942771bc9..9b8193655 100644
--- a/docs/pt/docs/advanced/testing-websockets.md
+++ b/docs/pt/docs/advanced/testing-websockets.md
@@ -8,6 +8,6 @@ Para isso, você utiliza o `TestClient` dentro de uma instrução `with`, conect
/// note | Nota
-Para mais detalhes, confira a documentação do Starlette para testar WebSockets.
+Para mais detalhes, confira a documentação do Starlette para testar WebSockets.
///
diff --git a/docs/pt/docs/advanced/using-request-directly.md b/docs/pt/docs/advanced/using-request-directly.md
index f31e2ed15..f4fb0ed8f 100644
--- a/docs/pt/docs/advanced/using-request-directly.md
+++ b/docs/pt/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@ Porém há situações em que você possa precisar acessar o objeto `Request` di
## Detalhes sobre o objeto `Request`
-Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto `Request` do Starlette diretamente quando precisar.
+Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto `Request` do Starlette diretamente quando precisar.
Isso significaria também que se você obtiver informações do objeto `Request` diretamente (ler o corpo da requisição por exemplo), as informações não serão validadas, convertidas ou documentadas (com o OpenAPI, para a interface de usuário automática da API) pelo FastAPI.
@@ -45,7 +45,7 @@ Do mesmo jeito, você pode declarar qualquer outro parâmetro normalmente, e al
## Documentação do `Request`
-Você pode ler mais sobre os detalhes do objeto `Request` no site da documentação oficial do Starlette..
+Você pode ler mais sobre os detalhes do objeto `Request` no site da documentação oficial do Starlette..
/// note | Detalhes Técnicos
diff --git a/docs/pt/docs/advanced/websockets.md b/docs/pt/docs/advanced/websockets.md
index 82e443886..721c0b403 100644
--- a/docs/pt/docs/advanced/websockets.md
+++ b/docs/pt/docs/advanced/websockets.md
@@ -182,5 +182,5 @@ Se você precisa de algo fácil de integrar com o FastAPI, mas que seja mais rob
Para aprender mais sobre as opções, verifique a documentação do Starlette para:
-* A classe `WebSocket`.
-* Manipulação de WebSockets baseada em classes.
+* A classe `WebSocket`.
+* Manipulação de WebSockets baseada em classes.
diff --git a/docs/pt/docs/alternatives.md b/docs/pt/docs/alternatives.md
index 29c9693bb..66cf3fe12 100644
--- a/docs/pt/docs/alternatives.md
+++ b/docs/pt/docs/alternatives.md
@@ -419,7 +419,7 @@ Controlar toda a validação de dados, serialização de dados e modelo de docum
///
-### Starlette
+### Starlette
Starlette é um framework/caixa de ferramentas ASGI peso leve, o que é ideal para construir serviços assíncronos de alta performance.
@@ -465,7 +465,7 @@ Então, qualquer coisa que você faz com Starlette, você pode fazer diretamente
///
-### Uvicorn
+### Uvicorn
Uvicorn é um servidor ASGI peso leve, construído com uvloop e httptools.
diff --git a/docs/pt/docs/deployment/manually.md b/docs/pt/docs/deployment/manually.md
index 46e580807..c7caabbcd 100644
--- a/docs/pt/docs/deployment/manually.md
+++ b/docs/pt/docs/deployment/manually.md
@@ -52,7 +52,7 @@ A principal coisa que você precisa para executar uma aplicação **FastAPI** (o
Existem diversas alternativas, incluindo:
-* Uvicorn: um servidor ASGI de alta performance.
+* Uvicorn: um servidor ASGI de alta performance.
* Hypercorn: um servidor ASGI compátivel com HTTP/2, Trio e outros recursos.
* Daphne: servidor ASGI construído para Django Channels.
* Granian: um servidor HTTP Rust para aplicações Python.
diff --git a/docs/pt/docs/fastapi-cli.md b/docs/pt/docs/fastapi-cli.md
index 829686631..f33c2ba2a 100644
--- a/docs/pt/docs/fastapi-cli.md
+++ b/docs/pt/docs/fastapi-cli.md
@@ -60,7 +60,7 @@ O FastAPI CLI recebe o caminho do seu programa Python, detecta automaticamente a
Para produção você usaria `fastapi run` no lugar. 🚀
-Internamente, **FastAPI CLI** usa Uvicorn, um servidor ASGI de alta performance e pronto para produção. 😎
+Internamente, **FastAPI CLI** usa Uvicorn, um servidor ASGI de alta performance e pronto para produção. 😎
## `fastapi dev`
diff --git a/docs/pt/docs/features.md b/docs/pt/docs/features.md
index a90a8094b..ccc3300d6 100644
--- a/docs/pt/docs/features.md
+++ b/docs/pt/docs/features.md
@@ -159,7 +159,7 @@ Qualquer integração é projetada para ser tão simples de usar (com dependênc
## Recursos do Starlette
-**FastAPI** é totalmente compatível com (e baseado no) Starlette. Então, qualquer código adicional Starlette que você tiver, também funcionará.
+**FastAPI** é totalmente compatível com (e baseado no) Starlette. Então, qualquer código adicional Starlette que você tiver, também funcionará.
`FastAPI` é na verdade uma sub-classe do `Starlette`. Então, se você já conhece ou usa Starlette, a maioria das funcionalidades se comportará da mesma forma.
diff --git a/docs/pt/docs/history-design-future.md b/docs/pt/docs/history-design-future.md
index 4ec217405..1d0768c62 100644
--- a/docs/pt/docs/history-design-future.md
+++ b/docs/pt/docs/history-design-future.md
@@ -58,7 +58,7 @@ Após testar várias alternativas, eu decidi que usaria o **Starlette**, outro requisito chave.
+Durante o desenvolvimento, eu também contribuí com o **Starlette**, outro requisito chave.
## Desenvolvimento
diff --git a/docs/pt/docs/how-to/custom-request-and-route.md b/docs/pt/docs/how-to/custom-request-and-route.md
index 8f432f6fe..151a0f5d4 100644
--- a/docs/pt/docs/how-to/custom-request-and-route.md
+++ b/docs/pt/docs/how-to/custom-request-and-route.md
@@ -66,7 +66,7 @@ O dicionário `scope` e a função `receive` são ambos parte da especificação
E essas duas coisas, `scope` e `receive`, são o que é necessário para criar uma nova instância de `Request`.
-Para aprender mais sobre o `Request` confira a documentação do Starlette sobre Requests.
+Para aprender mais sobre o `Request` confira a documentação do Starlette sobre Requests.
///
diff --git a/docs/pt/docs/index.md b/docs/pt/docs/index.md
index ce9929bf4..a361913c3 100644
--- a/docs/pt/docs/index.md
+++ b/docs/pt/docs/index.md
@@ -123,7 +123,7 @@ Se você estiver construindo uma aplicação Starlette para as partes web.
+* Starlette para as partes web.
* Pydantic para a parte de dados.
## Instalação
@@ -229,7 +229,7 @@ INFO: Application startup complete.
Sobre o comando fastapi dev main.py...
-O comando `fastapi dev` lê o seu arquivo `main.py`, identifica o aplicativo **FastAPI** nele, e inicia um servidor usando o Uvicorn.
+O comando `fastapi dev` lê o seu arquivo `main.py`, identifica o aplicativo **FastAPI** nele, e inicia um servidor usando o Uvicorn.
Por padrão, o `fastapi dev` iniciará com *auto-reload* habilitado para desenvolvimento local.
@@ -471,7 +471,7 @@ Utilizado pelo Starlette:
Utilizado pelo FastAPI / Starlette:
-* uvicorn - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
+* uvicorn - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
* `fastapi-cli` - que disponibiliza o comando `fastapi`.
### Sem as dependências `standard`
diff --git a/docs/pt/docs/tutorial/background-tasks.md b/docs/pt/docs/tutorial/background-tasks.md
index 0f3796371..b8ab58cda 100644
--- a/docs/pt/docs/tutorial/background-tasks.md
+++ b/docs/pt/docs/tutorial/background-tasks.md
@@ -61,7 +61,7 @@ E então outra tarefa em segundo plano gerada na _função de operação de cami
## Detalhes técnicos
-A classe `BackgroundTasks` vem diretamente de `starlette.background`.
+A classe `BackgroundTasks` vem diretamente de `starlette.background`.
Ela é importada/incluída diretamente no FastAPI para que você possa importá-la do `fastapi` e evitar a importação acidental da alternativa `BackgroundTask` (sem o `s` no final) de `starlette.background`.
@@ -69,7 +69,7 @@ Usando apenas `BackgroundTasks` (e não `BackgroundTask`), é então possível u
Ainda é possível usar `BackgroundTask` sozinho no FastAPI, mas você deve criar o objeto em seu código e retornar uma Starlette `Response` incluindo-o.
-Você pode ver mais detalhes na documentação oficiais da Starlette para tarefas em segundo plano .
+Você pode ver mais detalhes na documentação oficiais da Starlette para tarefas em segundo plano .
## Ressalva
diff --git a/docs/pt/docs/tutorial/first-steps.md b/docs/pt/docs/tutorial/first-steps.md
index 5184d2d5f..e696bbbb7 100644
--- a/docs/pt/docs/tutorial/first-steps.md
+++ b/docs/pt/docs/tutorial/first-steps.md
@@ -155,7 +155,7 @@ Você também pode usá-lo para gerar código automaticamente para clientes que
`FastAPI` é uma classe que herda diretamente de `Starlette`.
-Você pode usar todas as funcionalidades do Starlette com `FastAPI` também.
+Você pode usar todas as funcionalidades do Starlette com `FastAPI` também.
///
diff --git a/docs/pt/docs/tutorial/handling-errors.md b/docs/pt/docs/tutorial/handling-errors.md
index 098195db7..5cb92c744 100644
--- a/docs/pt/docs/tutorial/handling-errors.md
+++ b/docs/pt/docs/tutorial/handling-errors.md
@@ -83,7 +83,7 @@ Mas caso você precise, para um cenário mais complexo, você pode adicionar hea
## Instalando manipuladores de exceções customizados
-Você pode adicionar manipuladores de exceção customizados com a mesma seção de utilidade de exceções presentes no Starlette
+Você pode adicionar manipuladores de exceção customizados com a mesma seção de utilidade de exceções presentes no Starlette
Digamos que você tenha uma exceção customizada `UnicornException` que você (ou uma biblioteca que você use) precise lançar (`raise`).
diff --git a/docs/pt/docs/tutorial/middleware.md b/docs/pt/docs/tutorial/middleware.md
index 32b81c646..0f5009b6d 100644
--- a/docs/pt/docs/tutorial/middleware.md
+++ b/docs/pt/docs/tutorial/middleware.md
@@ -37,7 +37,7 @@ A função middleware recebe:
Tenha em mente que cabeçalhos proprietários personalizados podem ser adicionados usando o prefixo 'X-'.
-Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando o parâmetro `expose_headers` documentado em Documentos CORS da Starlette.
+Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) usando o parâmetro `expose_headers` documentado em Documentos CORS da Starlette.
///
diff --git a/docs/pt/docs/tutorial/static-files.md b/docs/pt/docs/tutorial/static-files.md
index 0660078f4..30e1af8e6 100644
--- a/docs/pt/docs/tutorial/static-files.md
+++ b/docs/pt/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@ Todos esses parâmetros podem ser diferentes de "`static`", ajuste-os de acordo
## Mais informações
-Para mais detalhes e opções, verifique Starlette's docs about Static Files.
+Para mais detalhes e opções, verifique Starlette's docs about Static Files.
diff --git a/docs/pt/docs/tutorial/testing.md b/docs/pt/docs/tutorial/testing.md
index 8eb2f29b7..dc505105a 100644
--- a/docs/pt/docs/tutorial/testing.md
+++ b/docs/pt/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# Testando
-Graças ao Starlette, testar aplicativos **FastAPI** é fácil e agradável.
+Graças ao Starlette, testar aplicativos **FastAPI** é fácil e agradável.
Ele é baseado no HTTPX, que por sua vez é projetado com base em Requests, por isso é muito familiar e intuitivo.
diff --git a/docs/ru/docs/advanced/events.md b/docs/ru/docs/advanced/events.md
index 6e1b49035..20d1df98a 100644
--- a/docs/ru/docs/advanced/events.md
+++ b/docs/ru/docs/advanced/events.md
@@ -154,7 +154,7 @@ async with lifespan(app):
/// info | Информация
-Вы можете прочитать больше про обработчики `lifespan` в Starlette в документации Starlette по Lifespan.
+Вы можете прочитать больше про обработчики `lifespan` в Starlette в документации Starlette по Lifespan.
Включая то, как работать с состоянием lifespan, которое можно использовать в других частях вашего кода.
diff --git a/docs/ru/docs/advanced/middleware.md b/docs/ru/docs/advanced/middleware.md
index 28802fd57..82c86b231 100644
--- a/docs/ru/docs/advanced/middleware.md
+++ b/docs/ru/docs/advanced/middleware.md
@@ -94,4 +94,4 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
- `ProxyHeadersMiddleware` от Uvicorn
- MessagePack
-Чтобы увидеть другие доступные middleware, посмотрите документацию по middleware в Starlette и список ASGI Awesome.
+Чтобы увидеть другие доступные middleware, посмотрите документацию по middleware в Starlette и список ASGI Awesome.
diff --git a/docs/ru/docs/advanced/response-cookies.md b/docs/ru/docs/advanced/response-cookies.md
index 3aa32b9bb..9319aba6e 100644
--- a/docs/ru/docs/advanced/response-cookies.md
+++ b/docs/ru/docs/advanced/response-cookies.md
@@ -48,4 +48,4 @@
///
-Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с документацией Starlette.
+Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с документацией Starlette.
diff --git a/docs/ru/docs/advanced/response-headers.md b/docs/ru/docs/advanced/response-headers.md
index 81e52cb69..1c9360b31 100644
--- a/docs/ru/docs/advanced/response-headers.md
+++ b/docs/ru/docs/advanced/response-headers.md
@@ -38,4 +38,4 @@
Помните, что собственные проприетарные заголовки можно добавлять, используя префикс `X-`.
-Но если у вас есть пользовательские заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, описанный в документации Starlette по CORS.
+Но если у вас есть пользовательские заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, описанный в документации Starlette по CORS.
diff --git a/docs/ru/docs/advanced/templates.md b/docs/ru/docs/advanced/templates.md
index 5675ff48a..204e88760 100644
--- a/docs/ru/docs/advanced/templates.md
+++ b/docs/ru/docs/advanced/templates.md
@@ -123,4 +123,4 @@ Item ID: 42
## Подробнее { #more-details }
-Больше подробностей, включая то, как тестировать шаблоны, смотрите в документации Starlette по шаблонам.
+Больше подробностей, включая то, как тестировать шаблоны, смотрите в документации Starlette по шаблонам.
diff --git a/docs/ru/docs/advanced/testing-events.md b/docs/ru/docs/advanced/testing-events.md
index 1bf8e4723..e0ec77439 100644
--- a/docs/ru/docs/advanced/testing-events.md
+++ b/docs/ru/docs/advanced/testing-events.md
@@ -5,7 +5,7 @@
{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
-Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.io/lifespan/#running-lifespan-in-tests)
+Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Для устаревших событий `startup` и `shutdown` вы можете использовать `TestClient` следующим образом:
diff --git a/docs/ru/docs/advanced/testing-websockets.md b/docs/ru/docs/advanced/testing-websockets.md
index 7c0ca2594..e840a03f2 100644
--- a/docs/ru/docs/advanced/testing-websockets.md
+++ b/docs/ru/docs/advanced/testing-websockets.md
@@ -8,6 +8,6 @@
/// note | Примечание
-Подробности смотрите в документации Starlette по тестированию WebSocket.
+Подробности смотрите в документации Starlette по тестированию WebSocket.
///
diff --git a/docs/ru/docs/advanced/using-request-directly.md b/docs/ru/docs/advanced/using-request-directly.md
index bff2ddcb7..b92221610 100644
--- a/docs/ru/docs/advanced/using-request-directly.md
+++ b/docs/ru/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@
## Подробности об объекте `Request` { #details-about-the-request-object }
-Так как под капотом **FastAPI** — это **Starlette** с дополнительным слоем инструментов, вы можете при необходимости напрямую использовать объект `Request` из Starlette.
+Так как под капотом **FastAPI** — это **Starlette** с дополнительным слоем инструментов, вы можете при необходимости напрямую использовать объект `Request` из Starlette.
Это также означает, что если вы получаете данные напрямую из объекта `Request` (например, читаете тело запроса), то они не будут валидироваться, конвертироваться или документироваться (с OpenAPI, для автоматического пользовательского интерфейса API) средствами FastAPI.
@@ -45,7 +45,7 @@
## Документация по `Request` { #request-documentation }
-Подробнее об объекте `Request` на официальном сайте документации Starlette.
+Подробнее об объекте `Request` на официальном сайте документации Starlette.
/// note | Технические детали
diff --git a/docs/ru/docs/advanced/websockets.md b/docs/ru/docs/advanced/websockets.md
index b73fa1ddb..f26185bea 100644
--- a/docs/ru/docs/advanced/websockets.md
+++ b/docs/ru/docs/advanced/websockets.md
@@ -182,5 +182,5 @@ Client #1596980209979 left the chat
Для более глубокого изучения темы воспользуйтесь документацией Starlette:
-* The `WebSocket` class.
-* Class-based WebSocket handling.
+* The `WebSocket` class.
+* Class-based WebSocket handling.
diff --git a/docs/ru/docs/alternatives.md b/docs/ru/docs/alternatives.md
index 6380bcc45..17b54aad2 100644
--- a/docs/ru/docs/alternatives.md
+++ b/docs/ru/docs/alternatives.md
@@ -417,7 +417,7 @@ Pydantic — это библиотека для определения вали
///
-### Starlette { #starlette }
+### Starlette { #starlette }
Starlette — это лёгкий ASGI фреймворк/набор инструментов, идеально подходящий для создания высокопроизводительных asyncio‑сервисов.
@@ -462,7 +462,7 @@ ASGI — это новый «стандарт», разрабатываемый
///
-### Uvicorn { #uvicorn }
+### Uvicorn { #uvicorn }
Uvicorn — молниеносный ASGI-сервер, построенный на uvloop и httptools.
diff --git a/docs/ru/docs/deployment/manually.md b/docs/ru/docs/deployment/manually.md
index 37fed5780..93287372a 100644
--- a/docs/ru/docs/deployment/manually.md
+++ b/docs/ru/docs/deployment/manually.md
@@ -52,7 +52,7 @@ FastAPI использует стандарт для построения Python
Есть несколько альтернатив, например:
-* Uvicorn: высокопроизводительный ASGI‑сервер.
+* Uvicorn: высокопроизводительный ASGI‑сервер.
* Hypercorn: ASGI‑сервер, среди прочего совместимый с HTTP/2 и Trio.
* Daphne: ASGI‑сервер, созданный для Django Channels.
* Granian: HTTP‑сервер на Rust для Python‑приложений.
diff --git a/docs/ru/docs/fastapi-cli.md b/docs/ru/docs/fastapi-cli.md
index 156e3d200..72cf55e7b 100644
--- a/docs/ru/docs/fastapi-cli.md
+++ b/docs/ru/docs/fastapi-cli.md
@@ -52,7 +52,7 @@ FastAPI CLI берет путь к вашей Python-программе (нап
Для работы в режиме продакшн вместо `fastapi dev` нужно использовать `fastapi run`. 🚀
-Внутри **FastAPI CLI** используется Uvicorn, высокопроизводительный, готовый к работе в продакшне ASGI-сервер. 😎
+Внутри **FastAPI CLI** используется Uvicorn, высокопроизводительный, готовый к работе в продакшне ASGI-сервер. 😎
## `fastapi dev` { #fastapi-dev }
diff --git a/docs/ru/docs/features.md b/docs/ru/docs/features.md
index 91ffe331b..703ff951e 100644
--- a/docs/ru/docs/features.md
+++ b/docs/ru/docs/features.md
@@ -159,7 +159,7 @@ FastAPI включает в себя чрезвычайно простую в и
## Возможности Starlette { #starlette-features }
-**FastAPI** основан на Starlette и полностью совместим с ним. Так что любой дополнительный код Starlette, который у вас есть, также будет работать.
+**FastAPI** основан на Starlette и полностью совместим с ним. Так что любой дополнительный код Starlette, который у вас есть, также будет работать.
На самом деле, `FastAPI` — это подкласс `Starlette`. Таким образом, если вы уже знаете или используете Starlette, большая часть функционала будет работать так же.
diff --git a/docs/ru/docs/history-design-future.md b/docs/ru/docs/history-design-future.md
index d679af3e3..9cdd53376 100644
--- a/docs/ru/docs/history-design-future.md
+++ b/docs/ru/docs/history-design-future.md
@@ -58,7 +58,7 @@
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить поддержку в редакторах кода (проверки типов, автозавершение) на основе тестов в нескольких редакторах.
-В то же время, я принимал участие в разработке **Starlette**, ещё один из основных компонентов FastAPI.
+В то же время, я принимал участие в разработке **Starlette**, ещё один из основных компонентов FastAPI.
## Разработка { #development }
diff --git a/docs/ru/docs/how-to/custom-request-and-route.md b/docs/ru/docs/how-to/custom-request-and-route.md
index df8a5ee3c..1b8d7f7ed 100644
--- a/docs/ru/docs/how-to/custom-request-and-route.md
+++ b/docs/ru/docs/how-to/custom-request-and-route.md
@@ -66,7 +66,7 @@
Именно этих двух компонентов — `scope` и `receive` — достаточно, чтобы создать новый экземпляр `Request`.
-Чтобы узнать больше о `Request`, см. документацию Starlette о запросах.
+Чтобы узнать больше о `Request`, см. документацию Starlette о запросах.
///
diff --git a/docs/ru/docs/index.md b/docs/ru/docs/index.md
index 1fcc9ea9d..75cd63223 100644
--- a/docs/ru/docs/index.md
+++ b/docs/ru/docs/index.md
@@ -123,7 +123,7 @@ FastAPI — это современный, быстрый (высокопрои
FastAPI стоит на плечах гигантов:
-* Starlette для части, связанной с вебом.
+* Starlette для части, связанной с вебом.
* Pydantic для части, связанной с данными.
## Установка { #installation }
@@ -229,7 +229,7 @@ INFO: Application startup complete.
О команде fastapi dev main.py...
-Команда `fastapi dev` читает ваш файл `main.py`, находит в нём приложение **FastAPI** и запускает сервер с помощью Uvicorn.
+Команда `fastapi dev` читает ваш файл `main.py`, находит в нём приложение **FastAPI** и запускает сервер с помощью Uvicorn.
По умолчанию `fastapi dev` запускается с включённой авто-перезагрузкой для локальной разработки.
@@ -470,7 +470,7 @@ FastAPI зависит от Pydantic и Starlette.
Используется FastAPI:
-* uvicorn — сервер, который загружает и обслуживает ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
+* uvicorn — сервер, который загружает и обслуживает ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
* `fastapi-cli[standard]` — чтобы предоставить команду `fastapi`.
* Включает `fastapi-cloud-cli`, который позволяет развернуть ваше приложение FastAPI в FastAPI Cloud.
diff --git a/docs/ru/docs/tutorial/background-tasks.md b/docs/ru/docs/tutorial/background-tasks.md
index 9b6f0c8d3..1ed8522d6 100644
--- a/docs/ru/docs/tutorial/background-tasks.md
+++ b/docs/ru/docs/tutorial/background-tasks.md
@@ -61,7 +61,7 @@
## Технические детали { #technical-details }
-Класс `BackgroundTasks` приходит напрямую из `starlette.background`.
+Класс `BackgroundTasks` приходит напрямую из `starlette.background`.
Он импортируется/включается прямо в FastAPI, чтобы вы могли импортировать его из `fastapi` и избежать случайного импорта альтернативного `BackgroundTask` (без `s` на конце) из `starlette.background`.
@@ -69,7 +69,7 @@
По‑прежнему можно использовать один `BackgroundTask` в FastAPI, но тогда вам нужно создать объект в своём коде и вернуть Starlette `Response`, включающий его.
-Подробнее см. в официальной документации Starlette по фоновым задачам.
+Подробнее см. в официальной документации Starlette по фоновым задачам.
## Предостережение { #caveat }
diff --git a/docs/ru/docs/tutorial/first-steps.md b/docs/ru/docs/tutorial/first-steps.md
index 9cdf76f5d..c82118cbe 100644
--- a/docs/ru/docs/tutorial/first-steps.md
+++ b/docs/ru/docs/tutorial/first-steps.md
@@ -155,7 +155,7 @@ OpenAPI определяет схему API для вашего API. И эта
`FastAPI` — это класс, который напрямую наследуется от `Starlette`.
-Вы можете использовать весь функционал Starlette и в `FastAPI`.
+Вы можете использовать весь функционал Starlette и в `FastAPI`.
///
diff --git a/docs/ru/docs/tutorial/handling-errors.md b/docs/ru/docs/tutorial/handling-errors.md
index 33b7babf5..2378c8b04 100644
--- a/docs/ru/docs/tutorial/handling-errors.md
+++ b/docs/ru/docs/tutorial/handling-errors.md
@@ -81,7 +81,7 @@
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
-Вы можете добавить пользовательские обработчики исключений с помощью то же самое исключение - утилиты от Starlette.
+Вы можете добавить пользовательские обработчики исключений с помощью то же самое исключение - утилиты от Starlette.
Допустим, у вас есть пользовательское исключение `UnicornException`, которое вы (или используемая вами библиотека) можете `вызвать`.
diff --git a/docs/ru/docs/tutorial/middleware.md b/docs/ru/docs/tutorial/middleware.md
index ea535a151..5803b398b 100644
--- a/docs/ru/docs/tutorial/middleware.md
+++ b/docs/ru/docs/tutorial/middleware.md
@@ -39,7 +39,7 @@
Имейте в виду, что можно добавлять свои собственные заголовки при помощи префикса 'X-'.
-Если же вы хотите добавить собственные заголовки, которые клиент сможет увидеть в браузере, то вам потребуется добавить их в настройки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, см. документацию Starlette's CORS docs.
+Если же вы хотите добавить собственные заголовки, которые клиент сможет увидеть в браузере, то вам потребуется добавить их в настройки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}), используя параметр `expose_headers`, см. документацию Starlette's CORS docs.
///
diff --git a/docs/ru/docs/tutorial/static-files.md b/docs/ru/docs/tutorial/static-files.md
index 282c84db1..8455aea0a 100644
--- a/docs/ru/docs/tutorial/static-files.md
+++ b/docs/ru/docs/tutorial/static-files.md
@@ -38,4 +38,4 @@ OpenAPI и документация из вашего главного прил
## Больше информации { #more-info }
-Для получения дополнительной информации о деталях и настройках ознакомьтесь с Документацией Starlette о статических файлах.
+Для получения дополнительной информации о деталях и настройках ознакомьтесь с Документацией Starlette о статических файлах.
diff --git a/docs/ru/docs/tutorial/testing.md b/docs/ru/docs/tutorial/testing.md
index 94e9ae8ae..0224798b1 100644
--- a/docs/ru/docs/tutorial/testing.md
+++ b/docs/ru/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# Тестирование { #testing }
-Благодаря Starlette, тестировать приложения **FastAPI** легко и приятно.
+Благодаря Starlette, тестировать приложения **FastAPI** легко и приятно.
Тестирование основано на библиотеке HTTPX, которая в свою очередь основана на библиотеке Requests, так что все действия знакомы и интуитивно понятны.
diff --git a/docs/tr/docs/advanced/testing-websockets.md b/docs/tr/docs/advanced/testing-websockets.md
index ddacca449..effe557d1 100644
--- a/docs/tr/docs/advanced/testing-websockets.md
+++ b/docs/tr/docs/advanced/testing-websockets.md
@@ -8,6 +8,6 @@ Bu işlem için, `TestClient`'ı bir `with` ifadesinde kullanarak WebSocket'e ba
/// note | Not
-Daha fazla detay için Starlette'in Websockets'i Test Etmek dokümantasyonunu inceleyin.
+Daha fazla detay için Starlette'in Websockets'i Test Etmek dokümantasyonunu inceleyin.
///
diff --git a/docs/tr/docs/alternatives.md b/docs/tr/docs/alternatives.md
index c98b966b5..9b603ea81 100644
--- a/docs/tr/docs/alternatives.md
+++ b/docs/tr/docs/alternatives.md
@@ -415,7 +415,7 @@ Bütün veri doğrulama, veri dönüştürme ve JSON Şemasına bağlı otomatik
///
-### Starlette
+### Starlette
Starlette hafif bir ASGI framework'ü ve yüksek performanslı asyncio servisleri oluşturmak için ideal.
@@ -460,7 +460,7 @@ Yani, Starlette ile yapabileceğiniz her şeyi, Starlette'in bir nevi güçlendi
///
-### Uvicorn
+### Uvicorn
Uvicorn, uvlook ile httptools üzerine kurulu ışık hzında bir ASGI sunucusudur.
diff --git a/docs/tr/docs/features.md b/docs/tr/docs/features.md
index 5d40b1086..86085c5e9 100644
--- a/docs/tr/docs/features.md
+++ b/docs/tr/docs/features.md
@@ -166,7 +166,7 @@ Bütün entegrasyonlar kullanımı kolay olmak üzere (zorunluluklar ile beraber
## Starlette özellikleri
-**FastAPI**, Starlette ile tamamiyle uyumlu ve üzerine kurulu. Yani FastAPI üzerine ekleme yapacağınız herhangi bir Starlette kodu da çalışacaktır.
+**FastAPI**, Starlette ile tamamiyle uyumlu ve üzerine kurulu. Yani FastAPI üzerine ekleme yapacağınız herhangi bir Starlette kodu da çalışacaktır.
`FastAPI` aslında `Starlette`'nin bir sub-class'ı. Eğer Starlette'nin nasıl kullanılacağını biliyor isen, çoğu işlevini aynı şekilde yapıyor.
diff --git a/docs/tr/docs/history-design-future.md b/docs/tr/docs/history-design-future.md
index 8b2662bc3..cad290828 100644
--- a/docs/tr/docs/history-design-future.md
+++ b/docs/tr/docs/history-design-future.md
@@ -58,7 +58,7 @@ Hepsi, tüm geliştiriciler için en iyi geliştirme deneyimini sağlayacak şek
Sonra, JSON Schema ile tamamen uyumlu olmasını sağlamak, kısıtlama bildirimlerini tanımlamanın farklı yollarını desteklemek ve birkaç editördeki testlere dayanarak editör desteğini (tip kontrolleri, otomatik tamamlama) geliştirmek için katkıda bulundum.
-Geliştirme sırasında, diğer ana gereksinim olan **Starlette**'e de katkıda bulundum.
+Geliştirme sırasında, diğer ana gereksinim olan **Starlette**'e de katkıda bulundum.
## Geliştirme
diff --git a/docs/tr/docs/index.md b/docs/tr/docs/index.md
index c7a2b2fbd..516d5959e 100644
--- a/docs/tr/docs/index.md
+++ b/docs/tr/docs/index.md
@@ -123,7 +123,7 @@ Eğer API yerine, terminalde kullanılmak üzere bir Starlette.
+* Web tarafı için Starlette.
* Data tarafı için Pydantic.
## Kurulum
@@ -138,7 +138,7 @@ $ pip install fastapi
-Uygulamamızı kullanılabilir hale getirmek için
Uvicorn ya da
Hypercorn gibi bir ASGI sunucusuna ihtiyacımız olacak.
+Uygulamamızı kullanılabilir hale getirmek için
Uvicorn ya da
Hypercorn gibi bir ASGI sunucusuna ihtiyacımız olacak.
@@ -463,7 +463,7 @@ Starlette tarafında kullanılan:
Hem FastAPI hem de Starlette tarafından kullanılan:
-*
uvicorn - oluşturduğumuz uygulamayı servis edecek web sunucusu görevini üstlenir.
+*
uvicorn - oluşturduğumuz uygulamayı servis edecek web sunucusu görevini üstlenir.
*
orjson - `ORJSONResponse` kullanacaksanız gereklidir.
*
ujson - `UJSONResponse` kullanacaksanız gerekli.
diff --git a/docs/tr/docs/tutorial/first-steps.md b/docs/tr/docs/tutorial/first-steps.md
index 2d2949b50..9a8ef762d 100644
--- a/docs/tr/docs/tutorial/first-steps.md
+++ b/docs/tr/docs/tutorial/first-steps.md
@@ -139,7 +139,7 @@ Ayrıca, API'ınızla iletişim kuracak önyüz, mobil veya IoT uygulamaları gi
`FastAPI` doğrudan `Starlette`'i miras alan bir sınıftır.
-
Starlette'in tüm işlevselliğini `FastAPI` ile de kullanabilirsiniz.
+
Starlette'in tüm işlevselliğini `FastAPI` ile de kullanabilirsiniz.
///
diff --git a/docs/tr/docs/tutorial/static-files.md b/docs/tr/docs/tutorial/static-files.md
index db30f13bc..4542aca77 100644
--- a/docs/tr/docs/tutorial/static-files.md
+++ b/docs/tr/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@ Bu parametrelerin hepsi "`static`"den farklı olabilir, bunları kendi uygulaman
## Daha Fazla Bilgi
-Daha fazla detay ve seçenek için
Starlette'in Statik Dosyalar hakkındaki dokümantasyonunu incelleyin.
+Daha fazla detay ve seçenek için
Starlette'in Statik Dosyalar hakkındaki dokümantasyonunu incelleyin.
diff --git a/docs/uk/docs/alternatives.md b/docs/uk/docs/alternatives.md
index 1acbe237a..786df45c5 100644
--- a/docs/uk/docs/alternatives.md
+++ b/docs/uk/docs/alternatives.md
@@ -415,7 +415,7 @@ Pydantic — це бібліотека для визначення переві
///
-###
Starlette
+###
Starlette
Starlette — це легкий фреймворк/набір інструментів
ASGI, який ідеально підходить для створення високопродуктивних asyncio сервісів.
@@ -460,7 +460,7 @@ ASGI — це новий «стандарт», який розробляєтьс
///
-###
Uvicorn
+###
Uvicorn
Uvicorn — це блискавичний сервер ASGI, побудований на uvloop і httptools.
diff --git a/docs/uk/docs/fastapi-cli.md b/docs/uk/docs/fastapi-cli.md
index 6bbbbc326..f18b10471 100644
--- a/docs/uk/docs/fastapi-cli.md
+++ b/docs/uk/docs/fastapi-cli.md
@@ -60,7 +60,7 @@ FastAPI CLI приймає шлях до Вашої Python програми (н
Натомість, для запуску у продакшн використовуйте `fastapi run`. 🚀
-Всередині **FastAPI CLI** використовує
Uvicorn, високопродуктивний, production-ready, ASGI cервер. 😎
+Всередині **FastAPI CLI** використовує
Uvicorn, високопродуктивний, production-ready, ASGI cервер. 😎
## `fastapi dev`
diff --git a/docs/uk/docs/features.md b/docs/uk/docs/features.md
index 7d679d8ee..aa0ef7c79 100644
--- a/docs/uk/docs/features.md
+++ b/docs/uk/docs/features.md
@@ -147,7 +147,7 @@ FastAPI має розумні налаштування **за замовчува
## Можливості Starlette
-**FastAPI** повністю сумісний із (та побудований на основі)
Starlette. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме.
+**FastAPI** повністю сумісний із (та побудований на основі)
Starlette. Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме.
**FastAPI** фактично є підкласом **Starlette**. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само.
diff --git a/docs/uk/docs/index.md b/docs/uk/docs/index.md
index 7e919e257..0811a4c7b 100644
--- a/docs/uk/docs/index.md
+++ b/docs/uk/docs/index.md
@@ -112,7 +112,7 @@ FastAPI - це сучасний, швидкий (високопродуктив
FastAPI стоїть на плечах гігантів:
-*
Starlette для web частини.
+*
Starlette для web частини.
*
Pydantic для частини даних.
## Вставновлення
@@ -127,7 +127,7 @@ $ pip install fastapi
-Вам також знадобиться сервер ASGI для продакшину, наприклад
Uvicorn або
Hypercorn.
+Вам також знадобиться сервер ASGI для продакшину, наприклад
Uvicorn або
Hypercorn.
@@ -452,7 +452,7 @@ Starlette використовує:
FastAPI / Starlette використовують:
-*
uvicorn - для сервера, який завантажує та обслуговує вашу програму.
+*
uvicorn - для сервера, який завантажує та обслуговує вашу програму.
*
orjson - Необхідно, якщо Ви хочете використовувати `ORJSONResponse`.
*
ujson - Необхідно, якщо Ви хочете використовувати `UJSONResponse`.
diff --git a/docs/uk/docs/tutorial/background-tasks.md b/docs/uk/docs/tutorial/background-tasks.md
index 912ba8c2a..0a9349650 100644
--- a/docs/uk/docs/tutorial/background-tasks.md
+++ b/docs/uk/docs/tutorial/background-tasks.md
@@ -62,7 +62,7 @@
## Технічні деталі
-Клас `BackgroundTasks` походить безпосередньо з
`starlette.background`.
+Клас `BackgroundTasks` походить безпосередньо з
`starlette.background`.
Він імпортується безпосередньо у FastAPI, щоб Ви могли використовувати його з `fastapi` і випадково не імпортували `BackgroundTask` (без s в кінці) з `starlette.background`.
@@ -70,7 +70,7 @@
Також можна використовувати `BackgroundTask` окремо в FastAPI, але для цього Вам доведеться створити об'єкт у коді та повернути Starlette `Response`, включаючи його.
-Детальніше можна почитати в
офіційній документації Starlette про фонові задачі .
+Детальніше можна почитати в
офіційній документації Starlette про фонові задачі .
## Застереження
diff --git a/docs/uk/docs/tutorial/first-steps.md b/docs/uk/docs/tutorial/first-steps.md
index e910c4ccc..3f861cb48 100644
--- a/docs/uk/docs/tutorial/first-steps.md
+++ b/docs/uk/docs/tutorial/first-steps.md
@@ -163,7 +163,7 @@ OpenAPI описує схему для вашого API. І ця схема вк
`FastAPI` це клас, який успадковується безпосередньо від `Starlette`.
-Ви також можете використовувати всю функціональність
Starlette у `FastAPI`.
+Ви також можете використовувати всю функціональність
Starlette у `FastAPI`.
///
diff --git a/docs/uk/docs/tutorial/handling-errors.md b/docs/uk/docs/tutorial/handling-errors.md
index 12a356cd0..32de73b2a 100644
--- a/docs/uk/docs/tutorial/handling-errors.md
+++ b/docs/uk/docs/tutorial/handling-errors.md
@@ -81,7 +81,7 @@
## Встановлення власних обробників помилок
-Ви можете додати власні обробники помилок за допомогою
тих самих утиліт обробки помилок зі Starlette.
+Ви можете додати власні обробники помилок за допомогою
тих самих утиліт обробки помилок зі Starlette.
Припустимо, у Вас є власний обʼєкт помилки `UnicornException`, яке Ви (або бібліотека, яку Ви використовуєте) може `згенерувати` (`raise`).
diff --git a/docs/uk/docs/tutorial/middleware.md b/docs/uk/docs/tutorial/middleware.md
index 807be484a..13ce8573d 100644
--- a/docs/uk/docs/tutorial/middleware.md
+++ b/docs/uk/docs/tutorial/middleware.md
@@ -39,7 +39,7 @@
Не забувайте, що власні заголовки можна додавати, використовуючи
префікс 'X-'.
-Але якщо у Вас є власні заголовки, які Ви хочете, щоб браузерний клієнт міг побачити, потрібно додати їх до Вашої конфігурації CORS (див. [CORS (Обмін ресурсами між різними джерелами)](cors.md){.internal-link target=_blank} за допомогою параметра `expose_headers`, описаного в
документації Starlette по CORS.
+Але якщо у Вас є власні заголовки, які Ви хочете, щоб браузерний клієнт міг побачити, потрібно додати їх до Вашої конфігурації CORS (див. [CORS (Обмін ресурсами між різними джерелами)](cors.md){.internal-link target=_blank} за допомогою параметра `expose_headers`, описаного в
документації Starlette по CORS.
///
diff --git a/docs/uk/docs/tutorial/static-files.md b/docs/uk/docs/tutorial/static-files.md
index a84782d8f..3427f2376 100644
--- a/docs/uk/docs/tutorial/static-files.md
+++ b/docs/uk/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@
## Додаткова інформація
-Детальніше про налаштування та можливості можна дізнатися в
документації Starlette про статичні файли.
+Детальніше про налаштування та можливості можна дізнатися в
документації Starlette про статичні файли.
diff --git a/docs/uk/docs/tutorial/testing.md b/docs/uk/docs/tutorial/testing.md
index 25fc370d6..1105c6b0a 100644
--- a/docs/uk/docs/tutorial/testing.md
+++ b/docs/uk/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# Тестування
-Тестування **FastAPI** додатків є простим та ефективним завдяки бібліотеці
Starlette, яка базується на
HTTPX.
+Тестування **FastAPI** додатків є простим та ефективним завдяки бібліотеці
Starlette, яка базується на
HTTPX.
Оскільки HTTPX розроблений на основі Requests, його API є інтуїтивно зрозумілим для тих, хто вже знайомий з Requests.
З його допомогою Ви можете використовувати
pytest безпосередньо з **FastAPI**.
diff --git a/docs/vi/docs/fastapi-cli.md b/docs/vi/docs/fastapi-cli.md
index d9e315ae4..e758f4d3a 100644
--- a/docs/vi/docs/fastapi-cli.md
+++ b/docs/vi/docs/fastapi-cli.md
@@ -52,7 +52,7 @@ FastAPI CLI nhận đường dẫn đến chương trình Python của bạn (vd
Đối với vận hành thực tế (production), bạn sẽ sử dụng `fastapi run` thay thế. 🚀
-Ở bên trong, **FastAPI CLI** sử dụng
Uvicorn, một server ASGI có hiệu suất cao, sẵn sàng cho vận hành thực tế (production). 😎
+Ở bên trong, **FastAPI CLI** sử dụng
Uvicorn, một server ASGI có hiệu suất cao, sẵn sàng cho vận hành thực tế (production). 😎
## `fastapi dev`
diff --git a/docs/vi/docs/index.md b/docs/vi/docs/index.md
index e7df2bf72..a5ac1bfb7 100644
--- a/docs/vi/docs/index.md
+++ b/docs/vi/docs/index.md
@@ -124,7 +124,7 @@ Nếu bạn đang xây dựng một
CLI
FastAPI đứng trên vai những người khổng lồ:
-* Starlette cho phần web.
+* Starlette cho phần web.
* Pydantic cho phần data.
## Cài đặt
@@ -139,7 +139,7 @@ $ pip install fastapi
-Bạn cũng sẽ cần một ASGI server cho production như
Uvicorn hoặc
Hypercorn.
+Bạn cũng sẽ cần một ASGI server cho production như
Uvicorn hoặc
Hypercorn.
@@ -464,7 +464,7 @@ Sử dụng Starlette:
Sử dụng bởi FastAPI / Starlette:
-*
uvicorn - Server để chạy ứng dụng của bạn.
+*
uvicorn - Server để chạy ứng dụng của bạn.
*
orjson - Bắt buộc nếu bạn muốn sử dụng `ORJSONResponse`.
*
ujson - Bắt buộc nếu bạn muốn sử dụng `UJSONResponse`.
diff --git a/docs/vi/docs/tutorial/first-steps.md b/docs/vi/docs/tutorial/first-steps.md
index 901c8fd59..d1650539c 100644
--- a/docs/vi/docs/tutorial/first-steps.md
+++ b/docs/vi/docs/tutorial/first-steps.md
@@ -139,7 +139,7 @@ Bạn cũng có thể sử dụng nó để sinh code tự động, với các c
`FastAPI` là một class kế thừa trực tiếp `Starlette`.
-Bạn cũng có thể sử dụng tất cả
Starlette chức năng với `FastAPI`.
+Bạn cũng có thể sử dụng tất cả
Starlette chức năng với `FastAPI`.
///
diff --git a/docs/vi/docs/tutorial/static-files.md b/docs/vi/docs/tutorial/static-files.md
index ecf8c2485..1bbec29e7 100644
--- a/docs/vi/docs/tutorial/static-files.md
+++ b/docs/vi/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@ Tất cả các tham số này có thể khác với `static`, điều chỉnh c
## Thông tin thêm
-Để biết thêm chi tiết và tùy chọn, hãy xem
Starlette's docs about Static Files.
+Để biết thêm chi tiết và tùy chọn, hãy xem
Starlette's docs about Static Files.
diff --git a/docs/zh-hant/docs/fastapi-cli.md b/docs/zh-hant/docs/fastapi-cli.md
index 3c644ce46..b107e7e73 100644
--- a/docs/zh-hant/docs/fastapi-cli.md
+++ b/docs/zh-hant/docs/fastapi-cli.md
@@ -60,7 +60,7 @@ FastAPI CLI 接收你的 Python 程式路徑(例如 `main.py`),並自動
在生產環境,你應該使用 `fastapi run` 命令。 🚀
-**FastAPI CLI** 內部使用了
Uvicorn,這是一個高效能、適合生產環境的 ASGI 伺服器。 😎
+**FastAPI CLI** 內部使用了
Uvicorn,這是一個高效能、適合生產環境的 ASGI 伺服器。 😎
## `fastapi dev`
diff --git a/docs/zh-hant/docs/features.md b/docs/zh-hant/docs/features.md
index 3a1392b51..f44d28a7f 100644
--- a/docs/zh-hant/docs/features.md
+++ b/docs/zh-hant/docs/features.md
@@ -167,7 +167,7 @@ FastAPI 有一個使用簡單,但是非常強大的
Starlette的CORS文档中记录的`expose_headers`参数。
+但是,如果你有自定义头部,你希望浏览器中的客户端能够看到它们,你需要将它们添加到你的CORS配置中(在[CORS(跨源资源共享)](../tutorial/cors.md){.internal-link target=_blank}中阅读更多),使用在Starlette的CORS文档中记录的`expose_headers`参数。
diff --git a/docs/zh/docs/advanced/templates.md b/docs/zh/docs/advanced/templates.md
index 8b7019ede..e627eed98 100644
--- a/docs/zh/docs/advanced/templates.md
+++ b/docs/zh/docs/advanced/templates.md
@@ -122,4 +122,4 @@ Item ID: 42
## 更多说明
-包括测试模板等更多详情,请参阅 Starlette 官方文档 - 模板。
+包括测试模板等更多详情,请参阅 Starlette 官方文档 - 模板。
diff --git a/docs/zh/docs/advanced/testing-websockets.md b/docs/zh/docs/advanced/testing-websockets.md
index 5d713d5f7..b84647a3e 100644
--- a/docs/zh/docs/advanced/testing-websockets.md
+++ b/docs/zh/docs/advanced/testing-websockets.md
@@ -8,6 +8,6 @@
/// note | 笔记
-更多细节详见 Starlette 官档 - 测试 WebSockets。
+更多细节详见 Starlette 官档 - 测试 WebSockets。
///
diff --git a/docs/zh/docs/advanced/using-request-directly.md b/docs/zh/docs/advanced/using-request-directly.md
index db0fcafdf..a9658c034 100644
--- a/docs/zh/docs/advanced/using-request-directly.md
+++ b/docs/zh/docs/advanced/using-request-directly.md
@@ -15,7 +15,7 @@
## `Request` 对象的细节
-实际上,**FastAPI** 的底层是 **Starlette**,**FastAPI** 只不过是在 **Starlette** 顶层提供了一些工具,所以能直接使用 Starlette 的 `Request` 对象。
+实际上,**FastAPI** 的底层是 **Starlette**,**FastAPI** 只不过是在 **Starlette** 顶层提供了一些工具,所以能直接使用 Starlette 的 `Request` 对象。
但直接从 `Request` 对象提取数据时(例如,读取请求体),**FastAPI** 不会验证、转换和存档数据(为 API 文档使用 OpenAPI)。
@@ -45,7 +45,7 @@
## `Request` 文档
-更多细节详见 Starlette 官档 - `Request` 对象。
+更多细节详见 Starlette 官档 - `Request` 对象。
/// note | 技术细节
diff --git a/docs/zh/docs/advanced/websockets.md b/docs/zh/docs/advanced/websockets.md
index d91aacc03..005ed9242 100644
--- a/docs/zh/docs/advanced/websockets.md
+++ b/docs/zh/docs/advanced/websockets.md
@@ -172,5 +172,5 @@ Client #1596980209979 left the chat
要了解更多选项,请查看 Starlette 的文档:
-* [WebSocket 类](https://www.starlette.io/websockets/)
-* [基于类的 WebSocket 处理](https://www.starlette.io/endpoints/#websocketendpoint)。
+* [WebSocket 类](https://www.starlette.dev/websockets/)
+* [基于类的 WebSocket 处理](https://www.starlette.dev/endpoints/#websocketendpoint)。
diff --git a/docs/zh/docs/deployment/manually.md b/docs/zh/docs/deployment/manually.md
index 3dc5942e3..2c2784a64 100644
--- a/docs/zh/docs/deployment/manually.md
+++ b/docs/zh/docs/deployment/manually.md
@@ -52,7 +52,7 @@ FastAPI 使用了一种用于构建 Python Web 框架和服务器的标准,称
除此之外,还有其他一些可选的 ASGI 服务器,例如:
-* Uvicorn:高性能 ASGI 服务器。
+* Uvicorn:高性能 ASGI 服务器。
* Hypercorn:与 HTTP/2 和 Trio 等兼容的 ASGI 服务器。
* Daphne:为 Django Channels 构建的 ASGI 服务器。
* Granian:基于 Rust 的 HTTP 服务器,专为 Python 应用设计。
diff --git a/docs/zh/docs/fastapi-cli.md b/docs/zh/docs/fastapi-cli.md
index 8a70e1d80..3b67eb664 100644
--- a/docs/zh/docs/fastapi-cli.md
+++ b/docs/zh/docs/fastapi-cli.md
@@ -52,7 +52,7 @@ FastAPI CLI 接收你的 Python 程序路径,自动检测包含 FastAPI 的变
在生产环境中,你应该使用 `fastapi run` 命令。🚀
-在内部,**FastAPI CLI** 使用了 Uvicorn,这是一个高性能、适用于生产环境的 ASGI 服务器。😎
+在内部,**FastAPI CLI** 使用了 Uvicorn,这是一个高性能、适用于生产环境的 ASGI 服务器。😎
## `fastapi dev`
diff --git a/docs/zh/docs/features.md b/docs/zh/docs/features.md
index 24dc3e8ce..eaf8daff7 100644
--- a/docs/zh/docs/features.md
+++ b/docs/zh/docs/features.md
@@ -165,7 +165,7 @@ FastAPI 有一个使用非常简单,但是非常强大的.
+更多细节查看 Starlette's official docs for Background Tasks.
## 告诫
diff --git a/docs/zh/docs/tutorial/first-steps.md b/docs/zh/docs/tutorial/first-steps.md
index 80a34116a..2d7c35c8c 100644
--- a/docs/zh/docs/tutorial/first-steps.md
+++ b/docs/zh/docs/tutorial/first-steps.md
@@ -155,7 +155,7 @@ OpenAPI 为你的 API 定义 API 模式。该模式中包含了你的 API 发送
`FastAPI` 是直接从 `Starlette` 继承的类。
-你可以通过 `FastAPI` 使用所有的 Starlette 的功能。
+你可以通过 `FastAPI` 使用所有的 Starlette 的功能。
///
diff --git a/docs/zh/docs/tutorial/handling-errors.md b/docs/zh/docs/tutorial/handling-errors.md
index 0b887c292..ae667b74a 100644
--- a/docs/zh/docs/tutorial/handling-errors.md
+++ b/docs/zh/docs/tutorial/handling-errors.md
@@ -83,7 +83,7 @@
## 安装自定义异常处理器
-添加自定义处理器,要使用 [Starlette 的异常工具](https://www.starlette.io/exceptions/)。
+添加自定义处理器,要使用 [Starlette 的异常工具](https://www.starlette.dev/exceptions/)。
假设要触发的自定义异常叫作 `UnicornException`。
diff --git a/docs/zh/docs/tutorial/middleware.md b/docs/zh/docs/tutorial/middleware.md
index 258ca7482..5608c4ee1 100644
--- a/docs/zh/docs/tutorial/middleware.md
+++ b/docs/zh/docs/tutorial/middleware.md
@@ -37,7 +37,7 @@
请记住可以 用'X-' 前缀添加专有自定义请求头.
-但是如果你想让浏览器中的客户端看到你的自定义请求头, 你需要把它们加到 CORS 配置 ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) 的 `expose_headers` 参数中,在 Starlette's CORS docs文档中.
+但是如果你想让浏览器中的客户端看到你的自定义请求头, 你需要把它们加到 CORS 配置 ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) 的 `expose_headers` 参数中,在 Starlette's CORS docs文档中.
///
diff --git a/docs/zh/docs/tutorial/static-files.md b/docs/zh/docs/tutorial/static-files.md
index c19079565..1a0d4504c 100644
--- a/docs/zh/docs/tutorial/static-files.md
+++ b/docs/zh/docs/tutorial/static-files.md
@@ -37,4 +37,4 @@
## 更多信息
-更多细节和选择查阅 Starlette's docs about Static Files.
+更多细节和选择查阅 Starlette's docs about Static Files.
diff --git a/docs/zh/docs/tutorial/testing.md b/docs/zh/docs/tutorial/testing.md
index 3e0c48caf..3877adbac 100644
--- a/docs/zh/docs/tutorial/testing.md
+++ b/docs/zh/docs/tutorial/testing.md
@@ -1,6 +1,6 @@
# 测试
-感谢 Starlette,测试**FastAPI** 应用轻松又愉快。
+感谢 Starlette,测试**FastAPI** 应用轻松又愉快。
它基于 HTTPX, 而HTTPX又是基于Requests设计的,所以很相似且易懂。
diff --git a/docs_src/dependencies/tutorial008e.py b/docs_src/dependencies/tutorial008e.py
new file mode 100644
index 000000000..1ed056e91
--- /dev/null
+++ b/docs_src/dependencies/tutorial008e.py
@@ -0,0 +1,15 @@
+from fastapi import Depends, FastAPI
+
+app = FastAPI()
+
+
+def get_username():
+ try:
+ yield "Rick"
+ finally:
+ print("Cleanup up before response is sent")
+
+
+@app.get("/users/me")
+def get_user_me(username: str = Depends(get_username, scope="function")):
+ return username
diff --git a/docs_src/dependencies/tutorial008e_an.py b/docs_src/dependencies/tutorial008e_an.py
new file mode 100644
index 000000000..c8a0af2b3
--- /dev/null
+++ b/docs_src/dependencies/tutorial008e_an.py
@@ -0,0 +1,16 @@
+from fastapi import Depends, FastAPI
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+
+def get_username():
+ try:
+ yield "Rick"
+ finally:
+ print("Cleanup up before response is sent")
+
+
+@app.get("/users/me")
+def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
+ return username
diff --git a/docs_src/dependencies/tutorial008e_an_py39.py b/docs_src/dependencies/tutorial008e_an_py39.py
new file mode 100644
index 000000000..80a44c7e2
--- /dev/null
+++ b/docs_src/dependencies/tutorial008e_an_py39.py
@@ -0,0 +1,17 @@
+from typing import Annotated
+
+from fastapi import Depends, FastAPI
+
+app = FastAPI()
+
+
+def get_username():
+ try:
+ yield "Rick"
+ finally:
+ print("Cleanup up before response is sent")
+
+
+@app.get("/users/me")
+def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
+ return username
diff --git a/docs_src/dependencies/tutorial013a.py b/docs_src/dependencies/tutorial013a.py
index 83687af24..423e3be18 100644
--- a/docs_src/dependencies/tutorial013a.py
+++ b/docs_src/dependencies/tutorial013a.py
@@ -27,7 +27,7 @@ async def get_database_connection():
yield connection
-GlobalDatabaseConnection = Depends(get_database_connection, dependency_scope="lifespan")
+GlobalDatabaseConnection = Depends(get_database_connection, scope="lifespan")
@app.get("/users/")
diff --git a/docs_src/dependencies/tutorial013a_an_py39.py b/docs_src/dependencies/tutorial013a_an_py39.py
index 62f10a6e1..d30d75d5d 100644
--- a/docs_src/dependencies/tutorial013a_an_py39.py
+++ b/docs_src/dependencies/tutorial013a_an_py39.py
@@ -28,7 +28,7 @@ async def get_database_connection():
GlobalDatabaseConnection = Annotated[
- MyDatabaseConnection, Depends(get_database_connection, dependency_scope="lifespan")
+ MyDatabaseConnection, Depends(get_database_connection, scope="lifespan")
]
diff --git a/docs_src/dependencies/tutorial013b.py b/docs_src/dependencies/tutorial013b.py
index 3123b64f5..789254a57 100644
--- a/docs_src/dependencies/tutorial013b.py
+++ b/docs_src/dependencies/tutorial013b.py
@@ -30,9 +30,9 @@ async def get_database_connection():
yield connection
-GlobalDatabaseConnection = Depends(get_database_connection, dependency_scope="lifespan")
+GlobalDatabaseConnection = Depends(get_database_connection, scope="lifespan")
DedicatedDatabaseConnection = Depends(
- get_database_connection, dependency_scope="lifespan", use_cache=False
+ get_database_connection, scope="lifespan", use_cache=False
)
diff --git a/docs_src/dependencies/tutorial013b_an_py39.py b/docs_src/dependencies/tutorial013b_an_py39.py
index cc7205f40..af3475c26 100644
--- a/docs_src/dependencies/tutorial013b_an_py39.py
+++ b/docs_src/dependencies/tutorial013b_an_py39.py
@@ -31,11 +31,11 @@ async def get_database_connection():
GlobalDatabaseConnection = Annotated[
- MyDatabaseConnection, Depends(get_database_connection, dependency_scope="lifespan")
+ MyDatabaseConnection, Depends(get_database_connection, scope="lifespan")
]
DedicatedDatabaseConnection = Annotated[
MyDatabaseConnection,
- Depends(get_database_connection, dependency_scope="lifespan", use_cache=False),
+ Depends(get_database_connection, scope="lifespan", use_cache=False),
]
diff --git a/docs_src/dependencies/tutorial013c.py b/docs_src/dependencies/tutorial013c.py
index c8814adc0..dcd1bac73 100644
--- a/docs_src/dependencies/tutorial013c.py
+++ b/docs_src/dependencies/tutorial013c.py
@@ -31,7 +31,7 @@ async def get_configuration() -> dict:
}
-GlobalConfiguration = Depends(get_configuration, dependency_scope="lifespan")
+GlobalConfiguration = Depends(get_configuration, scope="lifespan")
async def get_database_connection(configuration: dict = GlobalConfiguration):
@@ -39,7 +39,7 @@ async def get_database_connection(configuration: dict = GlobalConfiguration):
yield connection
-GlobalDatabaseConnection = Depends(get_database_connection, dependency_scope="lifespan")
+GlobalDatabaseConnection = Depends(get_database_connection, scope="lifespan")
@app.get("/users/{user_id}")
diff --git a/docs_src/dependencies/tutorial013c_an_py39.py b/docs_src/dependencies/tutorial013c_an_py39.py
index a64e72b8a..d9fcceed2 100644
--- a/docs_src/dependencies/tutorial013c_an_py39.py
+++ b/docs_src/dependencies/tutorial013c_an_py39.py
@@ -33,7 +33,7 @@ async def get_configuration() -> dict:
GlobalConfiguration = Annotated[
- dict, Depends(get_configuration, dependency_scope="lifespan")
+ dict, Depends(get_configuration, scope="lifespan")
]
@@ -44,7 +44,7 @@ async def get_database_connection(configuration: GlobalConfiguration):
GlobalDatabaseConnection = Annotated[
get_database_connection,
- Depends(get_database_connection, dependency_scope="lifespan"),
+ Depends(get_database_connection, scope="lifespan"),
]
diff --git a/docs_src/dependencies/tutorial013d.py b/docs_src/dependencies/tutorial013d.py
index 01e2831d7..8543c159a 100644
--- a/docs_src/dependencies/tutorial013d.py
+++ b/docs_src/dependencies/tutorial013d.py
@@ -25,7 +25,7 @@ async def get_database_connection():
yield connection
-GlobalDatabaseConnection = Depends(get_database_connection, dependency_scope="lifespan")
+GlobalDatabaseConnection = Depends(get_database_connection, scope="lifespan")
async def get_user_record(
diff --git a/docs_src/dependencies/tutorial013d_an_py39.py b/docs_src/dependencies/tutorial013d_an_py39.py
index fa6b0831b..77c0494e6 100644
--- a/docs_src/dependencies/tutorial013d_an_py39.py
+++ b/docs_src/dependencies/tutorial013d_an_py39.py
@@ -28,7 +28,7 @@ async def get_database_connection():
GlobalDatabaseConnection = Annotated[
- MyDatabaseConnection, Depends(get_database_connection, dependency_scope="lifespan")
+ MyDatabaseConnection, Depends(get_database_connection, scope="lifespan")
]
diff --git a/fastapi/__init__.py b/fastapi/__init__.py
index 2091f0d1f..f4a952bf5 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.119.0"
+__version__ = "0.121.0"
from starlette import status as status
diff --git a/fastapi/_compat/__init__.py b/fastapi/_compat/__init__.py
index b2ae5adc7..0aadd68de 100644
--- a/fastapi/_compat/__init__.py
+++ b/fastapi/_compat/__init__.py
@@ -30,6 +30,10 @@ from .main import serialize_sequence_value as serialize_sequence_value
from .main import (
with_info_plain_validator_function as with_info_plain_validator_function,
)
+from .may_v1 import CoreSchema as CoreSchema
+from .may_v1 import GetJsonSchemaHandler as GetJsonSchemaHandler
+from .may_v1 import JsonSchemaValue as JsonSchemaValue
+from .may_v1 import _normalize_errors as _normalize_errors
from .model_field import ModelField as ModelField
from .shared import PYDANTIC_V2 as PYDANTIC_V2
from .shared import PYDANTIC_VERSION_MINOR_TUPLE as PYDANTIC_VERSION_MINOR_TUPLE
@@ -44,7 +48,3 @@ from .shared import (
from .shared import lenient_issubclass as lenient_issubclass
from .shared import sequence_types as sequence_types
from .shared import value_is_sequence as value_is_sequence
-from .v1 import CoreSchema as CoreSchema
-from .v1 import GetJsonSchemaHandler as GetJsonSchemaHandler
-from .v1 import JsonSchemaValue as JsonSchemaValue
-from .v1 import _normalize_errors as _normalize_errors
diff --git a/fastapi/_compat/main.py b/fastapi/_compat/main.py
index 3f758f072..e5275950e 100644
--- a/fastapi/_compat/main.py
+++ b/fastapi/_compat/main.py
@@ -1,3 +1,4 @@
+import sys
from functools import lru_cache
from typing import (
Any,
@@ -8,7 +9,7 @@ from typing import (
Type,
)
-from fastapi._compat import v1
+from fastapi._compat import may_v1
from fastapi._compat.shared import PYDANTIC_V2, lenient_issubclass
from fastapi.types import ModelNameMap
from pydantic import BaseModel
@@ -50,7 +51,9 @@ else:
@lru_cache
def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
- if lenient_issubclass(model, v1.BaseModel):
+ if lenient_issubclass(model, may_v1.BaseModel):
+ from fastapi._compat import v1
+
return v1.get_model_fields(model)
else:
from . import v2
@@ -59,7 +62,7 @@ def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
def _is_undefined(value: object) -> bool:
- if isinstance(value, v1.UndefinedType):
+ if isinstance(value, may_v1.UndefinedType):
return True
elif PYDANTIC_V2:
from . import v2
@@ -69,7 +72,9 @@ def _is_undefined(value: object) -> bool:
def _get_model_config(model: BaseModel) -> Any:
- if isinstance(model, v1.BaseModel):
+ if isinstance(model, may_v1.BaseModel):
+ from fastapi._compat import v1
+
return v1._get_model_config(model)
elif PYDANTIC_V2:
from . import v2
@@ -80,7 +85,9 @@ def _get_model_config(model: BaseModel) -> Any:
def _model_dump(
model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
) -> Any:
- if isinstance(model, v1.BaseModel):
+ if isinstance(model, may_v1.BaseModel):
+ from fastapi._compat import v1
+
return v1._model_dump(model, mode=mode, **kwargs)
elif PYDANTIC_V2:
from . import v2
@@ -89,7 +96,7 @@ def _model_dump(
def _is_error_wrapper(exc: Exception) -> bool:
- if isinstance(exc, v1.ErrorWrapper):
+ if isinstance(exc, may_v1.ErrorWrapper):
return True
elif PYDANTIC_V2:
from . import v2
@@ -99,7 +106,9 @@ def _is_error_wrapper(exc: Exception) -> bool:
def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
- if isinstance(field_info, v1.FieldInfo):
+ if isinstance(field_info, may_v1.FieldInfo):
+ from fastapi._compat import v1
+
return v1.copy_field_info(field_info=field_info, annotation=annotation)
else:
assert PYDANTIC_V2
@@ -111,7 +120,9 @@ def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
def create_body_model(
*, fields: Sequence[ModelField], model_name: str
) -> Type[BaseModel]:
- if fields and isinstance(fields[0], v1.ModelField):
+ if fields and isinstance(fields[0], may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.create_body_model(fields=fields, model_name=model_name)
else:
assert PYDANTIC_V2
@@ -123,7 +134,9 @@ def create_body_model(
def get_annotation_from_field_info(
annotation: Any, field_info: FieldInfo, field_name: str
) -> Any:
- if isinstance(field_info, v1.FieldInfo):
+ if isinstance(field_info, may_v1.FieldInfo):
+ from fastapi._compat import v1
+
return v1.get_annotation_from_field_info(
annotation=annotation, field_info=field_info, field_name=field_name
)
@@ -137,7 +150,9 @@ def get_annotation_from_field_info(
def is_bytes_field(field: ModelField) -> bool:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.is_bytes_field(field)
else:
assert PYDANTIC_V2
@@ -147,7 +162,9 @@ def is_bytes_field(field: ModelField) -> bool:
def is_bytes_sequence_field(field: ModelField) -> bool:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.is_bytes_sequence_field(field)
else:
assert PYDANTIC_V2
@@ -157,7 +174,9 @@ def is_bytes_sequence_field(field: ModelField) -> bool:
def is_scalar_field(field: ModelField) -> bool:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.is_scalar_field(field)
else:
assert PYDANTIC_V2
@@ -167,7 +186,9 @@ def is_scalar_field(field: ModelField) -> bool:
def is_scalar_sequence_field(field: ModelField) -> bool:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.is_scalar_sequence_field(field)
else:
assert PYDANTIC_V2
@@ -177,7 +198,9 @@ def is_scalar_sequence_field(field: ModelField) -> bool:
def is_sequence_field(field: ModelField) -> bool:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.is_sequence_field(field)
else:
assert PYDANTIC_V2
@@ -187,7 +210,9 @@ def is_sequence_field(field: ModelField) -> bool:
def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.serialize_sequence_value(field=field, value=value)
else:
assert PYDANTIC_V2
@@ -197,7 +222,9 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
def _model_rebuild(model: Type[BaseModel]) -> None:
- if lenient_issubclass(model, v1.BaseModel):
+ if lenient_issubclass(model, may_v1.BaseModel):
+ from fastapi._compat import v1
+
v1._model_rebuild(model)
elif PYDANTIC_V2:
from . import v2
@@ -206,9 +233,18 @@ def _model_rebuild(model: Type[BaseModel]) -> None:
def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
- v1_model_fields = [field for field in fields if isinstance(field, v1.ModelField)]
- v1_flat_models = v1.get_flat_models_from_fields(v1_model_fields, known_models=set()) # type: ignore[attr-defined]
- all_flat_models = v1_flat_models
+ v1_model_fields = [
+ field for field in fields if isinstance(field, may_v1.ModelField)
+ ]
+ if v1_model_fields:
+ from fastapi._compat import v1
+
+ v1_flat_models = v1.get_flat_models_from_fields(
+ v1_model_fields, known_models=set()
+ )
+ all_flat_models = v1_flat_models
+ else:
+ all_flat_models = set()
if PYDANTIC_V2:
from . import v2
@@ -222,6 +258,8 @@ def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
model_name_map = v2.get_model_name_map(all_flat_models)
return model_name_map
+ from fastapi._compat import v1
+
model_name_map = v1.get_model_name_map(all_flat_models)
return model_name_map
@@ -232,17 +270,35 @@ def get_definitions(
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> Tuple[
- Dict[Tuple[ModelField, Literal["validation", "serialization"]], v1.JsonSchemaValue],
+ Dict[
+ Tuple[ModelField, Literal["validation", "serialization"]],
+ may_v1.JsonSchemaValue,
+ ],
Dict[str, Dict[str, Any]],
]:
- v1_fields = [field for field in fields if isinstance(field, v1.ModelField)]
- v1_field_maps, v1_definitions = v1.get_definitions(
- fields=v1_fields,
- model_name_map=model_name_map,
- separate_input_output_schemas=separate_input_output_schemas,
- )
- if not PYDANTIC_V2:
- return v1_field_maps, v1_definitions
+ if sys.version_info < (3, 14):
+ v1_fields = [field for field in fields if isinstance(field, may_v1.ModelField)]
+ v1_field_maps, v1_definitions = may_v1.get_definitions(
+ fields=v1_fields,
+ model_name_map=model_name_map,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ if not PYDANTIC_V2:
+ return v1_field_maps, v1_definitions
+ else:
+ from . import v2
+
+ v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]
+ v2_field_maps, v2_definitions = v2.get_definitions(
+ fields=v2_fields,
+ model_name_map=model_name_map,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ all_definitions = {**v1_definitions, **v2_definitions}
+ all_field_maps = {**v1_field_maps, **v2_field_maps}
+ return all_field_maps, all_definitions
+
+ # Pydantic v1 is not supported since Python 3.14
else:
from . import v2
@@ -252,9 +308,7 @@ def get_definitions(
model_name_map=model_name_map,
separate_input_output_schemas=separate_input_output_schemas,
)
- all_definitions = {**v1_definitions, **v2_definitions}
- all_field_maps = {**v1_field_maps, **v2_field_maps}
- return all_field_maps, all_definitions
+ return v2_field_maps, v2_definitions
def get_schema_from_model_field(
@@ -262,11 +316,14 @@ def get_schema_from_model_field(
field: ModelField,
model_name_map: ModelNameMap,
field_mapping: Dict[
- Tuple[ModelField, Literal["validation", "serialization"]], v1.JsonSchemaValue
+ Tuple[ModelField, Literal["validation", "serialization"]],
+ may_v1.JsonSchemaValue,
],
separate_input_output_schemas: bool = True,
) -> Dict[str, Any]:
- if isinstance(field, v1.ModelField):
+ if isinstance(field, may_v1.ModelField):
+ from fastapi._compat import v1
+
return v1.get_schema_from_model_field(
field=field,
model_name_map=model_name_map,
@@ -286,7 +343,7 @@ def get_schema_from_model_field(
def _is_model_field(value: Any) -> bool:
- if isinstance(value, v1.ModelField):
+ if isinstance(value, may_v1.ModelField):
return True
elif PYDANTIC_V2:
from . import v2
@@ -296,7 +353,7 @@ def _is_model_field(value: Any) -> bool:
def _is_model_class(value: Any) -> bool:
- if lenient_issubclass(value, v1.BaseModel):
+ if lenient_issubclass(value, may_v1.BaseModel):
return True
elif PYDANTIC_V2:
from . import v2
diff --git a/fastapi/_compat/may_v1.py b/fastapi/_compat/may_v1.py
new file mode 100644
index 000000000..beea4d167
--- /dev/null
+++ b/fastapi/_compat/may_v1.py
@@ -0,0 +1,123 @@
+import sys
+from typing import Any, Dict, List, Literal, Sequence, Tuple, Type, Union
+
+from fastapi.types import ModelNameMap
+
+if sys.version_info >= (3, 14):
+
+ class AnyUrl:
+ pass
+
+ class BaseConfig:
+ pass
+
+ class BaseModel:
+ pass
+
+ class Color:
+ pass
+
+ class CoreSchema:
+ pass
+
+ class ErrorWrapper:
+ pass
+
+ class FieldInfo:
+ pass
+
+ class GetJsonSchemaHandler:
+ pass
+
+ class JsonSchemaValue:
+ pass
+
+ class ModelField:
+ pass
+
+ class NameEmail:
+ pass
+
+ class RequiredParam:
+ pass
+
+ class SecretBytes:
+ pass
+
+ class SecretStr:
+ pass
+
+ class Undefined:
+ pass
+
+ class UndefinedType:
+ pass
+
+ class Url:
+ pass
+
+ from .v2 import ValidationError, create_model
+
+ def get_definitions(
+ *,
+ fields: List[ModelField],
+ model_name_map: ModelNameMap,
+ separate_input_output_schemas: bool = True,
+ ) -> Tuple[
+ Dict[
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+ ],
+ Dict[str, Dict[str, Any]],
+ ]:
+ return {}, {} # pragma: no cover
+
+
+else:
+ from .v1 import AnyUrl as AnyUrl
+ from .v1 import BaseConfig as BaseConfig
+ from .v1 import BaseModel as BaseModel
+ from .v1 import Color as Color
+ from .v1 import CoreSchema as CoreSchema
+ from .v1 import ErrorWrapper as ErrorWrapper
+ from .v1 import FieldInfo as FieldInfo
+ from .v1 import GetJsonSchemaHandler as GetJsonSchemaHandler
+ from .v1 import JsonSchemaValue as JsonSchemaValue
+ from .v1 import ModelField as ModelField
+ from .v1 import NameEmail as NameEmail
+ from .v1 import RequiredParam as RequiredParam
+ from .v1 import SecretBytes as SecretBytes
+ from .v1 import SecretStr as SecretStr
+ from .v1 import Undefined as Undefined
+ from .v1 import UndefinedType as UndefinedType
+ from .v1 import Url as Url
+ from .v1 import ValidationError, create_model
+ from .v1 import get_definitions as get_definitions
+
+
+RequestErrorModel: Type[BaseModel] = create_model("Request")
+
+
+def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
+ use_errors: List[Any] = []
+ for error in errors:
+ if isinstance(error, ErrorWrapper):
+ new_errors = ValidationError( # type: ignore[call-arg]
+ errors=[error], model=RequestErrorModel
+ ).errors()
+ use_errors.extend(new_errors)
+ elif isinstance(error, list):
+ use_errors.extend(_normalize_errors(error))
+ else:
+ use_errors.append(error)
+ return use_errors
+
+
+def _regenerate_error_with_loc(
+ *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...]
+) -> List[Dict[str, Any]]:
+ updated_loc_errors: List[Any] = [
+ {**err, "loc": loc_prefix + err.get("loc", ())}
+ for err in _normalize_errors(errors)
+ ]
+
+ return updated_loc_errors
diff --git a/fastapi/_compat/shared.py b/fastapi/_compat/shared.py
index 495d5c5f7..cabf48228 100644
--- a/fastapi/_compat/shared.py
+++ b/fastapi/_compat/shared.py
@@ -16,7 +16,7 @@ from typing import (
Union,
)
-from fastapi._compat import v1
+from fastapi._compat import may_v1
from fastapi.types import UnionType
from pydantic import BaseModel
from pydantic.version import VERSION as PYDANTIC_VERSION
@@ -98,7 +98,9 @@ def value_is_sequence(value: Any) -> bool:
def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
return (
- lenient_issubclass(annotation, (BaseModel, v1.BaseModel, Mapping, UploadFile))
+ lenient_issubclass(
+ annotation, (BaseModel, may_v1.BaseModel, Mapping, UploadFile)
+ )
or _annotation_is_sequence(annotation)
or is_dataclass(annotation)
)
@@ -195,12 +197,12 @@ def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
def annotation_is_pydantic_v1(annotation: Any) -> bool:
- if lenient_issubclass(annotation, v1.BaseModel):
+ if lenient_issubclass(annotation, may_v1.BaseModel):
return True
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
- if lenient_issubclass(arg, v1.BaseModel):
+ if lenient_issubclass(arg, may_v1.BaseModel):
return True
if field_annotation_is_sequence(annotation):
for sub_annotation in get_args(annotation):
diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py
index f0ac51634..e17ce8bea 100644
--- a/fastapi/_compat/v1.py
+++ b/fastapi/_compat/v1.py
@@ -54,13 +54,15 @@ if not PYDANTIC_V2:
from pydantic.schema import TypeModelSet as TypeModelSet
from pydantic.schema import (
field_schema,
- get_flat_models_from_fields,
model_process_schema,
)
from pydantic.schema import (
get_annotation_from_field_info as get_annotation_from_field_info,
)
from pydantic.schema import get_flat_models_from_field as get_flat_models_from_field
+ from pydantic.schema import (
+ get_flat_models_from_fields as get_flat_models_from_fields,
+ )
from pydantic.schema import get_model_name_map as get_model_name_map
from pydantic.types import SecretBytes as SecretBytes
from pydantic.types import SecretStr as SecretStr
@@ -99,7 +101,6 @@ else:
from pydantic.v1.schema import TypeModelSet as TypeModelSet
from pydantic.v1.schema import (
field_schema,
- get_flat_models_from_fields,
model_process_schema,
)
from pydantic.v1.schema import (
@@ -108,6 +109,9 @@ else:
from pydantic.v1.schema import (
get_flat_models_from_field as get_flat_models_from_field,
)
+ from pydantic.v1.schema import (
+ get_flat_models_from_fields as get_flat_models_from_fields,
+ )
from pydantic.v1.schema import get_model_name_map as get_model_name_map
from pydantic.v1.types import ( # type: ignore[assignment]
SecretBytes as SecretBytes,
@@ -215,32 +219,6 @@ def is_pv1_scalar_sequence_field(field: ModelField) -> bool:
return False
-def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
- use_errors: List[Any] = []
- for error in errors:
- if isinstance(error, ErrorWrapper):
- new_errors = ValidationError( # type: ignore[call-arg]
- errors=[error], model=RequestErrorModel
- ).errors()
- use_errors.extend(new_errors)
- elif isinstance(error, list):
- use_errors.extend(_normalize_errors(error))
- else:
- use_errors.append(error)
- return use_errors
-
-
-def _regenerate_error_with_loc(
- *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...]
-) -> List[Dict[str, Any]]:
- updated_loc_errors: List[Any] = [
- {**err, "loc": loc_prefix + err.get("loc", ())}
- for err in _normalize_errors(errors)
- ]
-
- return updated_loc_errors
-
-
def _model_rebuild(model: Type[BaseModel]) -> None:
model.update_forward_refs()
diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 29606b9f3..6a87b9ae9 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -15,7 +15,7 @@ from typing import (
cast,
)
-from fastapi._compat import shared, v1
+from fastapi._compat import may_v1, shared
from fastapi.openapi.constants import REF_TEMPLATE
from fastapi.types import IncEx, ModelNameMap
from pydantic import BaseModel, TypeAdapter, create_model
@@ -116,7 +116,7 @@ class ModelField:
None,
)
except ValidationError as exc:
- return None, v1._regenerate_error_with_loc(
+ return None, may_v1._regenerate_error_with_loc(
errors=exc.errors(include_url=False), loc_prefix=loc
)
@@ -207,11 +207,31 @@ def get_definitions(
override_mode: Union[Literal["validation"], None] = (
None if separate_input_output_schemas else "validation"
)
- flat_models = get_flat_models_from_fields(fields, known_models=set())
- flat_model_fields = [
- ModelField(field_info=FieldInfo(annotation=model), name=model.__name__)
- for model in flat_models
+ validation_fields = [field for field in fields if field.mode == "validation"]
+ serialization_fields = [field for field in fields if field.mode == "serialization"]
+ flat_validation_models = get_flat_models_from_fields(
+ validation_fields, known_models=set()
+ )
+ flat_serialization_models = get_flat_models_from_fields(
+ serialization_fields, known_models=set()
+ )
+ flat_validation_model_fields = [
+ ModelField(
+ field_info=FieldInfo(annotation=model),
+ name=model.__name__,
+ mode="validation",
+ )
+ for model in flat_validation_models
+ ]
+ flat_serialization_model_fields = [
+ ModelField(
+ field_info=FieldInfo(annotation=model),
+ name=model.__name__,
+ mode="serialization",
+ )
+ for model in flat_serialization_models
]
+ flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields
input_types = {f.type_ for f in fields}
unique_flat_model_fields = {
f for f in flat_model_fields if f.type_ not in input_types
diff --git a/fastapi/applications.py b/fastapi/applications.py
index 1c0d410df..864611121 100644
--- a/fastapi/applications.py
+++ b/fastapi/applications.py
@@ -15,9 +15,9 @@ from typing import (
Union,
)
+from annotated_doc import Doc
from fastapi import routing
from fastapi.datastructures import Default, DefaultPlaceholder
-from fastapi.dependencies.utils import is_coroutine_callable
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
@@ -36,7 +36,7 @@ from fastapi.openapi.utils import get_openapi
from fastapi.params import Depends
from fastapi.routing import merge_lifespan_context
from fastapi.types import DecoratedCallable, IncEx
-from fastapi.utils import generate_unique_id
+from fastapi.utils import generate_unique_id, call_asynchronously
from starlette.applications import Starlette
from starlette.concurrency import run_in_threadpool
from starlette.datastructures import State
@@ -49,7 +49,7 @@ from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
from starlette.types import ASGIApp, ExceptionHandler, Lifespan, Receive, Scope, Send
-from typing_extensions import Annotated, Doc, deprecated
+from typing_extensions import Annotated, deprecated
AppType = TypeVar("AppType", bound="FastAPI")
@@ -81,7 +81,7 @@ class FastAPI(Starlette):
errors.
Read more in the
- [Starlette docs for Applications](https://www.starlette.io/applications/#instantiating-the-application).
+ [Starlette docs for Applications](https://www.starlette.dev/applications/#instantiating-the-application).
"""
),
] = False,
@@ -944,7 +944,7 @@ class FastAPI(Starlette):
This is simply inherited from Starlette.
Read more about it in the
- [Starlette docs for Applications](https://www.starlette.io/applications/#storing-state-on-the-app-instance).
+ [Starlette docs for Applications](https://www.starlette.dev/applications/#storing-state-on-the-app-instance).
"""
),
] = State()
@@ -1069,10 +1069,7 @@ class FastAPI(Starlette):
)
try:
for handler in self._on_startup:
- if is_coroutine_callable(handler):
- await handler()
- else:
- await run_in_threadpool(handler)
+ await call_asynchronously(handler)
yield {
"__fastapi__": {
"lifespan_scoped_dependencies": lifespan_scoped_dependencies
@@ -1080,10 +1077,7 @@ class FastAPI(Starlette):
}
finally:
for handler in self._on_shutdown:
- if is_coroutine_callable(handler):
- await handler()
- else:
- await run_in_threadpool(handler)
+ await call_asynchronously(handler)
def openapi(self) -> Dict[str, Any]:
"""
diff --git a/fastapi/background.py b/fastapi/background.py
index 203578a41..6d4a30d44 100644
--- a/fastapi/background.py
+++ b/fastapi/background.py
@@ -1,7 +1,8 @@
from typing import Any, Callable
+from annotated_doc import Doc
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
-from typing_extensions import Annotated, Doc, ParamSpec
+from typing_extensions import Annotated, ParamSpec
P = ParamSpec("P")
diff --git a/fastapi/datastructures.py b/fastapi/datastructures.py
index 34185b96a..8ad9aa11a 100644
--- a/fastapi/datastructures.py
+++ b/fastapi/datastructures.py
@@ -10,6 +10,7 @@ from typing import (
cast,
)
+from annotated_doc import Doc
from fastapi._compat import (
CoreSchema,
GetJsonSchemaHandler,
@@ -22,7 +23,7 @@ from starlette.datastructures import Headers as Headers # noqa: F401
from starlette.datastructures import QueryParams as QueryParams # noqa: F401
from starlette.datastructures import State as State # noqa: F401
from starlette.datastructures import UploadFile as StarletteUploadFile
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class UploadFile(StarletteUploadFile):
diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py
index 1f4192a51..22df740c9 100644
--- a/fastapi/dependencies/models.py
+++ b/fastapi/dependencies/models.py
@@ -1,9 +1,18 @@
+import inspect
+import sys
from dataclasses import dataclass, field
-from typing import Any, Callable, List, Optional, Sequence, Tuple, Union, cast
+from functools import cached_property
+from typing import Any, Callable, List, Optional, Sequence, Union, Tuple, cast
from fastapi._compat import ModelField
from fastapi.security.base import SecurityBase
-from typing_extensions import TypeAlias
+from fastapi.types import EndpointDependencyCacheKey, LifespanDependencyCacheKey
+from typing_extensions import Literal, TypeAlias
+
+if sys.version_info >= (3, 13): # pragma: no cover
+ from inspect import iscoroutinefunction
+else: # pragma: no cover
+ from asyncio import iscoroutinefunction
@dataclass
@@ -12,11 +21,6 @@ class SecurityRequirement:
scopes: Optional[Sequence[str]] = None
-LifespanDependantCacheKey: TypeAlias = Union[
- Tuple[Callable[..., Any], Union[str, int]], Callable[..., Any]
-]
-
-
@dataclass
class LifespanDependant:
call: Callable[..., Any]
@@ -25,40 +29,55 @@ class LifespanDependant:
name: Optional[str] = None
use_cache: bool = True
index: Optional[int] = None
- cache_key: LifespanDependantCacheKey = field(init=False)
- def __post_init__(self) -> None:
+ @cached_property
+ def is_gen_callable(self) -> bool:
+ if inspect.isgeneratorfunction(self.call):
+ return True
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return inspect.isgeneratorfunction(dunder_call)
+
+ @cached_property
+ def is_async_gen_callable(self) -> bool:
+ if inspect.isasyncgenfunction(self.call):
+ return True
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return inspect.isasyncgenfunction(dunder_call)
+
+ @cached_property
+ def is_coroutine_callable(self) -> bool:
+ if inspect.isroutine(self.call):
+ return iscoroutinefunction(self.call)
+ if inspect.isclass(self.call):
+ return False
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return iscoroutinefunction(dunder_call)
+
+ @cached_property
+ def cache_key(self) -> LifespanDependencyCacheKey:
if self.use_cache:
- self.cache_key = self.call
+ return self.call
elif self.name is not None:
- self.cache_key = (self.caller, self.name)
+ return self.caller, self.name
else:
assert self.index is not None, (
"Lifespan dependency must have an associated name or index."
)
- self.cache_key = (self.caller, self.index)
-
-
-EndpointDependantCacheKey: TypeAlias = Tuple[
- Optional[Callable[..., Any]], Tuple[str, ...]
-]
-
+ return self.caller, self.index
@dataclass
class EndpointDependant:
- endpoint_dependencies: List["EndpointDependant"] = field(default_factory=list)
- lifespan_dependencies: List[LifespanDependant] = field(default_factory=list)
- name: Optional[str] = None
- call: Optional[Callable[..., Any]] = None
- use_cache: bool = True
- index: Optional[int] = None
- cache_key: Tuple[Optional[Callable[..., Any]], Tuple[str, ...]] = field(init=False)
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)
+ endpoint_dependencies: List["EndpointDependant"] = field(default_factory=list)
+ lifespan_dependencies: List[LifespanDependant] = field(default_factory=list)
security_requirements: List[SecurityRequirement] = field(default_factory=list)
+ name: Optional[str] = None
+ call: Optional[Callable[..., Any]] = None
+ index: Optional[int] = None
request_param_name: Optional[str] = None
websocket_param_name: Optional[str] = None
http_connection_param_name: Optional[str] = None
@@ -66,10 +85,48 @@ class EndpointDependant:
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
+ scope: Union[Literal["function", "request"], None] = None
+
+ @cached_property
+ def cache_key(self) -> EndpointDependencyCacheKey:
+ return (
+ self.call,
+ tuple(sorted(set(self.security_scopes or []))),
+ self.computed_scope or "",
+ )
- def __post_init__(self) -> None:
- self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))
+ @cached_property
+ def is_gen_callable(self) -> bool:
+ if inspect.isgeneratorfunction(self.call):
+ return True
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return inspect.isgeneratorfunction(dunder_call)
+
+ @cached_property
+ def is_async_gen_callable(self) -> bool:
+ if inspect.isasyncgenfunction(self.call):
+ return True
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return inspect.isasyncgenfunction(dunder_call)
+
+ @cached_property
+ def is_coroutine_callable(self) -> bool:
+ if inspect.isroutine(self.call):
+ return iscoroutinefunction(self.call)
+ if inspect.isclass(self.call):
+ return False
+ dunder_call = getattr(self.call, "__call__", None) # noqa: B004
+ return iscoroutinefunction(dunder_call)
+
+ @cached_property
+ def computed_scope(self) -> Union[str, None]:
+ if self.scope:
+ return self.scope
+ if self.is_gen_callable or self.is_async_gen_callable:
+ return "request"
+ return None
# Kept for backwards compatibility
@property
@@ -87,5 +144,4 @@ class EndpointDependant:
# Kept for backwards compatibility
-Dependant = EndpointDependant
-CacheKey: TypeAlias = Union[EndpointDependantCacheKey, LifespanDependantCacheKey]
+Dependant: TypeAlias = EndpointDependant
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index 56c178c76..a58f24f56 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -1,5 +1,4 @@
import inspect
-import sys
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
from dataclasses import dataclass
@@ -43,9 +42,9 @@ from fastapi._compat import (
is_uploadfile_or_nonable_uploadfile_annotation,
is_uploadfile_sequence_annotation,
lenient_issubclass,
+ may_v1,
sequence_types,
serialize_sequence_value,
- v1,
value_is_sequence,
)
from fastapi._compat.shared import annotation_is_pydantic_v1
@@ -54,23 +53,13 @@ from fastapi.concurrency import (
asynccontextmanager,
contextmanager_in_threadpool,
)
-from fastapi.dependencies.models import (
- CacheKey,
- EndpointDependant,
- EndpointDependantCacheKey,
- LifespanDependant,
- LifespanDependantCacheKey,
- SecurityRequirement,
-)
-from fastapi.exceptions import (
- DependencyScopeConflict,
- InvalidDependencyScope,
- UninitializedLifespanDependency,
-)
+from fastapi.dependencies.models import EndpointDependant, LifespanDependant, SecurityRequirement
+from fastapi.exceptions import DependencyScopeError, InvalidDependencyScope, UninitializedLifespanDependency
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.types import EndpointDependencyCacheKey, LifespanDependencyCacheKey
from fastapi.utils import create_model_field, get_path_param_names
from pydantic import BaseModel
from pydantic.fields import FieldInfo
@@ -86,15 +75,10 @@ from starlette.datastructures import (
from starlette.requests import HTTPConnection, Request
from starlette.responses import Response
from starlette.websockets import WebSocket
-from typing_extensions import Annotated, get_args, get_origin
+from typing_extensions import Annotated, Literal, get_args, get_origin
from .. import temp_pydantic_v1_params
-if sys.version_info >= (3, 13): # pragma: no cover
- from inspect import iscoroutinefunction
-else: # pragma: no cover
- from asyncio import iscoroutinefunction
-
multipart_not_installed_error = (
'Form data requires "python-multipart" to be installed. \n'
'You can install "python-multipart" with: \n\n'
@@ -137,86 +121,33 @@ def ensure_multipart_is_installed() -> None:
raise RuntimeError(multipart_not_installed_error) from None
-def get_param_sub_dependant(
- *,
- param_name: str,
- depends: params.Depends,
- path: str,
- caller: Callable[..., Any],
- security_scopes: Optional[List[str]] = None,
-) -> Union[EndpointDependant, LifespanDependant]:
- assert depends.dependency
- return get_sub_dependant(
- depends=depends,
- dependency=depends.dependency,
- path=path,
- name=param_name,
- security_scopes=security_scopes,
- caller=caller,
- )
-
-
def get_parameterless_sub_dependant(
- *, depends: params.Depends, path: str, caller: Callable[..., Any], index: int
+ *, depends: params.Depends, path: str, caller: Callable[..., Any], index: int
) -> Union[EndpointDependant, LifespanDependant]:
assert callable(depends.dependency), (
"A parameter-less dependency must have a callable dependency"
)
- return get_sub_dependant(
- depends=depends,
- dependency=depends.dependency,
- path=path,
- caller=caller,
- index=index,
- )
-
-
-def get_sub_dependant(
- *,
- depends: params.Depends,
- dependency: Callable[..., Any],
- path: str,
- caller: Callable[..., Any],
- name: Optional[str] = None,
- security_scopes: Optional[List[str]] = None,
- index: Optional[int] = None,
-) -> Union[EndpointDependant, LifespanDependant]:
- if depends.dependency_scope == "lifespan":
+ if depends.scope in ("request", "function", None):
+ use_security_scopes: List[str] = []
+ if isinstance(depends, params.Security) and depends.scopes:
+ use_security_scopes.extend(depends.scopes)
+ return get_endpoint_dependant(
+ path=path,
+ call=depends.dependency,
+ security_scopes=use_security_scopes,
+ index=index
+ )
+ elif depends.scope == "lifespan":
return get_lifespan_dependant(
caller=caller,
- call=dependency,
- name=name,
+ call=depends.dependency,
use_cache=depends.use_cache,
index=index,
)
- elif depends.dependency_scope == "endpoint":
- security_requirement = None
- security_scopes = security_scopes or []
- if isinstance(depends, params.Security):
- dependency_scopes = depends.scopes
- security_scopes.extend(dependency_scopes)
- if isinstance(dependency, SecurityBase):
- use_scopes: List[str] = []
- if isinstance(dependency, (OAuth2, OpenIdConnect)):
- use_scopes = security_scopes
- security_requirement = SecurityRequirement(
- security_scheme=dependency, scopes=use_scopes
- )
- sub_dependant = get_endpoint_dependant(
- path=path,
- call=dependency,
- name=name,
- security_scopes=security_scopes,
- use_cache=depends.use_cache,
- index=index,
- )
- if security_requirement:
- sub_dependant.security_requirements.append(security_requirement)
- return sub_dependant
else:
raise InvalidDependencyScope(
- f'Dependency "{name}" of {caller} has an invalid '
- f'scope: "{depends.dependency_scope}"'
+ f'Dependency "{index}" of {caller} has an invalid '
+ f'scope: "{depends.scope}"'
)
@@ -224,7 +155,7 @@ def get_flat_dependant(
dependant: EndpointDependant,
*,
skip_repeats: bool = False,
- visited: Optional[List[CacheKey]] = None,
+ visited: Optional[List[EndpointDependencyCacheKey]] = None,
) -> EndpointDependant:
if visited is None:
visited = []
@@ -333,20 +264,18 @@ def get_lifespan_dependant(
is_path_param=False,
)
if param_details.depends is None:
- raise DependencyScopeConflict(
- f'Lifespan scoped dependency "{dependant.name}" was defined '
+ raise DependencyScopeError(
+ f'Dependency "{dependant.name}" has "lifespan" scope, but was defined'
f'with an invalid argument: "{param_name}" which is '
- f'"endpoint" scoped. Lifespan scoped dependencies may only '
+ f'not a valid sub-dependency. Lifespan scoped dependencies may only '
f"use lifespan scoped sub-dependencies."
)
- if param_details.depends.dependency_scope != "lifespan":
- raise DependencyScopeConflict(
- f"Lifespan scoped dependency {dependant.name} was defined with the "
- f'sub-dependency "{param_name}" which is '
- f'"{param_details.depends.dependency_scope}" scoped. '
- f"Lifespan scoped dependencies may only use lifespan scoped "
- f"sub-dependencies."
+ if param_details.depends.scope != "lifespan":
+ raise DependencyScopeError(
+ f'Dependency "{dependant.name}" has "lifespan" scope, but was defined with the '
+ f'sub-dependency "{param_name}" which has "{param_details.depends.scope}" scope. Lifespan scoped '
+ f'dependencies may only use lifespan scoped sub-dependencies.'
)
assert param_details.depends.dependency is not None
@@ -370,18 +299,28 @@ def get_endpoint_dependant(
security_scopes: Optional[List[str]] = None,
use_cache: bool = True,
index: Optional[int] = None,
+ scope: Union[Literal["function", "request"], None] = None,
) -> EndpointDependant:
- path_param_names = get_path_param_names(path)
- endpoint_signature = get_typed_signature(call)
- signature_params = endpoint_signature.parameters
dependant = EndpointDependant(
call=call,
name=name,
path=path,
security_scopes=security_scopes,
use_cache=use_cache,
+ scope=scope,
index=index,
)
+ path_param_names = get_path_param_names(path)
+ endpoint_signature = get_typed_signature(call)
+ signature_params = endpoint_signature.parameters
+ if isinstance(call, SecurityBase):
+ use_scopes: List[str] = []
+ if isinstance(call, (OAuth2, OpenIdConnect)):
+ use_scopes = security_scopes or use_scopes
+ security_requirement = SecurityRequirement(
+ security_scheme=call, scopes=use_scopes
+ )
+ dependant.security_requirements.append(security_requirement)
for param_name, param in signature_params.items():
is_path_param = param_name in path_param_names
param_details = analyze_param(
@@ -391,18 +330,45 @@ def get_endpoint_dependant(
is_path_param=is_path_param,
)
if param_details.depends is not None:
- sub_dependant = get_param_sub_dependant(
- param_name=param_name,
- depends=param_details.depends,
- path=path,
- security_scopes=security_scopes,
- caller=call,
- )
- if isinstance(sub_dependant, EndpointDependant):
- dependant.endpoint_dependencies.append(sub_dependant)
+ assert param_details.depends.dependency
+ if (
+ (dependant.is_gen_callable or dependant.is_async_gen_callable)
+ and dependant.computed_scope == "request"
+ and param_details.depends.scope == "function"
+ ):
+ assert dependant.call
+ raise DependencyScopeError(
+ f'The dependency "{dependant.call.__name__}" has a scope of '
+ '"request", it cannot depend on dependencies with scope "function".'
+ )
+ use_security_scopes = security_scopes or []
+ if isinstance(param_details.depends, params.Security):
+ if param_details.depends.scopes:
+ use_security_scopes.extend(param_details.depends.scopes)
+ if param_details.depends.scope == "lifespan":
+ dependant.lifespan_dependencies.append(get_lifespan_dependant(
+ caller=call,
+ call=param_details.depends.dependency,
+ name=param_name,
+ use_cache=param_details.depends.use_cache,
+ index=index
+ ))
+ elif param_details.depends.scope in ("request", "function", None):
+ dependant.endpoint_dependencies.append(get_endpoint_dependant(
+ path=path,
+ call=param_details.depends.dependency,
+ name=param_name,
+ security_scopes=use_security_scopes,
+ use_cache=param_details.depends.use_cache,
+ index=index,
+ scope=param_details.depends.scope
+ ))
else:
- assert isinstance(sub_dependant, LifespanDependant)
- dependant.lifespan_dependencies.append(sub_dependant)
+ raise InvalidDependencyScope(
+ f'Dependency "{name}" of {call} has an invalid '
+ f'scope: "{param_details.depends.scope}"'
+ )
+
continue
if add_non_field_param_to_dependency(
param_name=param_name,
@@ -479,7 +445,7 @@ def analyze_param(
fastapi_annotations = [
arg
for arg in annotated_args[1:]
- if isinstance(arg, (FieldInfo, v1.FieldInfo, params.Depends))
+ if isinstance(arg, (FieldInfo, may_v1.FieldInfo, params.Depends))
]
fastapi_specific_annotations = [
arg
@@ -496,21 +462,21 @@ def analyze_param(
)
]
if fastapi_specific_annotations:
- fastapi_annotation: Union[FieldInfo, v1.FieldInfo, params.Depends, None] = (
- fastapi_specific_annotations[-1]
- )
+ fastapi_annotation: Union[
+ FieldInfo, may_v1.FieldInfo, params.Depends, None
+ ] = fastapi_specific_annotations[-1]
else:
fastapi_annotation = None
# Set default for Annotated FieldInfo
- if isinstance(fastapi_annotation, (FieldInfo, v1.FieldInfo)):
+ if isinstance(fastapi_annotation, (FieldInfo, may_v1.FieldInfo)):
# Copy `field_info` because we mutate `field_info.default` below.
field_info = copy_field_info(
field_info=fastapi_annotation, annotation=use_annotation
)
assert field_info.default in {
Undefined,
- v1.Undefined,
- } or field_info.default in {RequiredParam, v1.RequiredParam}, (
+ may_v1.Undefined,
+ } or field_info.default in {RequiredParam, may_v1.RequiredParam}, (
f"`{field_info.__class__.__name__}` default value cannot be set in"
f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
)
@@ -534,7 +500,7 @@ def analyze_param(
)
depends = value
# Get FieldInfo from default value
- elif isinstance(value, (FieldInfo, v1.FieldInfo)):
+ elif isinstance(value, (FieldInfo, may_v1.FieldInfo)):
assert field_info is None, (
"Cannot specify FastAPI annotations in `Annotated` and default value"
f" together for {param_name!r}"
@@ -623,7 +589,8 @@ def analyze_param(
type_=use_annotation_from_field_info,
default=field_info.default,
alias=alias,
- required=field_info.default in (RequiredParam, v1.RequiredParam, Undefined),
+ required=field_info.default
+ in (RequiredParam, may_v1.RequiredParam, Undefined),
field_info=field_info,
)
if is_path_param:
@@ -660,50 +627,28 @@ def add_param_to_fields(*, field: ModelField, dependant: EndpointDependant) -> N
dependant.cookie_params.append(field)
-def is_coroutine_callable(call: Callable[..., Any]) -> bool:
- if inspect.isroutine(call):
- return iscoroutinefunction(call)
- if inspect.isclass(call):
- return False
- dunder_call = getattr(call, "__call__", None) # noqa: B004
- return iscoroutinefunction(dunder_call)
-
-
-def is_async_gen_callable(call: Callable[..., Any]) -> bool:
- if inspect.isasyncgenfunction(call):
- return True
- dunder_call = getattr(call, "__call__", None) # noqa: B004
- return inspect.isasyncgenfunction(dunder_call)
-
-
-def is_gen_callable(call: Callable[..., Any]) -> bool:
- if inspect.isgeneratorfunction(call):
- return True
- dunder_call = getattr(call, "__call__", None) # noqa: B004
- return inspect.isgeneratorfunction(dunder_call)
-
-
-async def solve_generator(
- *, call: Callable[..., Any], stack: AsyncExitStack, sub_values: Dict[str, Any]
+async def _solve_generator(
+ *, dependant: Union[EndpointDependant, LifespanDependant], stack: AsyncExitStack, sub_values: Dict[str, Any]
) -> Any:
- if is_gen_callable(call):
- cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
- elif is_async_gen_callable(call):
- cm = asynccontextmanager(call)(**sub_values)
+ assert dependant.call
+ if dependant.is_gen_callable:
+ cm = contextmanager_in_threadpool(contextmanager(dependant.call)(**sub_values))
+ elif dependant.is_async_gen_callable:
+ cm = asynccontextmanager(dependant.call)(**sub_values)
return await stack.enter_async_context(cm)
@dataclass
class SolvedLifespanDependant:
value: Any
- dependency_cache: Dict[LifespanDependantCacheKey, Any]
+ dependency_cache: Dict[LifespanDependencyCacheKey, Any]
async def solve_lifespan_dependant(
*,
dependant: LifespanDependant,
dependency_overrides_provider: Optional[Any] = None,
- dependency_cache: Dict[LifespanDependantCacheKey, Callable[..., Any]],
+ dependency_cache: Dict[LifespanDependencyCacheKey, Callable[..., Any]],
async_exit_stack: AsyncExitStack,
) -> SolvedLifespanDependant:
if dependant.use_cache and dependant.cache_key in dependency_cache:
@@ -743,11 +688,11 @@ async def solve_lifespan_dependant(
)
dependency_arguments[sub_dependant.name] = solved_sub_dependant.value
- if is_gen_callable(call) or is_async_gen_callable(call):
- value = await solve_generator(
- call=call, stack=async_exit_stack, sub_values=dependency_arguments
+ if dependant_to_solve.is_gen_callable or dependant_to_solve.is_async_gen_callable:
+ value = await _solve_generator(
+ dependant=dependant_to_solve, stack=async_exit_stack, sub_values=dependency_arguments
)
- elif is_coroutine_callable(call):
+ elif dependant_to_solve.is_coroutine_callable:
value = await call(**dependency_arguments)
else:
value = await run_in_threadpool(call, **dependency_arguments)
@@ -767,7 +712,7 @@ class SolvedDependency:
errors: List[Any]
background_tasks: Optional[StarletteBackgroundTasks]
response: Response
- dependency_cache: Dict[EndpointDependantCacheKey, Any]
+ dependency_cache: Dict[EndpointDependencyCacheKey, Any]
async def solve_dependencies(
@@ -778,10 +723,20 @@ async def solve_dependencies(
background_tasks: Optional[StarletteBackgroundTasks] = None,
response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None,
- dependency_cache: Optional[Dict[EndpointDependantCacheKey, Any]] = None,
+ dependency_cache: Optional[Dict[EndpointDependencyCacheKey, Any]] = None,
+ # TODO: remove this parameter later, no longer used, not removing it yet as some
+ # people might be monkey patching this function (although that's not supported)
async_exit_stack: AsyncExitStack,
embed_body_fields: bool,
) -> SolvedDependency:
+ request_astack = request.scope.get("fastapi_inner_astack")
+ assert isinstance(request_astack, AsyncExitStack), (
+ "fastapi_inner_astack not found in request scope"
+ )
+ function_astack = request.scope.get("fastapi_function_astack")
+ assert isinstance(function_astack, AsyncExitStack), (
+ "fastapi_function_astack not found in request scope"
+ )
values: Dict[str, Any] = {}
errors: List[Any] = []
@@ -818,12 +773,8 @@ async def solve_dependencies(
response.status_code = None # type: ignore
if dependency_cache is None:
dependency_cache = {}
- sub_dependant: EndpointDependant
for sub_dependant in dependant.endpoint_dependencies:
sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
- sub_dependant.cache_key = cast(
- Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key
- )
call = sub_dependant.call
use_sub_dependant = sub_dependant
if (
@@ -840,6 +791,7 @@ async def solve_dependencies(
call=call,
name=sub_dependant.name,
security_scopes=sub_dependant.security_scopes,
+ scope=sub_dependant.scope,
)
solved_result = await solve_dependencies(
@@ -859,11 +811,18 @@ async def solve_dependencies(
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=solved_result.values
+ elif (
+ use_sub_dependant.is_gen_callable or use_sub_dependant.is_async_gen_callable
+ ):
+ use_astack = request_astack
+ if sub_dependant.scope == "function":
+ use_astack = function_astack
+ solved = await _solve_generator(
+ dependant=use_sub_dependant,
+ stack=use_astack,
+ sub_values=solved_result.values,
)
- elif is_coroutine_callable(call):
+ elif use_sub_dependant.is_coroutine_callable:
solved = await call(**solved_result.values)
else:
solved = await run_in_threadpool(call, **solved_result.values)
@@ -936,7 +895,7 @@ def _validate_value_with_model_field(
if _is_error_wrapper(errors_): # type: ignore[arg-type]
return None, [errors_]
elif isinstance(errors_, list):
- new_errors = v1._regenerate_error_with_loc(errors=errors_, loc_prefix=())
+ new_errors = may_v1._regenerate_error_with_loc(errors=errors_, loc_prefix=())
return None, new_errors
else:
return v_, []
diff --git a/fastapi/encoders.py b/fastapi/encoders.py
index 8ff7d58dd..6fc6228e1 100644
--- a/fastapi/encoders.py
+++ b/fastapi/encoders.py
@@ -17,13 +17,14 @@ from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
from uuid import UUID
-from fastapi._compat import v1
+from annotated_doc import Doc
+from fastapi._compat import may_v1
from fastapi.types import IncEx
from pydantic import BaseModel
from pydantic.color import Color
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
from ._compat import Url, _is_undefined, _model_dump
@@ -59,7 +60,7 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str,
- v1.Color: str,
+ may_v1.Color: str,
datetime.date: isoformat,
datetime.datetime: isoformat,
datetime.time: isoformat,
@@ -76,19 +77,19 @@ ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
IPv6Interface: str,
IPv6Network: str,
NameEmail: str,
- v1.NameEmail: str,
+ may_v1.NameEmail: str,
Path: str,
Pattern: lambda o: o.pattern,
SecretBytes: str,
- v1.SecretBytes: str,
+ may_v1.SecretBytes: str,
SecretStr: str,
- v1.SecretStr: str,
+ may_v1.SecretStr: str,
set: list,
UUID: str,
Url: str,
- v1.Url: str,
+ may_v1.Url: str,
AnyUrl: str,
- v1.AnyUrl: str,
+ may_v1.AnyUrl: str,
}
@@ -220,10 +221,10 @@ def jsonable_encoder(
include = set(include)
if exclude is not None and not isinstance(exclude, (set, dict)):
exclude = set(exclude)
- if isinstance(obj, (BaseModel, v1.BaseModel)):
+ if isinstance(obj, (BaseModel, may_v1.BaseModel)):
# TODO: remove when deprecating Pydantic v1
encoders: Dict[Any, Any] = {}
- if isinstance(obj, v1.BaseModel):
+ if isinstance(obj, may_v1.BaseModel):
encoders = getattr(obj.__config__, "json_encoders", {}) # type: ignore[attr-defined]
if custom_encoder:
encoders = {**encoders, **custom_encoder}
diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py
index 95fd60477..01b9bbe44 100644
--- a/fastapi/exceptions.py
+++ b/fastapi/exceptions.py
@@ -1,9 +1,10 @@
from typing import Any, Dict, Optional, Sequence, Type, Union
+from annotated_doc import Doc
from pydantic import BaseModel, create_model
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.exceptions import WebSocketException as StarletteWebSocketException
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class HTTPException(StarletteHTTPException):
@@ -145,23 +146,31 @@ class FastAPIError(RuntimeError):
A generic, FastAPI-specific error.
"""
-
class DependencyError(FastAPIError):
+ """
+ A generic error regarding to dependencies.
+ """
pass
-
class InvalidDependencyScope(DependencyError):
+ """
+ A dependency was declared with an unsupported scope value.
+ """
pass
-
-class DependencyScopeConflict(DependencyError):
- pass
-
+class DependencyScopeError(DependencyError):
+ """
+ A dependency declared that it depends on another dependency with an invalid
+ (narrower) scope.
+ """
class UninitializedLifespanDependency(DependencyError):
+ """
+ A bug in FastAPI caused a lifespan-scoped dependency to not initialize properly
+ at the point where we handled a request that depended on it.
+ """
pass
-
class ValidationException(Exception):
def __init__(self, errors: Sequence[Any]) -> None:
self._errors = errors
diff --git a/fastapi/lifespan.py b/fastapi/lifespan.py
index 7d53fc00a..4f5a20a4f 100644
--- a/fastapi/lifespan.py
+++ b/fastapi/lifespan.py
@@ -3,7 +3,7 @@ from __future__ import annotations
from contextlib import AsyncExitStack
from typing import TYPE_CHECKING, Any, Callable, Dict, List
-from fastapi.dependencies.models import LifespanDependant, LifespanDependantCacheKey
+from fastapi.dependencies.models import LifespanDependant, LifespanDependencyCacheKey
from fastapi.dependencies.utils import solve_lifespan_dependant
from fastapi.routing import APIRoute, APIWebSocketRoute
@@ -12,7 +12,7 @@ if TYPE_CHECKING: # pragma: nocover
def _get_lifespan_dependants(app: FastAPI) -> List[LifespanDependant]:
- lifespan_dependants_cache: Dict[LifespanDependantCacheKey, LifespanDependant] = {}
+ lifespan_dependants_cache: Dict[LifespanDependencyCacheKey, LifespanDependant] = {}
for route in app.router.routes:
if not isinstance(route, (APIWebSocketRoute, APIRoute)):
continue
@@ -28,9 +28,9 @@ def _get_lifespan_dependants(app: FastAPI) -> List[LifespanDependant]:
async def resolve_lifespan_dependants(
*, app: FastAPI, async_exit_stack: AsyncExitStack
-) -> Dict[LifespanDependantCacheKey, Callable[..., Any]]:
+) -> Dict[LifespanDependencyCacheKey, Callable[..., Any]]:
lifespan_dependants = _get_lifespan_dependants(app)
- dependency_cache: Dict[LifespanDependantCacheKey, Callable[..., Any]] = {}
+ dependency_cache: Dict[LifespanDependencyCacheKey, Callable[..., Any]] = {}
for lifespan_dependant in lifespan_dependants:
solved_dependency = await solve_lifespan_dependant(
dependant=lifespan_dependant,
diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py
index f181b43c1..74b23a370 100644
--- a/fastapi/openapi/docs.py
+++ b/fastapi/openapi/docs.py
@@ -1,9 +1,10 @@
import json
from typing import Any, Dict, Optional
+from annotated_doc import Doc
from fastapi.encoders import jsonable_encoder
from starlette.responses import HTMLResponse
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
swagger_ui_default_parameters: Annotated[
Dict[str, Any],
diff --git a/fastapi/param_functions.py b/fastapi/param_functions.py
index 4112e90e2..5838c1bd5 100644
--- a/fastapi/param_functions.py
+++ b/fastapi/param_functions.py
@@ -1,12 +1,10 @@
-from __future__ import annotations
-
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
+from annotated_doc import Doc
from fastapi import params
from fastapi._compat import Undefined
from fastapi.openapi.models import Example
-from fastapi.params import DependencyScope
-from typing_extensions import Annotated, Doc, deprecated
+from typing_extensions import Annotated, Literal, deprecated
_Unset: Any = Undefined
@@ -2247,33 +2245,28 @@ def Depends( # noqa: N802
"""
),
] = True,
- dependency_scope: Annotated[
- DependencyScope,
+ scope: Annotated[
+ Union[Literal["function", "request", "lifespan"], None],
Doc(
"""
- The scope in which the dependency value should be evaluated. Can be
- either `"endpoint"` or `"lifespan"`.
-
- If `dependency_scope` is set to "endpoint" (the default), the
- dependency will be setup and teardown for every request.
-
- If `dependency_scope` is set to `"lifespan"` the dependency would
- be setup at the start of the entire application's lifespan. The
- evaluated dependency would be then reused across all endpoints.
- The dependency would be teared down as a part of the application's
- shutdown process.
+ Mainly for dependencies with `yield`, define when the dependency function
+ should start (the code before `yield`) and when it should end (the code
+ after `yield`).
- Note that dependencies defined with the `"endpoint"` scope may use
- sub-dependencies defined with the `"lifespan"` scope, but not the
- other way around;
- Dependencies defined with the `"lifespan"` scope may not use
- sub-dependencies with `"endpoint"` scope, nor can they use
- other "endpoint scoped" arguments such as "Path", "Body", "Query",
- or any other annotation which does not make sense in a scope of an
- application's entire lifespan.
+ * `"function"`: start the dependency before the *path operation function*
+ that handles the request, end the dependency after the *path operation
+ function* ends, but **before** the response is sent back to the client.
+ So, the dependency function will be executed **around** the *path operation
+ **function***.
+ * `"request"`: start the dependency before the *path operation function*
+ that handles the request (similar to when using `"function"`), but end
+ **after** the response is sent back to the client. So, the dependency
+ function will be executed **around** the **request** and response cycle.
+ * `"lifespan"`: start the dependency when the FastAPI application starts,
+ end the dependency during the FastAPI application shuts down.
"""
),
- ] = "endpoint",
+ ] = None,
) -> Any:
"""
Declare a FastAPI dependency.
@@ -2304,9 +2297,7 @@ def Depends( # noqa: N802
return commons
```
"""
- return params.Depends(
- dependency=dependency, use_cache=use_cache, dependency_scope=dependency_scope
- )
+ return params.Depends(dependency=dependency, use_cache=use_cache, scope=scope)
def Security( # noqa: N802
diff --git a/fastapi/params.py b/fastapi/params.py
index 28d6e67cb..96bd67024 100644
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -1,4 +1,5 @@
import warnings
+from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
@@ -11,9 +12,11 @@ from ._compat import (
PYDANTIC_VERSION_MINOR_TUPLE,
Undefined,
)
+from .exceptions import InvalidDependencyScope
_Unset: Any = Undefined
-DependencyScope: TypeAlias = Literal["endpoint", "lifespan"]
+_EndpointDependencyScope: TypeAlias = Literal["request", "function"]
+DependencyScope: TypeAlias = Union[Literal["lifespan"], _EndpointDependencyScope]
class ParamTypes(Enum):
@@ -762,38 +765,18 @@ class File(Form): # type: ignore[misc]
)
+@dataclass
class Depends:
- def __init__(
- self,
- dependency: Optional[Callable[..., Any]] = None,
- *,
- use_cache: bool = True,
- dependency_scope: DependencyScope = "endpoint",
- ):
- self.dependency = dependency
- self.use_cache = use_cache
- self.dependency_scope = dependency_scope
-
- def __repr__(self) -> str:
- attr = getattr(self.dependency, "__name__", type(self.dependency).__name__)
- cache = "" if self.use_cache else ", use_cache=False"
- if self.dependency_scope == "endpoint":
- dependency_scope = ""
- else:
- dependency_scope = f', dependency_scope="{self.dependency_scope}"'
+ dependency: Optional[Callable[..., Any]] = None
+ use_cache: bool = True
+ scope: Union[DependencyScope, None] = None
- return f"{self.__class__.__name__}({attr}{cache}{dependency_scope})"
+ def __post_init__(self):
+ if self.scope not in ("lifespan", "request", "function", None):
+ raise InvalidDependencyScope(f"Dependency received an invalid scope: \"{self.scope}\"")
+@dataclass
class Security(Depends):
- def __init__(
- self,
- dependency: Optional[Callable[..., Any]] = None,
- *,
- scopes: Optional[Sequence[str]] = None,
- use_cache: bool = True,
- ):
- super().__init__(
- dependency=dependency, use_cache=use_cache, dependency_scope="endpoint"
- )
- self.scopes = scopes or []
+ scope: Union[_EndpointDependencyScope, None] = None
+ scopes: Optional[Sequence[str]] = None
diff --git a/fastapi/routing.py b/fastapi/routing.py
index 9803699bb..fc3f4aaa9 100644
--- a/fastapi/routing.py
+++ b/fastapi/routing.py
@@ -24,6 +24,7 @@ from typing import (
Union,
)
+from annotated_doc import Doc
from fastapi import params, temp_pydantic_v1_params
from fastapi._compat import (
ModelField,
@@ -76,7 +77,7 @@ from starlette.routing import (
from starlette.routing import Mount as Mount # noqa
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
from starlette.websockets import WebSocket
-from typing_extensions import Annotated, Doc, deprecated
+from typing_extensions import Annotated, deprecated
if sys.version_info >= (3, 13): # pragma: no cover
from inspect import iscoroutinefunction
@@ -103,10 +104,11 @@ def request_response(
async def app(scope: Scope, receive: Receive, send: Send) -> None:
# Starts customization
response_awaited = False
- async with AsyncExitStack() as stack:
- scope["fastapi_inner_astack"] = stack
- # Same as in Starlette
- response = await f(request)
+ async with AsyncExitStack() as request_stack:
+ scope["fastapi_inner_astack"] = request_stack
+ async with AsyncExitStack() as function_stack:
+ scope["fastapi_function_astack"] = function_stack
+ response = await f(request)
await response(scope, receive, send)
# Continues customization
response_awaited = True
@@ -139,11 +141,11 @@ def websocket_session(
session = WebSocket(scope, receive=receive, send=send)
async def app(scope: Scope, receive: Receive, send: Send) -> None:
- # Starts customization
- async with AsyncExitStack() as stack:
- scope["fastapi_inner_astack"] = stack
- # Same as in Starlette
- await func(session)
+ async with AsyncExitStack() as request_stack:
+ scope["fastapi_inner_astack"] = request_stack
+ async with AsyncExitStack() as function_stack:
+ scope["fastapi_function_astack"] = function_stack
+ await func(session)
# Same as in Starlette
await wrap_app_handling_exceptions(app, session)(scope, receive, send)
@@ -479,7 +481,7 @@ class APIWebSocketRoute(routing.WebSocketRoute):
self.dependencies = list(dependencies or [])
self.path_regex, self.path_format, self.param_convertors = compile_path(path)
self.dependant = get_endpoint_dependant(
- path=self.path_format, call=self.endpoint
+ path=self.path_format, call=self.endpoint, scope="function"
)
for i, depends in list(enumerate(self.dependencies))[::-1]:
sub_dependant = get_parameterless_sub_dependant(
@@ -642,7 +644,7 @@ class APIRoute(routing.Route):
assert callable(endpoint), "An endpoint must be a callable"
self.dependant = get_endpoint_dependant(
- path=self.path_format, call=self.endpoint
+ path=self.path_format, call=self.endpoint, scope="function"
)
for i, depends in list(enumerate(self.dependencies))[::-1]:
sub_dependant = get_parameterless_sub_dependant(
diff --git a/fastapi/security/api_key.py b/fastapi/security/api_key.py
index 6d6dd01d9..496c815a7 100644
--- a/fastapi/security/api_key.py
+++ b/fastapi/security/api_key.py
@@ -1,11 +1,12 @@
from typing import Optional
+from annotated_doc import Doc
from fastapi.openapi.models import APIKey, APIKeyIn
from fastapi.security.base import SecurityBase
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.status import HTTP_403_FORBIDDEN
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class APIKeyBase(SecurityBase):
diff --git a/fastapi/security/http.py b/fastapi/security/http.py
index 9ab2df3c9..3a5985650 100644
--- a/fastapi/security/http.py
+++ b/fastapi/security/http.py
@@ -2,6 +2,7 @@ import binascii
from base64 import b64decode
from typing import Optional
+from annotated_doc import Doc
from fastapi.exceptions import HTTPException
from fastapi.openapi.models import HTTPBase as HTTPBaseModel
from fastapi.openapi.models import HTTPBearer as HTTPBearerModel
@@ -10,7 +11,7 @@ from fastapi.security.utils import get_authorization_scheme_param
from pydantic import BaseModel
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class HTTPBasicCredentials(BaseModel):
diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py
index fdedbc2da..f8d97d762 100644
--- a/fastapi/security/oauth2.py
+++ b/fastapi/security/oauth2.py
@@ -1,5 +1,6 @@
from typing import Any, Dict, List, Optional, Union, cast
+from annotated_doc import Doc
from fastapi.exceptions import HTTPException
from fastapi.openapi.models import OAuth2 as OAuth2Model
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
@@ -10,7 +11,7 @@ from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
# TODO: import from typing when deprecating Python 3.9
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class OAuth2PasswordRequestForm:
diff --git a/fastapi/security/open_id_connect_url.py b/fastapi/security/open_id_connect_url.py
index c8cceb911..5e99798e6 100644
--- a/fastapi/security/open_id_connect_url.py
+++ b/fastapi/security/open_id_connect_url.py
@@ -1,11 +1,12 @@
from typing import Optional
+from annotated_doc import Doc
from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
from fastapi.security.base import SecurityBase
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.status import HTTP_403_FORBIDDEN
-from typing_extensions import Annotated, Doc
+from typing_extensions import Annotated
class OpenIdConnect(SecurityBase):
diff --git a/fastapi/temp_pydantic_v1_params.py b/fastapi/temp_pydantic_v1_params.py
index 0535ee727..e41d71230 100644
--- a/fastapi/temp_pydantic_v1_params.py
+++ b/fastapi/temp_pydantic_v1_params.py
@@ -5,8 +5,8 @@ from fastapi.openapi.models import Example
from fastapi.params import ParamTypes
from typing_extensions import Annotated, deprecated
+from ._compat.may_v1 import FieldInfo, Undefined
from ._compat.shared import PYDANTIC_VERSION_MINOR_TUPLE
-from ._compat.v1 import FieldInfo, Undefined
_Unset: Any = Undefined
diff --git a/fastapi/types.py b/fastapi/types.py
index 3205654c7..562f849a5 100644
--- a/fastapi/types.py
+++ b/fastapi/types.py
@@ -1,6 +1,6 @@
import types
from enum import Enum
-from typing import Any, Callable, Dict, Set, Type, TypeVar, Union
+from typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, Union
from pydantic import BaseModel
@@ -8,3 +8,8 @@ DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
UnionType = getattr(types, "UnionType", Union)
ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str]
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]]
+
+LifespanDependencyCacheKey = Union[
+ Tuple[Callable[..., Any], Union[str, int]], Callable[..., Any]
+]
+EndpointDependencyCacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...], str]
diff --git a/fastapi/utils.py b/fastapi/utils.py
index 3ea9271b1..8be10ff15 100644
--- a/fastapi/utils.py
+++ b/fastapi/utils.py
@@ -1,5 +1,8 @@
+import inspect
import re
+import sys
import warnings
+from collections.abc import Callable
from dataclasses import is_dataclass
from typing import (
TYPE_CHECKING,
@@ -10,10 +13,12 @@ from typing import (
Set,
Type,
Union,
- cast,
+ cast, Awaitable, TypeVar,
)
from weakref import WeakKeyDictionary
+from starlette.concurrency import run_in_threadpool
+
import fastapi
from fastapi._compat import (
PYDANTIC_V2,
@@ -25,16 +30,25 @@ from fastapi._compat import (
Validator,
annotation_is_pydantic_v1,
lenient_issubclass,
- v1,
+ may_v1,
)
from fastapi.datastructures import DefaultPlaceholder, DefaultType
from pydantic import BaseModel
from pydantic.fields import FieldInfo
-from typing_extensions import Literal
+from typing_extensions import Literal, TypeIs, ParamSpec
if TYPE_CHECKING: # pragma: nocover
from .routing import APIRoute
+if sys.version_info >= (3, 13): # pragma: no cover
+ from inspect import iscoroutinefunction
+else: # pragma: no cover
+ from asyncio import iscoroutinefunction
+
+
+_T = TypeVar("_T")
+_P = ParamSpec("_P")
+
# Cache for `create_cloned_field`
_CLONED_TYPES_CACHE: MutableMapping[Type[BaseModel], Type[BaseModel]] = (
WeakKeyDictionary()
@@ -87,8 +101,8 @@ def create_model_field(
) -> ModelField:
class_validators = class_validators or {}
- v1_model_config = v1.BaseConfig
- v1_field_info = field_info or v1.FieldInfo()
+ v1_model_config = may_v1.BaseConfig
+ v1_field_info = field_info or may_v1.FieldInfo()
v1_kwargs = {
"name": name,
"field_info": v1_field_info,
@@ -102,9 +116,11 @@ def create_model_field(
if (
annotation_is_pydantic_v1(type_)
- or isinstance(field_info, v1.FieldInfo)
+ or isinstance(field_info, may_v1.FieldInfo)
or version == "1"
):
+ from fastapi._compat import v1
+
try:
return v1.ModelField(**v1_kwargs) # type: ignore[no-any-return]
except RuntimeError:
@@ -122,6 +138,8 @@ def create_model_field(
raise fastapi.exceptions.FastAPIError(_invalid_args_message) from None
# Pydantic v2 is not installed, but it's not a Pydantic v1 ModelField, it could be
# a Pydantic v1 type, like a constrained int
+ from fastapi._compat import v1
+
try:
return v1.ModelField(**v1_kwargs) # type: ignore[no-any-return]
except RuntimeError:
@@ -138,6 +156,9 @@ def create_cloned_field(
if isinstance(field, v2.ModelField):
return field
+
+ from fastapi._compat import v1
+
# cloned_types caches already cloned types to support recursive models and improve
# performance by avoiding unnecessary cloning
if cloned_types is None:
@@ -245,3 +266,24 @@ def get_value_or_default(
if not isinstance(item, DefaultPlaceholder):
return item
return first_item
+
+
+def _is_coroutine_callable(
+ callable_: Callable[..., Any]
+) -> TypeIs[Callable[..., Awaitable[Any]]]:
+ if inspect.isroutine(callable_):
+ return iscoroutinefunction(callable_)
+ if inspect.isclass(callable_):
+ return False
+ dunder_call = getattr(callable_, "__call__", None) # noqa: B004
+ return iscoroutinefunction(dunder_call)
+
+async def call_asynchronously(
+ callable_: Union[Callable[_P, _T], Callable[_P, Awaitable[_T]]],
+ *args: _P.args,
+ **kwargs: _P.kwargs
+) -> _T:
+ if _is_coroutine_callable(callable_):
+ return await callable_(*args, **kwargs)
+ else:
+ return await run_in_threadpool(callable_, *args, **kwargs)
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index cac8059f4..7d2be0074 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,6 +7,8 @@ name = "fastapi"
dynamic = ["version"]
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
readme = "README.md"
+license = "MIT"
+license-files = ["LICENSE"]
requires-python = ">=3.8"
authors = [
{ name = "Sebastián Ramírez", email = "tiangolo@gmail.com" },
@@ -31,7 +33,6 @@ classifiers = [
"Framework :: Pydantic :: 1",
"Framework :: Pydantic :: 2",
"Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
@@ -44,9 +45,10 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
dependencies = [
- "starlette>=0.40.0,<0.49.0",
+ "starlette>=0.40.0,<0.50.0",
"pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0",
"typing-extensions>=4.8.0",
+ "annotated-doc>=0.0.2",
]
[project.urls]
diff --git a/requirements-docs-tests.txt b/requirements-docs-tests.txt
index a909d0eef..9350f3ee4 100644
--- a/requirements-docs-tests.txt
+++ b/requirements-docs-tests.txt
@@ -1,4 +1,4 @@
# For mkdocstrings and tests
httpx >=0.23.0,<1.0.0
# For linting and generating docs versions
-ruff ==0.13.2
+ruff ==0.14.3
diff --git a/requirements-docs.txt b/requirements-docs.txt
index 0013f9f79..696eb2a33 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -11,9 +11,9 @@ jieba==0.42.1
pillow==11.3.0
# For image processing by Material for MkDocs
cairosvg==2.8.2
-mkdocstrings[python]==0.26.1
-griffe-typingdoc==0.2.9
+mkdocstrings[python]==0.30.1
+griffe-typingdoc==0.3.0
# For griffe, it formats with black
black==25.1.0
-mkdocs-macros-plugin==1.4.0
+mkdocs-macros-plugin==1.4.1
markdown-include-variants==0.0.5
diff --git a/tests/test_compat.py b/tests/test_compat.py
index f79dbdabc..0184c9a2e 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -7,7 +7,7 @@ from fastapi._compat import (
get_cached_model_fields,
is_scalar_field,
is_uploadfile_sequence_annotation,
- v1,
+ may_v1,
)
from fastapi._compat.shared import is_bytes_sequence_annotation
from fastapi.testclient import TestClient
@@ -27,7 +27,10 @@ def test_model_field_default_required():
assert field.default is Undefined
+@needs_py_lt_314
def test_v1_plain_validator_function():
+ from fastapi._compat import v1
+
# For coverage
def func(v): # pragma: no cover
return v
@@ -135,6 +138,8 @@ def test_is_uploadfile_sequence_annotation():
@needs_py_lt_314
def test_is_pv1_scalar_field():
+ from fastapi._compat import v1
+
# For coverage
class Model(v1.BaseModel):
foo: Union[str, Dict[str, Any]]
@@ -143,8 +148,11 @@ def test_is_pv1_scalar_field():
assert not is_scalar_field(fields[0])
+@needs_py_lt_314
def test_get_model_fields_cached():
- class Model(v1.BaseModel):
+ from fastapi._compat import v1
+
+ class Model(may_v1.BaseModel):
foo: str
non_cached_fields = v1.get_model_fields(Model)
diff --git a/tests/test_dependency_paramless.py b/tests/test_dependency_paramless.py
new file mode 100644
index 000000000..9c3cc3878
--- /dev/null
+++ b/tests/test_dependency_paramless.py
@@ -0,0 +1,78 @@
+from typing import Union
+
+from fastapi import FastAPI, HTTPException, Security
+from fastapi.security import (
+ OAuth2PasswordBearer,
+ SecurityScopes,
+)
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+app = FastAPI()
+
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
+
+
+def process_auth(
+ credentials: Annotated[Union[str, None], Security(oauth2_scheme)],
+ security_scopes: SecurityScopes,
+):
+ # This is an incorrect way of using it, this is not checking if the scopes are
+ # provided by the token, only if the endpoint is requesting them, but the test
+ # here is just to check if FastAPI is indeed registering and passing the scopes
+ # correctly when using Security with parameterless dependencies.
+ if "a" not in security_scopes.scopes or "b" not in security_scopes.scopes:
+ raise HTTPException(detail="a or b not in scopes", status_code=401)
+ return {"token": credentials, "scopes": security_scopes.scopes}
+
+
+@app.get("/get-credentials")
+def get_credentials(
+ credentials: Annotated[dict, Security(process_auth, scopes=["a", "b"])],
+):
+ return credentials
+
+
+@app.get(
+ "/parameterless-with-scopes",
+ dependencies=[Security(process_auth, scopes=["a", "b"])],
+)
+def get_parameterless_with_scopes():
+ return {"status": "ok"}
+
+
+@app.get(
+ "/parameterless-without-scopes",
+ dependencies=[Security(process_auth)],
+)
+def get_parameterless_without_scopes():
+ return {"status": "ok"}
+
+
+client = TestClient(app)
+
+
+def test_get_credentials():
+ response = client.get("/get-credentials", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200, response.text
+ assert response.json() == {"token": "token", "scopes": ["a", "b"]}
+
+
+def test_parameterless_with_scopes():
+ response = client.get(
+ "/parameterless-with-scopes", headers={"authorization": "Bearer token"}
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"status": "ok"}
+
+
+def test_parameterless_without_scopes():
+ response = client.get(
+ "/parameterless-without-scopes", headers={"authorization": "Bearer token"}
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "a or b not in scopes"}
+
+
+def test_call_get_parameterless_without_scopes_for_coverage():
+ assert get_parameterless_without_scopes() == {"status": "ok"}
diff --git a/tests/test_dependency_yield_scope.py b/tests/test_dependency_yield_scope.py
new file mode 100644
index 000000000..a5227dd7a
--- /dev/null
+++ b/tests/test_dependency_yield_scope.py
@@ -0,0 +1,184 @@
+import json
+from typing import Any, Tuple
+
+import pytest
+from fastapi import Depends, FastAPI
+from fastapi.exceptions import FastAPIError
+from fastapi.responses import StreamingResponse
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+
+class Session:
+ def __init__(self) -> None:
+ self.open = True
+
+
+def dep_session() -> Any:
+ s = Session()
+ yield s
+ s.open = False
+
+
+SessionFuncDep = Annotated[Session, Depends(dep_session, scope="function")]
+SessionRequestDep = Annotated[Session, Depends(dep_session, scope="request")]
+SessionDefaultDep = Annotated[Session, Depends(dep_session)]
+
+
+class NamedSession:
+ def __init__(self, name: str = "default") -> None:
+ self.name = name
+ self.open = True
+
+
+def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any:
+ assert session is session_b
+ named_session = NamedSession(name="named")
+ yield named_session, session_b
+ named_session.open = False
+
+
+NamedSessionsDep = Annotated[Tuple[NamedSession, Session], Depends(get_named_session)]
+
+
+def get_named_func_session(session: SessionFuncDep) -> Any:
+ named_session = NamedSession(name="named")
+ yield named_session, session
+ named_session.open = False
+
+
+def get_named_regular_func_session(session: SessionFuncDep) -> Any:
+ named_session = NamedSession(name="named")
+ return named_session, session
+
+
+BrokenSessionsDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_func_session)
+]
+NamedSessionsFuncDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
+]
+
+RegularSessionsDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_regular_func_session)
+]
+
+app = FastAPI()
+
+
+@app.get("/function-scope")
+def function_scope(session: SessionFuncDep) -> Any:
+ def iter_data():
+ yield json.dumps({"is_open": session.open})
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/request-scope")
+def request_scope(session: SessionRequestDep) -> Any:
+ def iter_data():
+ yield json.dumps({"is_open": session.open})
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/two-scopes")
+def get_stream_session(
+ function_session: SessionFuncDep, request_session: SessionRequestDep
+) -> Any:
+ def iter_data():
+ yield json.dumps(
+ {"func_is_open": function_session.open, "req_is_open": request_session.open}
+ )
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/sub")
+def get_sub(sessions: NamedSessionsDep) -> Any:
+ def iter_data():
+ yield json.dumps(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/named-function-scope")
+def get_named_function_scope(sessions: NamedSessionsFuncDep) -> Any:
+ def iter_data():
+ yield json.dumps(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+ return StreamingResponse(iter_data())
+
+
+@app.get("/regular-function-scope")
+def get_regular_function_scope(sessions: RegularSessionsDep) -> Any:
+ def iter_data():
+ yield json.dumps(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+ return StreamingResponse(iter_data())
+
+
+client = TestClient(app)
+
+
+def test_function_scope() -> None:
+ response = client.get("/function-scope")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["is_open"] is False
+
+
+def test_request_scope() -> None:
+ response = client.get("/request-scope")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["is_open"] is True
+
+
+def test_two_scopes() -> None:
+ response = client.get("/two-scopes")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["func_is_open"] is False
+ assert data["req_is_open"] is True
+
+
+def test_sub() -> None:
+ response = client.get("/sub")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["named_session_open"] is True
+ assert data["session_open"] is True
+
+
+def test_broken_scope() -> None:
+ with pytest.raises(
+ FastAPIError,
+ match='The dependency "get_named_func_session" has a scope of "request", it cannot depend on dependencies with scope "function"',
+ ):
+
+ @app.get("/broken-scope")
+ def get_broken(sessions: BrokenSessionsDep) -> Any: # pragma: no cover
+ pass
+
+
+def test_named_function_scope() -> None:
+ response = client.get("/named-function-scope")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["named_session_open"] is False
+ assert data["session_open"] is False
+
+
+def test_regular_function_scope() -> None:
+ response = client.get("/regular-function-scope")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["named_session_open"] is True
+ assert data["session_open"] is False
diff --git a/tests/test_dependency_yield_scope_websockets.py b/tests/test_dependency_yield_scope_websockets.py
new file mode 100644
index 000000000..52a30ae7a
--- /dev/null
+++ b/tests/test_dependency_yield_scope_websockets.py
@@ -0,0 +1,201 @@
+from contextvars import ContextVar
+from typing import Any, Dict, Tuple
+
+import pytest
+from fastapi import Depends, FastAPI, WebSocket
+from fastapi.exceptions import FastAPIError
+from fastapi.testclient import TestClient
+from typing_extensions import Annotated
+
+global_context: ContextVar[Dict[str, Any]] = ContextVar("global_context", default={}) # noqa: B039
+
+
+class Session:
+ def __init__(self) -> None:
+ self.open = True
+
+
+async def dep_session() -> Any:
+ s = Session()
+ yield s
+ s.open = False
+ global_state = global_context.get()
+ global_state["session_closed"] = True
+
+
+SessionFuncDep = Annotated[Session, Depends(dep_session, scope="function")]
+SessionRequestDep = Annotated[Session, Depends(dep_session, scope="request")]
+SessionDefaultDep = Annotated[Session, Depends(dep_session)]
+
+
+class NamedSession:
+ def __init__(self, name: str = "default") -> None:
+ self.name = name
+ self.open = True
+
+
+def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any:
+ assert session is session_b
+ named_session = NamedSession(name="named")
+ yield named_session, session_b
+ named_session.open = False
+ global_state = global_context.get()
+ global_state["named_session_closed"] = True
+
+
+NamedSessionsDep = Annotated[Tuple[NamedSession, Session], Depends(get_named_session)]
+
+
+def get_named_func_session(session: SessionFuncDep) -> Any:
+ named_session = NamedSession(name="named")
+ yield named_session, session
+ named_session.open = False
+ global_state = global_context.get()
+ global_state["named_func_session_closed"] = True
+
+
+def get_named_regular_func_session(session: SessionFuncDep) -> Any:
+ named_session = NamedSession(name="named")
+ return named_session, session
+
+
+BrokenSessionsDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_func_session)
+]
+NamedSessionsFuncDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
+]
+
+RegularSessionsDep = Annotated[
+ Tuple[NamedSession, Session], Depends(get_named_regular_func_session)
+]
+
+app = FastAPI()
+
+
+@app.websocket("/function-scope")
+async def function_scope(websocket: WebSocket, session: SessionFuncDep) -> Any:
+ await websocket.accept()
+ await websocket.send_json({"is_open": session.open})
+
+
+@app.websocket("/request-scope")
+async def request_scope(websocket: WebSocket, session: SessionRequestDep) -> Any:
+ await websocket.accept()
+ await websocket.send_json({"is_open": session.open})
+
+
+@app.websocket("/two-scopes")
+async def get_stream_session(
+ websocket: WebSocket,
+ function_session: SessionFuncDep,
+ request_session: SessionRequestDep,
+) -> Any:
+ await websocket.accept()
+ await websocket.send_json(
+ {"func_is_open": function_session.open, "req_is_open": request_session.open}
+ )
+
+
+@app.websocket("/sub")
+async def get_sub(websocket: WebSocket, sessions: NamedSessionsDep) -> Any:
+ await websocket.accept()
+ await websocket.send_json(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+
+@app.websocket("/named-function-scope")
+async def get_named_function_scope(
+ websocket: WebSocket, sessions: NamedSessionsFuncDep
+) -> Any:
+ await websocket.accept()
+ await websocket.send_json(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+
+@app.websocket("/regular-function-scope")
+async def get_regular_function_scope(
+ websocket: WebSocket, sessions: RegularSessionsDep
+) -> Any:
+ await websocket.accept()
+ await websocket.send_json(
+ {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
+ )
+
+
+client = TestClient(app)
+
+
+def test_function_scope() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/function-scope") as websocket:
+ data = websocket.receive_json()
+ assert data["is_open"] is True
+ assert global_state["session_closed"] is True
+
+
+def test_request_scope() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/request-scope") as websocket:
+ data = websocket.receive_json()
+ assert data["is_open"] is True
+ assert global_state["session_closed"] is True
+
+
+def test_two_scopes() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/two-scopes") as websocket:
+ data = websocket.receive_json()
+ assert data["func_is_open"] is True
+ assert data["req_is_open"] is True
+ assert global_state["session_closed"] is True
+
+
+def test_sub() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/sub") as websocket:
+ data = websocket.receive_json()
+ assert data["named_session_open"] is True
+ assert data["session_open"] is True
+ assert global_state["session_closed"] is True
+ assert global_state["named_session_closed"] is True
+
+
+def test_broken_scope() -> None:
+ with pytest.raises(
+ FastAPIError,
+ match='The dependency "get_named_func_session" has a scope of "request", it cannot depend on dependencies with scope "function"',
+ ):
+
+ @app.websocket("/broken-scope")
+ async def get_broken(
+ websocket: WebSocket, sessions: BrokenSessionsDep
+ ) -> Any: # pragma: no cover
+ pass
+
+
+def test_named_function_scope() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/named-function-scope") as websocket:
+ data = websocket.receive_json()
+ assert data["named_session_open"] is True
+ assert data["session_open"] is True
+ assert global_state["session_closed"] is True
+ assert global_state["named_func_session_closed"] is True
+
+
+def test_regular_function_scope() -> None:
+ global_context.set({})
+ global_state = global_context.get()
+ with client.websocket_connect("/regular-function-scope") as websocket:
+ data = websocket.receive_json()
+ assert data["named_session_open"] is True
+ assert data["session_open"] is True
+ assert global_state["session_closed"] is True
diff --git a/tests/test_get_model_definitions_formfeed_escape.py b/tests/test_get_model_definitions_formfeed_escape.py
index 439e6d448..6601585ef 100644
--- a/tests/test_get_model_definitions_formfeed_escape.py
+++ b/tests/test_get_model_definitions_formfeed_escape.py
@@ -5,7 +5,6 @@ import fastapi.openapi.utils
import pydantic.schema
import pytest
from fastapi import FastAPI
-from fastapi._compat import v1
from pydantic import BaseModel
from starlette.testclient import TestClient
@@ -165,6 +164,8 @@ def test_model_description_escaped_with_formfeed(sort_reversed: bool):
Test `get_model_definitions` with models passed in different order.
"""
+ from fastapi._compat import v1
+
all_fields = fastapi.openapi.utils.get_fields_from_routes(app.routes)
flat_models = v1.get_flat_models_from_fields(all_fields, known_models=set())
diff --git a/tests/test_lifespan_scoped_dependencies/test_dependency_overrides.py b/tests/test_lifespan_scoped_dependencies/test_dependency_overrides.py
index eb61ed248..3fd337c35 100644
--- a/tests/test_lifespan_scoped_dependencies/test_dependency_overrides.py
+++ b/tests/test_lifespan_scoped_dependencies/test_dependency_overrides.py
@@ -16,7 +16,7 @@ from fastapi import (
Request,
WebSocket,
)
-from fastapi.exceptions import DependencyScopeConflict
+from fastapi.exceptions import DependencyScopeError
from fastapi.params import Security
from fastapi.security import SecurityScopes
from fastapi.testclient import TestClient
@@ -107,7 +107,7 @@ def test_endpoint_dependencies(
None,
Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
),
],
@@ -147,7 +147,7 @@ def test_router_dependencies(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -185,12 +185,12 @@ def test_router_dependencies(
@pytest.mark.parametrize("use_cache", [True, False])
@pytest.mark.parametrize("dependency_style", list(DependencyStyle))
@pytest.mark.parametrize("routing_style", ["app", "router"])
-@pytest.mark.parametrize("main_dependency_scope", ["endpoint", "lifespan"])
+@pytest.mark.parametrize("main_dependency_scope", ["function", "request", "lifespan"])
def test_dependency_cache_in_same_dependency(
dependency_style: DependencyStyle,
routing_style,
use_cache,
- main_dependency_scope: Literal["endpoint", "lifespan"],
+ main_dependency_scope: Literal["function", "request", "lifespan"],
is_websocket,
):
dependency_factory = DependencyFactory(dependency_style)
@@ -198,7 +198,7 @@ def test_dependency_cache_in_same_dependency(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -225,7 +225,7 @@ def test_dependency_cache_in_same_dependency(
Depends(
dependency,
use_cache=use_cache,
- dependency_scope=main_dependency_scope,
+ scope=main_dependency_scope,
),
],
)
@@ -275,7 +275,7 @@ def test_dependency_cache_in_same_endpoint(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -344,7 +344,7 @@ def test_dependency_cache_in_different_endpoints(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -425,7 +425,7 @@ def test_no_cached_dependency(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=False,
)
@@ -495,11 +495,11 @@ def test_override_lifespan_scoped_dependency_cannot_use_endpoint_scoped_paramete
path="/test",
is_websocket=is_websocket,
annotation=Annotated[
- None, Depends(dependency_func, dependency_scope="lifespan")
+ None, Depends(dependency_func, scope="lifespan")
],
)
- with pytest.raises(DependencyScopeConflict):
+ with pytest.raises(DependencyScopeError):
with TestClient(app):
pass
@@ -515,7 +515,7 @@ def test_non_override_lifespan_scoped_dependency_can_use_overridden_lifespan_sco
async def lifespan_scoped_dependency(
param: Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="lifespan"),
+ Depends(dependency_factory.get_dependency(), scope="lifespan"),
],
) -> AsyncGenerator[int, None]:
yield param
@@ -527,7 +527,7 @@ def test_non_override_lifespan_scoped_dependency_can_use_overridden_lifespan_sco
path="/test",
is_websocket=is_websocket,
annotation=Annotated[
- int, Depends(lifespan_scoped_dependency, dependency_scope="lifespan")
+ int, Depends(lifespan_scoped_dependency, scope="lifespan")
],
)
@@ -568,13 +568,13 @@ def test_override_lifespan_scoped_dependency_cannot_use_endpoint_scoped_dependen
path="/test",
is_websocket=is_websocket,
annotation=Annotated[
- None, Depends(dependency_func, dependency_scope="lifespan")
+ None, Depends(dependency_func, scope="lifespan")
],
)
app.dependency_overrides[dependency_func] = override_dependency_func
- with pytest.raises(DependencyScopeConflict):
+ with pytest.raises(DependencyScopeError):
with TestClient(app):
pass
@@ -591,7 +591,7 @@ def test_bad_override_lifespan_scoped_dependencies(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
diff --git a/tests/test_lifespan_scoped_dependencies/test_endpoint_usage.py b/tests/test_lifespan_scoped_dependencies/test_endpoint_usage.py
index 0770c07fb..23875e978 100644
--- a/tests/test_lifespan_scoped_dependencies/test_endpoint_usage.py
+++ b/tests/test_lifespan_scoped_dependencies/test_endpoint_usage.py
@@ -21,7 +21,7 @@ from fastapi import (
)
from fastapi.dependencies.utils import get_endpoint_dependant
from fastapi.exceptions import (
- DependencyScopeConflict,
+ DependencyScopeError,
InvalidDependencyScope,
UninitializedLifespanDependency,
)
@@ -103,7 +103,7 @@ def test_endpoint_dependencies(
None,
Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
),
],
@@ -138,7 +138,7 @@ def test_router_dependencies(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -171,19 +171,19 @@ def test_router_dependencies(
@pytest.mark.parametrize("use_cache", [True, False])
@pytest.mark.parametrize("dependency_style", list(DependencyStyle))
@pytest.mark.parametrize("routing_style", ["app", "router"])
-@pytest.mark.parametrize("main_dependency_scope", ["endpoint", "lifespan"])
+@pytest.mark.parametrize("main_dependency_scope", ["request", "function", "lifespan"])
def test_dependency_cache_in_same_dependency(
dependency_style: DependencyStyle,
routing_style,
use_cache,
- main_dependency_scope: Literal["endpoint", "lifespan"],
+ main_dependency_scope: Literal["request", "function", "lifespan"],
is_websocket: bool,
):
dependency_factory = DependencyFactory(dependency_style)
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -210,7 +210,7 @@ def test_dependency_cache_in_same_dependency(
Depends(
dependency,
use_cache=use_cache,
- dependency_scope=main_dependency_scope,
+ scope=main_dependency_scope,
),
],
)
@@ -253,7 +253,7 @@ def test_dependency_cache_in_same_endpoint(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -315,7 +315,7 @@ def test_dependency_cache_in_different_endpoints(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -391,7 +391,7 @@ def test_no_cached_dependency(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=False,
)
@@ -448,13 +448,13 @@ def test_lifespan_scoped_dependency_cannot_use_endpoint_scoped_parameters(
app = FastAPI()
- with pytest.raises(DependencyScopeConflict):
+ with pytest.raises(DependencyScopeError):
create_endpoint_1_annotation(
router=app,
path="/test",
is_websocket=is_websocket,
annotation=Annotated[
- None, Depends(dependency_func, dependency_scope="lifespan")
+ None, Depends(dependency_func, scope="lifespan")
],
)
@@ -469,7 +469,7 @@ def test_lifespan_scoped_dependency_can_use_other_lifespan_scoped_dependencies(
async def lifespan_scoped_dependency(
param: Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="lifespan"),
+ Depends(dependency_factory.get_dependency(), scope="lifespan"),
],
) -> AsyncGenerator[int, None]:
yield param
@@ -503,8 +503,12 @@ def test_lifespan_scoped_dependency_can_use_other_lifespan_scoped_dependencies(
(DependencyStyle.ASYNC_GENERATOR, True),
],
)
+@pytest.mark.parametrize("endpoint_dependency_scope", ["request", "function"])
def test_the_same_dependency_can_work_in_different_scopes(
- dependency_style: DependencyStyle, supports_teardown, is_websocket
+ dependency_style: DependencyStyle,
+ supports_teardown,
+ is_websocket,
+ endpoint_dependency_scope: Literal["request", "function"],
):
dependency_factory = DependencyFactory(dependency_style)
app = FastAPI()
@@ -515,11 +519,11 @@ def test_the_same_dependency_can_work_in_different_scopes(
is_websocket=is_websocket,
annotation1=Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="endpoint"),
+ Depends(dependency_factory.get_dependency(), scope=endpoint_dependency_scope),
],
annotation2=Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="lifespan"),
+ Depends(dependency_factory.get_dependency(), scope="lifespan"),
],
)
if is_websocket:
@@ -620,7 +624,7 @@ def test_lifespan_scoped_dependency_can_be_used_alongside_custom_lifespans(
is_websocket=is_websocket,
annotation=Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="lifespan"),
+ Depends(dependency_factory.get_dependency(), scope="lifespan"),
],
expected_value=1,
)
@@ -650,13 +654,13 @@ def test_lifespan_scoped_dependency_cannot_use_endpoint_scoped_dependencies(
app = FastAPI()
- with pytest.raises(DependencyScopeConflict):
+ with pytest.raises(DependencyScopeError):
create_endpoint_1_annotation(
router=app,
path="/test",
is_websocket=is_websocket,
annotation=Annotated[
- None, Depends(dependency_func, dependency_scope="lifespan")
+ None, Depends(dependency_func, scope="lifespan")
],
)
@@ -679,7 +683,7 @@ def test_dependencies_must_provide_correct_dependency_scope(
with pytest.raises(
InvalidDependencyScope,
- match=r'Dependency "value" of .* has an invalid scope: ' r'"incorrect"',
+ match=r'Dependency received an invalid scope: "incorrect"',
):
create_endpoint_1_annotation(
router=router,
@@ -689,7 +693,7 @@ def test_dependencies_must_provide_correct_dependency_scope(
None,
Depends(
dependency_factory.get_dependency(),
- dependency_scope="incorrect",
+ scope="incorrect",
use_cache=use_cache,
),
],
@@ -714,12 +718,12 @@ def test_endpoints_report_incorrect_dependency_scope(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
# We intentionally change the dependency scope here to bypass the
# validation at the function level.
- depends.dependency_scope = "asdad"
+ depends.scope = "asdad"
with pytest.raises(InvalidDependencyScope):
create_endpoint_1_annotation(
@@ -739,11 +743,11 @@ def test_endpoints_report_incorrect_dependency_scope_at_router_scope(
):
dependency_factory = DependencyFactory(DependencyStyle.ASYNC_GENERATOR)
- depends = Depends(dependency_factory.get_dependency(), dependency_scope="lifespan")
+ depends = Depends(dependency_factory.get_dependency(), scope="lifespan")
# We intentionally change the dependency scope here to bypass the
# validation at the function level.
- depends.dependency_scope = "asdad"
+ depends.scope = "asdad"
if routing_style == "app":
app = FastAPI(dependencies=[depends])
@@ -777,7 +781,7 @@ def test_endpoints_report_uninitialized_dependency(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -827,7 +831,7 @@ def test_endpoints_report_uninitialized_internal_lifespan(
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -867,7 +871,7 @@ def test_bad_lifespan_scoped_dependencies(
dependency_factory = DependencyFactory(dependency_style, should_error=True)
depends = Depends(
dependency_factory.get_dependency(),
- dependency_scope="lifespan",
+ scope="lifespan",
use_cache=use_cache,
)
@@ -904,7 +908,7 @@ def test_endpoint_dependant_backwards_compatibility():
dependency1: Annotated[int, Depends(dependency_factory.get_dependency())],
dependency2: Annotated[
int,
- Depends(dependency_factory.get_dependency(), dependency_scope="lifespan"),
+ Depends(dependency_factory.get_dependency(), scope="lifespan"),
],
):
pass # pragma: nocover
diff --git a/tests/test_no_schema_split.py b/tests/test_no_schema_split.py
new file mode 100644
index 000000000..b0b5958c1
--- /dev/null
+++ b/tests/test_no_schema_split.py
@@ -0,0 +1,203 @@
+# Test with parts from, and to verify the report in:
+# https://github.com/fastapi/fastapi/discussions/14177
+# Made an issue in:
+# https://github.com/fastapi/fastapi/issues/14247
+from enum import Enum
+from typing import List
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+from pydantic import BaseModel, Field
+
+from tests.utils import pydantic_snapshot
+
+
+class MessageEventType(str, Enum):
+ alpha = "alpha"
+ beta = "beta"
+
+
+class MessageEvent(BaseModel):
+ event_type: MessageEventType = Field(default=MessageEventType.alpha)
+ output: str
+
+
+class MessageOutput(BaseModel):
+ body: str = ""
+ events: List[MessageEvent] = []
+
+
+class Message(BaseModel):
+ input: str
+ output: MessageOutput
+
+
+app = FastAPI(title="Minimal FastAPI App", version="1.0.0")
+
+
+@app.post("/messages", response_model=Message)
+async def create_message(input_message: str) -> Message:
+ return Message(
+ input=input_message,
+ output=MessageOutput(body=f"Processed: {input_message}"),
+ )
+
+
+client = TestClient(app)
+
+
+def test_create_message():
+ response = client.post("/messages", params={"input_message": "Hello"})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "input": "Hello",
+ "output": {"body": "Processed: Hello", "events": []},
+ }
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "Minimal FastAPI App", "version": "1.0.0"},
+ "paths": {
+ "/messages": {
+ "post": {
+ "summary": "Create Message",
+ "operationId": "create_message_messages_post",
+ "parameters": [
+ {
+ "name": "input_message",
+ "in": "query",
+ "required": True,
+ "schema": {"type": "string", "title": "Input Message"},
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Message"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError"
+ },
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Message": {
+ "properties": {
+ "input": {"type": "string", "title": "Input"},
+ "output": {"$ref": "#/components/schemas/MessageOutput"},
+ },
+ "type": "object",
+ "required": ["input", "output"],
+ "title": "Message",
+ },
+ "MessageEvent": {
+ "properties": {
+ "event_type": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "$ref": "#/components/schemas/MessageEventType",
+ "default": "alpha",
+ }
+ ),
+ v1=snapshot(
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/MessageEventType"
+ }
+ ],
+ "default": "alpha",
+ }
+ ),
+ ),
+ "output": {"type": "string", "title": "Output"},
+ },
+ "type": "object",
+ "required": ["output"],
+ "title": "MessageEvent",
+ },
+ "MessageEventType": pydantic_snapshot(
+ v2=snapshot(
+ {
+ "type": "string",
+ "enum": ["alpha", "beta"],
+ "title": "MessageEventType",
+ }
+ ),
+ v1=snapshot(
+ {
+ "type": "string",
+ "enum": ["alpha", "beta"],
+ "title": "MessageEventType",
+ "description": "An enumeration.",
+ }
+ ),
+ ),
+ "MessageOutput": {
+ "properties": {
+ "body": {"type": "string", "title": "Body", "default": ""},
+ "events": {
+ "items": {"$ref": "#/components/schemas/MessageEvent"},
+ "type": "array",
+ "title": "Events",
+ "default": [],
+ },
+ },
+ "type": "object",
+ "title": "MessageOutput",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
+ )
diff --git a/tests/test_params_repr.py b/tests/test_params_repr.py
index 8921026b2..1529fe72f 100644
--- a/tests/test_params_repr.py
+++ b/tests/test_params_repr.py
@@ -2,7 +2,7 @@ from typing import Any, List
import pytest
from dirty_equals import IsOneOf
-from fastapi.params import Body, Cookie, Depends, Header, Param, Path, Query
+from fastapi.params import Body, Cookie, Header, Param, Path, Query
test_data: List[Any] = ["teststr", None, ..., 1, []]
@@ -142,32 +142,3 @@ def test_body_repr_number():
def test_body_repr_list():
assert repr(Body([])) == "Body([])"
-
-
-@pytest.mark.parametrize(
- ["depends", "expected_repr"],
- [
- [Depends(), "Depends(NoneType)"],
- [Depends(get_user), "Depends(get_user)"],
- [Depends(use_cache=False), "Depends(NoneType, use_cache=False)"],
- [Depends(get_user, use_cache=False), "Depends(get_user, use_cache=False)"],
- [
- Depends(dependency_scope="lifespan"),
- 'Depends(NoneType, dependency_scope="lifespan")',
- ],
- [
- Depends(get_user, dependency_scope="lifespan"),
- 'Depends(get_user, dependency_scope="lifespan")',
- ],
- [
- Depends(use_cache=False, dependency_scope="lifespan"),
- 'Depends(NoneType, use_cache=False, dependency_scope="lifespan")',
- ],
- [
- Depends(get_user, use_cache=False, dependency_scope="lifespan"),
- 'Depends(get_user, use_cache=False, dependency_scope="lifespan")',
- ],
- ],
-)
-def test_depends_repr(depends, expected_repr):
- assert repr(depends) == expected_repr
diff --git a/tests/test_pydantic_v1_v2_multifile/test_multifile.py b/tests/test_pydantic_v1_v2_multifile/test_multifile.py
index 4472bd73e..e66d102fb 100644
--- a/tests/test_pydantic_v1_v2_multifile/test_multifile.py
+++ b/tests/test_pydantic_v1_v2_multifile/test_multifile.py
@@ -1028,17 +1028,6 @@ def test_openapi_schema():
"type": "object",
"title": "HTTPValidationError",
},
- "SubItem-Output": {
- "properties": {
- "new_sub_name": {
- "type": "string",
- "title": "New Sub Name",
- }
- },
- "type": "object",
- "required": ["new_sub_name"],
- "title": "SubItem",
- },
"ValidationError": {
"properties": {
"loc": {
@@ -1113,11 +1102,11 @@ def test_openapi_schema():
"title": "New Description",
},
"new_sub": {
- "$ref": "#/components/schemas/SubItem-Output"
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
},
"new_multi": {
"items": {
- "$ref": "#/components/schemas/SubItem-Output"
+ "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
},
"type": "array",
"title": "New Multi",
diff --git a/tests/test_response_model_as_return_annotation.py b/tests/test_response_model_as_return_annotation.py
index c3c0ed6c4..1745c69b6 100644
--- a/tests/test_response_model_as_return_annotation.py
+++ b/tests/test_response_model_as_return_annotation.py
@@ -2,12 +2,13 @@ from typing import List, Union
import pytest
from fastapi import FastAPI
-from fastapi._compat import v1
from fastapi.exceptions import FastAPIError, ResponseValidationError
from fastapi.responses import JSONResponse, Response
from fastapi.testclient import TestClient
from pydantic import BaseModel
+from tests.utils import needs_pydanticv1
+
class BaseUser(BaseModel):
name: str
@@ -511,7 +512,10 @@ def test_invalid_response_model_field():
# TODO: remove when dropping Pydantic v1 support
+@needs_pydanticv1
def test_invalid_response_model_field_pv1():
+ from fastapi._compat import v1
+
app = FastAPI()
class Model(v1.BaseModel):
diff --git a/tests/test_top_level_security_scheme_in_openapi.py b/tests/test_top_level_security_scheme_in_openapi.py
new file mode 100644
index 000000000..e2de31af5
--- /dev/null
+++ b/tests/test_top_level_security_scheme_in_openapi.py
@@ -0,0 +1,60 @@
+# Test security scheme at the top level, including OpenAPI
+# Ref: https://github.com/fastapi/fastapi/discussions/14263
+# Ref: https://github.com/fastapi/fastapi/issues/14271
+from fastapi import Depends, FastAPI
+from fastapi.security import HTTPBearer
+from fastapi.testclient import TestClient
+from inline_snapshot import snapshot
+
+app = FastAPI()
+
+bearer_scheme = HTTPBearer()
+
+
+@app.get("/", dependencies=[Depends(bearer_scheme)])
+async def get_root():
+ return {"message": "Hello, World!"}
+
+
+client = TestClient(app)
+
+
+def test_get_root():
+ response = client.get("/", headers={"Authorization": "Bearer token"})
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Hello, World!"}
+
+
+def test_get_root_no_token():
+ response = client.get("/")
+ assert response.status_code == 403, response.text
+ assert response.json() == {"detail": "Not authenticated"}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == snapshot(
+ {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/": {
+ "get": {
+ "summary": "Get Root",
+ "operationId": "get_root__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "security": [{"HTTPBearer": []}],
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {"HTTPBearer": {"type": "http", "scheme": "bearer"}}
+ },
+ }
+ )
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial008e.py b/tests/test_tutorial/test_dependencies/test_tutorial008e.py
new file mode 100644
index 000000000..1ae9ab2cd
--- /dev/null
+++ b/tests/test_tutorial/test_dependencies/test_tutorial008e.py
@@ -0,0 +1,27 @@
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py39
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial008e",
+ "tutorial008e_an",
+ pytest.param("tutorial008e_an_py39", marks=needs_py39),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get_users_me(client: TestClient):
+ response = client.get("/users/me")
+ assert response.status_code == 200, response.text
+ assert response.json() == "Rick"