Compare commits

..

19 Commits

Author SHA1 Message Date
pre-commit-ci[bot]
6a10e4a712 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks 2025-10-22 13:05:04 +00:00
svlandeg
281641a957 Merge branch 'feature/python-tests-t' of https://github.com/fastapi/fastapi into feature/python-tests-t 2025-10-22 15:04:48 +02:00
svlandeg
5355c137c7 skipping one more orjson test 2025-10-22 15:04:39 +02:00
pre-commit-ci[bot]
55931329eb 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks 2025-10-22 13:00:29 +00:00
svlandeg
865927e914 skipping more tests and moving import to within functions 2025-10-22 15:00:13 +02:00
pre-commit-ci[bot]
11a894297d 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks 2025-10-22 12:53:25 +00:00
svlandeg
ecffbf617d skip ujson and orjson tests 2025-10-22 14:52:09 +02:00
svlandeg
ecb695df4d remove ujson from dependencies (as test) 2025-10-22 14:42:00 +02:00
svlandeg
025351e504 remove ujson-types 2025-10-22 14:35:41 +02:00
svlandeg
8e6bbcbb99 enable GIL again for more testing 2025-10-22 14:26:24 +02:00
svlandeg
002411e05d fix formatting 2025-10-21 10:32:02 +02:00
svlandeg
90e8bcc14d disable GIL to run test suite 2025-10-21 10:30:28 +02:00
svlandeg
479e646451 exclude pydantic v1 for Python 3.14t 2025-10-20 17:21:49 +02:00
Sofie Van Landeghem
f68bdddeca Merge branch 'master' into feature/python-tests-t 2025-10-20 14:51:19 +02:00
svlandeg
5a98af34ec remove allow prereleases 2025-10-10 13:58:32 +02:00
svlandeg
c013c04ea2 remove 3.13t 2025-10-10 13:58:09 +02:00
svlandeg
02415d5191 Merge branch 'master' into feature/python-tests-t 2025-10-10 13:48:05 +02:00
svlandeg
b24c50a4ee Merge branch 'master' into feature/python-tests-t 2025-09-23 14:43:57 +02:00
Patrick Arminio
fc6f9f8df7 Add tests for free threaded and 3.14 2025-07-29 12:03:13 +01:00
44 changed files with 172 additions and 656 deletions

View File

@@ -118,7 +118,7 @@ jobs:
path: docs/${{ matrix.lang }}/.cache
- name: Build Docs
run: python ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@v5
- uses: actions/upload-artifact@v4
with:
name: docs-site-${{ matrix.lang }}
path: ./site/**

View File

@@ -49,7 +49,7 @@ jobs:
run: |
rm -rf ./site
mkdir ./site
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v5
with:
path: ./site/
pattern: docs-site-*

View File

@@ -34,7 +34,7 @@ jobs:
requirements**.txt
pyproject.toml
- run: uv pip install -r requirements-github-actions.txt
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v5
with:
name: coverage-html
path: htmlcov

View File

@@ -48,6 +48,7 @@ jobs:
strategy:
matrix:
python-version:
- "3.14t"
- "3.14"
- "3.13"
- "3.12"
@@ -59,6 +60,8 @@ jobs:
exclude:
- python-version: "3.14"
pydantic-version: "pydantic-v1"
- python-version: "3.14t"
pydantic-version: "pydantic-v1"
fail-fast: false
steps:
- name: Dump GitHub context
@@ -90,6 +93,10 @@ jobs:
- name: Install older AnyIO in Python 3.8
if: matrix.python-version == '3.8'
run: uv pip install "anyio[trio]<4.0.0"
# - name: Set PYTHON_GIL
# if: endsWith(matrix.python-version, 't')
# run: |
# echo "PYTHON_GIL=0" >> "$GITHUB_ENV"
- run: mkdir coverage
- name: Test
run: bash scripts/test.sh
@@ -97,7 +104,7 @@ jobs:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
- name: Store coverage files
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }}
path: coverage
@@ -126,7 +133,7 @@ jobs:
- name: Install Dependencies
run: uv pip install -r requirements-tests.txt
- name: Get coverage files
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
pattern: coverage-*
path: coverage
@@ -136,7 +143,7 @@ jobs:
- run: coverage report
- run: coverage html --title "Coverage for ${{ github.sha }}"
- name: Store coverage HTML
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: coverage-html
path: htmlcov

View File

@@ -14,7 +14,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.2
rev: v0.13.3
hooks:
- id: ruff
args:

View File

@@ -55,7 +55,6 @@ The key features are:
<a href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi" target="_blank" title="Cut Code Review Time & Bugs in Half with CodeRabbit"><img src="https://fastapi.tiangolo.com/img/sponsors/coderabbit.png"></a>
<a href="https://subtotal.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=open-source" target="_blank" title="The Gold Standard in Retail Account Linking"><img src="https://fastapi.tiangolo.com/img/sponsors/subtotal.svg"></a>
<a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" target="_blank" title="Deploy enterprise applications at startup speed"><img src="https://fastapi.tiangolo.com/img/sponsors/railway.png"></a>
<a href="https://serpapi.com/?utm_source=fastapi_website" target="_blank" title="SerpApi: Web Search API"><img src="https://fastapi.tiangolo.com/img/sponsors/serpapi.png"></a>
<a href="https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
<a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>

View File

@@ -47,7 +47,7 @@ Das LLM wird dies wahrscheinlich falsch übersetzen. Interessant ist nur, ob es
//// tab | Info
Der Promptdesigner kann entscheiden, ob neutrale Anführungszeichen in typografische Anführungszeichen umgewandelt werden sollen. Es ist in Ordnung, sie unverändert zu lassen.
Der Prompt-Designer kann entscheiden, ob neutrale Anführungszeichen in typografische Anführungszeichen umgewandelt werden sollen. Es ist in Ordnung, sie unverändert zu lassen.
Siehe zum Beispiel den Abschnitt `### Quotes` in `docs/de/llm-prompt.md`.
@@ -459,7 +459,7 @@ Für einige sprachspezifische Anweisungen, siehe z. B. den Abschnitt `### Headin
* der Commit
* der Contextmanager
* die Coroutine
* die Datenbanksession
* die Datenbank-Session
* die Festplatte
* die Domain
* die Engine
@@ -496,7 +496,7 @@ Für einige sprachspezifische Anweisungen, siehe z. B. den Abschnitt `### Headin
//// tab | Info
Dies ist eine nicht vollständige und nicht normative Liste von (meist) technischen Begriffen, die in der Dokumentation vorkommen. Sie kann dem Promptdesigner helfen herauszufinden, bei welchen Begriffen das LLM Unterstützung braucht. Zum Beispiel, wenn es eine gute Übersetzung immer wieder auf eine suboptimale Übersetzung zurücksetzt. Oder wenn es Probleme hat, einen Begriff in Ihrer Sprache zu konjugieren/deklinieren.
Dies ist eine nicht vollständige und nicht normative Liste von (meist) technischen Begriffen, die in der Dokumentation vorkommen. Sie kann dem Prompt-Designer helfen herauszufinden, bei welchen Begriffen das LLM Unterstützung braucht. Zum Beispiel, wenn es eine gute Übersetzung immer wieder auf eine suboptimale Übersetzung zurücksetzt. Oder wenn es Probleme hat, einen Begriff in Ihrer Sprache zu konjugieren/deklinieren.
Siehe z. B. den Abschnitt `### List of English terms and their preferred German translations` in `docs/de/llm-prompt.md`.

View File

@@ -1,133 +0,0 @@
# Von Pydantic v1 zu Pydantic v2 migrieren { #migrate-from-pydantic-v1-to-pydantic-v2 }
Wenn Sie eine ältere FastAPI-App haben, nutzen Sie möglicherweise Pydantic Version 1.
FastAPI unterstützt seit Version 0.100.0 sowohl Pydantic v1 als auch v2.
Wenn Sie Pydantic v2 installiert hatten, wurde dieses verwendet. Wenn stattdessen Pydantic v1 installiert war, wurde jenes verwendet.
Pydantic v1 ist jetzt deprecatet und die Unterstützung dafür wird in den nächsten Versionen von FastAPI entfernt, Sie sollten also zu **Pydantic v2 migrieren**. Auf diese Weise erhalten Sie die neuesten Features, Verbesserungen und Fixes.
/// warning | Achtung
Außerdem hat das Pydantic-Team die Unterstützung für Pydantic v1 in den neuesten Python-Versionen eingestellt, beginnend mit **Python 3.14**.
Wenn Sie die neuesten Features von Python nutzen möchten, müssen Sie sicherstellen, dass Sie Pydantic v2 verwenden.
///
Wenn Sie eine ältere FastAPI-App mit Pydantic v1 haben, zeige ich Ihnen hier, wie Sie sie zu Pydantic v2 migrieren, und die **neuen Features in FastAPI 0.119.0**, die Ihnen bei einer schrittweisen Migration helfen.
## Offizieller Leitfaden { #official-guide }
Pydantic hat einen offiziellen <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Migrationsleitfaden</a> von v1 zu v2.
Er enthält auch, was sich geändert hat, wie Validierungen nun korrekter und strikter sind, mögliche Stolpersteine, usw.
Sie können ihn lesen, um besser zu verstehen, was sich geändert hat.
## Tests { #tests }
Stellen Sie sicher, dass Sie [Tests](../tutorial/testing.md){.internal-link target=_blank} für Ihre App haben und diese in Continuous Integration (CI) ausführen.
Auf diese Weise können Sie das Update durchführen und sicherstellen, dass weiterhin alles wie erwartet funktioniert.
## `bump-pydantic` { #bump-pydantic }
In vielen Fällen, wenn Sie reguläre Pydantic-Modelle ohne Anpassungen verwenden, können Sie den Großteil des Prozesses der Migration von Pydantic v1 auf Pydantic v2 automatisieren.
Sie können <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> vom selben Pydantic-Team verwenden.
Dieses Tool hilft Ihnen, den Großteil des zu ändernden Codes automatisch anzupassen.
Danach können Sie die Tests ausführen und prüfen, ob alles funktioniert. Falls ja, sind Sie fertig. 😎
## Pydantic v1 in v2 { #pydantic-v1-in-v2 }
Pydantic v2 enthält alles aus Pydantic v1 als Untermodul `pydantic.v1`.
Das bedeutet, Sie können die neueste Version von Pydantic v2 installieren und die alten Pydanticv1Komponenten aus diesem Untermodul importieren und verwenden, als hätten Sie das alte Pydantic v1 installiert.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### FastAPI-Unterstützung für Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Seit FastAPI 0.119.0 gibt es außerdem eine teilweise Unterstützung für Pydantic v1 innerhalb von Pydantic v2, um die Migration auf v2 zu erleichtern.
Sie könnten also Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass das Untermodul `pydantic.v1` verwendet wird, und in vielen Fällen würde es einfach funktionieren.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Achtung
Beachten Sie, dass, da das PydanticTeam Pydantic v1 in neueren PythonVersionen nicht mehr unterstützt, beginnend mit Python 3.14, auch die Verwendung von `pydantic.v1` unter Python 3.14 und höher nicht unterstützt wird.
///
### Pydantic v1 und v2 in derselben App { #pydantic-v1-and-v2-on-the-same-app }
Es wird von Pydantic **nicht unterstützt**, dass ein Pydanticv2Modell Felder hat, die als Pydanticv1Modelle definiert sind, und umgekehrt.
```mermaid
graph TB
subgraph "❌ Nicht unterstützt"
direction TB
subgraph V2["Pydantic-v2-Modell"]
V1Field["Pydantic-v1-Modell"]
end
subgraph V1["Pydantic-v1-Modell"]
V2Field["Pydantic-v2-Modell"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
... aber Sie können getrennte Modelle, die Pydantic v1 bzw. v2 nutzen, in derselben App verwenden.
```mermaid
graph TB
subgraph "✅ Unterstützt"
direction TB
subgraph V2["Pydantic-v2-Modell"]
V2Field["Pydantic-v2-Modell"]
end
subgraph V1["Pydantic-v1-Modell"]
V1Field["Pydantic-v1-Modell"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
In einigen Fällen ist es sogar möglich, sowohl Pydanticv1 als auch Pydanticv2Modelle in derselben **Pfadoperation** Ihrer FastAPIApp zu verwenden:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
Im obigen Beispiel ist das Eingabemodell ein Pydanticv1Modell, und das Ausgabemodell (definiert in `response_model=ItemV2`) ist ein Pydanticv2Modell.
### Pydantic v1 Parameter { #pydantic-v1-parameters }
Wenn Sie einige der FastAPI-spezifischen Tools für Parameter wie `Body`, `Query`, `Form`, usw. zusammen mit Pydanticv1Modellen verwenden müssen, können Sie die aus `fastapi.temp_pydantic_v1_params` importieren, während Sie die Migration zu Pydantic v2 abschließen:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### In Schritten migrieren { #migrate-in-steps }
/// tip | Tipp
Probieren Sie zuerst `bump-pydantic` aus. Wenn Ihre Tests erfolgreich sind und das funktioniert, sind Sie mit einem einzigen Befehl fertig. ✨
///
Wenn `bump-pydantic` für Ihren Anwendungsfall nicht funktioniert, können Sie die Unterstützung für Pydanticv1 und Pydanticv2Modelle in derselben App nutzen, um die Migration zu Pydantic v2 schrittweise durchzuführen.
Sie könnten zuerst Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass für all Ihre Modelle `pydantic.v1` verwendet wird.
Anschließend können Sie beginnen, Ihre Modelle gruppenweise von Pydantic v1 auf v2 zu migrieren in kleinen, schrittweisen Etappen. 🚶

View File

@@ -185,7 +185,7 @@ Example:
# FastAPI in Containern - Docker { #fastapi-in-containers-docker }
»»»
3.1) Do not apply rule 3 when there is no space before or no space after the hyphen.
3.1) Do not apply rule 3 when there is no space before or no space after the dash.
Example:
@@ -195,13 +195,13 @@ Example:
## Type hints and annotations { #type-hints-and-annotations }
»»»
Translate with (German) notice the hyphen:
Translate with (German) use a short dash:
«««
## Typhinweise und -annotationen { #type-hints-and-annotations }
»»»
Do NOT translate with (German) notice the dash:
Do NOT translate with (German):
«««
## Typhinweise und annotationen { #type-hints-and-annotations }
@@ -222,7 +222,7 @@ Ich versuche nicht, alles einzudeutschen. Das bezieht sich besonders auf Begriff
### List of English terms and their preferred German translations
Below is a list of English terms and their preferred German translations, separated by a colon («:»). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. In the below list, a term or a translation may be followed by an explanation in brackets, which explains when to translate the term this way. If a translation is preceded by «NOT», then that means: do NOT use this translation for this term. English nouns, starting with the word «the», have the German genus «der», «die», «das» prepended to their German translation, to help you to grammatically decline them in the translation. They are given in singular case, unless they have «(plural)» attached, which means they are given in plural case. Verbs are given in the full infinitive starting with the word «to».
Below is a list of English terms and their preferred German translations, separated by a colon («:»). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. A term or a translation may be followed by an explanation in brackets, which explains when to translate the term this way. If a translation is preceded by «NOT», then that means: do NOT use this translation for this term. English nouns, starting with the word «the», have the German genus «der», «die», «das» prepended to their German translation, to help you to grammatically decline them in the translation. They are given in singular case, unless they have «(plural)» attached, which means they are given in plural case. Verbs are given in the full infinitive starting with the word «to».
* «/// check»: «/// check | Testen»
* «/// danger»: «/// danger | Gefahr»

View File

@@ -26,9 +26,6 @@ gold:
- url: https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi
title: Deploy enterprise applications at startup speed
img: https://fastapi.tiangolo.com/img/sponsors/railway.png
- url: https://serpapi.com/?utm_source=fastapi_website
title: "SerpApi: Web Search API"
img: https://fastapi.tiangolo.com/img/sponsors/serpapi.png
silver:
- url: https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display
title: Pay as you go for market data

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -7,67 +7,6 @@ hide:
## Latest Changes
## 0.120.4
### Fixes
* 🐛 Fix security schemes in OpenAPI when added at the top level app. PR [#14266](https://github.com/fastapi/fastapi/pull/14266) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.120.3
### Refactors
* ♻️ Reduce internal cyclic recursion in dependencies, from 2 functions calling each other to 1 calling itself. PR [#14256](https://github.com/fastapi/fastapi/pull/14256) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor internals of dependencies, simplify code and remove `get_param_sub_dependant`. PR [#14255](https://github.com/fastapi/fastapi/pull/14255) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Refactor internals of dependencies, simplify using dataclasses. PR [#14254](https://github.com/fastapi/fastapi/pull/14254) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Update note for untranslated pages. PR [#14257](https://github.com/fastapi/fastapi/pull/14257) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.120.2
### Fixes
* 🐛 Fix separation of schemas with nested models introduced in 0.119.0. PR [#14246](https://github.com/fastapi/fastapi/pull/14246) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 🔧 Add sponsor: SerpApi. PR [#14248](https://github.com/fastapi/fastapi/pull/14248) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump actions/download-artifact from 5 to 6. PR [#14236](https://github.com/fastapi/fastapi/pull/14236) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#14237](https://github.com/fastapi/fastapi/pull/14237) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* ⬆ Bump actions/upload-artifact from 4 to 5. PR [#14235](https://github.com/fastapi/fastapi/pull/14235) by [@dependabot[bot]](https://github.com/apps/dependabot).
## 0.120.1
### Upgrades
* ⬆️ Bump Starlette to <`0.50.0`. PR [#14234](https://github.com/fastapi/fastapi/pull/14234) by [@YuriiMotov](https://github.com/YuriiMotov).
### Internal
* 🔧 Add `license` and `license-files` to `pyproject.toml`, remove `License` from `classifiers`. PR [#14230](https://github.com/fastapi/fastapi/pull/14230) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.120.0
There are no major nor breaking changes in this release. ☕️
The internal reference documentation now uses `annotated_doc.Doc` instead of `typing_extensions.Doc`, this adds a new (very small) dependency on [`annotated-doc`](https://github.com/fastapi/annotated-doc), a package made just to provide that `Doc` documentation utility class.
I would expect `typing_extensions.Doc` to be deprecated and then removed at some point from `typing_extensions`, for that reason there's the new `annotated-doc` micro-package. If you are curious about this, you can read more in the repo for [`annotated-doc`](https://github.com/fastapi/annotated-doc).
This new version `0.120.0` only contains that transition to the new home package for that utility class `Doc`.
### Translations
* 🌐 Sync German docs. PR [#14188](https://github.com/fastapi/fastapi/pull/14188) by [@nilslindemann](https://github.com/nilslindemann).
### Internal
* Migrate internal reference documentation from `typing_extensions.Doc` to `annotated_doc.Doc`. PR [#14222](https://github.com/fastapi/fastapi/pull/14222) by [@tiangolo](https://github.com/tiangolo).
* 🛠️ Update German LLM prompt and test file. PR [#14189](https://github.com/fastapi/fastapi/pull/14189) by [@nilslindemann](https://github.com/nilslindemann).
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#14181](https://github.com/fastapi/fastapi/pull/14181) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
## 0.119.1
### Fixes

View File

@@ -80,12 +80,6 @@
<img class="sponsor-image" src="/img/sponsors/railway-banner.png" />
</a>
</div>
<div class="item">
<a title="SerpApi: Web Search API" style="display: block; position: relative;" href="https://serpapi.com/?utm_source=fastapi_website" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/serpapi-banner.png" />
</a>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,9 +1,7 @@
/// warning
This page hasnt been translated into your language yet. 🌍
The current page still doesn't have a translation for this language.
Were 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}
But you can help translating it: [Contributing](https://fastapi.tiangolo.com/contributing/){.internal-link target=_blank}.
///

View File

@@ -1,6 +1,5 @@
from typing import Any
import orjson
from fastapi import FastAPI, Response
app = FastAPI()
@@ -10,6 +9,8 @@ class CustomORJSONResponse(Response):
media_type = "application/json"
def render(self, content: Any) -> bytes:
import orjson
assert orjson is not None, "orjson must be installed"
return orjson.dumps(content, option=orjson.OPT_INDENT_2)

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.120.4"
__version__ = "0.119.1"
from starlette import status as status

View File

@@ -207,31 +207,11 @@ def get_definitions(
override_mode: Union[Literal["validation"], None] = (
None if separate_input_output_schemas else "validation"
)
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_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
]
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

View File

@@ -13,7 +13,6 @@ from typing import (
Union,
)
from annotated_doc import Doc
from fastapi import routing
from fastapi.datastructures import Default, DefaultPlaceholder
from fastapi.exception_handlers import (
@@ -44,7 +43,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, deprecated
from typing_extensions import Annotated, Doc, deprecated
AppType = TypeVar("AppType", bound="FastAPI")

View File

@@ -1,8 +1,7 @@
from typing import Any, Callable
from annotated_doc import Doc
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from typing_extensions import Annotated, ParamSpec
from typing_extensions import Annotated, Doc, ParamSpec
P = ParamSpec("P")

View File

@@ -10,7 +10,6 @@ from typing import (
cast,
)
from annotated_doc import Doc
from fastapi._compat import (
CoreSchema,
GetJsonSchemaHandler,
@@ -23,7 +22,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
from typing_extensions import Annotated, Doc
class UploadFile(StarletteUploadFile):

View File

@@ -125,16 +125,60 @@ 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,
security_scopes: Optional[List[str]] = None,
) -> Dependant:
assert depends.dependency
return get_sub_dependant(
depends=depends,
dependency=depends.dependency,
path=path,
name=param_name,
security_scopes=security_scopes,
)
def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
assert callable(depends.dependency), (
"A parameter-less dependency must have a callable dependency"
)
use_security_scopes: List[str] = []
if isinstance(depends, params.Security) and depends.scopes:
use_security_scopes.extend(depends.scopes)
return get_dependant(
path=path, call=depends.dependency, security_scopes=use_security_scopes
return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
def get_sub_dependant(
*,
depends: params.Depends,
dependency: Callable[..., Any],
path: str,
name: Optional[str] = None,
security_scopes: Optional[List[str]] = None,
) -> Dependant:
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_dependant(
path=path,
call=dependency,
name=name,
security_scopes=security_scopes,
use_cache=depends.use_cache,
)
if security_requirement:
sub_dependant.security_requirements.append(security_requirement)
return sub_dependant
CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]]
@@ -238,6 +282,9 @@ def get_dependant(
security_scopes: Optional[List[str]] = None,
use_cache: bool = True,
) -> Dependant:
path_param_names = get_path_param_names(path)
endpoint_signature = get_typed_signature(call)
signature_params = endpoint_signature.parameters
dependant = Dependant(
call=call,
name=name,
@@ -245,17 +292,6 @@ def get_dependant(
security_scopes=security_scopes,
use_cache=use_cache,
)
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
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(
@@ -265,17 +301,11 @@ def get_dependant(
is_path_param=is_path_param,
)
if param_details.depends is not None:
assert param_details.depends.dependency
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)
sub_dependant = get_dependant(
sub_dependant = get_param_sub_dependant(
param_name=param_name,
depends=param_details.depends,
path=path,
call=param_details.depends.dependency,
name=param_name,
security_scopes=use_security_scopes,
use_cache=param_details.depends.use_cache,
security_scopes=security_scopes,
)
dependant.dependencies.append(sub_dependant)
continue

View File

@@ -17,14 +17,13 @@ from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
from uuid import UUID
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
from typing_extensions import Annotated, Doc
from ._compat import Url, _is_undefined, _model_dump

View File

@@ -1,10 +1,9 @@
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
from typing_extensions import Annotated, Doc
class HTTPException(StarletteHTTPException):

View File

@@ -1,10 +1,9 @@
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
from typing_extensions import Annotated, Doc
swagger_ui_default_parameters: Annotated[
Dict[str, Any],

View File

@@ -1,10 +1,9 @@
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 typing_extensions import Annotated, deprecated
from typing_extensions import Annotated, Doc, deprecated
_Unset: Any = Undefined

View File

@@ -1,5 +1,4 @@
import warnings
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
@@ -762,12 +761,26 @@ class File(Form): # type: ignore[misc]
)
@dataclass
class Depends:
dependency: Optional[Callable[..., Any]] = None
use_cache: bool = True
def __init__(
self, dependency: Optional[Callable[..., Any]] = None, *, use_cache: bool = True
):
self.dependency = dependency
self.use_cache = use_cache
def __repr__(self) -> str:
attr = getattr(self.dependency, "__name__", type(self.dependency).__name__)
cache = "" if self.use_cache else ", use_cache=False"
return f"{self.__class__.__name__}({attr}{cache})"
@dataclass
class Security(Depends):
scopes: Optional[Sequence[str]] = None
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)
self.scopes = scopes or []

View File

@@ -24,7 +24,6 @@ from typing import (
Union,
)
from annotated_doc import Doc
from fastapi import params, temp_pydantic_v1_params
from fastapi._compat import (
ModelField,
@@ -77,7 +76,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, deprecated
from typing_extensions import Annotated, Doc, deprecated
if sys.version_info >= (3, 13): # pragma: no cover
from inspect import iscoroutinefunction

View File

@@ -1,12 +1,11 @@
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
from typing_extensions import Annotated, Doc
class APIKeyBase(SecurityBase):

View File

@@ -2,7 +2,6 @@ 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
@@ -11,7 +10,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
from typing_extensions import Annotated, Doc
class HTTPBasicCredentials(BaseModel):

View File

@@ -1,6 +1,5 @@
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
@@ -11,7 +10,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
from typing_extensions import Annotated, Doc
class OAuth2PasswordRequestForm:

View File

@@ -1,12 +1,11 @@
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
from typing_extensions import Annotated, Doc
class OpenIdConnect(SecurityBase):

View File

@@ -7,8 +7,6 @@ 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" },
@@ -33,6 +31,7 @@ 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",
@@ -45,10 +44,9 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
dependencies = [
"starlette>=0.40.0,<0.50.0",
"starlette>=0.40.0,<0.49.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]
@@ -111,9 +109,9 @@ all = [
# For Starlette's schema generation, would not be used with FastAPI
"pyyaml >=5.3.1",
# For UJSONResponse
"ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0",
# "ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0",
# For ORJSONResponse
"orjson >=3.2.1",
# "orjson >=3.2.1",
# To validate email fields
"email-validator >=2.0.0",
# Uvicorn with uvloop

View File

@@ -12,7 +12,7 @@ pillow==11.3.0
# For image processing by Material for MkDocs
cairosvg==2.8.2
mkdocstrings[python]==0.26.1
griffe-typingdoc==0.3.0
griffe-typingdoc==0.2.9
# For griffe, it formats with black
black==25.1.0
mkdocs-macros-plugin==1.4.0

View File

@@ -1,6 +1,6 @@
from typing import Any
import orjson
import pytest
from fastapi import APIRouter, FastAPI
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
from fastapi.testclient import TestClient
@@ -10,6 +10,8 @@ class ORJSONResponse(JSONResponse):
media_type = "application/x-orjson"
def render(self, content: Any) -> bytes:
import orjson
return orjson.dumps(content)
@@ -118,6 +120,7 @@ html_type = "text/html; charset=utf-8"
override_type = "application/x-override"
@pytest.mark.skip(reason="skipping orjson tests")
def test_app():
with client:
response = client.get("/")
@@ -125,6 +128,7 @@ def test_app():
assert response.headers["content-type"] == orjson_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_app_override():
with client:
response = client.get("/override")
@@ -132,6 +136,7 @@ def test_app_override():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a():
with client:
response = client.get("/a")
@@ -139,6 +144,7 @@ def test_router_a():
assert response.headers["content-type"] == orjson_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a_override():
with client:
response = client.get("/a/override")
@@ -146,6 +152,7 @@ def test_router_a_override():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a_a():
with client:
response = client.get("/a/a")
@@ -153,6 +160,7 @@ def test_router_a_a():
assert response.headers["content-type"] == orjson_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a_a_override():
with client:
response = client.get("/a/a/override")
@@ -160,6 +168,7 @@ def test_router_a_a_override():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a_b():
with client:
response = client.get("/a/b")
@@ -167,6 +176,7 @@ def test_router_a_b():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_a_b_override():
with client:
response = client.get("/a/b/override")
@@ -174,6 +184,7 @@ def test_router_a_b_override():
assert response.headers["content-type"] == html_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b():
with client:
response = client.get("/b")
@@ -181,6 +192,7 @@ def test_router_b():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b_override():
with client:
response = client.get("/b/override")
@@ -188,6 +200,7 @@ def test_router_b_override():
assert response.headers["content-type"] == html_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b_a():
with client:
response = client.get("/b/a")
@@ -195,6 +208,7 @@ def test_router_b_a():
assert response.headers["content-type"] == text_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b_a_override():
with client:
response = client.get("/b/a/override")
@@ -202,6 +216,7 @@ def test_router_b_a_override():
assert response.headers["content-type"] == html_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b_a_c():
with client:
response = client.get("/b/a/c")
@@ -209,6 +224,7 @@ def test_router_b_a_c():
assert response.headers["content-type"] == html_type
@pytest.mark.skip(reason="skipping orjson tests")
def test_router_b_a_c_override():
with client:
response = client.get("/b/a/c/override")

View File

@@ -1,78 +0,0 @@
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"}

View File

@@ -1,203 +0,0 @@
# 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",
},
}
},
}
)

View File

@@ -1,3 +1,4 @@
import pytest
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
from fastapi.testclient import TestClient
@@ -15,6 +16,7 @@ def get_orjson_non_str_keys():
client = TestClient(app)
@pytest.mark.skip(reason="skipping orjson tests")
def test_orjson_non_str_keys():
with client:
response = client.get("/orjson_non_str_keys")

View File

@@ -1,7 +1,7 @@
from typing import Any, List
from dirty_equals import IsOneOf
from fastapi.params import Body, Cookie, Header, Param, Path, Query
from fastapi.params import Body, Cookie, Depends, Header, Param, Path, Query
test_data: List[Any] = ["teststr", None, ..., 1, []]
@@ -141,3 +141,12 @@ def test_body_repr_number():
def test_body_repr_list():
assert repr(Body([])) == "Body([])"
def test_depends_repr():
assert repr(Depends()) == "Depends(NoneType)"
assert repr(Depends(get_user)) == "Depends(get_user)"
assert repr(Depends(use_cache=False)) == "Depends(NoneType, use_cache=False)"
assert (
repr(Depends(get_user, use_cache=False)) == "Depends(get_user, use_cache=False)"
)

View File

@@ -1028,6 +1028,17 @@ 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": {
@@ -1102,11 +1113,11 @@ def test_openapi_schema():
"title": "New Description",
},
"new_sub": {
"$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
"$ref": "#/components/schemas/SubItem-Output"
},
"new_multi": {
"items": {
"$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
"$ref": "#/components/schemas/SubItem-Output"
},
"type": "array",
"title": "New Multi",

View File

@@ -1,60 +0,0 @@
# 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"}}
},
}
)

View File

@@ -1,3 +1,4 @@
import pytest
from fastapi.testclient import TestClient
from docs_src.custom_response.tutorial001 import app
@@ -5,12 +6,14 @@ from docs_src.custom_response.tutorial001 import app
client = TestClient(app)
@pytest.mark.skip(reason="skipping ujson tests")
def test_get_custom_response():
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}]
@pytest.mark.skip(reason="skipping ujson tests")
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text

View File

@@ -1,3 +1,4 @@
import pytest
from fastapi.testclient import TestClient
from docs_src.custom_response.tutorial001b import app
@@ -5,12 +6,14 @@ from docs_src.custom_response.tutorial001b import app
client = TestClient(app)
@pytest.mark.skip(reason="skipping orjson tests")
def test_get_custom_response():
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}]
@pytest.mark.skip(reason="skipping orjson tests")
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text

View File

@@ -1,3 +1,4 @@
import pytest
from fastapi.testclient import TestClient
from docs_src.custom_response.tutorial009c import app
@@ -5,6 +6,7 @@ from docs_src.custom_response.tutorial009c import app
client = TestClient(app)
@pytest.mark.skip(reason="skipping orjson tests")
def test_get():
response = client.get("/")
assert response.content == b'{\n "message": "Hello World"\n}'