diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index eba5fc57e..ccf964486 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -20,7 +20,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
- key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-docs
+ key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-docs-v2
- name: Install Flit
if: steps.cache.outputs.cache-hit != 'true'
run: python3.7 -m pip install flit
diff --git a/README.md b/README.md
index 53de38bd9..c9c69d3e8 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,7 @@ The key features are:
+
diff --git a/docs/az/mkdocs.yml b/docs/az/mkdocs.yml
index dbff7b26a..66220f63e 100644
--- a/docs/az/mkdocs.yml
+++ b/docs/az/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/de/mkdocs.yml b/docs/de/mkdocs.yml
index 8f29ef316..360fa8c4a 100644
--- a/docs/de/mkdocs.yml
+++ b/docs/de/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml
index df088f39f..ebbe446ee 100644
--- a/docs/en/data/people.yml
+++ b/docs/en/data/people.yml
@@ -1,12 +1,12 @@
maintainers:
- login: tiangolo
- answers: 1230
- prs: 269
+ answers: 1237
+ prs: 280
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=5cad72c846b7aba2e960546af490edc7375dafc4&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
- count: 316
+ count: 319
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
url: https://github.com/Kludex
- login: dmontagu
@@ -14,7 +14,7 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: ycd
- count: 219
+ count: 221
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
url: https://github.com/ycd
- login: Mause
@@ -34,7 +34,7 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
url: https://github.com/ArcLightSlavik
- login: raphaelauv
- count: 67
+ count: 68
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: falkben
@@ -57,18 +57,22 @@ experts:
count: 38
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
url: https://github.com/includeamin
+- login: STeveShary
+ count: 37
+ avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
+ url: https://github.com/STeveShary
- login: prostomarkeloff
count: 33
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4
url: https://github.com/prostomarkeloff
-- login: STeveShary
- count: 32
- avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
- url: https://github.com/STeveShary
- login: krishnardt
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
+- login: adriangb
+ count: 30
+ avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
+ url: https://github.com/adriangb
- login: wshayes
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
@@ -77,10 +81,10 @@ experts:
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
-- login: adriangb
- count: 28
- avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4
- url: https://github.com/adriangb
+- login: chbndrhnns
+ count: 25
+ avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
+ url: https://github.com/chbndrhnns
- login: ghandic
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
@@ -89,18 +93,14 @@ experts:
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9bcce836bbce55835291c5b2ac93a4e311f4b3c3&v=4
url: https://github.com/dbanty
-- login: chbndrhnns
- count: 24
- avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
- url: https://github.com/chbndrhnns
+- login: panla
+ count: 25
+ avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
+ url: https://github.com/panla
- login: SirTelemak
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
-- login: panla
- count: 23
- avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
- url: https://github.com/panla
- login: acnebs
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
@@ -129,22 +129,34 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
url: https://github.com/nkhitrov
+- login: acidjunk
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
+ url: https://github.com/acidjunk
- login: waynerv
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
-- login: acidjunk
- count: 15
- avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
- url: https://github.com/acidjunk
- login: dstlny
- count: 14
+ count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
+- login: jgould22
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: harunyasar
+ count: 14
+ avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
+ url: https://github.com/harunyasar
- login: haizaar
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/58201?u=4f1f9843d69433ca0d380d95146cfe119e5fdac4&v=4
url: https://github.com/haizaar
+- login: hellocoldworld
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/47581948?v=4
+ url: https://github.com/hellocoldworld
- login: David-Lor
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/17401854?u=474680c02b94cba810cb9032fb7eb787d9cc9d22&v=4
@@ -173,39 +185,47 @@ experts:
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/20441825?u=ee1e59446b98f8ec2363caeda4c17164d0d9cc7d&v=4
url: https://github.com/stefanondisponibile
-- login: hellocoldworld
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/47581948?v=4
- url: https://github.com/hellocoldworld
- login: oligond
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/2858306?u=1bb1182a5944e93624b7fb26585f22c8f7a9d76e&v=4
url: https://github.com/oligond
last_month_active:
-- login: insomnes
- count: 10
- avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
- url: https://github.com/insomnes
-- login: raphaelauv
- count: 6
- avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
- url: https://github.com/raphaelauv
-- login: jgould22
- count: 4
- avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
- url: https://github.com/jgould22
- login: harunyasar
- count: 4
+ count: 10
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
url: https://github.com/harunyasar
+- login: jgould22
+ count: 10
+ avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
+ url: https://github.com/jgould22
+- login: rafsaf
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=be9f06b8ced2d2b677297decc781fa8ce4f7ddbd&v=4
+ url: https://github.com/rafsaf
+- login: STeveShary
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
+ url: https://github.com/STeveShary
+- login: ahnaf-zamil
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/57180217?u=849128b146771ace47beca5b5ff68eb82905dd6d&v=4
+ url: https://github.com/ahnaf-zamil
+- login: lucastosetto
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/89307132?u=56326696423df7126c9e7c702ee58f294db69a2a&v=4
+ url: https://github.com/lucastosetto
+- login: blokje
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/851418?v=4
+ url: https://github.com/blokje
+- login: MatthijsKok
+ count: 3
+ avatarUrl: https://avatars.githubusercontent.com/u/7658129?u=1243e32d57e13abc45e3f5235ed5b9197e0d2b41&v=4
+ url: https://github.com/MatthijsKok
- login: Kludex
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
url: https://github.com/Kludex
-- login: panla
- count: 3
- avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
- url: https://github.com/panla
top_contributors:
- login: waynerv
count: 25
@@ -219,14 +239,14 @@ top_contributors:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
+- login: jaystone776
+ count: 15
+ avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
+ url: https://github.com/jaystone776
- login: euri10
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
-- login: jaystone776
- count: 13
- avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
- url: https://github.com/jaystone776
- login: mariacamilagl
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
@@ -285,7 +305,7 @@ top_contributors:
url: https://github.com/NinaHwang
top_reviewers:
- login: Kludex
- count: 91
+ count: 93
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=3682d9b9b93bef272f379ab623dc031c8d71432e&v=4
url: https://github.com/Kludex
- login: waynerv
@@ -301,7 +321,7 @@ top_reviewers:
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
- login: ycd
- count: 44
+ count: 45
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4
url: https://github.com/ycd
- login: AdrianDeAnda
@@ -312,12 +332,16 @@ top_reviewers:
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4
url: https://github.com/ArcLightSlavik
+- login: cikay
+ count: 24
+ avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
+ url: https://github.com/cikay
- login: dmontagu
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4
url: https://github.com/dmontagu
- login: cassiobotaro
- count: 22
+ count: 23
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4
url: https://github.com/cassiobotaro
- login: komtaki
@@ -336,6 +360,10 @@ top_reviewers:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
url: https://github.com/yanever
+- login: lsglucas
+ count: 16
+ avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
+ url: https://github.com/lsglucas
- login: SwftAlpc
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
@@ -356,26 +384,22 @@ top_reviewers:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
-- login: lsglucas
- count: 14
- avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
- url: https://github.com/lsglucas
- login: rjNemo
- count: 13
+ count: 14
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: RunningIkkyu
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4
url: https://github.com/RunningIkkyu
+- login: yezz123
+ count: 12
+ avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
+ url: https://github.com/yezz123
- login: sh0nk
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
-- login: yezz123
- count: 11
- avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
- url: https://github.com/yezz123
- login: mariacamilagl
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
@@ -400,6 +424,10 @@ top_reviewers:
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
url: https://github.com/kty4119
+- login: zy7y
+ count: 9
+ avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
+ url: https://github.com/zy7y
- login: bezaca
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
@@ -444,39 +472,31 @@ top_reviewers:
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/34248814?v=4
url: https://github.com/krocdort
+- login: dimaqq
+ count: 7
+ avatarUrl: https://avatars.githubusercontent.com/u/662249?v=4
+ url: https://github.com/dimaqq
- login: jovicon
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4
url: https://github.com/jovicon
+- login: NinaHwang
+ count: 6
+ avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4
+ url: https://github.com/NinaHwang
- login: diogoduartec
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31852339?u=b50fc11c531e9b77922e19edfc9e7233d4d7b92e&v=4
url: https://github.com/diogoduartec
-- login: nimctl
+- login: n25a
count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=e39b11d47188744ee07b2a1c7ce1a1bdf3c80760&v=4
- url: https://github.com/nimctl
+ avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=eb3c95338741c78fff7d9d5d7ace9617e53eee4a&v=4
+ url: https://github.com/n25a
+- login: izaguerreiro
+ count: 5
+ avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
+ url: https://github.com/izaguerreiro
- login: israteneda
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/20668624?u=d7b2961d330aca65fbce5bdb26a0800a3d23ed2d&v=4
url: https://github.com/israteneda
-- login: juntatalor
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/8134632?v=4
- url: https://github.com/juntatalor
-- login: SnkSynthesis
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/63564282?u=0078826509dbecb2fdb543f4e881c9cd06157893&v=4
- url: https://github.com/SnkSynthesis
-- login: anthonycepeda
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4
- url: https://github.com/anthonycepeda
-- login: oandersonmagalhaes
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4
- url: https://github.com/oandersonmagalhaes
-- login: qysfblog
- count: 5
- avatarUrl: https://avatars.githubusercontent.com/u/52229895?v=4
- url: https://github.com/qysfblog
diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml
index baa2e440d..b98e68b65 100644
--- a/docs/en/data/sponsors.yml
+++ b/docs/en/data/sponsors.yml
@@ -5,6 +5,9 @@ gold:
- url: https://cryptapi.io/
title: "CryptAPI: Your easy to use, secure and privacy oriented payment gateway."
img: https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg
+ - url: https://www.dropbase.io/careers
+ title: Dropbase - seamlessly collect, clean, and centralize data.
+ img: https://fastapi.tiangolo.com/img/sponsors/dropbase.svg
silver:
- url: https://www.deta.sh/?ref=fastapi
title: The launchpad for all your (team's) ideas
diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml
index 759748728..0c4e716d7 100644
--- a/docs/en/data/sponsors_badge.yml
+++ b/docs/en/data/sponsors_badge.yml
@@ -7,3 +7,4 @@ logins:
- koaning
- deepset-ai
- cryptapi
+ - DropbaseHQ
diff --git a/docs/en/docs/advanced/extending-openapi.md b/docs/en/docs/advanced/extending-openapi.md
index d8d280ba6..d1b14bc00 100644
--- a/docs/en/docs/advanced/extending-openapi.md
+++ b/docs/en/docs/advanced/extending-openapi.md
@@ -233,3 +233,82 @@ Now, to be able to test that everything works, create a *path operation*:
Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page.
And even without Internet, you would be able to see the docs for your API and interact with it.
+
+## Configuring Swagger UI
+
+You can configure some extra Swagger UI parameters.
+
+To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function.
+
+`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly.
+
+FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs.
+
+### Disable Syntax Highlighting
+
+For example, you could disable syntax highlighting in Swagger UI.
+
+Without changing the settings, syntax highlighting is enabled by default:
+
+
+
+But you can disable it by setting `syntaxHighlight` to `False`:
+
+```Python hl_lines="3"
+{!../../../docs_src/extending_openapi/tutorial003.py!}
+```
+
+...and then Swagger UI won't show the syntax highlighting anymore:
+
+
+
+### Change the Theme
+
+The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
+
+```Python hl_lines="3"
+{!../../../docs_src/extending_openapi/tutorial004.py!}
+```
+
+That configuration would change the syntax highlighting color theme:
+
+
+
+### Change Default Swagger UI Parameters
+
+FastAPI includes some default configuration parameters appropriate for most of the use cases.
+
+It includes these default configurations:
+
+```Python
+{!../../../fastapi/openapi/docs.py[ln:7-13]!}
+```
+
+You can override any of them by setting a different value in the argument `swagger_ui_parameters`.
+
+For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
+
+```Python hl_lines="3"
+{!../../../docs_src/extending_openapi/tutorial005.py!}
+```
+
+### Other Swagger UI Parameters
+
+To see all the other possible configurations you can use, read the official docs for Swagger UI parameters.
+
+### JavaScript-only settings
+
+Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions).
+
+FastAPI also includes these JavaScript-only `presets` settings:
+
+```JavaScript
+presets: [
+ SwaggerUIBundle.presets.apis,
+ SwaggerUIBundle.SwaggerUIStandalonePreset
+]
+```
+
+These are **JavaScript** objects, not strings, so you can't pass them from Python code directly.
+
+If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need.
diff --git a/docs/en/docs/img/sponsors/dropbase-banner.svg b/docs/en/docs/img/sponsors/dropbase-banner.svg
new file mode 100644
index 000000000..d65abf1d9
--- /dev/null
+++ b/docs/en/docs/img/sponsors/dropbase-banner.svg
@@ -0,0 +1,117 @@
+
+
diff --git a/docs/en/docs/img/sponsors/dropbase.svg b/docs/en/docs/img/sponsors/dropbase.svg
new file mode 100644
index 000000000..d0defb4df
--- /dev/null
+++ b/docs/en/docs/img/sponsors/dropbase.svg
@@ -0,0 +1,124 @@
+
+
diff --git a/docs/en/docs/img/tutorial/extending-openapi/image02.png b/docs/en/docs/img/tutorial/extending-openapi/image02.png
new file mode 100644
index 000000000..91453fb56
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image02.png differ
diff --git a/docs/en/docs/img/tutorial/extending-openapi/image03.png b/docs/en/docs/img/tutorial/extending-openapi/image03.png
new file mode 100644
index 000000000..e45a77d51
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image03.png differ
diff --git a/docs/en/docs/img/tutorial/extending-openapi/image04.png b/docs/en/docs/img/tutorial/extending-openapi/image04.png
new file mode 100644
index 000000000..394d2bb43
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image04.png differ
diff --git a/docs/en/docs/python-types.md b/docs/en/docs/python-types.md
index 76d442855..fe56dadec 100644
--- a/docs/en/docs/python-types.md
+++ b/docs/en/docs/python-types.md
@@ -252,7 +252,7 @@ The second type parameter is for the values of the `dict`:
=== "Python 3.9 and above"
```Python hl_lines="1"
- {!> ../../../docs_src/python_types/tutorial008.py!}
+ {!> ../../../docs_src/python_types/tutorial008_py39.py!}
```
This means:
diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 01df31267..6d5ee8ea1 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -2,6 +2,51 @@
## Latest Changes
+* 👥 Update FastAPI People. PR [#4502](https://github.com/tiangolo/fastapi/pull/4502) by [@github-actions[bot]](https://github.com/apps/github-actions).
+## 0.73.0
+
+### Features
+
+* ✨ Add support for declaring `UploadFile` parameters without explicit `File()`. PR [#4469](https://github.com/tiangolo/fastapi/pull/4469) by [@tiangolo](https://github.com/tiangolo). New docs: [Request Files - File Parameters with UploadFile](https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile).
+* ✨ Add support for tags with Enums. PR [#4468](https://github.com/tiangolo/fastapi/pull/4468) by [@tiangolo](https://github.com/tiangolo). New docs: [Path Operation Configuration - Tags with Enums](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags-with-enums).
+* ✨ Allow hiding from OpenAPI (and Swagger UI) `Query`, `Cookie`, `Header`, and `Path` parameters. PR [#3144](https://github.com/tiangolo/fastapi/pull/3144) by [@astraldawn](https://github.com/astraldawn). New docs: [Query Parameters and String Validations - Exclude from OpenAPI](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi).
+
+### Docs
+
+* 📝 Tweak and improve docs for Request Files. PR [#4470](https://github.com/tiangolo/fastapi/pull/4470) by [@tiangolo](https://github.com/tiangolo).
+
+### Fixes
+
+* 🐛 Fix bug preventing to use OpenAPI when using tuples. PR [#3874](https://github.com/tiangolo/fastapi/pull/3874) by [@victorbenichoux](https://github.com/victorbenichoux).
+* 🐛 Prefer custom encoder over defaults if specified in `jsonable_encoder`. PR [#2061](https://github.com/tiangolo/fastapi/pull/2061) by [@viveksunder](https://github.com/viveksunder).
+ * 💚 Duplicate PR to trigger CI. PR [#4467](https://github.com/tiangolo/fastapi/pull/4467) by [@tiangolo](https://github.com/tiangolo).
+
+### Internal
+
+* 🐛 Fix docs dependencies cache, to get the latest Material for MkDocs. PR [#4466](https://github.com/tiangolo/fastapi/pull/4466) by [@tiangolo](https://github.com/tiangolo).
+* 🔧 Add sponsor Dropbase. PR [#4465](https://github.com/tiangolo/fastapi/pull/4465) by [@tiangolo](https://github.com/tiangolo).
+
+## 0.72.0
+
+### Features
+
+* ✨ Enable configuring Swagger UI parameters. Original PR [#2568](https://github.com/tiangolo/fastapi/pull/2568) by [@jmriebold](https://github.com/jmriebold). Here are the new docs: [Configuring Swagger UI](https://fastapi.tiangolo.com/advanced/extending-openapi/#configuring-swagger-ui).
+
+### Docs
+
+* 📝 Update Python Types docs, add missing 3.6 / 3.9 example. PR [#4434](https://github.com/tiangolo/fastapi/pull/4434) by [@tiangolo](https://github.com/tiangolo).
+
+### Translations
+
+* 🌐 Update Chinese translation for `docs/help-fastapi.md`. PR [#3847](https://github.com/tiangolo/fastapi/pull/3847) by [@jaystone776](https://github.com/jaystone776).
+* 🌐 Fix Korean translation for `docs/ko/docs/index.md`. PR [#4195](https://github.com/tiangolo/fastapi/pull/4195) by [@kty4119](https://github.com/kty4119).
+* 🌐 Add Polish translation for `docs/pl/docs/index.md`. PR [#4245](https://github.com/tiangolo/fastapi/pull/4245) by [@MicroPanda123](https://github.com/MicroPanda123).
+* 🌐 Add Chinese translation for `docs\tutorial\path-operation-configuration.md`. PR [#3312](https://github.com/tiangolo/fastapi/pull/3312) by [@jaystone776](https://github.com/jaystone776).
+
+### Internal
+
+* 🔧 Enable MkDocs Material Insiders' `content.tabs.link`. PR [#4399](https://github.com/tiangolo/fastapi/pull/4399) by [@tiangolo](https://github.com/tiangolo).
+
## 0.71.0
### Features
diff --git a/docs/en/docs/tutorial/path-operation-configuration.md b/docs/en/docs/tutorial/path-operation-configuration.md
index 1ff448e76..884a762e2 100644
--- a/docs/en/docs/tutorial/path-operation-configuration.md
+++ b/docs/en/docs/tutorial/path-operation-configuration.md
@@ -64,6 +64,18 @@ They will be added to the OpenAPI schema and used by the automatic documentation
+### Tags with Enums
+
+If you have a big application, you might end up accumulating **several tags**, and you would want to make sure you always use the **same tag** for related *path operations*.
+
+In these cases, it could make sense to store the tags in an `Enum`.
+
+**FastAPI** supports that the same way as with plain strings:
+
+```Python hl_lines="1 8-10 13 18"
+{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
+```
+
## Summary and description
You can add a `summary` and `description`:
diff --git a/docs/en/docs/tutorial/query-params-str-validations.md b/docs/en/docs/tutorial/query-params-str-validations.md
index fcac1a4e0..ee62b9718 100644
--- a/docs/en/docs/tutorial/query-params-str-validations.md
+++ b/docs/en/docs/tutorial/query-params-str-validations.md
@@ -387,6 +387,22 @@ The docs will show it like this:
+## Exclude from OpenAPI
+
+To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter `include_in_schema` of `Query` to `False`:
+
+=== "Python 3.6 and above"
+
+ ```Python hl_lines="10"
+ {!> ../../../docs_src/query_params_str_validations/tutorial014.py!}
+ ```
+
+=== "Python 3.10 and above"
+
+ ```Python hl_lines="7"
+ {!> ../../../docs_src/query_params_str_validations/tutorial014_py310.py!}
+ ```
+
## Recap
You can declare additional validations and metadata for your parameters.
diff --git a/docs/en/docs/tutorial/request-files.md b/docs/en/docs/tutorial/request-files.md
index b7257c7eb..3ca471a91 100644
--- a/docs/en/docs/tutorial/request-files.md
+++ b/docs/en/docs/tutorial/request-files.md
@@ -17,7 +17,7 @@ Import `File` and `UploadFile` from `fastapi`:
{!../../../docs_src/request_files/tutorial001.py!}
```
-## Define `File` parameters
+## Define `File` Parameters
Create file parameters the same way you would for `Body` or `Form`:
@@ -41,9 +41,9 @@ Have in mind that this means that the whole contents will be stored in memory. T
But there are several cases in which you might benefit from using `UploadFile`.
-## `File` parameters with `UploadFile`
+## File Parameters with `UploadFile`
-Define a `File` parameter with a type of `UploadFile`:
+Define a file parameter with a type of `UploadFile`:
```Python hl_lines="12"
{!../../../docs_src/request_files/tutorial001.py!}
@@ -51,6 +51,7 @@ Define a `File` parameter with a type of `UploadFile`:
Using `UploadFile` has several advantages over `bytes`:
+* You don't have to use `File()` in the default value of the parameter.
* It uses a "spooled" file:
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
@@ -113,7 +114,31 @@ The way HTML forms (`
jinja2 - 기본 템플릿 설정을 사용하려면 필요.
* python-multipart - `request.form()`과 함께 "parsing"의 지원을 원하면 필요.
* itsdangerous - `SessionMiddleware` 지원을 위해 필요.
-* pyyaml - Starlette의 `SchemaGenerator` 지원을 위해 필요 (FastAPI와 쓸때는 필요가 없을 겁니다).
+* pyyaml - Starlette의 `SchemaGenerator` 지원을 위해 필요 (FastAPI와 쓸때는 필요 없을 것입니다).
* graphene - `GraphQLApp` 지원을 위해 필요.
* ujson - `UJSONResponse`를 사용하려면 필요.
diff --git a/docs/ko/mkdocs.yml b/docs/ko/mkdocs.yml
index 5ffb71877..1d4d30913 100644
--- a/docs/ko/mkdocs.yml
+++ b/docs/ko/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/pl/docs/index.md b/docs/pl/docs/index.md
index 95fb7ae21..4a300ae63 100644
--- a/docs/pl/docs/index.md
+++ b/docs/pl/docs/index.md
@@ -1,12 +1,8 @@
-
-{!../../../docs/missing-translation.md!}
-
-
- FastAPI framework, high performance, easy to learn, fast to code, ready for production + FastAPI to szybki, prosty w nauce i gotowy do użycia w produkcji framework
@@ -22,29 +18,28 @@
---
-**Documentation**: https://fastapi.tiangolo.com
+**Dokumentacja**: https://fastapi.tiangolo.com
-**Source Code**: https://github.com/tiangolo/fastapi
+**Kod żródłowy**: https://github.com/tiangolo/fastapi
---
-FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
+FastAPI to nowoczesny, wydajny framework webowy do budowania API z użyciem Pythona 3.6+ bazujący na standardowym typowaniu Pythona.
-The key features are:
+Kluczowe cechy:
-* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
+* **Wydajność**: FastAPI jest bardzo wydajny, na równi z **NodeJS** oraz **Go** (dzięki Starlette i Pydantic). [Jeden z najszybszych dostępnych frameworków Pythonowych](#wydajnosc).
+* **Szybkość kodowania**: Przyśpiesza szybkość pisania nowych funkcjonalności o około 200% do 300%. *
+* **Mniejsza ilość błędów**: Zmniejsza ilość ludzkich (dewelopera) błędy o około 40%. *
+* **Intuicyjność**: Wspaniałe wsparcie dla edytorów kodu. Dostępne wszędzie automatyczne uzupełnianie kodu. Krótszy czas debugowania.
+* **Łatwość**: Zaprojektowany by być prosty i łatwy do nauczenia. Mniej czasu spędzonego na czytanie dokumentacji.
+* **Kompaktowość**: Minimalizacja powtarzającego się kodu. Wiele funkcjonalności dla każdej deklaracji parametru. Mniej błędów.
+* **Solidność**: Kod gotowy dla środowiska produkcyjnego. Wraz z automatyczną interaktywną dokumentacją.
+* **Bazujący na standardach**: Oparty na (i w pełni kompatybilny z) otwartych standardach API: OpenAPI (wcześniej znane jako Swagger) oraz JSON Schema.
-* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
-* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
-* **Intuitive**: Great editor support. Completion everywhere. Less time debugging.
-* **Easy**: Designed to be easy to use and learn. Less time reading docs.
-* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
-* **Robust**: Get production-ready code. With automatic interactive documentation.
-* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema.
+* oszacowania bazowane na testach wykonanych przez wewnętrzny zespół deweloperów, budujących aplikacie używane na środowisku produkcyjnym.
-* estimation based on tests on an internal development team, building production applications.
-
-## Sponsors
+## Sponsorzy
@@ -59,9 +54,9 @@ The key features are:
-Other sponsors
+Inni sponsorzy
-## Opinions
+## Opinie
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
@@ -101,24 +96,24 @@ The key features are:
---
-## **Typer**, the FastAPI of CLIs
+## **Typer**, FastAPI aplikacji konsolowych
-If you are building a CLI app to be used in the terminal instead of a web API, check out **Typer**.
+Jeżeli tworzysz aplikacje CLI, która ma być używana w terminalu zamiast API, sprawdź **Typer**.
-**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
+**Typer** to młodsze rodzeństwo FastAPI. Jego celem jest pozostanie **FastAPI aplikacji konsolowych** . ⌨️ 🚀
-## Requirements
+## Wymagania
Python 3.6+
-FastAPI stands on the shoulders of giants:
+FastAPI oparty jest na:
-* Starlette for the web parts.
-* Pydantic for the data parts.
+* Starlette dla części webowej.
+* Pydantic dla części obsługujących dane.
-## Installation
+## Instalacja
async def...async def...uvicorn main:app --reload...uvicorn main:app --reload...ujson - for faster JSON "parsing".
-* email_validator - for email validation.
+* ujson - dla szybszego "parsowania" danych JSON.
+* email_validator - dla walidacji adresów email.
-Used by Starlette:
+Używane przez Starlette:
-* requests - Required if you want to use the `TestClient`.
-* aiofiles - Required if you want to use `FileResponse` or `StaticFiles`.
-* jinja2 - Required if you want to use the default template configuration.
-* python-multipart - Required if you want to support form "parsing", with `request.form()`.
-* itsdangerous - Required for `SessionMiddleware` support.
-* pyyaml - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
-* graphene - Required for `GraphQLApp` support.
-* ujson - Required if you want to use `UJSONResponse`.
+* requests - Wymagane jeżeli chcesz korzystać z `TestClient`.
+* aiofiles - Wymagane jeżeli chcesz korzystać z `FileResponse` albo `StaticFiles`.
+* jinja2 - Wymagane jeżeli chcesz używać domyślnej konfiguracji szablonów.
+* python-multipart - Wymagane jeżelich chcesz wsparcie "parsowania" formularzy, używając `request.form()`.
+* itsdangerous - Wymagany dla wsparcia `SessionMiddleware`.
+* pyyaml - Wymagane dla wsparcia `SchemaGenerator` z Starlette (z FastAPI prawdopodobnie tego nie potrzebujesz).
+* graphene - Wymagane dla wsparcia `GraphQLApp`.
+* ujson - Wymagane jeżeli chcesz korzystać z `UJSONResponse`.
-Used by FastAPI / Starlette:
+Używane przez FastAPI / Starlette:
-* uvicorn - for the server that loads and serves your application.
-* orjson - Required if you want to use `ORJSONResponse`.
+* uvicorn - jako serwer, który ładuje i obsługuje Twoją aplikację.
+* orjson - Wymagane jeżeli chcesz używać `ORJSONResponse`.
-You can install all of these with `pip install fastapi[all]`.
+Możesz zainstalować wszystkie te aplikacje przy pomocy `pip install fastapi[all]`.
-## License
+## Licencja
-This project is licensed under the terms of the MIT license.
+Ten projekt jest na licencji MIT.
diff --git a/docs/pl/mkdocs.yml b/docs/pl/mkdocs.yml
index 724dc2725..3c1351a12 100644
--- a/docs/pl/mkdocs.yml
+++ b/docs/pl/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/pt/mkdocs.yml b/docs/pt/mkdocs.yml
index f36ecf49d..f202f306d 100644
--- a/docs/pt/mkdocs.yml
+++ b/docs/pt/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/ru/mkdocs.yml b/docs/ru/mkdocs.yml
index eb2597de7..6e17c287e 100644
--- a/docs/ru/mkdocs.yml
+++ b/docs/ru/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/sq/mkdocs.yml b/docs/sq/mkdocs.yml
index 5afe0a665..d9c3dad4c 100644
--- a/docs/sq/mkdocs.yml
+++ b/docs/sq/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/tr/mkdocs.yml b/docs/tr/mkdocs.yml
index 40ba6d522..f6ed7f5b9 100644
--- a/docs/tr/mkdocs.yml
+++ b/docs/tr/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/uk/mkdocs.yml b/docs/uk/mkdocs.yml
index a116d90a3..d0de8cc0e 100644
--- a/docs/uk/mkdocs.yml
+++ b/docs/uk/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
diff --git a/docs/zh/docs/help-fastapi.md b/docs/zh/docs/help-fastapi.md
index 99e37b7c1..6f3f5159b 100644
--- a/docs/zh/docs/help-fastapi.md
+++ b/docs/zh/docs/help-fastapi.md
@@ -1,107 +1,149 @@
-# 帮助 FastAPI - 获取帮助
+# 帮助 FastAPI 与求助
-你喜欢 **FastAPI** 吗?
+您喜欢 **FastAPI** 吗?
-您愿意去帮助 FastAPI,帮助其他用户以及作者吗?
+想帮助 FastAPI?其它用户?还有项目作者?
-或者你想要获得有关 **FastAPI** 的帮助?
+或要求助怎么使用 **FastAPI**?
-下面是一些非常简单的方式去提供帮助(有些只需单击一两次链接)。
+以下几种帮助的方式都非常简单(有些只需要点击一两下鼠标)。
-以及几种获取帮助的途径。
+求助的渠道也很多。
-## 在 GitHub 上 Star **FastAPI**
+## 订阅新闻邮件
-你可以在 GitHub 上 "star" FastAPI(点击右上角的 star 按钮):https://github.com/tiangolo/fastapi。
+您可以订阅 [**FastAPI 和它的小伙伴** 新闻邮件](/newsletter/){.internal-link target=_blank}(不会经常收到)
-通过添加 star,其他用户将会更容易发现 FastAPI,并了解已经有许多人认为它有用。
+* FastAPI 及其小伙伴的新闻 🚀
+* 指南 📝
+* 功能 ✨
+* 破坏性更改 🚨
+* 开发技巧 ✅
-## Watch GitHub 仓库的版本发布
+## 在推特上关注 FastAPI
-你可以在 GitHub 上 "watch" FastAPI(点击右上角的 watch 按钮):https://github.com/tiangolo/fastapi。
+在 **Twitter** 上关注 @fastapi 获取 **FastAPI** 的最新消息。🐦
-这时你可以选择 "Releases only" 选项。
+## 在 GitHub 上为 **FastAPI** 加星
-之后,只要有 **FastAPI** 的新版本(包含缺陷修复和新功能)发布,你都会(通过电子邮件)收到通知。
+您可以在 GitHub 上 **Star** FastAPI(只要点击右上角的星星就可以了): https://github.com/tiangolo/fastapi。⭐️
-## 加入聊天室
+**Star** 以后,其它用户就能更容易找到 FastAPI,并了解到已经有其他用户在使用它了。
-加入 Gitter 上的聊天室:https://gitter.im/tiangolo/fastapi。
+## 关注 GitHub 资源库的版本发布
-在这里你可以快速提问、帮助他人、分享想法等。
+您还可以在 GitHub 上 **Watch** FastAPI,(点击右上角的 **Watch** 按钮)https://github.com/tiangolo/fastapi。👀
-## 与作者联系
+您可以选择只关注发布(**Releases only**)。
-你可以联系 我 (Sebastián Ramírez / `tiangolo`) - FastAPI 的作者。
+这样,您就可以(在电子邮件里)接收到 **FastAPI** 新版发布的通知,及时了解 bug 修复与新功能。
-你可以:
+## 联系作者
-* 在 **GitHub** 上关注我。
- * 查看我创建的其他的可能对你有帮助的开源项目。
- * 关注我以了解我创建的新开源项目。
-* 在 **Twitter** 上关注我。
- * 告诉我你是如何使用 FastAPI 的(我很乐意听到)。
- * 提出问题。
-* 在 **Linkedin** 上联系我。
- * 与我交流。
- * 认可我的技能或推荐我 :)
-* 在 **Medium** 上阅读我写的文章(或关注我)。
- * 阅读我创建的其他想法,文章和工具。
- * 关注我以了解我发布的新内容。
+您可以联系项目作者,就是我(Sebastián Ramírez / `tiangolo`)。
-## 发布和 **FastAPI** 有关的推特
+您可以:
- 发布和 **FastAPI** 有关的推特 让我和其他人知道你为什么喜欢它。
+* 在 **GitHub** 上关注我
+ * 了解其它我创建的开源项目,或许对您会有帮助
+ * 关注我什么时候创建新的开源项目
+* 在 **Twitter** 上关注我
+ * 告诉我您使用 FastAPI(我非常乐意听到这种消息)
+ * 接收我发布公告或新工具的消息
+ * 您还可以关注@fastapi on Twitter,这是个独立的账号
+* 在**领英**上联系我
+ * 接收我发布公告或新工具的消息(虽然我用 Twitter 比较多)
+* 阅读我在 **Dev.to** 或 **Medium** 上的文章,或关注我
+ * 阅读我的其它想法、文章,了解我创建的工具
+ * 关注我,这样就可以随时看到我发布的新文章
-## 告诉我你正在如何使用 **FastAPI**
+## Tweet about **FastAPI**
-我很乐意听到有关 **FastAPI** 被如何使用、你喜欢它的哪一点、被投入使用的项目/公司等等信息。
+Tweet about **FastAPI** 让我和大家知道您为什么喜欢 FastAPI。🎉
-你可以通过以下平台让我知道:
-
-* **Twitter**。
-* **Linkedin**。
-* **Medium**。
+知道有人使用 **FastAPI**,我会很开心,我也想知道您为什么喜欢 FastAPI,以及您在什么项目/哪些公司使用 FastAPI,等等。
## 为 FastAPI 投票
-* 在 Slant 上为 **FastAPI** 投票。
+* 在 Slant 上为 **FastAPI** 投票
+* 在 AlternativeTo 上为 **FastAPI** 投票
-## 帮助他人解决 GitHub 的 issues
+## 在 GitHub 上帮助其他人解决问题
-你可以查看 已有的 issues 并尝试帮助其他人。
+您可以查看现有 issues,并尝试帮助其他人解决问题,说不定您能解决这些问题呢。🤓
-## Watch GitHub 仓库
+如果帮助很多人解决了问题,您就有可能成为 [FastAPI 的官方专家](fastapi-people.md#experts){.internal-link target=_blank}。🎉
-你可以在 GitHub 上 "watch" FastAPI(点击右上角的 "watch" 按钮):https://github.com/tiangolo/fastapi。
+## 监听 GitHub 资源库
-如果你选择的是 "Watching" 而不是 "Releases only" 选项,你会在其他人创建了新的 issue 时收到通知。
+您可以在 GitHub 上「监听」FastAPI(点击右上角的 "watch" 按钮): https://github.com/tiangolo/fastapi. 👀
-然后你可以尝试帮助他们解决这些 issue。
+如果您选择 "Watching" 而不是 "Releases only",有人创建新 Issue 时,您会接收到通知。
-## 创建 issue
+然后您就可以尝试并帮助他们解决问题。
-你可以在 GitHub 仓库中 创建一个新 issue 用来:
+## 创建 Issue
-* 报告 bug 或问题。
-* 提议新的特性。
-* 提问。
+您可以在 GitHub 资源库中创建 Issue,例如:
-## 创建 Pull Request
+* 提出**问题**或**意见**
+* 提出新**特性**建议
-你可以 创建一个 Pull Request 用来:
+**注意**:如果您创建 Issue,我会要求您也要帮助别的用户。😉
-* 纠正你在文档中发现的错别字。
-* 添加新的文档内容。
-* 修复已有的 bug 或问题。
-* 添加新的特性。
+## 创建 PR
+
+您可以创建 PR 为源代码做[贡献](contributing.md){.internal-link target=_blank},例如:
+
+* 修改文档错别字
+* 编辑这个文件,分享 FastAPI 的文章、视频、博客,不论是您自己的,还是您看到的都成
+ * 注意,添加的链接要放在对应区块的开头
+* [翻译文档](contributing.md#translations){.internal-link target=_blank}
+ * 审阅别人翻译的文档
+* 添加新的文档内容
+* 修复现有问题/Bug
+* 添加新功能
+
+## 加入聊天
+
+快加入 👥 Discord 聊天服务器 👥 和 FastAPI 社区里的小伙伴一起哈皮吧。
+
+!!! tip "提示"
+
+ 如有问题,请在 GitHub Issues 里提问,在这里更容易得到 [FastAPI 专家](fastapi-people.md#experts){.internal-link target=_blank}的帮助。
+
+ 聊天室仅供闲聊。
+
+我们之前还使用过 Gitter chat,但它不支持频道等高级功能,聊天也比较麻烦,所以现在推荐使用 Discord。
+
+### 别在聊天室里提问
+
+注意,聊天室更倾向于“闲聊”,经常有人会提出一些笼统得让人难以回答的问题,所以在这里提问一般没人回答。
+
+GitHub Issues 里提供了模板,指引您提出正确的问题,有利于获得优质的回答,甚至可能解决您还没有想到的问题。而且就算答疑解惑要耗费不少时间,我还是会尽量在 GitHub 里回答问题。但在聊天室里,我就没功夫这么做了。😅
+
+聊天室里的聊天内容也不如 GitHub 里好搜索,聊天里的问答很容易就找不到了。只有在 GitHub Issues 里的问答才能帮助您成为 [FastAPI 专家](fastapi-people.md#experts){.internal-link target=_blank},在 GitHub Issues 中为您带来更多关注。
+
+另一方面,聊天室里有成千上万的用户,在这里,您有很大可能遇到聊得来的人。😄
## 赞助作者
-你还可以通过 GitHub sponsors 在经济上支持作者(我)。
+您还可以通过 GitHub 赞助商资助本项目的作者(就是我)。
-这样你可以给我买杯咖啡☕️以示谢意😄。
+给我买杯咖啡 ☕️ 以示感谢 😄
+
+当然您也可以成为 FastAPI 的金牌或银牌赞助商。🏅🎉
+
+## 赞助 FastAPI 使用的工具
+
+如您在本文档中所见,FastAPI 站在巨人的肩膀上,它们分别是 Starlette 和 Pydantic。
+
+您还可以赞助:
+
+* Samuel Colvin (Pydantic)
+* Encode (Starlette, Uvicorn)
---
-感谢!
+谢谢!🚀
+
diff --git a/docs/zh/docs/tutorial/path-operation-configuration.md b/docs/zh/docs/tutorial/path-operation-configuration.md
new file mode 100644
index 000000000..ad0e08d30
--- /dev/null
+++ b/docs/zh/docs/tutorial/path-operation-configuration.md
@@ -0,0 +1,101 @@
+# 路径操作配置
+
+*路径操作装饰器*支持多种配置参数。
+
+!!! warning "警告"
+
+ 注意:以下参数应直接传递给**路径操作装饰器**,不能传递给*路径操作函数*。
+
+## `status_code` 状态码
+
+`status_code` 用于定义*路径操作*响应中的 HTTP 状态码。
+
+可以直接传递 `int` 代码, 比如 `404`。
+
+如果记不住数字码的涵义,也可以用 `status` 的快捷常量:
+
+```Python hl_lines="3 17"
+{!../../../docs_src/path_operation_configuration/tutorial001.py!}
+```
+
+状态码在响应中使用,并会被添加到 OpenAPI 概图。
+
+!!! note "技术细节"
+
+ 也可以使用 `from starlette import status` 导入状态码。
+
+ **FastAPI** 的`fastapi.status` 和 `starlette.status` 一样,只是快捷方式。实际上,`fastapi.status` 直接继承自 Starlette。
+
+## `tags` 参数
+
+`tags` 参数的值是由 `str` 组成的 `list` (一般只有一个 `str` ),`tags` 用于为*路径操作*添加标签:
+
+```Python hl_lines="17 22 27"
+{!../../../docs_src/path_operation_configuration/tutorial002.py!}
+```
+
+OpenAPI 概图会自动添加标签,供 API 文档接口使用:
+
+
+
+## `summary` 和 `description` 参数
+
+路径装饰器还支持 `summary` 和 `description` 这两个参数:
+
+```Python hl_lines="20-21"
+{!../../../docs_src/path_operation_configuration/tutorial003.py!}
+```
+
+## 文档字符串(`docstring`)
+
+描述内容比较长且占用多行时,可以在函数的 docstring 中声明*路径操作*的描述,**FastAPI** 支持从文档字符串中读取描述内容。
+
+文档字符串支持 Markdown,能正确解析和显示 Markdown 的内容,但要注意文档字符串的缩进。
+
+```Python hl_lines="19-27"
+{!../../../docs_src/path_operation_configuration/tutorial004.py!}
+```
+
+下图为 Markdown 文本在 API 文档中的显示效果:
+
+
+
+## 响应描述
+
+`response_description` 参数用于定义响应的描述说明:
+
+```Python hl_lines="21"
+{!../../../docs_src/path_operation_configuration/tutorial005.py!}
+```
+
+!!! info "说明"
+
+ 注意,`response_description` 只用于描述响应,`description` 一般则用于描述*路径操作*。
+
+!!! check "检查"
+
+ OpenAPI 规定每个*路径操作*都要有响应描述。
+
+ 如果没有定义响应描述,**FastAPI** 则自动生成内容为 "Successful response" 的响应描述。
+
+
+
+## 弃用*路径操作*
+
+`deprecated` 参数可以把*路径操作*标记为弃用,无需直接删除:
+
+```Python hl_lines="16"
+{!../../../docs_src/path_operation_configuration/tutorial006.py!}
+```
+
+API 文档会把该路径操作标记为弃用:
+
+
+
+下图显示了正常*路径操作*与弃用*路径操作* 的区别:
+
+
+
+## 小结
+
+通过传递参数给*路径操作装饰器* ,即可轻松地配置*路径操作*、添加元数据。
diff --git a/docs/zh/mkdocs.yml b/docs/zh/mkdocs.yml
index 33d40129a..1d050fddd 100644
--- a/docs/zh/mkdocs.yml
+++ b/docs/zh/mkdocs.yml
@@ -20,6 +20,7 @@ theme:
features:
- search.suggest
- search.highlight
+ - content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
@@ -77,6 +78,7 @@ nav:
- tutorial/request-files.md
- tutorial/request-forms-and-files.md
- tutorial/handling-errors.md
+ - tutorial/path-operation-configuration.md
- tutorial/body-updates.md
- 依赖项:
- tutorial/dependencies/index.md
diff --git a/docs_src/extending_openapi/tutorial003.py b/docs_src/extending_openapi/tutorial003.py
new file mode 100644
index 000000000..6c24ce758
--- /dev/null
+++ b/docs_src/extending_openapi/tutorial003.py
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"syntaxHighlight": False})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+ return {"message": f"Hello {username}"}
diff --git a/docs_src/extending_openapi/tutorial004.py b/docs_src/extending_openapi/tutorial004.py
new file mode 100644
index 000000000..cc569ce45
--- /dev/null
+++ b/docs_src/extending_openapi/tutorial004.py
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"syntaxHighlight.theme": "obsidian"})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+ return {"message": f"Hello {username}"}
diff --git a/docs_src/extending_openapi/tutorial005.py b/docs_src/extending_openapi/tutorial005.py
new file mode 100644
index 000000000..b4449f5c6
--- /dev/null
+++ b/docs_src/extending_openapi/tutorial005.py
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"deepLinking": False})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+ return {"message": f"Hello {username}"}
diff --git a/docs_src/path_operation_configuration/tutorial002b.py b/docs_src/path_operation_configuration/tutorial002b.py
new file mode 100644
index 000000000..d53b4d817
--- /dev/null
+++ b/docs_src/path_operation_configuration/tutorial002b.py
@@ -0,0 +1,20 @@
+from enum import Enum
+
+from fastapi import FastAPI
+
+app = FastAPI()
+
+
+class Tags(Enum):
+ items = "items"
+ users = "users"
+
+
+@app.get("/items/", tags=[Tags.items])
+async def get_items():
+ return ["Portal gun", "Plumbus"]
+
+
+@app.get("/users/", tags=[Tags.users])
+async def read_users():
+ return ["Rick", "Morty"]
diff --git a/docs_src/python_types/tutorial008.py b/docs_src/python_types/tutorial008.py
index a393385b0..9fb1043bb 100644
--- a/docs_src/python_types/tutorial008.py
+++ b/docs_src/python_types/tutorial008.py
@@ -1,4 +1,7 @@
-def process_items(prices: dict[str, float]):
+from typing import Dict
+
+
+def process_items(prices: Dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
diff --git a/docs_src/python_types/tutorial008_py39.py b/docs_src/python_types/tutorial008_py39.py
new file mode 100644
index 000000000..a393385b0
--- /dev/null
+++ b/docs_src/python_types/tutorial008_py39.py
@@ -0,0 +1,4 @@
+def process_items(prices: dict[str, float]):
+ for item_name, item_price in prices.items():
+ print(item_name)
+ print(item_price)
diff --git a/docs_src/query_params_str_validations/tutorial014.py b/docs_src/query_params_str_validations/tutorial014.py
new file mode 100644
index 000000000..fb50bc27b
--- /dev/null
+++ b/docs_src/query_params_str_validations/tutorial014.py
@@ -0,0 +1,15 @@
+from typing import Optional
+
+from fastapi import FastAPI, Query
+
+app = FastAPI()
+
+
+@app.get("/items/")
+async def read_items(
+ hidden_query: Optional[str] = Query(None, include_in_schema=False)
+):
+ if hidden_query:
+ return {"hidden_query": hidden_query}
+ else:
+ return {"hidden_query": "Not found"}
diff --git a/docs_src/query_params_str_validations/tutorial014_py310.py b/docs_src/query_params_str_validations/tutorial014_py310.py
new file mode 100644
index 000000000..7ae39c7f9
--- /dev/null
+++ b/docs_src/query_params_str_validations/tutorial014_py310.py
@@ -0,0 +1,11 @@
+from fastapi import FastAPI, Query
+
+app = FastAPI()
+
+
+@app.get("/items/")
+async def read_items(hidden_query: str | None = Query(None, include_in_schema=False)):
+ if hidden_query:
+ return {"hidden_query": hidden_query}
+ else:
+ return {"hidden_query": "Not found"}
diff --git a/docs_src/request_files/tutorial001.py b/docs_src/request_files/tutorial001.py
index fffb56af8..0fb1dd571 100644
--- a/docs_src/request_files/tutorial001.py
+++ b/docs_src/request_files/tutorial001.py
@@ -9,5 +9,5 @@ async def create_file(file: bytes = File(...)):
@app.post("/uploadfile/")
-async def create_upload_file(file: UploadFile = File(...)):
+async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
diff --git a/docs_src/request_files/tutorial001_02.py b/docs_src/request_files/tutorial001_02.py
new file mode 100644
index 000000000..26a4c9cbf
--- /dev/null
+++ b/docs_src/request_files/tutorial001_02.py
@@ -0,0 +1,21 @@
+from typing import Optional
+
+from fastapi import FastAPI, File, UploadFile
+
+app = FastAPI()
+
+
+@app.post("/files/")
+async def create_file(file: Optional[bytes] = File(None)):
+ if not file:
+ return {"message": "No file sent"}
+ else:
+ return {"file_size": len(file)}
+
+
+@app.post("/uploadfile/")
+async def create_upload_file(file: Optional[UploadFile] = None):
+ if not file:
+ return {"message": "No upload file sent"}
+ else:
+ return {"filename": file.filename}
diff --git a/docs_src/request_files/tutorial001_02_py310.py b/docs_src/request_files/tutorial001_02_py310.py
new file mode 100644
index 000000000..0e576251b
--- /dev/null
+++ b/docs_src/request_files/tutorial001_02_py310.py
@@ -0,0 +1,19 @@
+from fastapi import FastAPI, File, UploadFile
+
+app = FastAPI()
+
+
+@app.post("/files/")
+async def create_file(file: bytes | None = File(None)):
+ if not file:
+ return {"message": "No file sent"}
+ else:
+ return {"file_size": len(file)}
+
+
+@app.post("/uploadfile/")
+async def create_upload_file(file: UploadFile | None = None):
+ if not file:
+ return {"message": "No upload file sent"}
+ else:
+ return {"filename": file.filename}
diff --git a/docs_src/request_files/tutorial001_03.py b/docs_src/request_files/tutorial001_03.py
new file mode 100644
index 000000000..abcac9e4c
--- /dev/null
+++ b/docs_src/request_files/tutorial001_03.py
@@ -0,0 +1,15 @@
+from fastapi import FastAPI, File, UploadFile
+
+app = FastAPI()
+
+
+@app.post("/files/")
+async def create_file(file: bytes = File(..., description="A file read as bytes")):
+ return {"file_size": len(file)}
+
+
+@app.post("/uploadfile/")
+async def create_upload_file(
+ file: UploadFile = File(..., description="A file read as UploadFile")
+):
+ return {"filename": file.filename}
diff --git a/docs_src/request_files/tutorial002.py b/docs_src/request_files/tutorial002.py
index 6fdf16a75..94abb7c6c 100644
--- a/docs_src/request_files/tutorial002.py
+++ b/docs_src/request_files/tutorial002.py
@@ -12,7 +12,7 @@ async def create_files(files: List[bytes] = File(...)):
@app.post("/uploadfiles/")
-async def create_upload_files(files: List[UploadFile] = File(...)):
+async def create_upload_files(files: List[UploadFile]):
return {"filenames": [file.filename for file in files]}
diff --git a/docs_src/request_files/tutorial002_py39.py b/docs_src/request_files/tutorial002_py39.py
index 26cd56769..2779618bd 100644
--- a/docs_src/request_files/tutorial002_py39.py
+++ b/docs_src/request_files/tutorial002_py39.py
@@ -10,7 +10,7 @@ async def create_files(files: list[bytes] = File(...)):
@app.post("/uploadfiles/")
-async def create_upload_files(files: list[UploadFile] = File(...)):
+async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
diff --git a/docs_src/request_files/tutorial003.py b/docs_src/request_files/tutorial003.py
new file mode 100644
index 000000000..4a91b7a8b
--- /dev/null
+++ b/docs_src/request_files/tutorial003.py
@@ -0,0 +1,37 @@
+from typing import List
+
+from fastapi import FastAPI, File, UploadFile
+from fastapi.responses import HTMLResponse
+
+app = FastAPI()
+
+
+@app.post("/files/")
+async def create_files(
+ files: List[bytes] = File(..., description="Multiple files as bytes")
+):
+ return {"file_sizes": [len(file) for file in files]}
+
+
+@app.post("/uploadfiles/")
+async def create_upload_files(
+ files: List[UploadFile] = File(..., description="Multiple files as UploadFile")
+):
+ return {"filenames": [file.filename for file in files]}
+
+
+@app.get("/")
+async def main():
+ content = """
+
+
+
+
+ """
+ return HTMLResponse(content=content)
diff --git a/docs_src/request_files/tutorial003_py39.py b/docs_src/request_files/tutorial003_py39.py
new file mode 100644
index 000000000..d853f48d1
--- /dev/null
+++ b/docs_src/request_files/tutorial003_py39.py
@@ -0,0 +1,35 @@
+from fastapi import FastAPI, File, UploadFile
+from fastapi.responses import HTMLResponse
+
+app = FastAPI()
+
+
+@app.post("/files/")
+async def create_files(
+ files: list[bytes] = File(..., description="Multiple files as bytes")
+):
+ return {"file_sizes": [len(file) for file in files]}
+
+
+@app.post("/uploadfiles/")
+async def create_upload_files(
+ files: list[UploadFile] = File(..., description="Multiple files as UploadFile")
+):
+ return {"filenames": [file.filename for file in files]}
+
+
+@app.get("/")
+async def main():
+ content = """
+
+
+
+
+ """
+ return HTMLResponse(content=content)
diff --git a/fastapi/__init__.py b/fastapi/__init__.py
index 5b735aed5..8718788fa 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.71.0"
+__version__ = "0.73.0"
from starlette import status as status
diff --git a/fastapi/applications.py b/fastapi/applications.py
index 0c25026e2..dbfd76fb9 100644
--- a/fastapi/applications.py
+++ b/fastapi/applications.py
@@ -1,3 +1,4 @@
+from enum import Enum
from typing import Any, Callable, Coroutine, Dict, List, Optional, Sequence, Type, Union
from fastapi import routing
@@ -65,6 +66,7 @@ class FastAPI(Starlette):
callbacks: Optional[List[BaseRoute]] = None,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
+ swagger_ui_parameters: Optional[Dict[str, Any]] = None,
**extra: Any,
) -> None:
self._debug: bool = debug
@@ -120,6 +122,7 @@ class FastAPI(Starlette):
self.redoc_url = redoc_url
self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url
self.swagger_ui_init_oauth = swagger_ui_init_oauth
+ self.swagger_ui_parameters = swagger_ui_parameters
self.extra = extra
self.dependency_overrides: Dict[Callable[..., Any], Callable[..., Any]] = {}
@@ -174,6 +177,7 @@ class FastAPI(Starlette):
title=self.title + " - Swagger UI",
oauth2_redirect_url=oauth2_redirect_url,
init_oauth=self.swagger_ui_init_oauth,
+ swagger_ui_parameters=self.swagger_ui_parameters,
)
self.add_route(self.docs_url, swagger_ui_html, include_in_schema=False)
@@ -216,7 +220,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -270,7 +274,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -339,7 +343,7 @@ class FastAPI(Starlette):
router: routing.APIRouter,
*,
prefix: str = "",
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
@@ -365,7 +369,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -416,7 +420,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -467,7 +471,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -518,7 +522,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -569,7 +573,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -620,7 +624,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -671,7 +675,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -722,7 +726,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
diff --git a/fastapi/datastructures.py b/fastapi/datastructures.py
index b13171287..b20a25ab6 100644
--- a/fastapi/datastructures.py
+++ b/fastapi/datastructures.py
@@ -1,4 +1,4 @@
-from typing import Any, Callable, Iterable, Type, TypeVar
+from typing import Any, Callable, Dict, Iterable, Type, TypeVar
from starlette.datastructures import URL as URL # noqa: F401
from starlette.datastructures import Address as Address # noqa: F401
@@ -20,6 +20,10 @@ class UploadFile(StarletteUploadFile):
raise ValueError(f"Expected UploadFile, received: {type(v)}")
return v
+ @classmethod
+ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+ field_schema.update({"type": "string", "format": "binary"})
+
class DefaultPlaceholder:
"""
diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index 35ba44aab..d4028d067 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -390,6 +390,8 @@ def get_param_field(
field.required = required
if not had_schema and not is_scalar_field(field=field):
field.field_info = params.Body(field_info.default)
+ if not had_schema and lenient_issubclass(field.type_, UploadFile):
+ field.field_info = params.File(field_info.default)
return field
@@ -701,25 +703,6 @@ def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
return missing_field_error
-def get_schema_compatible_field(*, field: ModelField) -> ModelField:
- out_field = field
- if lenient_issubclass(field.type_, UploadFile):
- use_type: type = bytes
- if field.shape in sequence_shapes:
- use_type = List[bytes]
- out_field = create_response_field(
- name=field.name,
- type_=use_type,
- class_validators=field.class_validators,
- model_config=field.model_config,
- default=field.default,
- required=field.required,
- alias=field.alias,
- field_info=field.field_info,
- )
- return out_field
-
-
def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
flat_dependant = get_flat_dependant(dependant)
if not flat_dependant.body_params:
@@ -729,9 +712,8 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
embed = getattr(field_info, "embed", None)
body_param_names_set = {param.name for param in flat_dependant.body_params}
if len(body_param_names_set) == 1 and not embed:
- final_field = get_schema_compatible_field(field=first_param)
- check_file_field(final_field)
- return final_field
+ check_file_field(first_param)
+ return first_param
# If one field requires to embed, all have to be embedded
# in case a sub-dependency is evaluated with a single unique body field
# That is combined (embedded) with other body fields
@@ -740,7 +722,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
model_name = "Body_" + name
BodyModel: Type[BaseModel] = create_model(model_name)
for f in flat_dependant.body_params:
- BodyModel.__fields__[f.name] = get_schema_compatible_field(field=f)
+ BodyModel.__fields__[f.name] = f
required = any(True for f in flat_dependant.body_params if f.required)
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
diff --git a/fastapi/encoders.py b/fastapi/encoders.py
index 3f599c9fa..4b7ffe313 100644
--- a/fastapi/encoders.py
+++ b/fastapi/encoders.py
@@ -34,9 +34,17 @@ def jsonable_encoder(
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
- custom_encoder: Dict[Any, Callable[[Any], Any]] = {},
+ custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None,
sqlalchemy_safe: bool = True,
) -> Any:
+ custom_encoder = custom_encoder or {}
+ if custom_encoder:
+ if type(obj) in custom_encoder:
+ return custom_encoder[type(obj)](obj)
+ else:
+ for encoder_type, encoder_instance in custom_encoder.items():
+ if isinstance(obj, encoder_type):
+ return encoder_instance(obj)
if include is not None and not isinstance(include, (set, dict)):
include = set(include)
if exclude is not None and not isinstance(exclude, (set, dict)):
@@ -118,14 +126,6 @@ def jsonable_encoder(
)
return encoded_list
- if custom_encoder:
- if type(obj) in custom_encoder:
- return custom_encoder[type(obj)](obj)
- else:
- for encoder_type, encoder in custom_encoder.items():
- if isinstance(obj, encoder_type):
- return encoder(obj)
-
if type(obj) in ENCODERS_BY_TYPE:
return ENCODERS_BY_TYPE[type(obj)](obj)
for encoder, classes_tuple in encoders_by_class_tuples.items():
diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py
index fd22e4e8c..1be90d188 100644
--- a/fastapi/openapi/docs.py
+++ b/fastapi/openapi/docs.py
@@ -4,6 +4,14 @@ from typing import Any, Dict, Optional
from fastapi.encoders import jsonable_encoder
from starlette.responses import HTMLResponse
+swagger_ui_default_parameters = {
+ "dom_id": "#swagger-ui",
+ "layout": "BaseLayout",
+ "deepLinking": True,
+ "showExtensions": True,
+ "showCommonExtensions": True,
+}
+
def get_swagger_ui_html(
*,
@@ -14,7 +22,11 @@ def get_swagger_ui_html(
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
oauth2_redirect_url: Optional[str] = None,
init_oauth: Optional[Dict[str, Any]] = None,
+ swagger_ui_parameters: Optional[Dict[str, Any]] = None,
) -> HTMLResponse:
+ current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
+ if swagger_ui_parameters:
+ current_swagger_ui_parameters.update(swagger_ui_parameters)
html = f"""
@@ -34,19 +46,17 @@ def get_swagger_ui_html(
url: '{openapi_url}',
"""
+ for key, value in current_swagger_ui_parameters.items():
+ html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n"
+
if oauth2_redirect_url:
html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"
html += """
- dom_id: '#swagger-ui',
- presets: [
+ presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
- layout: "BaseLayout",
- deepLinking: true,
- showExtensions: true,
- showCommonExtensions: true
})"""
if init_oauth:
diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py
index 361c75005..9c6598d2d 100644
--- a/fastapi/openapi/models.py
+++ b/fastapi/openapi/models.py
@@ -123,7 +123,7 @@ class Schema(BaseModel):
oneOf: Optional[List["Schema"]] = None
anyOf: Optional[List["Schema"]] = None
not_: Optional["Schema"] = Field(None, alias="not")
- items: Optional["Schema"] = None
+ items: Optional[Union["Schema", List["Schema"]]] = None
properties: Optional[Dict[str, "Schema"]] = None
additionalProperties: Optional[Union["Schema", Reference, bool]] = None
description: Optional[str] = None
diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py
index 0e73e21bf..aff76b15e 100644
--- a/fastapi/openapi/utils.py
+++ b/fastapi/openapi/utils.py
@@ -92,6 +92,8 @@ def get_openapi_operation_parameters(
for param in all_route_params:
field_info = param.field_info
field_info = cast(Param, field_info)
+ if not field_info.include_in_schema:
+ continue
parameter = {
"name": param.alias,
"in": field_info.in_.value,
diff --git a/fastapi/param_functions.py b/fastapi/param_functions.py
index ff65d7271..a553a1461 100644
--- a/fastapi/param_functions.py
+++ b/fastapi/param_functions.py
@@ -20,6 +20,7 @@ def Path( # noqa: N802
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
) -> Any:
return params.Path(
@@ -37,6 +38,7 @@ def Path( # noqa: N802
example=example,
examples=examples,
deprecated=deprecated,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -57,6 +59,7 @@ def Query( # noqa: N802
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
) -> Any:
return params.Query(
@@ -74,6 +77,7 @@ def Query( # noqa: N802
example=example,
examples=examples,
deprecated=deprecated,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -95,6 +99,7 @@ def Header( # noqa: N802
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
) -> Any:
return params.Header(
@@ -113,6 +118,7 @@ def Header( # noqa: N802
example=example,
examples=examples,
deprecated=deprecated,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -133,6 +139,7 @@ def Cookie( # noqa: N802
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
) -> Any:
return params.Cookie(
@@ -150,6 +157,7 @@ def Cookie( # noqa: N802
example=example,
examples=examples,
deprecated=deprecated,
+ include_in_schema=include_in_schema,
**extra,
)
diff --git a/fastapi/params.py b/fastapi/params.py
index 3cab98b78..042bbd42f 100644
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -31,11 +31,13 @@ class Param(FieldInfo):
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
):
self.deprecated = deprecated
self.example = example
self.examples = examples
+ self.include_in_schema = include_in_schema
super().__init__(
default,
alias=alias,
@@ -75,6 +77,7 @@ class Path(Param):
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
):
self.in_ = self.in_
@@ -93,6 +96,7 @@ class Path(Param):
deprecated=deprecated,
example=example,
examples=examples,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -117,6 +121,7 @@ class Query(Param):
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
):
super().__init__(
@@ -134,6 +139,7 @@ class Query(Param):
deprecated=deprecated,
example=example,
examples=examples,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -159,6 +165,7 @@ class Header(Param):
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
):
self.convert_underscores = convert_underscores
@@ -177,6 +184,7 @@ class Header(Param):
deprecated=deprecated,
example=example,
examples=examples,
+ include_in_schema=include_in_schema,
**extra,
)
@@ -201,6 +209,7 @@ class Cookie(Param):
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
+ include_in_schema: bool = True,
**extra: Any,
):
super().__init__(
@@ -218,6 +227,7 @@ class Cookie(Param):
deprecated=deprecated,
example=example,
examples=examples,
+ include_in_schema=include_in_schema,
**extra,
)
diff --git a/fastapi/routing.py b/fastapi/routing.py
index 4b4119014..880e1f51e 100644
--- a/fastapi/routing.py
+++ b/fastapi/routing.py
@@ -1,9 +1,9 @@
import asyncio
import dataclasses
import email.message
-import enum
import inspect
import json
+from enum import Enum, IntEnum
from typing import (
Any,
Callable,
@@ -305,7 +305,7 @@ class APIRoute(routing.Route):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -330,7 +330,7 @@ class APIRoute(routing.Route):
openapi_extra: Optional[Dict[str, Any]] = None,
) -> None:
# normalise enums e.g. http.HTTPStatus
- if isinstance(status_code, enum.IntEnum):
+ if isinstance(status_code, IntEnum):
status_code = int(status_code)
self.path = path
self.endpoint = endpoint
@@ -438,7 +438,7 @@ class APIRouter(routing.Router):
self,
*,
prefix: str = "",
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
default_response_class: Type[Response] = Default(JSONResponse),
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
@@ -468,7 +468,7 @@ class APIRouter(routing.Router):
self.prefix = prefix
if tags:
assert not isinstance(tags, str), """tags should not be a string: please use ["tag"] instead of "tag"."""
- self.tags: List[str] = tags or []
+ self.tags: List[Union[str, Enum]] = tags or []
self.dependencies = list(dependencies or []) or []
self.deprecated = deprecated
self.include_in_schema = include_in_schema
@@ -485,7 +485,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -560,7 +560,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -637,7 +637,7 @@ class APIRouter(routing.Router):
router: "APIRouter",
*,
prefix: str = "",
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
default_response_class: Type[Response] = Default(JSONResponse),
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
@@ -742,7 +742,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -794,7 +794,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -846,7 +846,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -898,7 +898,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -950,7 +950,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -1002,7 +1002,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -1054,7 +1054,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@@ -1106,7 +1106,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
- tags: Optional[List[str]] = None,
+ tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py
index e2aa8adf8..fa82b5ea8 100644
--- a/tests/test_jsonable_encoder.py
+++ b/tests/test_jsonable_encoder.py
@@ -161,6 +161,21 @@ def test_custom_encoders():
assert encoded_instance["dt_field"] == instance.dt_field.isoformat()
+def test_custom_enum_encoders():
+ def custom_enum_encoder(v: Enum):
+ return v.value.lower()
+
+ class MyEnum(Enum):
+ ENUM_VAL_1 = "ENUM_VAL_1"
+
+ instance = MyEnum.ENUM_VAL_1
+
+ encoded_instance = jsonable_encoder(
+ instance, custom_encoder={MyEnum: custom_enum_encoder}
+ )
+ assert encoded_instance == custom_enum_encoder(instance)
+
+
def test_encode_model_with_path(model_with_path):
if isinstance(model_with_path.path, PureWindowsPath):
expected = "\\foo\\bar"
diff --git a/tests/test_param_include_in_schema.py b/tests/test_param_include_in_schema.py
new file mode 100644
index 000000000..4eaac72d8
--- /dev/null
+++ b/tests/test_param_include_in_schema.py
@@ -0,0 +1,239 @@
+from typing import Optional
+
+import pytest
+from fastapi import Cookie, FastAPI, Header, Path, Query
+from fastapi.testclient import TestClient
+
+app = FastAPI()
+
+
+@app.get("/hidden_cookie")
+async def hidden_cookie(
+ hidden_cookie: Optional[str] = Cookie(None, include_in_schema=False)
+):
+ return {"hidden_cookie": hidden_cookie}
+
+
+@app.get("/hidden_header")
+async def hidden_header(
+ hidden_header: Optional[str] = Header(None, include_in_schema=False)
+):
+ return {"hidden_header": hidden_header}
+
+
+@app.get("/hidden_path/{hidden_path}")
+async def hidden_path(hidden_path: str = Path(..., include_in_schema=False)):
+ return {"hidden_path": hidden_path}
+
+
+@app.get("/hidden_query")
+async def hidden_query(
+ hidden_query: Optional[str] = Query(None, include_in_schema=False)
+):
+ return {"hidden_query": hidden_query}
+
+
+client = TestClient(app)
+
+openapi_shema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/hidden_cookie": {
+ "get": {
+ "summary": "Hidden Cookie",
+ "operationId": "hidden_cookie_hidden_cookie_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/hidden_header": {
+ "get": {
+ "summary": "Hidden Header",
+ "operationId": "hidden_header_hidden_header_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/hidden_path/{hidden_path}": {
+ "get": {
+ "summary": "Hidden Path",
+ "operationId": "hidden_path_hidden_path__hidden_path__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/hidden_query": {
+ "get": {
+ "summary": "Hidden Query",
+ "operationId": "hidden_query_hidden_query_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"type": "string"},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == openapi_shema
+
+
+@pytest.mark.parametrize(
+ "path,cookies,expected_status,expected_response",
+ [
+ (
+ "/hidden_cookie",
+ {},
+ 200,
+ {"hidden_cookie": None},
+ ),
+ (
+ "/hidden_cookie",
+ {"hidden_cookie": "somevalue"},
+ 200,
+ {"hidden_cookie": "somevalue"},
+ ),
+ ],
+)
+def test_hidden_cookie(path, cookies, expected_status, expected_response):
+ response = client.get(path, cookies=cookies)
+ assert response.status_code == expected_status
+ assert response.json() == expected_response
+
+
+@pytest.mark.parametrize(
+ "path,headers,expected_status,expected_response",
+ [
+ (
+ "/hidden_header",
+ {},
+ 200,
+ {"hidden_header": None},
+ ),
+ (
+ "/hidden_header",
+ {"Hidden-Header": "somevalue"},
+ 200,
+ {"hidden_header": "somevalue"},
+ ),
+ ],
+)
+def test_hidden_header(path, headers, expected_status, expected_response):
+ response = client.get(path, headers=headers)
+ assert response.status_code == expected_status
+ assert response.json() == expected_response
+
+
+def test_hidden_path():
+ response = client.get("/hidden_path/hidden_path")
+ assert response.status_code == 200
+ assert response.json() == {"hidden_path": "hidden_path"}
+
+
+@pytest.mark.parametrize(
+ "path,expected_status,expected_response",
+ [
+ (
+ "/hidden_query",
+ 200,
+ {"hidden_query": None},
+ ),
+ (
+ "/hidden_query?hidden_query=somevalue",
+ 200,
+ {"hidden_query": "somevalue"},
+ ),
+ ],
+)
+def test_hidden_query(path, expected_status, expected_response):
+ response = client.get(path)
+ assert response.status_code == expected_status
+ assert response.json() == expected_response
diff --git a/tests/test_tuples.py b/tests/test_tuples.py
new file mode 100644
index 000000000..4cd5ee3af
--- /dev/null
+++ b/tests/test_tuples.py
@@ -0,0 +1,267 @@
+from typing import List, Tuple
+
+from fastapi import FastAPI, Form
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class ItemGroup(BaseModel):
+ items: List[Tuple[str, str]]
+
+
+class Coordinate(BaseModel):
+ x: float
+ y: float
+
+
+@app.post("/model-with-tuple/")
+def post_model_with_tuple(item_group: ItemGroup):
+ return item_group
+
+
+@app.post("/tuple-of-models/")
+def post_tuple_of_models(square: Tuple[Coordinate, Coordinate]):
+ return square
+
+
+@app.post("/tuple-form/")
+def hello(values: Tuple[int, int] = Form(...)):
+ return values
+
+
+client = TestClient(app)
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/model-with-tuple/": {
+ "post": {
+ "summary": "Post Model With Tuple",
+ "operationId": "post_model_with_tuple_model_with_tuple__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/ItemGroup"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/tuple-of-models/": {
+ "post": {
+ "summary": "Post Tuple Of Models",
+ "operationId": "post_tuple_of_models_tuple_of_models__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Square",
+ "maxItems": 2,
+ "minItems": 2,
+ "type": "array",
+ "items": [
+ {"$ref": "#/components/schemas/Coordinate"},
+ {"$ref": "#/components/schemas/Coordinate"},
+ ],
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/tuple-form/": {
+ "post": {
+ "summary": "Hello",
+ "operationId": "hello_tuple_form__post",
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_hello_tuple_form__post"
+ }
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "Body_hello_tuple_form__post": {
+ "title": "Body_hello_tuple_form__post",
+ "required": ["values"],
+ "type": "object",
+ "properties": {
+ "values": {
+ "title": "Values",
+ "maxItems": 2,
+ "minItems": 2,
+ "type": "array",
+ "items": [{"type": "integer"}, {"type": "integer"}],
+ }
+ },
+ },
+ "Coordinate": {
+ "title": "Coordinate",
+ "required": ["x", "y"],
+ "type": "object",
+ "properties": {
+ "x": {"title": "X", "type": "number"},
+ "y": {"title": "Y", "type": "number"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ItemGroup": {
+ "title": "ItemGroup",
+ "required": ["items"],
+ "type": "object",
+ "properties": {
+ "items": {
+ "title": "Items",
+ "type": "array",
+ "items": {
+ "maxItems": 2,
+ "minItems": 2,
+ "type": "array",
+ "items": [{"type": "string"}, {"type": "string"}],
+ },
+ }
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"type": "string"},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+def test_model_with_tuple_valid():
+ data = {"items": [["foo", "bar"], ["baz", "whatelse"]]}
+ response = client.post("/model-with-tuple/", json=data)
+ assert response.status_code == 200, response.text
+ assert response.json() == data
+
+
+def test_model_with_tuple_invalid():
+ data = {"items": [["foo", "bar"], ["baz", "whatelse", "too", "much"]]}
+ response = client.post("/model-with-tuple/", json=data)
+ assert response.status_code == 422, response.text
+
+ data = {"items": [["foo", "bar"], ["baz"]]}
+ response = client.post("/model-with-tuple/", json=data)
+ assert response.status_code == 422, response.text
+
+
+def test_tuple_with_model_valid():
+ data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}]
+ response = client.post("/tuple-of-models/", json=data)
+ assert response.status_code == 200, response.text
+ assert response.json() == data
+
+
+def test_tuple_with_model_invalid():
+ data = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}]
+ response = client.post("/tuple-of-models/", json=data)
+ assert response.status_code == 422, response.text
+
+ data = [{"x": 1, "y": 2}]
+ response = client.post("/tuple-of-models/", json=data)
+ assert response.status_code == 422, response.text
+
+
+def test_tuple_form_valid():
+ response = client.post("/tuple-form/", data=[("values", "1"), ("values", "2")])
+ assert response.status_code == 200, response.text
+ assert response.json() == [1, 2]
+
+
+def test_tuple_form_invalid():
+ response = client.post(
+ "/tuple-form/", data=[("values", "1"), ("values", "2"), ("values", "3")]
+ )
+ assert response.status_code == 422, response.text
+
+ response = client.post("/tuple-form/", data=[("values", "1")])
+ assert response.status_code == 422, response.text
diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial003.py b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py
new file mode 100644
index 000000000..0184dd9f8
--- /dev/null
+++ b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py
@@ -0,0 +1,41 @@
+from fastapi.testclient import TestClient
+
+from docs_src.extending_openapi.tutorial003 import app
+
+client = TestClient(app)
+
+
+def test_swagger_ui():
+ response = client.get("/docs")
+ assert response.status_code == 200, response.text
+ assert (
+ '"syntaxHighlight": false' in response.text
+ ), "syntaxHighlight should be included and converted to JSON"
+ assert (
+ '"dom_id": "#swagger-ui"' in response.text
+ ), "default configs should be preserved"
+ assert "presets: [" in response.text, "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.presets.apis," in response.text
+ ), "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"layout": "BaseLayout",' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"deepLinking": true,' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showExtensions": true,' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showCommonExtensions": true,' in response.text
+ ), "default configs should be preserved"
+
+
+def test_get_users():
+ response = client.get("/users/foo")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Hello foo"}
diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial004.py b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py
new file mode 100644
index 000000000..4f7615126
--- /dev/null
+++ b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py
@@ -0,0 +1,44 @@
+from fastapi.testclient import TestClient
+
+from docs_src.extending_openapi.tutorial004 import app
+
+client = TestClient(app)
+
+
+def test_swagger_ui():
+ response = client.get("/docs")
+ assert response.status_code == 200, response.text
+ assert (
+ '"syntaxHighlight": false' not in response.text
+ ), "not used parameters should not be included"
+ assert (
+ '"syntaxHighlight.theme": "obsidian"' in response.text
+ ), "parameters with middle dots should be included in a JSON compatible way"
+ assert (
+ '"dom_id": "#swagger-ui"' in response.text
+ ), "default configs should be preserved"
+ assert "presets: [" in response.text, "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.presets.apis," in response.text
+ ), "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"layout": "BaseLayout",' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"deepLinking": true,' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showExtensions": true,' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showCommonExtensions": true,' in response.text
+ ), "default configs should be preserved"
+
+
+def test_get_users():
+ response = client.get("/users/foo")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Hello foo"}
diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial005.py b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py
new file mode 100644
index 000000000..24aeb93db
--- /dev/null
+++ b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py
@@ -0,0 +1,44 @@
+from fastapi.testclient import TestClient
+
+from docs_src.extending_openapi.tutorial005 import app
+
+client = TestClient(app)
+
+
+def test_swagger_ui():
+ response = client.get("/docs")
+ assert response.status_code == 200, response.text
+ assert (
+ '"deepLinking": false,' in response.text
+ ), "overridden configs should be preserved"
+ assert (
+ '"deepLinking": true' not in response.text
+ ), "overridden configs should not include the old value"
+ assert (
+ '"syntaxHighlight": false' not in response.text
+ ), "not used parameters should not be included"
+ assert (
+ '"dom_id": "#swagger-ui"' in response.text
+ ), "default configs should be preserved"
+ assert "presets: [" in response.text, "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.presets.apis," in response.text
+ ), "default configs should be preserved"
+ assert (
+ "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"layout": "BaseLayout",' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showExtensions": true,' in response.text
+ ), "default configs should be preserved"
+ assert (
+ '"showCommonExtensions": true,' in response.text
+ ), "default configs should be preserved"
+
+
+def test_get_users():
+ response = client.get("/users/foo")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Hello foo"}
diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial002b.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial002b.py
new file mode 100644
index 000000000..be9f2afec
--- /dev/null
+++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial002b.py
@@ -0,0 +1,56 @@
+from fastapi.testclient import TestClient
+
+from docs_src.path_operation_configuration.tutorial002b import app
+
+client = TestClient(app)
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "tags": ["items"],
+ "summary": "Get Items",
+ "operationId": "get_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ },
+ "/users/": {
+ "get": {
+ "tags": ["users"],
+ "summary": "Read Users",
+ "operationId": "read_users_users__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ },
+ },
+}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+def test_get_items():
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == ["Portal gun", "Plumbus"]
+
+
+def test_get_users():
+ response = client.get("/users/")
+ assert response.status_code == 200, response.text
+ assert response.json() == ["Rick", "Morty"]
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py
new file mode 100644
index 000000000..98ae5a684
--- /dev/null
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014.py
@@ -0,0 +1,82 @@
+from fastapi.testclient import TestClient
+
+from docs_src.query_params_str_validations.tutorial014 import app
+
+client = TestClient(app)
+
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"type": "string"},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+def test_hidden_query():
+ response = client.get("/items?hidden_query=somevalue")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"hidden_query": "somevalue"}
+
+
+def test_no_hidden_query():
+ response = client.get("/items")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"hidden_query": "Not found"}
diff --git a/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py
new file mode 100644
index 000000000..33f3d5f77
--- /dev/null
+++ b/tests/test_tutorial/test_query_params_str_validations/test_tutorial014_py310.py
@@ -0,0 +1,91 @@
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"type": "string"},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+}
+
+
+@pytest.fixture(name="client")
+def get_client():
+ from docs_src.query_params_str_validations.tutorial014_py310 import app
+
+ client = TestClient(app)
+ return client
+
+
+@needs_py310
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+@needs_py310
+def test_hidden_query(client: TestClient):
+ response = client.get("/items?hidden_query=somevalue")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"hidden_query": "somevalue"}
+
+
+@needs_py310
+def test_no_hidden_query(client: TestClient):
+ response = client.get("/items")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"hidden_query": "Not found"}
diff --git a/tests/test_tutorial/test_request_files/test_tutorial001_02.py b/tests/test_tutorial/test_request_files/test_tutorial001_02.py
new file mode 100644
index 000000000..e852a1b31
--- /dev/null
+++ b/tests/test_tutorial/test_request_files/test_tutorial001_02.py
@@ -0,0 +1,157 @@
+from fastapi.testclient import TestClient
+
+from docs_src.request_files.tutorial001_02 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/files/": {
+ "post": {
+ "summary": "Create File",
+ "operationId": "create_file_files__post",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_create_file_files__post"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ "/uploadfile/": {
+ "post": {
+ "summary": "Create Upload File",
+ "operationId": "create_upload_file_uploadfile__post",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "Body_create_file_files__post": {
+ "title": "Body_create_file_files__post",
+ "type": "object",
+ "properties": {
+ "file": {"title": "File", "type": "string", "format": "binary"}
+ },
+ },
+ "Body_create_upload_file_uploadfile__post": {
+ "title": "Body_create_upload_file_uploadfile__post",
+ "type": "object",
+ "properties": {
+ "file": {"title": "File", "type": "string", "format": "binary"}
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"type": "string"},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+}
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == openapi_schema
+
+
+def test_post_form_no_body():
+ response = client.post("/files/")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "No file sent"}
+
+
+def test_post_uploadfile_no_body():
+ response = client.post("/uploadfile/")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "No upload file sent"}
+
+
+def test_post_file(tmp_path):
+ path = tmp_path / "test.txt"
+ path.write_bytes(b"