diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml
index 3726b7d18d..98424a341d 100644
--- a/.github/DISCUSSION_TEMPLATE/questions.yml
+++ b/.github/DISCUSSION_TEMPLATE/questions.yml
@@ -123,6 +123,20 @@ body:
```
validations:
required: true
+ - type: input
+ id: pydantic-version
+ attributes:
+ label: Pydantic Version
+ description: |
+ What Pydantic version are you using?
+
+ You can find the Pydantic version with:
+
+ ```bash
+ python -c "import pydantic; print(pydantic.version.VERSION)"
+ ```
+ validations:
+ required: true
- type: input
id: python-version
attributes:
diff --git a/.github/actions/people/Dockerfile b/.github/actions/people/Dockerfile
index f0f389c644..1455106bde 100644
--- a/.github/actions/people/Dockerfile
+++ b/.github/actions/people/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.11
+FROM python:3.9
RUN pip install httpx PyGithub "pydantic==2.0.2" pydantic-settings "pyyaml>=5.3.1,<6.0.0"
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 19009447b0..dedf23fb94 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -44,7 +44,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
- key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v05
+ key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06
- name: Install docs extras
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-docs.txt
@@ -80,7 +80,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
- key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v05
+ key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06
- name: Install docs extras
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-docs.txt
diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml
index 3246231031..bb967fa118 100644
--- a/.github/workflows/issue-manager.yml
+++ b/.github/workflows/issue-manager.yml
@@ -19,6 +19,10 @@ jobs:
if: github.repository_owner == 'tiangolo'
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: tiangolo/issue-manager@0.4.0
with:
token: ${{ secrets.FASTAPI_ISSUE_MANAGER }}
diff --git a/.github/workflows/label-approved.yml b/.github/workflows/label-approved.yml
index 976d29f74d..2113c468ac 100644
--- a/.github/workflows/label-approved.yml
+++ b/.github/workflows/label-approved.yml
@@ -9,6 +9,10 @@ jobs:
if: github.repository_owner == 'tiangolo'
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: docker://tiangolo/label-approved:0.0.2
with:
token: ${{ secrets.FASTAPI_LABEL_APPROVED }}
diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml
index 0461f3dd3e..e38870f464 100644
--- a/.github/workflows/latest-changes.yml
+++ b/.github/workflows/latest-changes.yml
@@ -20,6 +20,10 @@ jobs:
latest-changes:
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
with:
# To allow latest-changes to commit to the main branch
diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml
index cd7affbc39..44ee83ec02 100644
--- a/.github/workflows/notify-translations.yml
+++ b/.github/workflows/notify-translations.yml
@@ -19,6 +19,10 @@ jobs:
notify-translations:
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
# Allow debugging with tmate
- name: Setup tmate session
diff --git a/.github/workflows/people.yml b/.github/workflows/people.yml
index aa7f34464e..4480a14274 100644
--- a/.github/workflows/people.yml
+++ b/.github/workflows/people.yml
@@ -15,6 +15,10 @@ jobs:
if: github.repository_owner == 'tiangolo'
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
# Ref: https://github.com/actions/runner/issues/2033
- name: Fix git safe.directory in container
diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml
index c6d894d9f6..4e689d95c1 100644
--- a/.github/workflows/smokeshow.yml
+++ b/.github/workflows/smokeshow.yml
@@ -14,6 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/setup-python@v4
with:
python-version: '3.9'
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b95358d013..6a512a019b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,6 +13,10 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
@@ -25,7 +29,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
- key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v03
+ key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-tests.txt
@@ -42,6 +46,10 @@ jobs:
pydantic-version: ["pydantic-v1", "pydantic-v2"]
fail-fast: false
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
@@ -54,7 +62,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
- key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v03
+ key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-tests.txt
@@ -80,6 +88,10 @@ jobs:
needs: [test]
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
@@ -110,6 +122,10 @@ jobs:
- coverage-combine
runs-on: ubuntu-latest
steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
diff --git a/docs/en/data/external_links.yml b/docs/en/data/external_links.yml
index ad738df353..a7f766d161 100644
--- a/docs/en/data/external_links.yml
+++ b/docs/en/data/external_links.yml
@@ -1,5 +1,9 @@
articles:
english:
+ - author: Adejumo Ridwan Suleiman
+ author_link: https://www.linkedin.com/in/adejumoridwan/
+ link: https://medium.com/python-in-plain-english/build-an-sms-spam-classifier-serverless-database-with-faunadb-and-fastapi-23dbb275bc5b
+ title: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI
- author: Raf Rasenberg
author_link: https://rafrasenberg.com/about/
link: https://rafrasenberg.com/fastapi-lambda/
diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml
index 56a886c68b..3a68ba62ba 100644
--- a/docs/en/data/github_sponsors.yml
+++ b/docs/en/data/github_sponsors.yml
@@ -2,6 +2,9 @@ sponsors:
- - login: cryptapi
avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4
url: https://github.com/cryptapi
+ - login: fern-api
+ avatarUrl: https://avatars.githubusercontent.com/u/102944815?v=4
+ url: https://github.com/fern-api
- login: nanram22
avatarUrl: https://avatars.githubusercontent.com/u/116367316?v=4
url: https://github.com/nanram22
@@ -29,15 +32,18 @@ sponsors:
- login: VincentParedes
avatarUrl: https://avatars.githubusercontent.com/u/103889729?v=4
url: https://github.com/VincentParedes
+- - login: arcticfly
+ avatarUrl: https://avatars.githubusercontent.com/u/41524992?u=03c88529a86cf51f7a380e890d84d84c71468848&v=4
+ url: https://github.com/arcticfly
- - login: getsentry
avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
url: https://github.com/getsentry
-- - login: takashi-yoneya
+- - login: acsone
+ avatarUrl: https://avatars.githubusercontent.com/u/7601056?v=4
+ url: https://github.com/acsone
+ - login: takashi-yoneya
avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4
url: https://github.com/takashi-yoneya
- - login: mercedes-benz
- avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4
- url: https://github.com/mercedes-benz
- login: xoflare
avatarUrl: https://avatars.githubusercontent.com/u/74335107?v=4
url: https://github.com/xoflare
@@ -50,12 +56,12 @@ sponsors:
- login: BoostryJP
avatarUrl: https://avatars.githubusercontent.com/u/57932412?v=4
url: https://github.com/BoostryJP
+ - login: jina-ai
+ avatarUrl: https://avatars.githubusercontent.com/u/60539444?v=4
+ url: https://github.com/jina-ai
- - login: HiredScore
avatarUrl: https://avatars.githubusercontent.com/u/3908850?v=4
url: https://github.com/HiredScore
- - login: petebachant
- avatarUrl: https://avatars.githubusercontent.com/u/4604869?u=b17a5a4ac82f77b7efff864d439e8068d2a36593&v=4
- url: https://github.com/petebachant
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
@@ -74,9 +80,6 @@ sponsors:
- login: RodneyU215
avatarUrl: https://avatars.githubusercontent.com/u/3329665?u=ec6a9adf8e7e8e306eed7d49687c398608d1604f&v=4
url: https://github.com/RodneyU215
- - login: tizz98
- avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4
- url: https://github.com/tizz98
- login: americanair
avatarUrl: https://avatars.githubusercontent.com/u/12281813?v=4
url: https://github.com/americanair
@@ -92,11 +95,17 @@ sponsors:
- - login: indeedeng
avatarUrl: https://avatars.githubusercontent.com/u/2905043?v=4
url: https://github.com/indeedeng
+ - login: iguit0
+ avatarUrl: https://avatars.githubusercontent.com/u/12905770?u=63a1a96d1e6c27d85c4f946b84836599de047f65&v=4
+ url: https://github.com/iguit0
+ - login: JacobKochems
+ avatarUrl: https://avatars.githubusercontent.com/u/41692189?u=a75f62ddc0d060ee6233a91e19c433d2687b8eb6&v=4
+ url: https://github.com/JacobKochems
- - login: Kludex
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: samuelcolvin
- avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
url: https://github.com/samuelcolvin
- login: jefftriplett
avatarUrl: https://avatars.githubusercontent.com/u/50527?u=af1ddfd50f6afd6d99f333ba2ac8d0a5b245ea74&v=4
@@ -104,9 +113,6 @@ sponsors:
- login: jstanden
avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4
url: https://github.com/jstanden
- - login: kamalgill
- avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4
- url: https://github.com/kamalgill
- login: dekoza
avatarUrl: https://avatars.githubusercontent.com/u/210980?u=c03c78a8ae1039b500dfe343665536ebc51979b2&v=4
url: https://github.com/dekoza
@@ -149,6 +155,9 @@ sponsors:
- login: zsinx6
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
url: https://github.com/zsinx6
+ - login: kennywakeland
+ avatarUrl: https://avatars.githubusercontent.com/u/3631417?u=7c8f743f1ae325dfadea7c62bbf1abd6a824fc55&v=4
+ url: https://github.com/kennywakeland
- login: aacayaco
avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4
url: https://github.com/aacayaco
@@ -194,9 +203,6 @@ sponsors:
- login: Shackelford-Arden
avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4
url: https://github.com/Shackelford-Arden
- - login: savannahostrowski
- avatarUrl: https://avatars.githubusercontent.com/u/8949415?u=c3177aa099fb2b8c36aeba349278b77f9a8df211&v=4
- url: https://github.com/savannahostrowski
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
@@ -236,9 +242,6 @@ sponsors:
- login: ygorpontelo
avatarUrl: https://avatars.githubusercontent.com/u/32963605?u=35f7103f9c4c4c2589ae5737ee882e9375ef072e&v=4
url: https://github.com/ygorpontelo
- - login: AlrasheedA
- avatarUrl: https://avatars.githubusercontent.com/u/33544979?u=7fe66bf62b47682612b222e3e8f4795ef3be769b&v=4
- url: https://github.com/AlrasheedA
- login: ProteinQure
avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4
url: https://github.com/ProteinQure
@@ -281,9 +284,6 @@ sponsors:
- login: DelfinaCare
avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4
url: https://github.com/DelfinaCare
- - login: khoadaniel
- avatarUrl: https://avatars.githubusercontent.com/u/84840546?v=4
- url: https://github.com/khoadaniel
- login: osawa-koki
avatarUrl: https://avatars.githubusercontent.com/u/94336223?u=59c6fe6945bcbbaff87b2a794238671b060620d2&v=4
url: https://github.com/osawa-koki
@@ -327,14 +327,11 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/861044?u=5abfca5588f3e906b31583d7ee62f6de4b68aa24&v=4
url: https://github.com/browniebroke
- login: janfilips
- avatarUrl: https://avatars.githubusercontent.com/u/870699?u=96df18ad355e58b9397accc55f4eeb7a86e959b0&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/870699?u=80702ec63f14e675cd4cdcc6ce3821d2ed207fd7&v=4
url: https://github.com/janfilips
- login: WillHogan
avatarUrl: https://avatars.githubusercontent.com/u/1661551?u=7036c064cf29781470573865264ec8e60b6b809f&v=4
url: https://github.com/WillHogan
- - login: NateShoffner
- avatarUrl: https://avatars.githubusercontent.com/u/1712163?u=b43cc2fa3fd8bec54b7706e4b98b72543c7bfea8&v=4
- url: https://github.com/NateShoffner
- login: my3
avatarUrl: https://avatars.githubusercontent.com/u/1825270?v=4
url: https://github.com/my3
@@ -344,12 +341,6 @@ sponsors:
- login: cbonoz
avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4
url: https://github.com/cbonoz
- - login: Patechoc
- avatarUrl: https://avatars.githubusercontent.com/u/2376641?u=23b49e9eda04f078cb74fa3f93593aa6a57bb138&v=4
- url: https://github.com/Patechoc
- - login: larsvik
- avatarUrl: https://avatars.githubusercontent.com/u/3442226?v=4
- url: https://github.com/larsvik
- login: anthonycorletti
avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4
url: https://github.com/anthonycorletti
@@ -359,6 +350,9 @@ sponsors:
- login: Alisa-lisa
avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
url: https://github.com/Alisa-lisa
+ - login: piotrgredowski
+ avatarUrl: https://avatars.githubusercontent.com/u/4294480?v=4
+ url: https://github.com/piotrgredowski
- login: danielunderwood
avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
url: https://github.com/danielunderwood
@@ -383,6 +377,9 @@ sponsors:
- login: mattwelke
avatarUrl: https://avatars.githubusercontent.com/u/7719209?u=80f02a799323b1472b389b836d95957c93a6d856&v=4
url: https://github.com/mattwelke
+ - login: harsh183
+ avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4
+ url: https://github.com/harsh183
- login: hcristea
avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
url: https://github.com/hcristea
@@ -428,9 +425,6 @@ sponsors:
- login: shuheng-liu
avatarUrl: https://avatars.githubusercontent.com/u/22414322?u=813c45f30786c6b511b21a661def025d8f7b609e&v=4
url: https://github.com/shuheng-liu
- - login: ghandic
- avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
- url: https://github.com/ghandic
- login: pers0n4
avatarUrl: https://avatars.githubusercontent.com/u/24864600?u=f211a13a7b572cbbd7779b9c8d8cb428cc7ba07e&v=4
url: https://github.com/pers0n4
@@ -458,9 +452,6 @@ sponsors:
- login: bnkc
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=527044d90b5ebb7f8dad517db5da1f45253b774b&v=4
url: https://github.com/bnkc
- - login: devbruce
- avatarUrl: https://avatars.githubusercontent.com/u/35563380?u=ca4e811ac7f7b3eb1600fa63285119fcdee01188&v=4
- url: https://github.com/devbruce
- login: declon
avatarUrl: https://avatars.githubusercontent.com/u/36180226?v=4
url: https://github.com/declon
@@ -476,6 +467,9 @@ sponsors:
- login: ArtyomVancyan
avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
url: https://github.com/ArtyomVancyan
+ - login: josehenriqueroveda
+ avatarUrl: https://avatars.githubusercontent.com/u/46685746?u=2e672057a7dbe1dba47e57c378fc0cac336022eb&v=4
+ url: https://github.com/josehenriqueroveda
- login: hgalytoby
avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=f4888c2c54929bd86eed0d3971d09fcb306e5088&v=4
url: https://github.com/hgalytoby
@@ -485,27 +479,36 @@ sponsors:
- login: conservative-dude
avatarUrl: https://avatars.githubusercontent.com/u/55538308?u=f250c44942ea6e73a6bd90739b381c470c192c11&v=4
url: https://github.com/conservative-dude
- - login: leo-jp-edwards
- avatarUrl: https://avatars.githubusercontent.com/u/58213433?u=2c128e8b0794b7a66211cd7d8ebe05db20b7e9c0&v=4
- url: https://github.com/leo-jp-edwards
- login: 0417taehyun
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
url: https://github.com/0417taehyun
- - login: ssbarnea
avatarUrl: https://avatars.githubusercontent.com/u/102495?u=b4bf6818deefe59952ac22fec6ed8c76de1b8f7c&v=4
url: https://github.com/ssbarnea
- - login: tomast1337
- avatarUrl: https://avatars.githubusercontent.com/u/15125899?u=2c2f2907012d820499e2c43632389184923513fe&v=4
- url: https://github.com/tomast1337
+ - login: Patechoc
+ avatarUrl: https://avatars.githubusercontent.com/u/2376641?u=23b49e9eda04f078cb74fa3f93593aa6a57bb138&v=4
+ url: https://github.com/Patechoc
+ - login: LanceMoe
+ avatarUrl: https://avatars.githubusercontent.com/u/18505474?u=7fd3ead4364bdf215b6d75cb122b3811c391ef6b&v=4
+ url: https://github.com/LanceMoe
- login: sadikkuzu
avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=d179c06bb9f65c4167fcab118526819f8e0dac17&v=4
url: https://github.com/sadikkuzu
- login: ruizdiazever
avatarUrl: https://avatars.githubusercontent.com/u/29817086?u=2df54af55663d246e3a4dc8273711c37f1adb117&v=4
url: https://github.com/ruizdiazever
+ - login: samnimoh
+ avatarUrl: https://avatars.githubusercontent.com/u/33413170?u=147bc516be6cb647b28d7e3b3fea3a018a331145&v=4
+ url: https://github.com/samnimoh
- login: danburonline
avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=2cad4388c1544e539ecb732d656e42fb07b4ff2d&v=4
url: https://github.com/danburonline
+ - login: iharshgor
+ avatarUrl: https://avatars.githubusercontent.com/u/35490011?u=2dea054476e752d9e92c9d71a9a7cc919b1c2f8e&v=4
+ url: https://github.com/iharshgor
- login: rwxd
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
url: https://github.com/rwxd
+ - login: ThomasPalma1
+ avatarUrl: https://avatars.githubusercontent.com/u/66331874?u=5763f7402d784ba189b60d704ff5849b4d0a63fb&v=4
+ url: https://github.com/ThomasPalma1
diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml
index dd2dbe5ea9..89d189564c 100644
--- a/docs/en/data/people.yml
+++ b/docs/en/data/people.yml
@@ -1,16 +1,16 @@
maintainers:
- login: tiangolo
- answers: 1844
- prs: 430
+ answers: 1849
+ prs: 466
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
- count: 434
+ count: 463
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: dmontagu
- count: 237
+ count: 239
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
url: https://github.com/dmontagu
- login: Mause
@@ -25,34 +25,34 @@ experts:
count: 193
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
-- login: euri10
- count: 152
- avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
- url: https://github.com/euri10
- login: jgould22
- count: 139
+ count: 157
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
+- login: euri10
+ count: 153
+ avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
+ url: https://github.com/euri10
- login: phy25
count: 126
- avatarUrl: https://avatars.githubusercontent.com/u/331403?u=191cd73f0c936497c8d1931a217bb3039d050265&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
- login: iudeen
- count: 118
+ count: 121
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: raphaelauv
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
-- login: ghandic
- count: 71
- avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
- url: https://github.com/ghandic
- login: ArcLightSlavik
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
+- login: ghandic
+ count: 71
+ avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
+ url: https://github.com/ghandic
- login: falkben
count: 57
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
@@ -61,10 +61,10 @@ experts:
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
-- login: adriangb
+- login: insomnes
count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
- url: https://github.com/adriangb
+ avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
+ url: https://github.com/insomnes
- login: yinziyan1206
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
@@ -73,22 +73,22 @@ experts:
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
-- login: insomnes
- count: 45
- avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
- url: https://github.com/insomnes
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
-- login: odiseo0
- count: 43
- avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=2da05dab6cc8e1ade557801634760a56e4101796&v=4
- url: https://github.com/odiseo0
+- login: adriangb
+ count: 44
+ avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
+ url: https://github.com/adriangb
- login: frankie567
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
+- login: odiseo0
+ count: 43
+ avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
+ url: https://github.com/odiseo0
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
@@ -97,14 +97,14 @@ experts:
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
-- login: chbndrhnns
- count: 35
- avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
- url: https://github.com/chbndrhnns
- login: krishnardt
count: 35
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
+- login: chbndrhnns
+ count: 35
+ avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
+ url: https://github.com/chbndrhnns
- login: panla
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
@@ -121,38 +121,38 @@ experts:
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
+- login: acnebs
+ count: 23
+ avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4
+ url: https://github.com/acnebs
- login: SirTelemak
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
-- login: acnebs
- count: 22
- avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
- url: https://github.com/acnebs
- login: rafsaf
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4
url: https://github.com/rafsaf
-- login: chris-allnutt
+- login: n8sty
count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
- url: https://github.com/chris-allnutt
+ avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
+ url: https://github.com/n8sty
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
-- login: n8sty
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
- url: https://github.com/n8sty
-- login: retnikt
- count: 18
- avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
- url: https://github.com/retnikt
+- login: chris-allnutt
+ count: 20
+ avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
+ url: https://github.com/chris-allnutt
- login: zoliknemet
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
+- login: retnikt
+ count: 18
+ avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
+ url: https://github.com/retnikt
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
@@ -198,38 +198,30 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4
url: https://github.com/jorgerpo
last_month_active:
-- login: jgould22
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
- login: Kludex
- count: 13
+ count: 24
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
-- login: abhint
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
- url: https://github.com/abhint
-- login: chrisK824
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
- url: https://github.com/chrisK824
+- login: jgould22
+ count: 17
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
- login: arjwilliams
- count: 3
+ count: 8
avatarUrl: https://avatars.githubusercontent.com/u/22227620?v=4
url: https://github.com/arjwilliams
-- login: wu-clan
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/52145145?u=f8c9e5c8c259d248e1683fedf5027b4ee08a0967&v=4
- url: https://github.com/wu-clan
- login: Ahmed-Abdou14
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=d1e1c064d57c3ad5b6481716928da840f6d5a492&v=4
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
url: https://github.com/Ahmed-Abdou14
-- login: esrefzeki
+- login: iudeen
count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/54935247?u=193cf5a169ca05fc54995a4dceabc82c7dc6e5ea&v=4
- url: https://github.com/esrefzeki
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
+ url: https://github.com/iudeen
+- login: mikeedjones
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/4087139?u=cc4a242896ac2fcf88a53acfaf190d0fe0a1f0c9&v=4
+ url: https://github.com/mikeedjones
top_contributors:
- login: waynerv
count: 25
@@ -240,7 +232,7 @@ top_contributors:
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
- login: Kludex
- count: 20
+ count: 21
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: jaystone776
@@ -264,7 +256,7 @@ top_contributors:
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
url: https://github.com/mariacamilagl
- login: Smlep
- count: 10
+ count: 11
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: Serrones
@@ -287,6 +279,10 @@ top_contributors:
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
url: https://github.com/Alexandrhub
+- login: NinaHwang
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4
+ url: https://github.com/NinaHwang
- login: batlopes
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
@@ -297,7 +293,7 @@ top_contributors:
url: https://github.com/wshayes
- login: samuelcolvin
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
url: https://github.com/samuelcolvin
- login: SwftAlpc
count: 5
@@ -311,10 +307,6 @@ top_contributors:
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4
url: https://github.com/ComicShrimp
-- login: NinaHwang
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4
- url: https://github.com/NinaHwang
- login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
@@ -335,10 +327,18 @@ top_contributors:
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
url: https://github.com/hitrust
+- login: JulianMaurin
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
+ url: https://github.com/JulianMaurin
- login: lsglucas
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
+- login: iudeen
+ count: 4
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
+ url: https://github.com/iudeen
- login: axel584
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
@@ -349,7 +349,7 @@ top_contributors:
url: https://github.com/ivan-abc
top_reviewers:
- login: Kludex
- count: 122
+ count: 136
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: BilalAlpaslan
@@ -357,7 +357,7 @@ top_reviewers:
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: yezz123
- count: 77
+ count: 78
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=d7062cbc6eb7671d5dc9cc0e32a24ae335e0f225&v=4
url: https://github.com/yezz123
- login: tokusumi
@@ -372,20 +372,20 @@ top_reviewers:
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
url: https://github.com/Laineyzhang55
+- login: iudeen
+ count: 46
+ avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
+ url: https://github.com/iudeen
- login: ycd
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
url: https://github.com/ycd
-- login: iudeen
- count: 44
- avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
- url: https://github.com/iudeen
- login: cikay
count: 41
avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
url: https://github.com/cikay
- login: Xewus
- count: 35
+ count: 38
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
- login: JarroVGIT
@@ -412,6 +412,10 @@ top_reviewers:
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
+- login: LorhanSohaky
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
+ url: https://github.com/LorhanSohaky
- login: Ryandaydev
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=809f3d1074d04bbc28012a7f17f06ea56f5bd71a&v=4
@@ -420,10 +424,6 @@ top_reviewers:
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
url: https://github.com/dmontagu
-- login: LorhanSohaky
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
- url: https://github.com/LorhanSohaky
- login: rjNemo
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
@@ -434,7 +434,7 @@ top_reviewers:
url: https://github.com/hard-coders
- login: odiseo0
count: 20
- avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=2da05dab6cc8e1ade557801634760a56e4101796&v=4
+ avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
url: https://github.com/odiseo0
- login: 0417taehyun
count: 19
@@ -456,6 +456,10 @@ top_reviewers:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
url: https://github.com/SwftAlpc
+- login: axel584
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
+ url: https://github.com/axel584
- login: DevDae
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
@@ -488,10 +492,6 @@ top_reviewers:
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
url: https://github.com/RunningIkkyu
-- login: axel584
- count: 12
- avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
- url: https://github.com/axel584
- login: ivan-abc
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
@@ -500,6 +500,10 @@ top_reviewers:
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
url: https://github.com/solomein-sv
+- login: wdh99
+ count: 11
+ avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
+ url: https://github.com/wdh99
- login: mariacamilagl
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
@@ -540,7 +544,3 @@ top_reviewers:
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
url: https://github.com/bezaca
-- login: oandersonmagalhaes
- count: 9
- avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4
- url: https://github.com/oandersonmagalhaes
diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml
index 53cdb9bad1..6d91195204 100644
--- a/docs/en/data/sponsors.yml
+++ b/docs/en/data/sponsors.yml
@@ -8,6 +8,9 @@ gold:
- url: https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge
title: Fern | SDKs and API docs
img: https://fastapi.tiangolo.com/img/sponsors/fern.svg
+ - url: https://www.porter.run
+ title: Deploy FastAPI on AWS with a few clicks
+ img: https://fastapi.tiangolo.com/img/sponsors/porter.png
silver:
- url: https://www.deta.sh/?ref=fastapi
title: The launchpad for all your (team's) ideas
@@ -37,3 +40,6 @@ bronze:
- url: https://www.flint.sh
title: IT expertise, consulting and development by passionate people
img: https://fastapi.tiangolo.com/img/sponsors/flint.png
+ - url: https://bit.ly/3JJ7y5C
+ title: Build cross-modal and multimodal applications on the cloud
+ img: https://fastapi.tiangolo.com/img/sponsors/jina2.svg
diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml
index b3cb063270..7c3bb2f479 100644
--- a/docs/en/data/sponsors_badge.yml
+++ b/docs/en/data/sponsors_badge.yml
@@ -17,3 +17,5 @@ logins:
- databento-bot
- nanram22
- Flint-company
+ - porter-dev
+ - fern-api
diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md
index f968489aed..cfdb607d77 100644
--- a/docs/en/docs/contributing.md
+++ b/docs/en/docs/contributing.md
@@ -126,7 +126,7 @@ And if you update that local FastAPI source code when you run that Python file a
That way, you don't have to "install" your local version to be able to test every change.
!!! note "Technical Details"
- This only happens when you install using this included `requiements.txt` instead of installing `pip install fastapi` directly.
+ This only happens when you install using this included `requirements.txt` instead of installing `pip install fastapi` directly.
That is because inside of the `requirements.txt` file, the local version of FastAPI is marked to be installed in "editable" mode, with the `-e` option.
diff --git a/docs/en/docs/img/sponsors/porter-banner.png b/docs/en/docs/img/sponsors/porter-banner.png
new file mode 100755
index 0000000000..fa2e741c0c
Binary files /dev/null and b/docs/en/docs/img/sponsors/porter-banner.png differ
diff --git a/docs/en/docs/img/sponsors/porter.png b/docs/en/docs/img/sponsors/porter.png
new file mode 100755
index 0000000000..582a151564
Binary files /dev/null and b/docs/en/docs/img/sponsors/porter.png differ
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 920886e5dd..9570bef363 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -2,15 +2,77 @@
## Latest Changes
+## 0.101.1
+
+### Fixes
+
+* ✨ Add `ResponseValidationError` printable details, to show up in server error logs. PR [#10078](https://github.com/tiangolo/fastapi/pull/10078) by [@tiangolo](https://github.com/tiangolo).
+
+### Refactors
+
+* ✏️ Fix typo in deprecation warnings in `fastapi/params.py`. PR [#9854](https://github.com/tiangolo/fastapi/pull/9854) by [@russbiggs](https://github.com/russbiggs).
+* ✏️ Fix typos in comments on internal code in `fastapi/concurrency.py` and `fastapi/routing.py`. PR [#9590](https://github.com/tiangolo/fastapi/pull/9590) by [@ElliottLarsen](https://github.com/ElliottLarsen).
+
+### Docs
+
+* ✏️ Fix typo in release notes. PR [#9835](https://github.com/tiangolo/fastapi/pull/9835) by [@francisbergin](https://github.com/francisbergin).
+* 📝 Add external article: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI. PR [#9847](https://github.com/tiangolo/fastapi/pull/9847) by [@adejumoridwan](https://github.com/adejumoridwan).
+* 📝 Fix typo in `docs/en/docs/contributing.md`. PR [#9878](https://github.com/tiangolo/fastapi/pull/9878) by [@VicenteMerino](https://github.com/VicenteMerino).
+* 📝 Fix code highlighting in `docs/en/docs/tutorial/bigger-applications.md`. PR [#9806](https://github.com/tiangolo/fastapi/pull/9806) by [@theonlykingpin](https://github.com/theonlykingpin).
+
+### Translations
+
+* 🌐 Add Japanese translation for `docs/ja/docs/deployment/concepts.md`. PR [#10062](https://github.com/tiangolo/fastapi/pull/10062) by [@tamtam-fitness](https://github.com/tamtam-fitness).
+* 🌐 Add Japanese translation for `docs/ja/docs/deployment/server-workers.md`. PR [#10064](https://github.com/tiangolo/fastapi/pull/10064) by [@tamtam-fitness](https://github.com/tamtam-fitness).
+* 🌐 Update Japanese translation for `docs/ja/docs/deployment/docker.md`. PR [#10073](https://github.com/tiangolo/fastapi/pull/10073) by [@tamtam-fitness](https://github.com/tamtam-fitness).
+* 🌐 Add Ukrainian translation for `docs/uk/docs/fastapi-people.md`. PR [#10059](https://github.com/tiangolo/fastapi/pull/10059) by [@rostik1410](https://github.com/rostik1410).
+* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/cookie-params.md`. PR [#10032](https://github.com/tiangolo/fastapi/pull/10032) by [@rostik1410](https://github.com/rostik1410).
+* 🌐 Add Russian translation for `docs/ru/docs/deployment/docker.md`. PR [#9971](https://github.com/tiangolo/fastapi/pull/9971) by [@Xewus](https://github.com/Xewus).
+* 🌐 Add Vietnamese translation for `docs/vi/docs/python-types.md`. PR [#10047](https://github.com/tiangolo/fastapi/pull/10047) by [@magiskboy](https://github.com/magiskboy).
+* 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/global-dependencies.md`. PR [#9970](https://github.com/tiangolo/fastapi/pull/9970) by [@dudyaosuplayer](https://github.com/dudyaosuplayer).
+* 🌐 Add Urdu translation for `docs/ur/docs/benchmarks.md`. PR [#9974](https://github.com/tiangolo/fastapi/pull/9974) by [@AhsanSheraz](https://github.com/AhsanSheraz).
+
+### Internal
+
+* 🔧 Add sponsor Porter. PR [#10051](https://github.com/tiangolo/fastapi/pull/10051) by [@tiangolo](https://github.com/tiangolo).
+* 🔧 Update sponsors, add Jina back as bronze sponsor. PR [#10050](https://github.com/tiangolo/fastapi/pull/10050) by [@tiangolo](https://github.com/tiangolo).
+* ⬆ Bump mypy from 1.4.0 to 1.4.1. PR [#9756](https://github.com/tiangolo/fastapi/pull/9756) by [@dependabot[bot]](https://github.com/apps/dependabot).
+* ⬆ Bump mkdocs-material from 9.1.17 to 9.1.21. PR [#9960](https://github.com/tiangolo/fastapi/pull/9960) by [@dependabot[bot]](https://github.com/apps/dependabot).
+
+## 0.101.0
+
+### Features
+
+* ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo).
+
+### Refactors
+
+* ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu).
+* ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen).
+
+### Upgrades
+
+* 📌 Do not allow Pydantic 2.1.0 that breaks (require 2.1.1). PR [#10012](https://github.com/tiangolo/fastapi/pull/10012) by [@tiangolo](https://github.com/tiangolo).
+
+### Translations
+
+* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337).
+* 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo).
+
+### Internal
+
+* 👷 Add GitHub Actions step dump context to debug external failures. PR [#10008](https://github.com/tiangolo/fastapi/pull/10008) by [@tiangolo](https://github.com/tiangolo).
+* 🔧 Restore MkDocs Material pin after the fix. PR [#10001](https://github.com/tiangolo/fastapi/pull/10001) by [@tiangolo](https://github.com/tiangolo).
+* 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo).
+* 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo).
+* 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo).
+* 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo).
* ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo).
* ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo).
-* ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen).
-* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337).
* 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo).
* 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo).
-* 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo).
## 0.100.1
@@ -74,7 +136,7 @@ A command line tool that will **process your code** and update most of the thing
### Pydantic v1
-**This version of FastAPI still supports Pydantic v1**. And although Pydantic v1 will be deprecated at some point, ti will still be supported for a while.
+**This version of FastAPI still supports Pydantic v1**. And although Pydantic v1 will be deprecated at some point, it will still be supported for a while.
This means that you can install the new Pydantic v2, and if something fails, you can install Pydantic v1 while you fix any problems you might have, but having the latest FastAPI.
diff --git a/docs/en/docs/tutorial/bigger-applications.md b/docs/en/docs/tutorial/bigger-applications.md
index daa7353a2e..26d26475f2 100644
--- a/docs/en/docs/tutorial/bigger-applications.md
+++ b/docs/en/docs/tutorial/bigger-applications.md
@@ -377,7 +377,7 @@ The `router` from `users` would overwrite the one from `items` and we wouldn't b
So, to be able to use both of them in the same file, we import the submodules directly:
-```Python hl_lines="4"
+```Python hl_lines="5"
{!../../../docs_src/bigger_applications/app/main.py!}
```
diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml
index 1b7a58ec03..4c4fe6b02e 100644
--- a/docs/en/mkdocs.yml
+++ b/docs/en/mkdocs.yml
@@ -244,7 +244,7 @@ extra:
- link: /uk/
name: uk
- link: /vi/
- name: vi
+ name: vi - Tiếng Việt
- link: /zh/
name: zh - 汉语
extra_css:
diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html
index 7e6c0f7634..983d7eb3e7 100644
--- a/docs/en/overrides/main.html
+++ b/docs/en/overrides/main.html
@@ -40,6 +40,12 @@
+
+
+
+
+---
+
+さて、**プロセス**と**プログラム**という用語の違いを確認したところで、デプロイメントについて話を続けます。
+
+## 起動時の実行
+
+ほとんどの場合、Web APIを作成するときは、クライアントがいつでもアクセスできるように、**常に**中断されることなく**実行される**ことを望みます。もちろん、特定の状況でのみ実行させたい特別な理由がある場合は別ですが、その時間のほとんどは、常に実行され、**利用可能**であることを望みます。
+
+### リモートサーバー上での実行
+
+リモートサーバー(クラウドサーバー、仮想マシンなど)をセットアップするときにできる最も簡単なことは、ローカルで開発するときと同じように、Uvicorn(または同様のもの)を手動で実行することです。 この方法は**開発中**には役に立つと思われます。
+
+しかし、サーバーへの接続が切れた場合、**実行中のプロセス**はおそらくダウンしてしまうでしょう。
+
+そしてサーバーが再起動された場合(アップデートやクラウドプロバイダーからのマイグレーションの後など)、おそらくあなたはそれに**気づかないでしょう**。そのため、プロセスを手動で再起動しなければならないことすら気づかないでしょう。つまり、APIはダウンしたままなのです。😱
+
+### 起動時に自動的に実行
+
+一般的に、サーバープログラム(Uvicornなど)はサーバー起動時に自動的に開始され、**人の介入**を必要とせずに、APIと一緒にプロセスが常に実行されるようにしたいと思われます(UvicornがFastAPIアプリを実行するなど)。
+
+### 別のプログラムの用意
+
+これを実現するために、通常は**別のプログラム**を用意し、起動時にアプリケーションが実行されるようにします。そして多くの場合、他のコンポーネントやアプリケーション、例えばデータベースも実行されるようにします。
+
+### 起動時に実行するツールの例
+
+実行するツールの例をいくつか挙げます:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Swarm モードによる Docker
+* Systemd
+* Supervisor
+* クラウドプロバイダーがサービスの一部として内部的に処理
+* そのほか...
+
+次の章で、より具体的な例を挙げていきます。
+
+## 再起動
+
+起動時にアプリケーションが実行されることを確認するのと同様に、失敗後にアプリケーションが**再起動**されることも確認したいと思われます。
+
+### 我々は間違いを犯す
+
+私たち人間は常に**間違い**を犯します。ソフトウェアには、ほとんど常に**バグ**があらゆる箇所に隠されています。🐛
+
+### 小さなエラーは自動的に処理される
+
+FastAPIでWeb APIを構築する際に、コードにエラーがある場合、FastAPIは通常、エラーを引き起こした単一のリクエストにエラーを含めます。🛡
+
+クライアントはそのリクエストに対して**500 Internal Server Error**を受け取りますが、アプリケーションは完全にクラッシュするのではなく、次のリクエストのために動作を続けます。
+
+### 重大なエラー - クラッシュ
+
+しかしながら、**アプリケーション全体をクラッシュさせるようなコードを書いて**UvicornとPythonをクラッシュさせるようなケースもあるかもしれません。💥
+
+それでも、ある箇所でエラーが発生したからといって、アプリケーションを停止させたままにしたくないでしょう。 少なくとも壊れていない*パスオペレーション*については、**実行し続けたい**はずです。
+
+### クラッシュ後の再起動
+
+しかし、実行中の**プロセス**をクラッシュさせるような本当にひどいエラーの場合、少なくとも2〜3回ほどプロセスを**再起動**させる外部コンポーネントが必要でしょう。
+
+!!! tip
+ ...とはいえ、アプリケーション全体が**すぐにクラッシュする**のであれば、いつまでも再起動し続けるのは意味がないでしょう。しかし、その場合はおそらく開発中か少なくともデプロイ直後に気づくと思われます。
+
+ そこで、**将来**クラッシュする可能性があり、それでも再スタートさせることに意味があるような、主なケースに焦点を当ててみます。
+
+あなたはおそらく**外部コンポーネント**がアプリケーションの再起動を担当することを望むと考えます。 なぜなら、その時点でUvicornとPythonを使った同じアプリケーションはすでにクラッシュしており、同じアプリケーションの同じコードに対して何もできないためです。
+
+### 自動的に再起動するツールの例
+
+ほとんどの場合、前述した**起動時にプログラムを実行する**ために使用されるツールは、自動で**再起動**することにも利用されます。
+
+例えば、次のようなものがあります:
+
+* Docker
+* Kubernetes
+* Docker Compose
+* Swarm モードによる Docker
+* Systemd
+* Supervisor
+* クラウドプロバイダーがサービスの一部として内部的に処理
+* そのほか...
+
+## レプリケーション - プロセスとメモリー
+
+FastAPI アプリケーションでは、Uvicorn のようなサーバープログラムを使用し、**1つのプロセス**で1度に複数のクライアントに同時に対応できます。
+
+しかし、多くの場合、複数のワーカー・プロセスを同時に実行したいと考えるでしょう。
+
+### 複数のプロセス - Worker
+
+クライアントの数が単一のプロセスで処理できる数を超えており(たとえば仮想マシンがそれほど大きくない場合)、かつサーバーの CPU に**複数のコア**がある場合、同じアプリケーションで同時に**複数のプロセス**を実行させ、すべてのリクエストを分散させることができます。
+
+同じAPIプログラムの**複数のプロセス**を実行する場合、それらは一般的に**Worker/ワーカー**と呼ばれます。
+
+### ワーカー・プロセス と ポート
+
+
+[HTTPSについて](./https.md){.internal-link target=_blank}のドキュメントで、1つのサーバーで1つのポートとIPアドレスの組み合わせでリッスンできるのは1つのプロセスだけであることを覚えていますでしょうか?
+
+これはいまだに同じです。
+
+そのため、**複数のプロセス**を同時に持つには**ポートでリッスンしている単一のプロセス**が必要であり、それが何らかの方法で各ワーカー・プロセスに通信を送信することが求められます。
+
+### プロセスあたりのメモリー
+
+さて、プログラムがメモリにロードする際には、例えば機械学習モデルや大きなファイルの内容を変数に入れたりする場合では、**サーバーのメモリ(RAM)**を少し消費します。
+
+そして複数のプロセスは通常、**メモリを共有しません**。これは、実行中の各プロセスがそれぞれ独自の変数やメモリ等を持っていることを意味します。つまり、コード内で大量のメモリを消費している場合、**各プロセス**は同等の量のメモリを消費することになります。
+
+### サーバーメモリー
+
+例えば、あなたのコードが **1GBのサイズの機械学習モデル**をロードする場合、APIで1つのプロセスを実行すると、少なくとも1GBのRAMを消費します。
+
+また、**4つのプロセス**(4つのワーカー)を起動すると、それぞれが1GBのRAMを消費します。つまり、合計でAPIは**4GBのRAM**を消費することになります。
+
+リモートサーバーや仮想マシンのRAMが3GBしかない場合、4GB以上のRAMをロードしようとすると問題が発生します。🚨
+
+### 複数プロセス - 例
+
+この例では、2つの**ワーカー・プロセス**を起動し制御する**マネージャー・ プロセス**があります。
+
+このマネージャー・ プロセスは、おそらくIPの**ポート**でリッスンしているものです。そして、すべての通信をワーカー・プロセスに転送します。
+
+これらのワーカー・プロセスは、アプリケーションを実行するものであり、**リクエスト**を受けて**レスポンス**を返すための主要な計算を行い、あなたが変数に入れたものは何でもRAMにロードします。
+
+
+
+### Thêm kiểu dữ liệu
+
+Hãy sửa một dòng từ phiên bản trước.
+
+Chúng ta sẽ thay đổi chính xác đoạn này, tham số của hàm, từ:
+
+```Python
+ first_name, last_name
+```
+
+sang:
+
+```Python
+ first_name: str, last_name: str
+```
+
+Chính là nó.
+
+Những thứ đó là "type hints":
+
+```Python hl_lines="1"
+{!../../../docs_src/python_types/tutorial002.py!}
+```
+
+Đó không giống như khai báo những giá trị mặc định giống như:
+
+```Python
+ first_name="john", last_name="doe"
+```
+
+Nó là một thứ khác.
+
+Chúng ta sử dụng dấu hai chấm (`:`), không phải dấu bằng (`=`).
+
+Và việc thêm gợi ý kiểu dữ liệu không làm thay đổi những gì xảy ra so với khi chưa thêm chúng.
+
+But now, imagine you are again in the middle of creating that function, but with type hints.
+
+Tại cùng một điểm, bạn thử kích hoạt autocomplete với `Ctrl+Space` và bạn thấy:
+
+
+
+Với cái đó, bạn có thể cuộn, nhìn thấy các lựa chọn, cho đến khi bạn tìm thấy một "tiếng chuông":
+
+
+
+## Động lực nhiều hơn
+
+Kiểm tra hàm này, nó đã có gợi ý kiểu dữ liệu:
+
+```Python hl_lines="1"
+{!../../../docs_src/python_types/tutorial003.py!}
+```
+
+Bởi vì trình soạn thảo biết kiểu dữ liệu của các biến, bạn không chỉ có được completion, bạn cũng được kiểm tra lỗi:
+
+
+
+Bây giờ bạn biết rằng bạn phải sửa nó, chuyển `age` sang một xâu với `str(age)`:
+
+```Python hl_lines="2"
+{!../../../docs_src/python_types/tutorial004.py!}
+```
+
+## Khai báo các kiểu dữ liệu
+
+Bạn mới chỉ nhìn thấy những nơi chủ yếu để đặt khai báo kiểu dữ liệu. Như là các tham số của hàm.
+
+Đây cũng là nơi chủ yếu để bạn sử dụng chúng với **FastAPI**.
+
+### Kiểu dữ liệu đơn giản
+
+Bạn có thể khai báo tất cả các kiểu dữ liệu chuẩn của Python, không chỉ là `str`.
+
+Bạn có thể sử dụng, ví dụ:
+
+* `int`
+* `float`
+* `bool`
+* `bytes`
+
+```Python hl_lines="1"
+{!../../../docs_src/python_types/tutorial005.py!}
+```
+
+### Các kiểu dữ liệu tổng quát với tham số kiểu dữ liệu
+
+Có một vài cấu trúc dữ liệu có thể chứa các giá trị khác nhau như `dict`, `list`, `set` và `tuple`. Và những giá trị nội tại cũng có thể có kiểu dữ liệu của chúng.
+
+Những kiểu dữ liệu nội bộ này được gọi là những kiểu dữ liệu "**tổng quát**". Và có khả năng khai báo chúng, thậm chí với các kiểu dữ liệu nội bộ của chúng.
+
+Để khai báo những kiểu dữ liệu và những kiểu dữ liệu nội bộ đó, bạn có thể sử dụng mô đun chuẩn của Python là `typing`. Nó có hỗ trợ những gợi ý kiểu dữ liệu này.
+
+#### Những phiên bản mới hơn của Python
+
+Cú pháp sử dụng `typing` **tương thích** với tất cả các phiên bản, từ Python 3.6 tới những phiên bản cuối cùng, bao gồm Python 3.9, Python 3.10,...
+
+As Python advances, **những phiên bản mới** mang tới sự hỗ trợ được cải tiến cho những chú thích kiểu dữ liệu và trong nhiều trường hợp bạn thậm chí sẽ không cần import và sử dụng mô đun `typing` để khai báo chú thích kiểu dữ liệu.
+
+Nếu bạn có thể chọn một phiên bản Python gần đây hơn cho dự án của bạn, ban sẽ có được những ưu điểm của những cải tiến đơn giản đó.
+
+Trong tất cả các tài liệu tồn tại những ví dụ tương thích với mỗi phiên bản Python (khi có một sự khác nhau).
+
+Cho ví dụ "**Python 3.6+**" có nghĩa là nó tương thích với Python 3.7 hoặc lớn hơn (bao gồm 3.7, 3.8, 3.9, 3.10,...). và "**Python 3.9+**" nghĩa là nó tương thích với Python 3.9 trở lên (bao gồm 3.10,...).
+
+Nếu bạn có thể sử dụng **phiên bản cuối cùng của Python**, sử dụng những ví dụ cho phiên bản cuối, những cái đó sẽ có **cú pháp đơn giản và tốt nhât**, ví dụ, "**Python 3.10+**".
+
+#### List
+
+Ví dụ, hãy định nghĩa một biến là `list` các `str`.
+
+=== "Python 3.9+"
+
+ Khai báo biến với cùng dấu hai chấm (`:`).
+
+ Tương tự kiểu dữ liệu `list`.
+
+ Như danh sách là một kiểu dữ liệu chứa một vài kiểu dữ liệu có sẵn, bạn đặt chúng trong các dấu ngoặc vuông:
+
+ ```Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial006_py39.py!}
+ ```
+
+=== "Python 3.6+"
+
+ Từ `typing`, import `List` (với chữ cái `L` viết hoa):
+
+ ``` Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial006.py!}
+ ```
+
+ Khai báo biến với cùng dấu hai chấm (`:`).
+
+ Tương tự như kiểu dữ liệu, `List` bạn import từ `typing`.
+
+ Như danh sách là một kiểu dữ liệu chứa các kiểu dữ liệu có sẵn, bạn đặt chúng bên trong dấu ngoặc vuông:
+
+ ```Python hl_lines="4"
+ {!> ../../../docs_src/python_types/tutorial006.py!}
+ ```
+
+!!! info
+ Các kiểu dữ liệu có sẵn bên trong dấu ngoặc vuông được gọi là "tham số kiểu dữ liệu".
+
+ Trong trường hợp này, `str` là tham số kiểu dữ liệu được truyền tới `List` (hoặc `list` trong Python 3.9 trở lên).
+
+Có nghĩa là: "biến `items` là một `list`, và mỗi phần tử trong danh sách này là một `str`".
+
+!!! tip
+ Nếu bạn sử dụng Python 3.9 hoặc lớn hơn, bạn không phải import `List` từ `typing`, bạn có thể sử dụng `list` để thay thế.
+
+Bằng cách này, trình soạn thảo của bạn có thể hỗ trợ trong khi xử lí các phần tử trong danh sách:
+
+
+
+Đa phần đều không thể đạt được nếu không có các kiểu dữ liệu.
+
+Chú ý rằng, biến `item` là một trong các phần tử trong danh sách `items`.
+
+Và do vậy, trình soạn thảo biết nó là một `str`, và cung cấp sự hỗ trợ cho nó.
+
+#### Tuple and Set
+
+Bạn sẽ làm điều tương tự để khai báo các `tuple` và các `set`:
+
+=== "Python 3.9+"
+
+ ```Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial007_py39.py!}
+ ```
+
+=== "Python 3.6+"
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial007.py!}
+ ```
+
+Điều này có nghĩa là:
+
+* Biến `items_t` là một `tuple` với 3 phần tử, một `int`, một `int` nữa, và một `str`.
+* Biến `items_s` là một `set`, và mỗi phần tử của nó có kiểu `bytes`.
+
+#### Dict
+
+Để định nghĩa một `dict`, bạn truyền 2 tham số kiểu dữ liệu, phân cách bởi dấu phẩy.
+
+Tham số kiểu dữ liệu đầu tiên dành cho khóa của `dict`.
+
+Tham số kiểu dữ liệu thứ hai dành cho giá trị của `dict`.
+
+=== "Python 3.9+"
+
+ ```Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial008_py39.py!}
+ ```
+
+=== "Python 3.6+"
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial008.py!}
+ ```
+
+Điều này có nghĩa là:
+
+* Biến `prices` là một `dict`:
+ * Khóa của `dict` này là kiểu `str` (đó là tên của mỗi vật phẩm).
+ * Giá trị của `dict` này là kiểu `float` (đó là giá của mỗi vật phẩm).
+
+#### Union
+
+Bạn có thể khai báo rằng một biến có thể là **một vài kiểu dữ liệu" bất kì, ví dụ, một `int` hoặc một `str`.
+
+Trong Python 3.6 hoặc lớn hơn (bao gồm Python 3.10) bạn có thể sử dụng kiểu `Union` từ `typing` và đặt trong dấu ngoặc vuông những giá trị được chấp nhận.
+
+In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a vertical bar (`|`).
+
+Trong Python 3.10 cũng có một **cú pháp mới** mà bạn có thể đặt những kiểu giá trị khả thi phân cách bởi một dấu sổ dọc (`|`).
+
+
+=== "Python 3.10+"
+
+ ```Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial008b_py310.py!}
+ ```
+
+=== "Python 3.6+"
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial008b.py!}
+ ```
+
+Trong cả hai trường hợp có nghĩa là `item` có thể là một `int` hoặc `str`.
+
+#### Khả năng `None`
+
+Bạn có thể khai báo một giá trị có thể có một kiểu dữ liệu, giống như `str`, nhưng nó cũng có thể là `None`.
+
+Trong Python 3.6 hoặc lớn hơn (bao gồm Python 3.10) bạn có thể khai báo nó bằng các import và sử dụng `Optional` từ mô đun `typing`.
+
+```Python hl_lines="1 4"
+{!../../../docs_src/python_types/tutorial009.py!}
+```
+
+Sử dụng `Optional[str]` thay cho `str` sẽ cho phép trình soạn thảo giúp bạn phát hiện các lỗi mà bạn có thể gặp như một giá trị luôn là một `str`, trong khi thực tế nó rất có thể là `None`.
+
+`Optional[Something]` là một cách viết ngắn gọn của `Union[Something, None]`, chúng là tương đương nhau.
+
+Điều này cũng có nghĩa là trong Python 3.10, bạn có thể sử dụng `Something | None`:
+
+=== "Python 3.10+"
+
+ ```Python hl_lines="1"
+ {!> ../../../docs_src/python_types/tutorial009_py310.py!}
+ ```
+
+=== "Python 3.6+"
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial009.py!}
+ ```
+
+=== "Python 3.6+ alternative"
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial009b.py!}
+ ```
+
+#### Sử dụng `Union` hay `Optional`
+
+If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view:
+
+Nếu bạn đang sử dụng phiên bản Python dưới 3.10, đây là một mẹo từ ý kiến rất "chủ quan" của tôi:
+
+* 🚨 Tránh sử dụng `Optional[SomeType]`
+* Thay vào đó ✨ **sử dụng `Union[SomeType, None]`** ✨.
+
+Cả hai là tương đương và bên dưới chúng giống nhau, nhưng tôi sẽ đễ xuất `Union` thay cho `Optional` vì từ "**tùy chọn**" có vẻ ngầm định giá trị là tùy chọn, và nó thực sự có nghĩa rằng "nó có thể là `None`", do đó nó không phải là tùy chọn và nó vẫn được yêu cầu.
+
+Tôi nghĩ `Union[SomeType, None]` là rõ ràng hơn về ý nghĩa của nó.
+
+Nó chỉ là về các từ và tên. Nhưng những từ đó có thể ảnh hưởng cách bạn và những đồng đội của bạn suy nghĩ về code.
+
+Cho một ví dụ, hãy để ý hàm này:
+
+```Python hl_lines="1 4"
+{!../../../docs_src/python_types/tutorial009c.py!}
+```
+
+Tham số `name` được định nghĩa là `Optional[str]`, nhưng nó **không phải là tùy chọn**, bạn không thể gọi hàm mà không có tham số:
+
+```Python
+say_hi() # Oh, no, this throws an error! 😱
+```
+
+Tham số `name` **vẫn được yêu cầu** (không phải là *tùy chọn*) vì nó không có giá trị mặc định. Trong khi đó, `name` chấp nhận `None` như là giá trị:
+
+```Python
+say_hi(name=None) # This works, None is valid 🎉
+```
+
+Tin tốt là, khi bạn sử dụng Python 3.10, bạn sẽ không phải lo lắng về điều đó, bạn sẽ có thể sử dụng `|` để định nghĩa hợp của các kiểu dữ liệu một cách đơn giản:
+
+```Python hl_lines="1 4"
+{!../../../docs_src/python_types/tutorial009c_py310.py!}
+```
+
+Và sau đó, bạn sẽ không phải lo rằng những cái tên như `Optional` và `Union`. 😎
+
+
+#### Những kiểu dữ liệu tổng quát
+
+Những kiểu dữ liệu này lấy tham số kiểu dữ liệu trong dấu ngoặc vuông được gọi là **Kiểu dữ liệu tổng quát**, cho ví dụ:
+
+=== "Python 3.10+"
+
+ Bạn có thể sử dụng các kiểu dữ liệu có sẵn như là kiểu dữ liệu tổng quát (với ngoặc vuông và kiểu dữ liệu bên trong):
+
+ * `list`
+ * `tuple`
+ * `set`
+ * `dict`
+
+ Và tương tự với Python 3.6, từ mô đun `typing`:
+
+ * `Union`
+ * `Optional` (tương tự như Python 3.6)
+ * ...và các kiểu dữ liệu khác.
+
+ Trong Python 3.10, thay vì sử dụng `Union` và `Optional`, bạn có thể sử dụng sổ dọc ('|') để khai báo hợp của các kiểu dữ liệu, điều đó tốt hơn và đơn giản hơn nhiều.
+
+=== "Python 3.9+"
+
+ Bạn có thể sử dụng các kiểu dữ liệu có sẵn tương tự như (với ngoặc vuông và kiểu dữ liệu bên trong):
+
+ * `list`
+ * `tuple`
+ * `set`
+ * `dict`
+
+ Và tương tự với Python 3.6, từ mô đun `typing`:
+
+ * `Union`
+ * `Optional`
+ * ...and others.
+
+=== "Python 3.6+"
+
+ * `List`
+ * `Tuple`
+ * `Set`
+ * `Dict`
+ * `Union`
+ * `Optional`
+ * ...và các kiểu khác.
+
+### Lớp như kiểu dữ liệu
+
+Bạn cũng có thể khai báo một lớp như là kiểu dữ liệu của một biến.
+
+Hãy nói rằng bạn muốn có một lớp `Person` với một tên:
+
+```Python hl_lines="1-3"
+{!../../../docs_src/python_types/tutorial010.py!}
+```
+
+Sau đó bạn có thể khai báo một biến có kiểu là `Person`:
+
+```Python hl_lines="6"
+{!../../../docs_src/python_types/tutorial010.py!}
+```
+
+Và lại một lần nữa, bạn có được tất cả sự hỗ trợ từ trình soạn thảo:
+
+
+
+Lưu ý rằng, điều này có nghĩa rằng "`one_person`" là một **thực thể** của lớp `Person`.
+
+Nó không có nghĩa "`one_person`" là một **lớp** gọi là `Person`.
+
+## Pydantic models
+
+Pydantic là một thư viện Python để validate dữ liệu hiệu năng cao.
+
+Bạn có thể khai báo "hình dạng" của dữa liệu như là các lớp với các thuộc tính.
+
+Và mỗi thuộc tính có một kiểu dữ liệu.
+
+Sau đó bạn tạo một thực thể của lớp đó với một vài giá trị và nó sẽ validate các giá trị, chuyển đổi chúng sang kiểu dữ liệu phù hợp (nếu đó là trường hợp) và cho bạn một object với toàn bộ dữ liệu.
+
+Và bạn nhận được tất cả sự hỗ trợ của trình soạn thảo với object kết quả đó.
+
+Một ví dụ từ tài liệu chính thức của Pydantic:
+
+=== "Python 3.10+"
+
+ ```Python
+ {!> ../../../docs_src/python_types/tutorial011_py310.py!}
+ ```
+
+=== "Python 3.9+"
+
+ ```Python
+ {!> ../../../docs_src/python_types/tutorial011_py39.py!}
+ ```
+
+=== "Python 3.6+"
+
+ ```Python
+ {!> ../../../docs_src/python_types/tutorial011.py!}
+ ```
+
+!!! info
+ Để học nhiều hơn về Pydantic, tham khảo tài liệu của nó.
+
+**FastAPI** được dựa hoàn toàn trên Pydantic.
+
+Bạn sẽ thấy nhiều ví dụ thực tế hơn trong [Hướng dẫn sử dụng](tutorial/index.md){.internal-link target=_blank}.
+
+!!! tip
+ Pydantic có một hành vi đặc biệt khi bạn sử dụng `Optional` hoặc `Union[Something, None]` mà không có giá trị mặc dịnh, bạn có thể đọc nhiều hơn về nó trong tài liệu của Pydantic về Required Optional fields.
+
+
+## Type Hints với Metadata Annotations
+
+Python cũng có một tính năng cho phép đặt **metadata bổ sung** trong những gợi ý kiểu dữ liệu này bằng cách sử dụng `Annotated`.
+
+=== "Python 3.9+"
+
+ Trong Python 3.9, `Annotated` là một phần của thư viện chuẩn, do đó bạn có thể import nó từ `typing`.
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial013_py39.py!}
+ ```
+
+=== "Python 3.6+"
+
+ Ở phiên bản dưới Python 3.9, bạn import `Annotated` từ `typing_extensions`.
+
+ Nó đã được cài đặt sẵng cùng với **FastAPI**.
+
+ ```Python hl_lines="1 4"
+ {!> ../../../docs_src/python_types/tutorial013.py!}
+ ```
+
+Python bản thân nó không làm bất kì điều gì với `Annotated`. Với các trình soạn thảo và các công cụ khác, kiểu dữ liệu vẫn là `str`.
+
+Nhưng bạn có thể sử dụng `Annotated` để cung cấp cho **FastAPI** metadata bổ sung về cách mà bạn muốn ứng dụng của bạn xử lí.
+
+Điều quan trọng cần nhớ là ***tham số kiểu dữ liệu* đầu tiên** bạn truyền tới `Annotated` là **kiểu giá trị thực sự**. Phần còn lại chỉ là metadata cho các công cụ khác.
+
+Bây giờ, bạn chỉ cần biết rằng `Annotated` tồn tại, và nó là tiêu chuẩn của Python. 😎
+
+
+Sau đó, bạn sẽ thấy sự **mạnh mẽ** mà nó có thể làm.
+
+!!! tip
+ Thực tế, cái này là **tiêu chuẩn của Python**, nghĩa là bạn vẫn sẽ có được **trải nghiệm phát triển tốt nhất có thể** với trình soạn thảo của bạn, với các công cụ bạn sử dụng để phân tích và tái cấu trúc code của bạn, etc. ✨
+
+ Và code của bạn sẽ tương thích với nhiều công cụ và thư viện khác của Python. 🚀
+
+## Các gợi ý kiểu dữ liệu trong **FastAPI**
+
+**FastAPI** lấy các ưu điểm của các gợi ý kiểu dữ liệu để thực hiện một số thứ.
+
+Với **FastAPI**, bạn khai báo các tham số với gợi ý kiểu và bạn có được:
+
+* **Sự hỗ trợ từ các trình soạn thảo**.
+* **Kiểm tra kiểu dữ liệu (type checking)**.
+
+...và **FastAPI** sử dụng các khia báo để:
+
+* **Định nghĩa các yêu cầu**: từ tham số đường dẫn của request, tham số query, headers, bodies, các phụ thuộc (dependencies),...
+* **Chuyển dổi dữ liệu*: từ request sang kiểu dữ liệu được yêu cầu.
+* **Kiểm tra tính đúng đắn của dữ liệu**: tới từ mỗi request:
+ * Sinh **lỗi tự động** để trả về máy khác khi dữ liệu không hợp lệ.
+* **Tài liệu hóa** API sử dụng OpenAPI:
+ * cái mà sau được được sử dụng bởi tài liệu tương tác người dùng.
+
+Điều này có thể nghe trừu tượng. Đừng lo lắng. Bạn sẽ thấy tất cả chúng trong [Hướng dẫn sử dụng](tutorial/index.md){.internal-link target=_blank}.
+
+Điều quan trọng là bằng việc sử dụng các kiểu dữ liệu chuẩn của Python (thay vì thêm các lớp, decorators,...), **FastAPI** sẽ thực hiện nhiều công việc cho bạn.
+
+!!! info
+ Nếu bạn đã đi qua toàn bộ các hướng dẫn và quay trở lại để tìm hiểu nhiều hơn về các kiểu dữ liệu, một tài nguyên tốt như "cheat sheet" từ `mypy`.
diff --git a/fastapi/__init__.py b/fastapi/__init__.py
index 7dfeca0d48..d8abf2103e 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.100.1"
+__version__ = "0.101.1"
from starlette import status as status
diff --git a/fastapi/concurrency.py b/fastapi/concurrency.py
index 31b878d5df..754061c862 100644
--- a/fastapi/concurrency.py
+++ b/fastapi/concurrency.py
@@ -19,7 +19,7 @@ async def contextmanager_in_threadpool(
) -> AsyncGenerator[_T, None]:
# blocking __exit__ from running waiting on a free thread
# can create race conditions/deadlocks if the context manager itself
- # has it's own internal pool (e.g. a database connection pool)
+ # has its own internal pool (e.g. a database connection pool)
# to avoid this we let __exit__ run without a capacity limit
# since we're creating a new limiter for each call, any non-zero limit
# works (1 is arbitrary)
diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py
index c1692f3961..42f4709fba 100644
--- a/fastapi/exceptions.py
+++ b/fastapi/exceptions.py
@@ -47,3 +47,9 @@ class ResponseValidationError(ValidationException):
def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None:
super().__init__(errors)
self.body = body
+
+ def __str__(self) -> str:
+ message = f"{len(self._errors)} validation errors:\n"
+ for err in self._errors:
+ message += f" {err}\n"
+ return message
diff --git a/fastapi/params.py b/fastapi/params.py
index 30af5713e7..2d8100650e 100644
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -69,7 +69,7 @@ class Param(FieldInfo):
self.deprecated = deprecated
if example is not _Unset:
warnings.warn(
- "`example` has been depreacated, please use `examples` instead",
+ "`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
stacklevel=4,
)
@@ -98,7 +98,7 @@ class Param(FieldInfo):
kwargs["examples"] = examples
if regex is not None:
warnings.warn(
- "`regex` has been depreacated, please use `pattern` instead",
+ "`regex` has been deprecated, please use `pattern` instead",
category=DeprecationWarning,
stacklevel=4,
)
@@ -512,7 +512,7 @@ class Body(FieldInfo):
self.deprecated = deprecated
if example is not _Unset:
warnings.warn(
- "`example` has been depreacated, please use `examples` instead",
+ "`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
stacklevel=4,
)
diff --git a/fastapi/routing.py b/fastapi/routing.py
index d8ff0579cb..1e3dfb4d52 100644
--- a/fastapi/routing.py
+++ b/fastapi/routing.py
@@ -83,7 +83,7 @@ def _prepare_response_content(
if read_with_orm_mode:
# Let from_orm extract the data from this model instead of converting
# it now to a dict.
- # Otherwise there's no way to extract lazy data that requires attribute
+ # Otherwise, there's no way to extract lazy data that requires attribute
# access instead of dict iteration, e.g. lazy relationships.
return res
return _model_dump(
@@ -448,9 +448,7 @@ class APIRoute(routing.Route):
self.response_field = create_response_field(
name=response_name,
type_=self.response_model,
- # TODO: This should actually set mode='serialization', just, that changes the schemas
- # mode="serialization",
- mode="validation",
+ mode="serialization",
)
# Create a clone of the field, so that a Pydantic submodel is not returned
# as is just because it's an instance of a subclass of a more limited class
@@ -458,7 +456,7 @@ class APIRoute(routing.Route):
# that doesn't have the hashed_password. But because it's a subclass, it
# would pass the validation and be returned as is.
# By being a new field, no inheritance will be passed as is. A new model
- # will be always created.
+ # will always be created.
# TODO: remove when deprecating Pydantic v1
self.secure_cloned_response_field: Optional[
ModelField
diff --git a/pyproject.toml b/pyproject.toml
index f0917578f9..9b7cca9c95 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,7 +42,7 @@ classifiers = [
]
dependencies = [
"starlette>=0.27.0,<0.28.0",
- "pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,<3.0.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.5.0",
]
dynamic = ["version"]
diff --git a/requirements-docs.txt b/requirements-docs.txt
index df60ca4df1..220d1ec3a5 100644
--- a/requirements-docs.txt
+++ b/requirements-docs.txt
@@ -1,6 +1,5 @@
-e .
-mkdocs==1.4.3
-mkdocs-material==9.1.17
+mkdocs-material==9.1.21
mdx-include >=1.4.1,<2.0.0
mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0
typer-cli >=0.0.13,<0.0.14
diff --git a/requirements-tests.txt b/requirements-tests.txt
index abefac6852..0113b6f7ae 100644
--- a/requirements-tests.txt
+++ b/requirements-tests.txt
@@ -2,7 +2,7 @@
pydantic-settings >=2.0.0
pytest >=7.1.3,<8.0.0
coverage[toml] >= 6.5.0,< 8.0
-mypy ==1.4.0
+mypy ==1.4.1
ruff ==0.0.275
black == 23.3.0
httpx >=0.23.0,<0.25.0
diff --git a/tests/test_computed_fields.py b/tests/test_computed_fields.py
new file mode 100644
index 0000000000..5286507b2d
--- /dev/null
+++ b/tests/test_computed_fields.py
@@ -0,0 +1,77 @@
+import pytest
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+
+from .utils import needs_pydanticv2
+
+
+@pytest.fixture(name="client")
+def get_client():
+ app = FastAPI()
+
+ from pydantic import BaseModel, computed_field
+
+ class Rectangle(BaseModel):
+ width: int
+ length: int
+
+ @computed_field
+ @property
+ def area(self) -> int:
+ return self.width * self.length
+
+ @app.get("/")
+ def read_root() -> Rectangle:
+ return Rectangle(width=3, length=4)
+
+ client = TestClient(app)
+ return client
+
+
+@needs_pydanticv2
+def test_get(client: TestClient):
+ response = client.get("/")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"width": 3, "length": 4, "area": 12}
+
+
+@needs_pydanticv2
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/": {
+ "get": {
+ "summary": "Read Root",
+ "operationId": "read_root__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Rectangle"}
+ }
+ },
+ }
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Rectangle": {
+ "properties": {
+ "width": {"type": "integer", "title": "Width"},
+ "length": {"type": "integer", "title": "Length"},
+ "area": {"type": "integer", "title": "Area", "readOnly": True},
+ },
+ "type": "object",
+ "required": ["width", "length", "area"],
+ "title": "Rectangle",
+ }
+ }
+ },
+ }
diff --git a/tests/test_filter_pydantic_sub_model_pv2.py b/tests/test_filter_pydantic_sub_model_pv2.py
index 656332a010..9f5e6b08fb 100644
--- a/tests/test_filter_pydantic_sub_model_pv2.py
+++ b/tests/test_filter_pydantic_sub_model_pv2.py
@@ -1,7 +1,7 @@
from typing import Optional
import pytest
-from dirty_equals import IsDict
+from dirty_equals import HasRepr, IsDict, IsOneOf
from fastapi import Depends, FastAPI
from fastapi.exceptions import ResponseValidationError
from fastapi.testclient import TestClient
@@ -66,7 +66,7 @@ def test_validator_is_cloned(client: TestClient):
"loc": ("response", "name"),
"msg": "Value error, name must end in A",
"input": "modelX",
- "ctx": {"error": "name must end in A"},
+ "ctx": {"error": HasRepr("ValueError('name must end in A')")},
"url": match_pydantic_error_url("value_error"),
}
)
@@ -139,7 +139,11 @@ def test_openapi_schema(client: TestClient):
},
"ModelA": {
"title": "ModelA",
- "required": ["name", "foo"],
+ "required": IsOneOf(
+ ["name", "description", "foo"],
+ # TODO remove when deprecating Pydantic v1
+ ["name", "foo"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py
index aa989c6128..931f08fc1c 100644
--- a/tests/test_multi_body_errors.py
+++ b/tests/test_multi_body_errors.py
@@ -51,7 +51,7 @@ def test_jsonable_encoder_requiring_error():
"loc": ["body", 0, "age"],
"msg": "Input should be greater than 0",
"input": -1.0,
- "ctx": {"gt": 0.0},
+ "ctx": {"gt": "0"},
"url": match_pydantic_error_url("greater_than"),
}
]
@@ -84,9 +84,23 @@ def test_put_incorrect_body_multiple():
"input": {"age": "five"},
"url": match_pydantic_error_url("missing"),
},
+ {
+ "ctx": {"class": "Decimal"},
+ "input": "five",
+ "loc": ["body", 0, "age", "is-instance[Decimal]"],
+ "msg": "Input should be an instance of Decimal",
+ "type": "is_instance_of",
+ "url": match_pydantic_error_url("is_instance_of"),
+ },
{
"type": "decimal_parsing",
- "loc": ["body", 0, "age"],
+ "loc": [
+ "body",
+ 0,
+ "age",
+ "function-after[to_decimal(), "
+ "union[int,constrained-str,function-plain[str()]]]",
+ ],
"msg": "Input should be a valid decimal",
"input": "five",
},
@@ -97,9 +111,23 @@ def test_put_incorrect_body_multiple():
"input": {"age": "six"},
"url": match_pydantic_error_url("missing"),
},
+ {
+ "ctx": {"class": "Decimal"},
+ "input": "six",
+ "loc": ["body", 1, "age", "is-instance[Decimal]"],
+ "msg": "Input should be an instance of Decimal",
+ "type": "is_instance_of",
+ "url": match_pydantic_error_url("is_instance_of"),
+ },
{
"type": "decimal_parsing",
- "loc": ["body", 1, "age"],
+ "loc": [
+ "body",
+ 1,
+ "age",
+ "function-after[to_decimal(), "
+ "union[int,constrained-str,function-plain[str()]]]",
+ ],
"msg": "Input should be a valid decimal",
"input": "six",
},
diff --git a/tests/test_response_model_as_return_annotation.py b/tests/test_response_model_as_return_annotation.py
index 85dd450eb0..6948430a13 100644
--- a/tests/test_response_model_as_return_annotation.py
+++ b/tests/test_response_model_as_return_annotation.py
@@ -277,13 +277,15 @@ def test_response_model_no_annotation_return_exact_dict():
def test_response_model_no_annotation_return_invalid_dict():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/response_model-no_annotation-return_invalid_dict")
+ assert "missing" in str(excinfo.value)
def test_response_model_no_annotation_return_invalid_model():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/response_model-no_annotation-return_invalid_model")
+ assert "missing" in str(excinfo.value)
def test_response_model_no_annotation_return_dict_with_extra_data():
@@ -313,13 +315,15 @@ def test_no_response_model_annotation_return_exact_dict():
def test_no_response_model_annotation_return_invalid_dict():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/no_response_model-annotation-return_invalid_dict")
+ assert "missing" in str(excinfo.value)
def test_no_response_model_annotation_return_invalid_model():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/no_response_model-annotation-return_invalid_model")
+ assert "missing" in str(excinfo.value)
def test_no_response_model_annotation_return_dict_with_extra_data():
@@ -395,13 +399,15 @@ def test_response_model_model1_annotation_model2_return_exact_dict():
def test_response_model_model1_annotation_model2_return_invalid_dict():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/response_model_model1-annotation_model2-return_invalid_dict")
+ assert "missing" in str(excinfo.value)
def test_response_model_model1_annotation_model2_return_invalid_model():
- with pytest.raises(ResponseValidationError):
+ with pytest.raises(ResponseValidationError) as excinfo:
client.get("/response_model_model1-annotation_model2-return_invalid_model")
+ assert "missing" in str(excinfo.value)
def test_response_model_model1_annotation_model2_return_dict_with_extra_data():
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001.py b/tests/test_tutorial/test_body_updates/test_tutorial001.py
index b02f7c81c3..f1a46210aa 100644
--- a/tests/test_tutorial/test_body_updates/test_tutorial001.py
+++ b/tests/test_tutorial/test_body_updates/test_tutorial001.py
@@ -1,7 +1,8 @@
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
+from ...utils import needs_pydanticv1, needs_pydanticv2
+
@pytest.fixture(name="client")
def get_client():
@@ -36,7 +37,181 @@ def test_put(client: TestClient):
}
+@needs_pydanticv2
def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ },
+ "put": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ },
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "type": "object",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "properties": {
+ "name": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Name",
+ },
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Price",
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_pydanticv1
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -124,36 +299,9 @@ def test_openapi_schema(client: TestClient):
"title": "Item",
"type": "object",
"properties": {
- "name": IsDict(
- {
- "title": "Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Name", "type": "string"}
- ),
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": IsDict(
- {
- "title": "Price",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Price", "type": "number"}
- ),
+ "name": {"title": "Name", "type": "string"},
+ "description": {"title": "Description", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
"tax": {"title": "Tax", "type": "number", "default": 10.5},
"tags": {
"title": "Tags",
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py
index 4af2652a78..ab696e4c8d 100644
--- a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py
+++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py
@@ -1,8 +1,7 @@
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py310
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(name="client")
@@ -41,7 +40,182 @@ def test_put(client: TestClient):
@needs_py310
+@needs_pydanticv2
def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ },
+ "put": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ },
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "type": "object",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "properties": {
+ "name": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Name",
+ },
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Price",
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_py310
+@needs_pydanticv1
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -129,36 +303,9 @@ def test_openapi_schema(client: TestClient):
"title": "Item",
"type": "object",
"properties": {
- "name": IsDict(
- {
- "title": "Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Name", "type": "string"}
- ),
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": IsDict(
- {
- "title": "Price",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Price", "type": "number"}
- ),
+ "name": {"title": "Name", "type": "string"},
+ "description": {"title": "Description", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
"tax": {"title": "Tax", "type": "number", "default": 10.5},
"tags": {
"title": "Tags",
diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py
index 832f453884..2ee6a5cb4c 100644
--- a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py
+++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py
@@ -1,8 +1,7 @@
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
+from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(name="client")
@@ -41,7 +40,182 @@ def test_put(client: TestClient):
@needs_py39
+@needs_pydanticv2
def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ },
+ "put": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ },
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "type": "object",
+ "properties": {
+ "name": {
+ "title": "Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "type": "object",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "properties": {
+ "name": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Name",
+ },
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Price",
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_py39
+@needs_pydanticv1
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -129,36 +303,9 @@ def test_openapi_schema(client: TestClient):
"title": "Item",
"type": "object",
"properties": {
- "name": IsDict(
- {
- "title": "Name",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Name", "type": "string"}
- ),
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
- "price": IsDict(
- {
- "title": "Price",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Price", "type": "number"}
- ),
+ "name": {"title": "Name", "type": "string"},
+ "description": {"title": "Description", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
"tax": {"title": "Tax", "type": "number", "default": 10.5},
"tags": {
"title": "Tags",
diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial002.py b/tests/test_tutorial/test_dataclasses/test_tutorial002.py
index 7d88e28616..4146f4cd65 100644
--- a/tests/test_tutorial/test_dataclasses/test_tutorial002.py
+++ b/tests/test_tutorial/test_dataclasses/test_tutorial002.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.dataclasses.tutorial002 import app
@@ -21,8 +21,7 @@ def test_get_item():
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
- data = response.json()
- assert data == {
+ assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
@@ -47,7 +46,11 @@ def test_openapi_schema():
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "price", "tags", "description", "tax"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
@@ -57,7 +60,6 @@ def test_openapi_schema():
"title": "Tags",
"type": "array",
"items": {"type": "string"},
- "default": [],
}
)
| IsDict(
diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial003.py b/tests/test_tutorial/test_dataclasses/test_tutorial003.py
index 597757e093..2e5809914f 100644
--- a/tests/test_tutorial/test_dataclasses/test_tutorial003.py
+++ b/tests/test_tutorial/test_dataclasses/test_tutorial003.py
@@ -1,8 +1,9 @@
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
from docs_src.dataclasses.tutorial003 import app
+from ...utils import needs_pydanticv1, needs_pydanticv2
+
client = TestClient(app)
@@ -52,7 +53,157 @@ def test_get_authors():
]
+@needs_pydanticv2
def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/authors/{author_id}/items/": {
+ "post": {
+ "summary": "Create Author Items",
+ "operationId": "create_author_items_authors__author_id__items__post",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Author Id", "type": "string"},
+ "name": "author_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Items",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ItemInput"},
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Author"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/authors/": {
+ "get": {
+ "summary": "Get Authors",
+ "operationId": "get_authors_authors__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Response Get Authors Authors Get",
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Author"
+ },
+ }
+ }
+ },
+ }
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "Author": {
+ "title": "Author",
+ "required": ["name", "items"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "items": {
+ "title": "Items",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ItemOutput"},
+ },
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ItemInput": {
+ "title": "Item",
+ "required": ["name"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "required": ["name", "description"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_pydanticv1
+def test_openapi_schema_pv1():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
@@ -136,22 +287,11 @@ def test_openapi_schema():
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "items": IsDict(
- {
- "title": "Items",
- "type": "array",
- "items": {"$ref": "#/components/schemas/Item"},
- "default": [],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {
- "title": "Items",
- "type": "array",
- "items": {"$ref": "#/components/schemas/Item"},
- }
- ),
+ "items": {
+ "title": "Items",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Item"},
+ },
},
},
"HTTPValidationError": {
@@ -171,16 +311,7 @@ def test_openapi_schema():
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
+ "description": {"title": "Description", "type": "string"},
},
},
"ValidationError": {
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003.py b/tests/test_tutorial/test_extra_models/test_tutorial003.py
index 21192b7db7..0ccb99948f 100644
--- a/tests/test_tutorial/test_extra_models/test_tutorial003.py
+++ b/tests/test_tutorial/test_extra_models/test_tutorial003.py
@@ -1,3 +1,4 @@
+from dirty_equals import IsOneOf
from fastapi.testclient import TestClient
from docs_src.extra_models.tutorial003 import app
@@ -76,7 +77,11 @@ def test_openapi_schema():
"schemas": {
"PlaneItem": {
"title": "PlaneItem",
- "required": ["description", "size"],
+ "required": IsOneOf(
+ ["description", "type", "size"],
+ # TODO: remove when deprecating Pydantic v1
+ ["description", "size"],
+ ),
"type": "object",
"properties": {
"description": {"title": "Description", "type": "string"},
@@ -86,7 +91,11 @@ def test_openapi_schema():
},
"CarItem": {
"title": "CarItem",
- "required": ["description"],
+ "required": IsOneOf(
+ ["description", "type"],
+ # TODO: remove when deprecating Pydantic v1
+ ["description"],
+ ),
"type": "object",
"properties": {
"description": {"title": "Description", "type": "string"},
diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py b/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py
index c17ddbbe1d..b2fe65fd9f 100644
--- a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py
+++ b/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py
@@ -1,4 +1,5 @@
import pytest
+from dirty_equals import IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -86,7 +87,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"PlaneItem": {
"title": "PlaneItem",
- "required": ["description", "size"],
+ "required": IsOneOf(
+ ["description", "type", "size"],
+ # TODO: remove when deprecating Pydantic v1
+ ["description", "size"],
+ ),
"type": "object",
"properties": {
"description": {"title": "Description", "type": "string"},
@@ -96,7 +101,11 @@ def test_openapi_schema(client: TestClient):
},
"CarItem": {
"title": "CarItem",
- "required": ["description"],
+ "required": IsOneOf(
+ ["description", "type"],
+ # TODO: remove when deprecating Pydantic v1
+ ["description"],
+ ),
"type": "object",
"properties": {
"description": {"title": "Description", "type": "string"},
diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py
index dd123f48d6..3ffc0bca7c 100644
--- a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py
+++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py
@@ -1,8 +1,9 @@
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
from docs_src.path_operation_advanced_configuration.tutorial004 import app
+from ...utils import needs_pydanticv1, needs_pydanticv2
+
client = TestClient(app)
@@ -18,7 +19,137 @@ def test_query_params_str_validations():
}
+@needs_pydanticv2
def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create an item",
+ "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_pydanticv1
+def test_openapi_schema_pv1():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -69,27 +200,9 @@ def test_openapi_schema():
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
+ "description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
+ "tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
index e7e9a982e1..ff98295a60 100644
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
+++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py
@@ -1,8 +1,9 @@
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
from docs_src.path_operation_configuration.tutorial005 import app
+from ...utils import needs_pydanticv1, needs_pydanticv2
+
client = TestClient(app)
@@ -18,7 +19,137 @@ def test_query_params_str_validations():
}
+@needs_pydanticv2
def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "The created item",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create an item",
+ "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_pydanticv1
+def test_openapi_schema_pv1():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -69,27 +200,9 @@ def test_openapi_schema():
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
+ "description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
+ "tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py
index ebfeb809cc..ad1c09eaec 100644
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py
+++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py
@@ -1,8 +1,7 @@
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py310
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(name="client")
@@ -27,7 +26,138 @@ def test_query_params_str_validations(client: TestClient):
@needs_py310
+@needs_pydanticv2
def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "The created item",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create an item",
+ "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_py310
+@needs_pydanticv1
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -78,27 +208,9 @@ def test_openapi_schema(client: TestClient):
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
+ "description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
+ "tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py
index 8e79afe968..045d1d402c 100644
--- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py
+++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py
@@ -1,8 +1,7 @@
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
+from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(name="client")
@@ -27,7 +26,138 @@ def test_query_params_str_validations(client: TestClient):
@needs_py39
+@needs_pydanticv2
def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "The created item",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ItemOutput"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create an item",
+ "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemInput"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ItemInput": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ItemOutput": {
+ "title": "Item",
+ "required": ["name", "description", "price", "tax", "tags"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "uniqueItems": True,
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+
+
+# TODO: remove when deprecating Pydantic v1
+@needs_py39
+@needs_pydanticv1
+def test_openapi_schema_pv1(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
@@ -78,27 +208,9 @@ def test_openapi_schema(client: TestClient):
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
- "description": IsDict(
- {
- "title": "Description",
- "anyOf": [{"type": "string"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Description", "type": "string"}
- ),
+ "description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
- "tax": IsDict(
- {
- "title": "Tax",
- "anyOf": [{"type": "number"}, {"type": "null"}],
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Tax", "type": "number"}
- ),
+ "tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
diff --git a/tests/test_tutorial/test_response_model/test_tutorial003.py b/tests/test_tutorial/test_response_model/test_tutorial003.py
index 20221399b1..384c8e0f14 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial003.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial003.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003 import app
@@ -70,7 +70,11 @@ def test_openapi_schema():
"schemas": {
"UserOut": {
"title": "UserOut",
- "required": ["username", "email"],
+ "required": IsOneOf(
+ ["username", "email", "full_name"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username", "email"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_01.py b/tests/test_tutorial/test_response_model/test_tutorial003_01.py
index e8f0658f4c..3a6a0b20d9 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial003_01.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial003_01.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003_01 import app
@@ -70,7 +70,11 @@ def test_openapi_schema():
"schemas": {
"BaseUser": {
"title": "BaseUser",
- "required": ["username", "email"],
+ "required": IsOneOf(
+ ["username", "email", "full_name"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username", "email"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py b/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py
index a69f8cc8de..6985b9de6a 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -79,7 +79,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"BaseUser": {
"title": "BaseUser",
- "required": ["username", "email"],
+ "required": IsOneOf(
+ ["username", "email", "full_name"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username", "email"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_py310.py b/tests/test_tutorial/test_response_model/test_tutorial003_py310.py
index 64dcd6cbdf..3a3aee38aa 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial003_py310.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial003_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -79,7 +79,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"UserOut": {
"title": "UserOut",
- "required": ["username", "email"],
+ "required": IsOneOf(
+ ["username", "email", "full_name"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username", "email"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial004.py b/tests/test_tutorial/test_response_model/test_tutorial004.py
index 8beb847d1d..e9bde18dd5 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial004.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial004.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial004 import app
@@ -79,7 +79,11 @@ def test_openapi_schema():
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax", "tags"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial004_py310.py b/tests/test_tutorial/test_response_model/test_tutorial004_py310.py
index 28eb88c347..6f8a3cbea8 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial004_py310.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial004_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -87,7 +87,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax", "tags"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial004_py39.py b/tests/test_tutorial/test_response_model/test_tutorial004_py39.py
index 9e1a21f8db..cfaa1eba21 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial004_py39.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial004_py39.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py39
@@ -87,7 +87,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax", "tags"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial005.py b/tests/test_tutorial/test_response_model/test_tutorial005.py
index 06e5d0fd15..b20864c07a 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial005.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial005.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial005 import app
@@ -102,7 +102,11 @@ def test_openapi_schema():
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial005_py310.py b/tests/test_tutorial/test_response_model/test_tutorial005_py310.py
index 0f15662439..de552c8f22 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial005_py310.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial005_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -112,7 +112,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial006.py b/tests/test_tutorial/test_response_model/test_tutorial006.py
index 6e6152b9f1..1e47e2eadb 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial006.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial006.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial006 import app
@@ -102,7 +102,11 @@ def test_openapi_schema():
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_response_model/test_tutorial006_py310.py b/tests/test_tutorial/test_response_model/test_tutorial006_py310.py
index 9a980ab5b0..40058b12de 100644
--- a/tests/test_tutorial/test_response_model/test_tutorial006_py310.py
+++ b/tests/test_tutorial/test_response_model/test_tutorial006_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -112,7 +112,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"Item": {
"title": "Item",
- "required": ["name", "price"],
+ "required": IsOneOf(
+ ["name", "description", "price", "tax"],
+ # TODO: remove when deprecating Pydantic v1
+ ["name", "price"],
+ ),
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005.py b/tests/test_tutorial/test_security/test_tutorial005.py
index 22ae76f428..c669c306dd 100644
--- a/tests/test_tutorial/test_security/test_tutorial005.py
+++ b/tests/test_tutorial/test_security/test_tutorial005.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.security.tutorial005 import (
@@ -267,7 +267,11 @@ def test_openapi_schema():
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005_an.py b/tests/test_tutorial/test_security/test_tutorial005_an.py
index 07239cc890..aaab04f78f 100644
--- a/tests/test_tutorial/test_security/test_tutorial005_an.py
+++ b/tests/test_tutorial/test_security/test_tutorial005_an.py
@@ -1,4 +1,4 @@
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from docs_src.security.tutorial005_an import (
@@ -267,7 +267,11 @@ def test_openapi_schema():
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005_an_py310.py b/tests/test_tutorial/test_security/test_tutorial005_an_py310.py
index 1ab836639e..243d0773c2 100644
--- a/tests/test_tutorial/test_security/test_tutorial005_an_py310.py
+++ b/tests/test_tutorial/test_security/test_tutorial005_an_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005_an_py39.py b/tests/test_tutorial/test_security/test_tutorial005_an_py39.py
index 6aabbe04ac..17a3f9aa2a 100644
--- a/tests/test_tutorial/test_security/test_tutorial005_an_py39.py
+++ b/tests/test_tutorial/test_security/test_tutorial005_an_py39.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py39
@@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005_py310.py b/tests/test_tutorial/test_security/test_tutorial005_py310.py
index c21884df83..06455cd632 100644
--- a/tests/test_tutorial/test_security/test_tutorial005_py310.py
+++ b/tests/test_tutorial/test_security/test_tutorial005_py310.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py310
@@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
diff --git a/tests/test_tutorial/test_security/test_tutorial005_py39.py b/tests/test_tutorial/test_security/test_tutorial005_py39.py
index 170c5d60b8..9455bfb4ef 100644
--- a/tests/test_tutorial/test_security/test_tutorial005_py39.py
+++ b/tests/test_tutorial/test_security/test_tutorial005_py39.py
@@ -1,5 +1,5 @@
import pytest
-from dirty_equals import IsDict
+from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
from ...utils import needs_py39
@@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient):
"schemas": {
"User": {
"title": "User",
- "required": ["username"],
+ "required": IsOneOf(
+ ["username", "email", "full_name", "disabled"],
+ # TODO: remove when deprecating Pydantic v1
+ ["username"],
+ ),
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},