Compare commits

...

32 Commits

Author SHA1 Message Date
Sebastián Ramírez
5826c4f31f 📝 Tweak release notes 2023-12-25 19:06:04 +01:00
Sebastián Ramírez
91510db620 🔖 Release version 0.106.0 2023-12-25 19:01:26 +01:00
Sebastián Ramírez
bcd5a424cd 📝 Update release notes 2023-12-25 19:00:47 +01:00
github-actions
678bed2fc9 📝 Update release notes 2023-12-25 17:57:54 +00:00
Sebastián Ramírez
a4aa79e0b4 Add support for raising exceptions (including HTTPException) in dependencies with yield in the exit code, do not support them in background tasks (#10831)
* ♻️ Refactor dependency AsyncExitStack logic, exit dependencies after creating the response, before sending it

*  Update tests for dependencies exit, check they are finished before the response is sent

* 🔥 Remove ExitAsyncStackMiddleware as it's no longer needed

* 📝 Update docs for dependencies with yield

* 📝 Update release notes

* 📝 Add source examples for new dependencies with yield raising

*  Add tests for new dependencies raising after yield

* 📝 Update release notes
2023-12-25 18:57:35 +01:00
github-actions
e7756ae7dc 📝 Update release notes 2023-12-20 17:06:01 +00:00
Sebastián Ramírez
dc2fdd56af 👥 Update FastAPI People (#10567)
Co-authored-by: github-actions <github-actions@github.com>
2023-12-20 18:05:37 +01:00
Sebastián Ramírez
36c2667768 📝 Update release notes 2023-12-12 00:34:36 +00:00
Sebastián Ramírez
d8185efb6e 🔖 Release version 0.105.0 2023-12-12 00:32:48 +00:00
github-actions
fc51d7e3c7 📝 Update release notes 2023-12-12 00:29:29 +00:00
Sebastián Ramírez
b98c65cb36 🔥 Remove unused NoneType (#10774) 2023-12-12 00:29:03 +00:00
github-actions
ba99214417 📝 Update release notes 2023-12-12 00:23:15 +00:00
Sebastián Ramírez
6f5aa81c07 Add support for multiple Annotated annotations, e.g. Annotated[str, Field(), Query()] (#10773) 2023-12-12 00:22:47 +00:00
github-actions
73dcc40f09 📝 Update release notes 2023-12-06 11:34:10 +00:00
Sebastián Ramírez
33493ce694 🔧 Update sponsors, add PropelAuth (#10760) 2023-12-06 12:33:48 +01:00
github-actions
01e570c56d 📝 Update release notes 2023-12-04 11:11:17 +00:00
Sebastián Ramírez
ca03379b65 👷 Update build docs, verify README on CI (#10750) 2023-12-04 12:10:54 +01:00
github-actions
8cf2fa0fe4 📝 Update release notes 2023-11-30 20:48:22 +00:00
Sebastián Ramírez
99a2ec981b 📝 Tweak default suggested configs for generating clients (#10736) 2023-11-30 21:48:01 +01:00
github-actions
6fb951bae2 📝 Update release notes 2023-11-28 12:10:38 +00:00
Sebastián Ramírez
13cef7a21a 🔧 Update sponsors, remove Fern (#10729) 2023-11-28 13:10:12 +01:00
github-actions
e9ce31e96b 📝 Update release notes 2023-11-28 10:52:59 +00:00
Sebastián Ramírez
1560879a84 🔧 Update sponsors, add Scalar (#10728) 2023-11-28 10:52:35 +00:00
github-actions
ac93277d3b 📝 Update release notes 2023-11-18 13:47:35 +00:00
Sebastián Ramírez
71d51a9953 🔧 Update sponsors, add Codacy (#10677) 2023-11-18 14:47:04 +01:00
github-actions
81bab77617 📝 Update release notes 2023-11-18 13:38:23 +00:00
Sebastián Ramírez
781984b226 🔧 Update sponsors, add Reflex (#10676) 2023-11-18 14:38:01 +01:00
github-actions
480620372a 📝 Update release notes 2023-11-04 02:03:01 +00:00
Sebastián Ramírez
b04d07c933 📝 Update release notes, move and check latest-changes (#10588) 2023-11-04 06:02:18 +04:00
github-actions
46335068d2 📝 Update release notes 2023-11-04 01:53:13 +00:00
Sebastián Ramírez
4f89886b00 👷 Upgrade latest-changes GitHub Action (#10587) 2023-11-04 05:52:42 +04:00
Sebastián Ramírez
1c25e2d8dc 📝 Update release notes 2023-10-30 15:12:57 +04:00
38 changed files with 774 additions and 397 deletions

View File

@@ -28,6 +28,8 @@ jobs:
- docs/**
- docs_src/**
- requirements-docs.txt
- .github/workflows/build-docs.yml
- .github/workflows/deploy-docs.yml
langs:
needs:
- changes
@@ -55,6 +57,8 @@ jobs:
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/griffe-typing-deprecated.git
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git
- name: Verify README
run: python ./scripts/docs.py verify-readme
- name: Export Language Codes
id: show-langs
run: |

View File

@@ -34,9 +34,12 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- uses: docker://tiangolo/latest-changes:0.0.3
- uses: docker://tiangolo/latest-changes:0.2.0
# - uses: tiangolo/latest-changes@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
latest_changes_file: docs/en/docs/release-notes.md
latest_changes_header: '## Latest Changes\n\n'
latest_changes_header: '## Latest Changes'
end_regex: '^## '
debug_logs: true
label_header_prefix: '### '

View File

@@ -29,7 +29,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v06
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v07
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-tests.txt
@@ -62,7 +62,7 @@ jobs:
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v06
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v07
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements-tests.txt

View File

@@ -48,9 +48,11 @@ The key features are:
<a href="https://cryptapi.io/" target="_blank" title="CryptAPI: Your easy to use, secure and privacy oriented payment gateway."><img src="https://fastapi.tiangolo.com/img/sponsors/cryptapi.svg"></a>
<a href="https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" target="_blank" title="Build, run and scale your apps on a modern, reliable, and secure PaaS."><img src="https://fastapi.tiangolo.com/img/sponsors/platform-sh.png"></a>
<a href="https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Fern | SDKs and API docs"><img src="https://fastapi.tiangolo.com/img/sponsors/fern.svg"></a>
<a href="https://www.porter.run" target="_blank" title="Deploy FastAPI on AWS with a few clicks"><img src="https://fastapi.tiangolo.com/img/sponsors/porter.png"></a>
<a href="https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor" target="_blank" title="Automate FastAPI documentation generation with Bump.sh"><img src="https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg"></a>
<a href="https://reflex.dev" target="_blank" title="Reflex"><img src="https://fastapi.tiangolo.com/img/sponsors/reflex.png"></a>
<a href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"><img src="https://fastapi.tiangolo.com/img/sponsors/scalar.svg"></a>
<a href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge" target="_blank" title="Auth, user management and more for your B2B product"><img src="https://fastapi.tiangolo.com/img/sponsors/propelauth.png"></a>
<a href="https://www.deta.sh/?ref=fastapi" target="_blank" title="The launchpad for all your (team's) ideas"><img src="https://fastapi.tiangolo.com/img/sponsors/deta.svg"></a>
<a href="https://training.talkpython.fm/fastapi-courses" target="_blank" title="FastAPI video courses on demand from people you trust"><img src="https://fastapi.tiangolo.com/img/sponsors/talkpython.png"></a>
<a href="https://testdriven.io/courses/tdd-fastapi/" target="_blank" title="Learn to build high-quality web apps with best practices"><img src="https://fastapi.tiangolo.com/img/sponsors/testdriven.svg"></a>
@@ -59,6 +61,7 @@ The key features are:
<a href="https://databento.com/" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
<a href="https://speakeasyapi.dev?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>
<a href="https://www.codacy.com/?utm_source=github&utm_medium=sponsors&utm_id=pioneers" target="_blank" title="Take code reviews from hours to minutes"><img src="https://fastapi.tiangolo.com/img/sponsors/codacy.png"></a>
<!-- /sponsors -->

View File

@@ -1,5 +1,8 @@
sponsors:
- - login: bump-sh
- - login: codacy
avatarUrl: https://avatars.githubusercontent.com/u/1834093?v=4
url: https://github.com/codacy
- login: bump-sh
avatarUrl: https://avatars.githubusercontent.com/u/33217836?v=4
url: https://github.com/bump-sh
- login: cryptapi
@@ -11,9 +14,6 @@ sponsors:
- login: fern-api
avatarUrl: https://avatars.githubusercontent.com/u/102944815?v=4
url: https://github.com/fern-api
- login: nanram22
avatarUrl: https://avatars.githubusercontent.com/u/116367316?v=4
url: https://github.com/nanram22
- - login: nihpo
avatarUrl: https://avatars.githubusercontent.com/u/1841030?u=0264956d7580f7e46687a762a7baa629f84cf97c&v=4
url: https://github.com/nihpo
@@ -32,12 +32,12 @@ sponsors:
- login: deepset-ai
avatarUrl: https://avatars.githubusercontent.com/u/51827949?v=4
url: https://github.com/deepset-ai
- login: databento
avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4
url: https://github.com/databento
- login: svix
avatarUrl: https://avatars.githubusercontent.com/u/80175132?v=4
url: https://github.com/svix
- login: databento-bot
avatarUrl: https://avatars.githubusercontent.com/u/98378480?u=494f679996e39427f7ddb1a7de8441b7c96fb670&v=4
url: https://github.com/databento-bot
- login: VincentParedes
avatarUrl: https://avatars.githubusercontent.com/u/103889729?v=4
url: https://github.com/VincentParedes
@@ -65,10 +65,7 @@ sponsors:
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
- - login: moellenbeck
avatarUrl: https://avatars.githubusercontent.com/u/169372?v=4
url: https://github.com/moellenbeck
- login: birkjernstrom
- - login: birkjernstrom
avatarUrl: https://avatars.githubusercontent.com/u/281715?u=4be14b43f76b4bd497b1941309bb390250b405e6&v=4
url: https://github.com/birkjernstrom
- login: yasyf
@@ -83,15 +80,15 @@ sponsors:
- login: mainframeindustries
avatarUrl: https://avatars.githubusercontent.com/u/55092103?v=4
url: https://github.com/mainframeindustries
- login: deployplex
avatarUrl: https://avatars.githubusercontent.com/u/57115726?v=4
url: https://github.com/deployplex
- - login: povilasb
avatarUrl: https://avatars.githubusercontent.com/u/1213442?u=b11f58ed6ceea6e8297c9b310030478ebdac894d&v=4
url: https://github.com/povilasb
- login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- - login: NateXVI
avatarUrl: https://avatars.githubusercontent.com/u/48195620?u=4bc8751ae50cb087c40c1fe811764aa070b9eea6&v=4
url: https://github.com/NateXVI
- - login: Kludex
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
@@ -125,12 +122,12 @@ sponsors:
- login: tcsmith
avatarUrl: https://avatars.githubusercontent.com/u/989034?u=7d8d741552b3279e8f4d3878679823a705a46f8f&v=4
url: https://github.com/tcsmith
- login: mrkmcknz
avatarUrl: https://avatars.githubusercontent.com/u/1089376?u=2b9b8a8c25c33a4f6c220095638bd821cdfd13a3&v=4
url: https://github.com/mrkmcknz
- login: mickaelandrieu
avatarUrl: https://avatars.githubusercontent.com/u/1247388?u=599f6e73e452a9453f2bd91e5c3100750e731ad4&v=4
url: https://github.com/mickaelandrieu
- login: knallgelb
avatarUrl: https://avatars.githubusercontent.com/u/2358812?u=c48cb6362b309d74cbf144bd6ad3aed3eb443e82&v=4
url: https://github.com/knallgelb
- login: Shark009
avatarUrl: https://avatars.githubusercontent.com/u/3163309?u=0c6f4091b0eda05c44c390466199826e6dc6e431&v=4
url: https://github.com/Shark009
@@ -174,7 +171,7 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=c485eefca5c6329600cae63dd35e4f5682ce6924&v=4
url: https://github.com/iwpnd
- login: FernandoCelmer
avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=ab6108a843a2fb9df0934f482375d2907609f3ff&v=4
avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=d29fff3fd862fda4ca752079f13f32e84c762ea4&v=4
url: https://github.com/FernandoCelmer
- login: simw
avatarUrl: https://avatars.githubusercontent.com/u/6322526?v=4
@@ -188,6 +185,9 @@ sponsors:
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
- login: drcat101
avatarUrl: https://avatars.githubusercontent.com/u/11951946?u=e714b957185b8cf3d301cced7fc3ad2842122c6a&v=4
url: https://github.com/drcat101
- login: jsoques
avatarUrl: https://avatars.githubusercontent.com/u/12414216?u=620921d94196546cc8b9eae2cc4cbc3f95bab42f&v=4
url: https://github.com/jsoques
@@ -212,9 +212,6 @@ sponsors:
- login: Filimoa
avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=0be845711495bbd7b756e13fcaeb8efc1ebd78ba&v=4
url: https://github.com/Filimoa
- login: rahulsalgare
avatarUrl: https://avatars.githubusercontent.com/u/21974430?u=ade6f182b94554ab8491d7421de5e78f711dcaf8&v=4
url: https://github.com/rahulsalgare
- login: BrettskiPy
avatarUrl: https://avatars.githubusercontent.com/u/30988215?u=d8a94a67e140d5ee5427724b292cc52d8827087a&v=4
url: https://github.com/BrettskiPy
@@ -269,9 +266,12 @@ sponsors:
- login: pyt3h
avatarUrl: https://avatars.githubusercontent.com/u/99658549?v=4
url: https://github.com/pyt3h
- - login: SebTota
avatarUrl: https://avatars.githubusercontent.com/u/25122511?v=4
url: https://github.com/SebTota
- login: apitally
avatarUrl: https://avatars.githubusercontent.com/u/138365043?v=4
url: https://github.com/apitally
- - login: getsentry
avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
url: https://github.com/getsentry
- - login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
@@ -299,6 +299,9 @@ sponsors:
- login: securancy
avatarUrl: https://avatars.githubusercontent.com/u/606673?v=4
url: https://github.com/securancy
- login: natehouk
avatarUrl: https://avatars.githubusercontent.com/u/805439?u=d8e4be629dc5d7efae7146157e41ee0bd129d9bc&v=4
url: https://github.com/natehouk
- login: browniebroke
avatarUrl: https://avatars.githubusercontent.com/u/861044?u=5abfca5588f3e906b31583d7ee62f6de4b68aa24&v=4
url: https://github.com/browniebroke
@@ -323,9 +326,9 @@ sponsors:
- login: anthonycorletti
avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4
url: https://github.com/anthonycorletti
- login: nikeee
avatarUrl: https://avatars.githubusercontent.com/u/4068864?u=bbe73151f2b409c120160d032dc9aa6875ef0c4b&v=4
url: https://github.com/nikeee
- login: erhan
avatarUrl: https://avatars.githubusercontent.com/u/3872888?u=cd9a20fcd33c5598d9d7797a78dedfc9148592f6&v=4
url: https://github.com/erhan
- login: Alisa-lisa
avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
url: https://github.com/Alisa-lisa
@@ -366,7 +369,7 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/8574425?u=aad2a9674273c9275fe414d99269b7418d144089&v=4
url: https://github.com/albertkun
- login: xncbf
avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=866a1311e4bd3ec5ae84185c4fcc99f397c883d7&v=4
avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=05cb2d7c797a02f666805ad4639d9582f31d432c&v=4
url: https://github.com/xncbf
- login: DMantis
avatarUrl: https://avatars.githubusercontent.com/u/9536869?v=4
@@ -398,9 +401,6 @@ sponsors:
- login: jangia
avatarUrl: https://avatars.githubusercontent.com/u/17927101?u=9261b9bb0c3e3bb1ecba43e8915dc58d8c9a077e&v=4
url: https://github.com/jangia
- login: timzaz
avatarUrl: https://avatars.githubusercontent.com/u/19709244?u=264d7db95c28156363760229c30ee1116efd4eeb&v=4
url: https://github.com/timzaz
- login: shuheng-liu
avatarUrl: https://avatars.githubusercontent.com/u/22414322?u=813c45f30786c6b511b21a661def025d8f7b609e&v=4
url: https://github.com/shuheng-liu
@@ -419,15 +419,15 @@ sponsors:
- login: joerambo
avatarUrl: https://avatars.githubusercontent.com/u/26282974?v=4
url: https://github.com/joerambo
- login: msniezynski
avatarUrl: https://avatars.githubusercontent.com/u/27588547?u=0e3be5ac57dcfdf124f470bcdf74b5bf79af1b6c&v=4
url: https://github.com/msniezynski
- login: rlnchow
avatarUrl: https://avatars.githubusercontent.com/u/28018479?u=a93ca9cf1422b9ece155784a72d5f2fdbce7adff&v=4
url: https://github.com/rlnchow
- login: mertguvencli
avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4
url: https://github.com/mertguvencli
- login: White-Mask
avatarUrl: https://avatars.githubusercontent.com/u/31826970?u=8625355dc25ddf9c85a8b2b0b9932826c4c8f44c&v=4
url: https://github.com/White-Mask
- login: HosamAlmoghraby
avatarUrl: https://avatars.githubusercontent.com/u/32025281?u=aa1b09feabccbf9dc506b81c71155f32d126cefa&v=4
url: https://github.com/HosamAlmoghraby
@@ -435,14 +435,11 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/33275230?u=eb223cad27017bb1e936ee9b429b450d092d0236&v=4
url: https://github.com/engineerjoe440
- login: bnkc
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=1a104991a2ea90bfe304bc0b9ef191c7e4891a0e&v=4
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=ea88e4bd668c984cff1bca3e71ab2deb37fafdc4&v=4
url: https://github.com/bnkc
- login: declon
avatarUrl: https://avatars.githubusercontent.com/u/36180226?v=4
url: https://github.com/declon
- login: miraedbswo
avatarUrl: https://avatars.githubusercontent.com/u/36796047?u=9e7a5b3e558edc61d35d0f9dfac37541bae7f56d&v=4
url: https://github.com/miraedbswo
- login: DSMilestone6538
avatarUrl: https://avatars.githubusercontent.com/u/37230924?u=f299dce910366471523155e0cb213356d34aadc1&v=4
url: https://github.com/DSMilestone6538
@@ -458,9 +455,6 @@ sponsors:
- login: ArtyomVancyan
avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
url: https://github.com/ArtyomVancyan
- login: josehenriqueroveda
avatarUrl: https://avatars.githubusercontent.com/u/46685746?u=2e672057a7dbe1dba47e57c378fc0cac336022eb&v=4
url: https://github.com/josehenriqueroveda
- login: hgalytoby
avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=f4888c2c54929bd86eed0d3971d09fcb306e5088&v=4
url: https://github.com/hgalytoby
@@ -473,12 +467,18 @@ sponsors:
- login: 0417taehyun
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
url: https://github.com/0417taehyun
- login: fernandosmither
avatarUrl: https://avatars.githubusercontent.com/u/66154723?u=a76a037b5d674938a75d2cff862fb6dfd63ec214&v=4
url: https://github.com/fernandosmither
- login: romabozhanovgithub
avatarUrl: https://avatars.githubusercontent.com/u/67696229?u=e4b921eef096415300425aca249348f8abb78ad7&v=4
url: https://github.com/romabozhanovgithub
- login: mbukeRepo
avatarUrl: https://avatars.githubusercontent.com/u/70356088?u=d2eb23e2b222a3b316c4183b05a3236b32819dc2&v=4
url: https://github.com/mbukeRepo
- login: PelicanQ
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
url: https://github.com/PelicanQ
- - login: ssbarnea
avatarUrl: https://avatars.githubusercontent.com/u/102495?u=b4bf6818deefe59952ac22fec6ed8c76de1b8f7c&v=4
url: https://github.com/ssbarnea
@@ -491,18 +491,15 @@ sponsors:
- login: sadikkuzu
avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=d179c06bb9f65c4167fcab118526819f8e0dac17&v=4
url: https://github.com/sadikkuzu
- login: samnimoh
avatarUrl: https://avatars.githubusercontent.com/u/33413170?u=147bc516be6cb647b28d7e3b3fea3a018a331145&v=4
url: https://github.com/samnimoh
- login: msniezynski
avatarUrl: https://avatars.githubusercontent.com/u/27588547?u=0e3be5ac57dcfdf124f470bcdf74b5bf79af1b6c&v=4
url: https://github.com/msniezynski
- login: danburonline
avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=2cad4388c1544e539ecb732d656e42fb07b4ff2d&v=4
url: https://github.com/danburonline
- login: rwxd
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
url: https://github.com/rwxd
- login: shywn-mrk
avatarUrl: https://avatars.githubusercontent.com/u/51455763?u=389e2608e4056fe5e1f23e9ad56a9415277504d3&v=4
url: https://github.com/shywn-mrk
- login: almeida-matheus
avatarUrl: https://avatars.githubusercontent.com/u/66216198?u=54335eaa0ced626be5c1ff52fead1ebc032286ec&v=4
url: https://github.com/almeida-matheus
- login: IvanReyesO7
avatarUrl: https://avatars.githubusercontent.com/u/74359151?u=4b2c368f71e1411b462a8c2290c920ad35dc1af8&v=4
url: https://github.com/IvanReyesO7

View File

@@ -1,12 +1,12 @@
maintainers:
- login: tiangolo
answers: 1868
prs: 496
answers: 1870
prs: 508
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
count: 501
count: 512
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: dmontagu
@@ -26,21 +26,21 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: jgould22
count: 168
count: 186
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: euri10
count: 153
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
- login: iudeen
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: phy25
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
- login: iudeen
count: 122
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: raphaelauv
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
@@ -61,14 +61,14 @@ experts:
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
- login: yinziyan1206
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: acidjunk
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: insomnes
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
@@ -77,18 +77,18 @@ experts:
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
url: https://github.com/adriangb
- login: yinziyan1206
count: 44
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: odiseo0
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
url: https://github.com/odiseo0
- login: insomnes
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: frankie567
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=c159fe047727aedecbbeeaa96a1b03ceb9d39add&v=4
url: https://github.com/frankie567
- login: odiseo0
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4
url: https://github.com/odiseo0
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
@@ -101,14 +101,14 @@ experts:
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
- login: n8sty
count: 36
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
- login: krishnardt
count: 35
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
- login: n8sty
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
- login: panla
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
@@ -137,18 +137,22 @@ experts:
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
url: https://github.com/rafsaf
- login: JavierSanchezCastro
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
- login: chris-allnutt
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
- login: ebottos94
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
url: https://github.com/ebottos94
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
- login: JavierSanchezCastro
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: chrisK824
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
@@ -161,10 +165,10 @@ experts:
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
url: https://github.com/retnikt
- login: ebottos94
- login: nymous
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
url: https://github.com/ebottos94
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
@@ -181,39 +185,47 @@ experts:
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
url: https://github.com/caeser1996
- login: nymous
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
- login: jonatasoli
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=071c062d2861d3dd127f6b4a5258cd8ef55d4c50&v=4
url: https://github.com/jonatasoli
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: jonatasoli
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=071c062d2861d3dd127f6b4a5258cd8ef55d4c50&v=4
url: https://github.com/jonatasoli
- login: abhint
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
url: https://github.com/abhint
last_month_active:
- login: jgould22
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: Kludex
count: 8
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: White-Mask
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31826970?u=8625355dc25ddf9c85a8b2b0b9932826c4c8f44c&v=4
url: https://github.com/White-Mask
- login: n8sty
count: 7
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
- login: chrisK824
- login: hasansezertasan
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
url: https://github.com/chrisK824
- login: danielfcollier
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=e389634d494d503cca867f76c2d00cacc273a46e&v=4
url: https://github.com/hasansezertasan
- login: pythonweb2
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/38995330?u=5799be795fc310f75f3a5fe9242307d59b194520&v=4
url: https://github.com/danielfcollier
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
url: https://github.com/pythonweb2
- login: ebottos94
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
url: https://github.com/ebottos94
top_contributors:
- login: waynerv
count: 25
@@ -349,17 +361,17 @@ top_contributors:
url: https://github.com/rostik1410
top_reviewers:
- login: Kludex
count: 139
count: 145
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: BilalAlpaslan
count: 82
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: yezz123
count: 80
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=d7062cbc6eb7671d5dc9cc0e32a24ae335e0f225&v=4
url: https://github.com/yezz123
- login: BilalAlpaslan
count: 79
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: iudeen
count: 54
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
@@ -376,14 +388,14 @@ top_reviewers:
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
url: https://github.com/Laineyzhang55
- login: Xewus
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
- login: ycd
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
url: https://github.com/ycd
- login: Xewus
count: 44
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
- login: cikay
count: 41
avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
@@ -412,14 +424,14 @@ top_reviewers:
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
- login: Ryandaydev
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=ba0eea19429e7cf77cf2ab8ad2f3d3af202bc1cf&v=4
url: https://github.com/Ryandaydev
- login: LorhanSohaky
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
url: https://github.com/LorhanSohaky
- login: Ryandaydev
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=809f3d1074d04bbc28012a7f17f06ea56f5bd71a&v=4
url: https://github.com/Ryandaydev
- login: dmontagu
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
@@ -476,14 +488,14 @@ top_reviewers:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
- login: peidrao
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=a66902b40c13647d0ed0e573d598128240a4dd04&v=4
url: https://github.com/peidrao
- login: sh0nk
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
- login: peidrao
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=a66902b40c13647d0ed0e573d598128240a4dd04&v=4
url: https://github.com/peidrao
- login: wdh99
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4

View File

@@ -5,15 +5,21 @@ gold:
- url: https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023
title: "Build, run and scale your apps on a modern, reliable, and secure PaaS."
img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png
- url: https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge
title: Fern | SDKs and API docs
img: https://fastapi.tiangolo.com/img/sponsors/fern.svg
- url: https://www.porter.run
title: Deploy FastAPI on AWS with a few clicks
img: https://fastapi.tiangolo.com/img/sponsors/porter.png
- url: https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor
title: Automate FastAPI documentation generation with Bump.sh
img: https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg
- url: https://reflex.dev
title: Reflex
img: https://fastapi.tiangolo.com/img/sponsors/reflex.png
- url: https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge
title: "Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"
img: https://fastapi.tiangolo.com/img/sponsors/scalar.svg
- url: https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge
title: Auth, user management and more for your B2B product
img: https://fastapi.tiangolo.com/img/sponsors/propelauth.png
silver:
- url: https://www.deta.sh/?ref=fastapi
title: The launchpad for all your (team's) ideas
@@ -39,6 +45,9 @@ silver:
- url: https://www.svix.com/
title: Svix - Webhooks as a service
img: https://fastapi.tiangolo.com/img/sponsors/svix.svg
- url: https://www.codacy.com/?utm_source=github&utm_medium=sponsors&utm_id=pioneers
title: Take code reviews from hours to minutes
img: https://fastapi.tiangolo.com/img/sponsors/codacy.png
bronze:
- url: https://www.exoflare.com/open-source/?utm_source=FastAPI&utm_campaign=open_source
title: Biosecurity risk assessments made easy.

View File

@@ -21,3 +21,5 @@ logins:
- fern-api
- ndimares
- svixhq
- Alek99
- codacy

View File

@@ -20,10 +20,9 @@ Some of them also ✨ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-autho
And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good service** but also want to make sure you have a **good and healthy framework**, FastAPI. 🙇
You might want to try their services and follow their guides:
For example, you might want to try <a href="https://speakeasyapi.dev/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>.
* <a href="https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=docs-generate-clients" class="external-link" target="_blank">Fern</a>
* <a href="https://speakeasyapi.dev/?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
There are also several other companies offering similar services that you can search and find online. 🤓
## Generate a TypeScript Frontend Client
@@ -88,7 +87,7 @@ It could look like this:
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios"
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
},
"author": "",
"license": "",
@@ -107,7 +106,7 @@ After having that NPM `generate-client` script there, you can run it with:
$ npm run generate-client
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes
```
</div>
@@ -247,7 +246,7 @@ Now as the end result is in a file `openapi.json`, you would modify the `package
"description": "",
"main": "index.js",
"scripts": {
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios"
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
},
"author": "",
"license": "",

View File

@@ -119,8 +119,6 @@ That's why when talking about version 2.0 it's common to say "Swagger", and for
These two were chosen for being fairly popular and stable, but doing a quick search, you could find dozens of additional alternative user interfaces for OpenAPI (that you can use with **FastAPI**).
For example, you could try <a href="https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=docs-alternatives" class="external-link" target="_blank">Fern</a> which is also a FastAPI sponsor. 😎🎉
### Flask REST frameworks
There are several Flask REST frameworks, but after investing the time and work into investigating them, I found that many are discontinued or abandoned, with several standing issues that made them unfit.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -7,13 +7,153 @@ hide:
## Latest Changes
## 0.106.0
### Breaking Changes
Using resources from dependencies with `yield` in background tasks is no longer supported.
This change is what supports the new features, read below. 🤓
### Dependencies with `yield`, `HTTPException` and Background Tasks
Dependencies with `yield` now can raise `HTTPException` and other exceptions after `yield`. 🎉
Read the new docs here: [Dependencies with `yield` and `HTTPException`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception).
```Python
from fastapi import Depends, FastAPI, HTTPException
from typing_extensions import Annotated
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Onwer error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item
```
---
Before FastAPI 0.106.0, raising exceptions after `yield` was not possible, the exit code in dependencies with `yield` was executed *after* the response was sent, so [Exception Handlers](https://fastapi.tiangolo.com/tutorial/handling-errors/#install-custom-exception-handlers) would have already run.
This was designed this way mainly to allow using the same objects "yielded" by dependencies inside of background tasks, because the exit code would be executed after the background tasks were finished.
Nevertheless, as this would mean waiting for the response to travel through the network while unnecessarily holding a resource in a dependency with yield (for example a database connection), this was changed in FastAPI 0.106.0.
Additionally, a background task is normally an independent set of logic that should be handled separately, with its own resources (e.g. its own database connection).
If you used to rely on this behavior, now you should create the resources for background tasks inside the background task itself, and use internally only data that doesn't depend on the resources of dependencies with `yield`.
For example, instead of using the same database session, you would create a new database session inside of the background task, and you would obtain the objects from the database using this new session. And then instead of passing the object from the database as a parameter to the background task function, you would pass the ID of that object and then obtain the object again inside the background task function.
The sequence of execution before FastAPI 0.106.0 was like this diagram:
Time flows from top to bottom. And each column is one of the parts interacting or executing code.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,tasks: Can raise exception for dependency, handled after response is sent
Note over client,operation: Can raise HTTPException and can change the response
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise
dep -->> handler: Raise HTTPException
handler -->> client: HTTP error response
dep -->> dep: Raise other exception
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise HTTPException
dep -->> handler: Auto forward exception
handler -->> client: HTTP error response
operation -->> dep: Raise other exception
dep -->> handler: Auto forward exception
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> dep: Raise other exception
end
Note over dep: After yield
opt Handle other exception
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
end
```
The new execution flow can be found in the docs: [Execution of dependencies with `yield`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#execution-of-dependencies-with-yield).
### Features
* ✨ Add support for raising exceptions (including `HTTPException`) in dependencies with `yield` in the exit code, do not support them in background tasks. PR [#10831](https://github.com/tiangolo/fastapi/pull/10831) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 👥 Update FastAPI People. PR [#10567](https://github.com/tiangolo/fastapi/pull/10567) by [@tiangolo](https://github.com/tiangolo).
## 0.105.0
### Features
* ✨ Add support for multiple Annotated annotations, e.g. `Annotated[str, Field(), Query()]`. PR [#10773](https://github.com/tiangolo/fastapi/pull/10773) by [@tiangolo](https://github.com/tiangolo).
### Refactors
* 🔥 Remove unused NoneType. PR [#10774](https://github.com/tiangolo/fastapi/pull/10774) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Tweak default suggested configs for generating clients. PR [#10736](https://github.com/tiangolo/fastapi/pull/10736) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 🔧 Update sponsors, add Scalar. PR [#10728](https://github.com/tiangolo/fastapi/pull/10728) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors, add PropelAuth. PR [#10760](https://github.com/tiangolo/fastapi/pull/10760) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update build docs, verify README on CI. PR [#10750](https://github.com/tiangolo/fastapi/pull/10750) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors, remove Fern. PR [#10729](https://github.com/tiangolo/fastapi/pull/10729) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors, add Codacy. PR [#10677](https://github.com/tiangolo/fastapi/pull/10677) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors, add Reflex. PR [#10676](https://github.com/tiangolo/fastapi/pull/10676) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update release notes, move and check latest-changes. PR [#10588](https://github.com/tiangolo/fastapi/pull/10588) by [@tiangolo](https://github.com/tiangolo).
* 👷 Upgrade latest-changes GitHub Action. PR [#10587](https://github.com/tiangolo/fastapi/pull/10587) by [@tiangolo](https://github.com/tiangolo).
## 0.104.1
### Fixes
* 📌 Pin Swagger UI version to 5.9.0 temporarily to handle a bug crashing it in 5.9.1. PR [#10529](https://github.com/tiangolo/fastapi/pull/10529) by [@alejandraklachquin](https://github.com/alejandraklachquin).
* This is not really a bug in FastAPI but in Swagger UI, nevertheless pinning the version will work while a soulution is found on the [Swagger UI side](https://github.com/swagger-api/swagger-ui/issues/9337).
* This is not really a bug in FastAPI but in Swagger UI, nevertheless pinning the version will work while a solution is found on the [Swagger UI side](https://github.com/swagger-api/swagger-ui/issues/9337).
### Docs

View File

@@ -1,8 +1,8 @@
# Dependencies with yield
FastAPI supports dependencies that do some <abbr title='sometimes also called "exit", "cleanup", "teardown", "close", "context managers", ...'>extra steps after finishing</abbr>.
FastAPI supports dependencies that do some <abbr title='sometimes also called "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code", etc.'>extra steps after finishing</abbr>.
To do this, use `yield` instead of `return`, and write the extra steps after.
To do this, use `yield` instead of `return`, and write the extra steps (code) after.
!!! tip
Make sure to use `yield` one single time.
@@ -21,7 +21,7 @@ To do this, use `yield` instead of `return`, and write the extra steps after.
For example, you could use this to create a database session and close it after finishing.
Only the code prior to and including the `yield` statement is executed before sending a response:
Only the code prior to and including the `yield` statement is executed before creating a response:
```Python hl_lines="2-4"
{!../../../docs_src/dependencies/tutorial007.py!}
@@ -40,7 +40,7 @@ The code following the `yield` statement is executed after the response has been
```
!!! tip
You can use `async` or normal functions.
You can use `async` or regular functions.
**FastAPI** will do the right thing with each, the same as with normal dependencies.
@@ -114,7 +114,7 @@ And, in turn, `dependency_b` needs the value from `dependency_a` (here named `de
{!> ../../../docs_src/dependencies/tutorial008.py!}
```
The same way, you could have dependencies with `yield` and `return` mixed.
The same way, you could have some dependencies with `yield` and some other dependencies with `return`, and have some of those depend on some of the others.
And you could have a single dependency that requires several other dependencies with `yield`, etc.
@@ -131,24 +131,38 @@ You can have any combinations of dependencies that you want.
You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions.
It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**.
The exit code in dependencies with `yield` is executed *after* the response is sent, so [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} will have already run. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`).
So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore.
This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.
Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*.
But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system.
If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code.
If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
The same way, you could raise an `HTTPException` or similar in the exit code, after the `yield`.
!!! tip
You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
This is a somewhat advanced technique, and in most of the cases you won't really need it, as you can raise exceptions (including `HTTPException`) from inside of the rest of your application code, for example, in the *path operation function*.
But it's there for you if you need it. 🤓
=== "Python 3.9+"
```Python hl_lines="18-22 31"
{!> ../../../docs_src/dependencies/tutorial008b_an_py39.py!}
```
=== "Python 3.8+"
```Python hl_lines="17-21 30"
{!> ../../../docs_src/dependencies/tutorial008b_an.py!}
```
=== "Python 3.8+ non-Annotated"
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="16-20 29"
{!> ../../../docs_src/dependencies/tutorial008b.py!}
```
An alternative you could use to catch exceptions (and possibly also raise another `HTTPException`) is ot create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
## Execution of dependencies with `yield`
The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code.
@@ -161,34 +175,30 @@ participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,tasks: Can raise exception for dependency, handled after response is sent
Note over client,operation: Can raise HTTPException and can change the response
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise
dep -->> handler: Raise HTTPException
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
dep -->> dep: Raise other exception
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise HTTPException
dep -->> handler: Auto forward exception
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
dep -->> handler: Auto forward exception
end
handler -->> client: HTTP error response
operation -->> dep: Raise other exception
dep -->> handler: Auto forward exception
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> dep: Raise other exception
end
Note over dep: After yield
opt Handle other exception
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
tasks -->> tasks: Handle exceptions in the background task code
end
```
@@ -198,10 +208,33 @@ participant tasks as Background tasks
After one of those responses is sent, no other response can be sent.
!!! tip
This diagram shows `HTTPException`, but you could also raise any other exception for which you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
This diagram shows `HTTPException`, but you could also raise any other exception that you catch in a dependency with `yield` or with a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
If you raise any exception, it will be passed to the dependencies with yield, including `HTTPException`, and then **again** to the exception handlers. If there's no exception handler for that exception, it will then be handled by the default internal `ServerErrorMiddleware`, returning a 500 HTTP status code, to let the client know that there was an error in the server.
## Dependencies with `yield`, `HTTPException` and Background Tasks
!!! warning
You most probably don't need these technical details, you can skip this section and continue below.
These details are useful mainly if you were using a version of FastAPI prior to 0.106.0 and used resources from dependencies with `yield` in background tasks.
Before FastAPI 0.106.0, raising exceptions after `yield` was not possible, the exit code in dependencies with `yield` was executed *after* the response was sent, so [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} would have already run.
This was designed this way mainly to allow using the same objects "yielded" by dependencies inside of background tasks, because the exit code would be executed after the background tasks were finished.
Nevertheless, as this would mean waiting for the response to travel through the network while unnecessarily holding a resource in a dependency with yield (for example a database connection), this was changed in FastAPI 0.106.0.
!!! tip
Additionally, a background task is normally an independent set of logic that should be handled separately, with its own resources (e.g. its own database connection).
So, this way you will probably have cleaner code.
If you used to rely on this behavior, now you should create the resources for background tasks inside the background task itself, and use internally only data that doesn't depend on the resources of dependencies with `yield`.
For example, instead of using the same database session, you would create a new database session inside of the background task, and you would obtain the objects from the database using this new session. And then instead of passing the object from the database as a parameter to the background task function, you would pass the ID of that object and then obtain the object again inside the background task function.
## Context Managers
### What are "Context Managers"
@@ -220,7 +253,7 @@ Underneath, the `open("./somefile.txt")` creates an object that is a called a "C
When the `with` block finishes, it makes sure to close the file, even if there were exceptions.
When you create a dependency with `yield`, **FastAPI** will internally convert it to a context manager, and combine it with some other related tools.
When you create a dependency with `yield`, **FastAPI** will internally create a context manager for it, and combine it with some other related tools.
### Using context managers in dependencies with `yield`

View File

@@ -34,12 +34,6 @@
<img class="sponsor-image" src="/img/sponsors/platform-sh-banner.png" />
</a>
</div>
<div class="item">
<a title="Fern | SDKs and API docs" style="display: block; position: relative;" href="https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=top-banner" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/fern-banner.svg" />
</a>
</div>
<div class="item">
<a title="Deploy FastAPI on AWS with a few clicks" style="display: block; position: relative;" href="https://www.porter.run" target="_blank">
<span class="sponsor-badge">sponsor</span>
@@ -52,6 +46,24 @@
<img class="sponsor-image" src="/img/sponsors/bump-sh-banner.svg" />
</a>
</div>
<div class="item">
<a title="Reflex" style="display: block; position: relative;" href="https://reflex.dev" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/reflex-banner.png" />
</a>
</div>
<div class="item">
<a title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files" style="display: block; position: relative;" href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=top-banner" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/scalar-banner.svg" />
</a>
</div>
<div class="item">
<a title="Auth, user management and more for your B2B product" style="display: block; position: relative;" href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=topbanner" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/propelauth-banner.png" />
</a>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,30 @@
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Onwer error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

View File

@@ -0,0 +1,31 @@
from fastapi import Depends, FastAPI, HTTPException
from typing_extensions import Annotated
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Onwer error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

View File

@@ -0,0 +1,32 @@
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Onwer error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

View File

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

View File

@@ -249,7 +249,12 @@ if PYDANTIC_V2:
return is_bytes_sequence_annotation(field.type_)
def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
return type(field_info).from_annotation(annotation)
cls = type(field_info)
merged_field_info = cls.from_annotation(annotation)
new_field_info = copy(field_info)
new_field_info.metadata = merged_field_info.metadata
new_field_info.annotation = merged_field_info.annotation
return new_field_info
def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
origin_type = (

View File

@@ -22,7 +22,6 @@ from fastapi.exception_handlers import (
)
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
from fastapi.logger import logger
from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
@@ -37,8 +36,6 @@ from starlette.datastructures import State
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.middleware.exceptions import ExceptionMiddleware
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
@@ -966,55 +963,6 @@ class FastAPI(Starlette):
self.middleware_stack: Union[ASGIApp, None] = None
self.setup()
def build_middleware_stack(self) -> ASGIApp:
# Duplicate/override from Starlette to add AsyncExitStackMiddleware
# inside of ExceptionMiddleware, inside of custom user middlewares
debug = self.debug
error_handler = None
exception_handlers = {}
for key, value in self.exception_handlers.items():
if key in (500, Exception):
error_handler = value
else:
exception_handlers[key] = value
middleware = (
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
+ self.user_middleware
+ [
Middleware(
ExceptionMiddleware, handlers=exception_handlers, debug=debug
),
# Add FastAPI-specific AsyncExitStackMiddleware for dependencies with
# contextvars.
# This needs to happen after user middlewares because those create a
# new contextvars context copy by using a new AnyIO task group.
# The initial part of dependencies with 'yield' is executed in the
# FastAPI code, inside all the middlewares. However, the teardown part
# (after 'yield') is executed in the AsyncExitStack in this middleware.
# If the AsyncExitStack lived outside of the custom middlewares and
# contextvars were set in a dependency with 'yield' in that internal
# contextvars context, the values would not be available in the
# outer context of the AsyncExitStack.
# By placing the middleware and the AsyncExitStack here, inside all
# user middlewares, the code before and after 'yield' in dependencies
# with 'yield' is executed in the same contextvars context. Thus, all values
# set in contextvars before 'yield' are still available after 'yield,' as
# expected.
# Additionally, by having this AsyncExitStack here, after the
# ExceptionMiddleware, dependencies can now catch handled exceptions,
# e.g. HTTPException, to customize the teardown code (e.g. DB session
# rollback).
Middleware(AsyncExitStackMiddleware),
]
)
app = self.router
for cls, options in reversed(middleware):
app = cls(app=app, **options)
return app
def openapi(self) -> Dict[str, Any]:
"""
Generate the OpenAPI schema of the application. This is called by FastAPI

View File

@@ -1,4 +1,3 @@
from contextlib import AsyncExitStack as AsyncExitStack # noqa
from contextlib import asynccontextmanager as asynccontextmanager
from typing import AsyncGenerator, ContextManager, TypeVar

View File

@@ -1,5 +1,5 @@
import inspect
from contextlib import contextmanager
from contextlib import AsyncExitStack, contextmanager
from copy import deepcopy
from typing import (
Any,
@@ -46,7 +46,6 @@ from fastapi._compat import (
)
from fastapi.background import BackgroundTasks
from fastapi.concurrency import (
AsyncExitStack,
asynccontextmanager,
contextmanager_in_threadpool,
)
@@ -325,10 +324,11 @@ def analyze_param(
field_info = None
depends = None
type_annotation: Any = Any
if (
annotation is not inspect.Signature.empty
and get_origin(annotation) is Annotated
):
use_annotation: Any = Any
if annotation is not inspect.Signature.empty:
use_annotation = annotation
type_annotation = annotation
if get_origin(use_annotation) is Annotated:
annotated_args = get_args(annotation)
type_annotation = annotated_args[0]
fastapi_annotations = [
@@ -336,14 +336,21 @@ def analyze_param(
for arg in annotated_args[1:]
if isinstance(arg, (FieldInfo, params.Depends))
]
assert (
len(fastapi_annotations) <= 1
), f"Cannot specify multiple `Annotated` FastAPI arguments for {param_name!r}"
fastapi_annotation = next(iter(fastapi_annotations), None)
fastapi_specific_annotations = [
arg
for arg in fastapi_annotations
if isinstance(arg, (params.Param, params.Body, params.Depends))
]
if fastapi_specific_annotations:
fastapi_annotation: Union[
FieldInfo, params.Depends, None
] = fastapi_specific_annotations[-1]
else:
fastapi_annotation = None
if isinstance(fastapi_annotation, FieldInfo):
# Copy `field_info` because we mutate `field_info.default` below.
field_info = copy_field_info(
field_info=fastapi_annotation, annotation=annotation
field_info=fastapi_annotation, annotation=use_annotation
)
assert field_info.default is Undefined or field_info.default is Required, (
f"`{field_info.__class__.__name__}` default value cannot be set in"
@@ -356,8 +363,6 @@ def analyze_param(
field_info.default = Required
elif isinstance(fastapi_annotation, params.Depends):
depends = fastapi_annotation
elif annotation is not inspect.Signature.empty:
type_annotation = annotation
if isinstance(value, params.Depends):
assert depends is None, (
@@ -402,15 +407,15 @@ def analyze_param(
# We might check here that `default_value is Required`, but the fact is that the same
# parameter might sometimes be a path parameter and sometimes not. See
# `tests/test_infer_param_optionality.py` for an example.
field_info = params.Path(annotation=type_annotation)
field_info = params.Path(annotation=use_annotation)
elif is_uploadfile_or_nonable_uploadfile_annotation(
type_annotation
) or is_uploadfile_sequence_annotation(type_annotation):
field_info = params.File(annotation=type_annotation, default=default_value)
field_info = params.File(annotation=use_annotation, default=default_value)
elif not field_annotation_is_scalar(annotation=type_annotation):
field_info = params.Body(annotation=type_annotation, default=default_value)
field_info = params.Body(annotation=use_annotation, default=default_value)
else:
field_info = params.Query(annotation=type_annotation, default=default_value)
field_info = params.Query(annotation=use_annotation, default=default_value)
field = None
if field_info is not None:
@@ -424,8 +429,8 @@ def analyze_param(
and getattr(field_info, "in_", None) is None
):
field_info.in_ = params.ParamTypes.query
use_annotation = get_annotation_from_field_info(
type_annotation,
use_annotation_from_field_info = get_annotation_from_field_info(
use_annotation,
field_info,
param_name,
)
@@ -436,7 +441,7 @@ def analyze_param(
field_info.alias = alias
field = create_response_field(
name=param_name,
type_=use_annotation,
type_=use_annotation_from_field_info,
default=field_info.default,
alias=alias,
required=field_info.default in (Required, Undefined),
@@ -466,16 +471,17 @@ def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
field_info = cast(params.Param, field.field_info)
if field_info.in_ == params.ParamTypes.path:
field_info = field.field_info
field_info_in = getattr(field_info, "in_", None)
if field_info_in == params.ParamTypes.path:
dependant.path_params.append(field)
elif field_info.in_ == params.ParamTypes.query:
elif field_info_in == params.ParamTypes.query:
dependant.query_params.append(field)
elif field_info.in_ == params.ParamTypes.header:
elif field_info_in == params.ParamTypes.header:
dependant.header_params.append(field)
else:
assert (
field_info.in_ == params.ParamTypes.cookie
field_info_in == params.ParamTypes.cookie
), f"non-body parameters must be in path, query, header or cookie: {field.name}"
dependant.cookie_params.append(field)
@@ -522,6 +528,7 @@ async def solve_dependencies(
response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
async_exit_stack: AsyncExitStack,
) -> Tuple[
Dict[str, Any],
List[Any],
@@ -568,6 +575,7 @@ async def solve_dependencies(
response=response,
dependency_overrides_provider=dependency_overrides_provider,
dependency_cache=dependency_cache,
async_exit_stack=async_exit_stack,
)
(
sub_values,
@@ -583,10 +591,8 @@ async def solve_dependencies(
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
solved = dependency_cache[sub_dependant.cache_key]
elif is_gen_callable(call) or is_async_gen_callable(call):
stack = request.scope.get("fastapi_astack")
assert isinstance(stack, AsyncExitStack)
solved = await solve_generator(
call=call, stack=stack, sub_values=sub_values
call=call, stack=async_exit_stack, sub_values=sub_values
)
elif is_coroutine_callable(call):
solved = await call(**sub_values)

View File

@@ -1,25 +0,0 @@
from typing import Optional
from fastapi.concurrency import AsyncExitStack
from starlette.types import ASGIApp, Receive, Scope, Send
class AsyncExitStackMiddleware:
def __init__(self, app: ASGIApp, context_name: str = "fastapi_astack") -> None:
self.app = app
self.context_name = context_name
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
dependency_exception: Optional[Exception] = None
async with AsyncExitStack() as stack:
scope[self.context_name] = stack
try:
await self.app(scope, receive, send)
except Exception as e:
dependency_exception = e
raise e
if dependency_exception:
# This exception was possibly handled by the dependency but it should
# still bubble up so that the ServerErrorMiddleware can return a 500
# or the ExceptionMiddleware can catch and handle any other exceptions
raise dependency_exception

View File

@@ -216,95 +216,124 @@ def get_request_handler(
actual_response_class = response_class
async def app(request: Request) -> Response:
try:
body: Any = None
if body_field:
if is_body_form:
body = await request.form()
stack = request.scope.get("fastapi_astack")
assert isinstance(stack, AsyncExitStack)
stack.push_async_callback(body.close)
exception_to_reraise: Optional[Exception] = None
response: Union[Response, None] = None
async with AsyncExitStack() as async_exit_stack:
# TODO: remove this scope later, after a few releases
# This scope fastapi_astack is no longer used by FastAPI, kept for
# compatibility, just in case
request.scope["fastapi_astack"] = async_exit_stack
try:
body: Any = None
if body_field:
if is_body_form:
body = await request.form()
async_exit_stack.push_async_callback(body.close)
else:
body_bytes = await request.body()
if body_bytes:
json_body: Any = Undefined
content_type_value = request.headers.get("content-type")
if not content_type_value:
json_body = await request.json()
else:
message = email.message.Message()
message["content-type"] = content_type_value
if message.get_content_maintype() == "application":
subtype = message.get_content_subtype()
if subtype == "json" or subtype.endswith("+json"):
json_body = await request.json()
if json_body != Undefined:
body = json_body
else:
body = body_bytes
except json.JSONDecodeError as e:
validation_error = RequestValidationError(
[
{
"type": "json_invalid",
"loc": ("body", e.pos),
"msg": "JSON decode error",
"input": {},
"ctx": {"error": e.msg},
}
],
body=e.doc,
)
exception_to_reraise = validation_error
raise validation_error from e
except HTTPException as e:
exception_to_reraise = e
raise
except Exception as e:
http_error = HTTPException(
status_code=400, detail="There was an error parsing the body"
)
exception_to_reraise = http_error
raise http_error from e
try:
solved_result = await solve_dependencies(
request=request,
dependant=dependant,
body=body,
dependency_overrides_provider=dependency_overrides_provider,
async_exit_stack=async_exit_stack,
)
values, errors, background_tasks, sub_response, _ = solved_result
except Exception as e:
exception_to_reraise = e
raise e
if errors:
validation_error = RequestValidationError(
_normalize_errors(errors), body=body
)
exception_to_reraise = validation_error
raise validation_error
else:
try:
raw_response = await run_endpoint_function(
dependant=dependant, values=values, is_coroutine=is_coroutine
)
except Exception as e:
exception_to_reraise = e
raise e
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = background_tasks
response = raw_response
else:
body_bytes = await request.body()
if body_bytes:
json_body: Any = Undefined
content_type_value = request.headers.get("content-type")
if not content_type_value:
json_body = await request.json()
else:
message = email.message.Message()
message["content-type"] = content_type_value
if message.get_content_maintype() == "application":
subtype = message.get_content_subtype()
if subtype == "json" or subtype.endswith("+json"):
json_body = await request.json()
if json_body != Undefined:
body = json_body
else:
body = body_bytes
except json.JSONDecodeError as e:
raise RequestValidationError(
[
{
"type": "json_invalid",
"loc": ("body", e.pos),
"msg": "JSON decode error",
"input": {},
"ctx": {"error": e.msg},
}
],
body=e.doc,
) from e
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=400, detail="There was an error parsing the body"
) from e
solved_result = await solve_dependencies(
request=request,
dependant=dependant,
body=body,
dependency_overrides_provider=dependency_overrides_provider,
)
values, errors, background_tasks, sub_response, _ = solved_result
if errors:
raise RequestValidationError(_normalize_errors(errors), body=body)
else:
raw_response = await run_endpoint_function(
dependant=dependant, values=values, is_coroutine=is_coroutine
)
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = background_tasks
return raw_response
response_args: Dict[str, Any] = {"background": background_tasks}
# If status_code was set, use it, otherwise use the default from the
# response class, in the case of redirect it's 307
current_status_code = (
status_code if status_code else sub_response.status_code
)
if current_status_code is not None:
response_args["status_code"] = current_status_code
if sub_response.status_code:
response_args["status_code"] = sub_response.status_code
content = await serialize_response(
field=response_field,
response_content=raw_response,
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
exclude_unset=response_model_exclude_unset,
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
)
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(sub_response.headers.raw)
return response
response_args: Dict[str, Any] = {"background": background_tasks}
# If status_code was set, use it, otherwise use the default from the
# response class, in the case of redirect it's 307
current_status_code = (
status_code if status_code else sub_response.status_code
)
if current_status_code is not None:
response_args["status_code"] = current_status_code
if sub_response.status_code:
response_args["status_code"] = sub_response.status_code
content = await serialize_response(
field=response_field,
response_content=raw_response,
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
exclude_unset=response_model_exclude_unset,
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
)
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(sub_response.headers.raw)
# This exception was possibly handled by the dependency but it should
# still bubble up so that the ServerErrorMiddleware can return a 500
# or the ExceptionMiddleware can catch and handle any other exceptions
if exception_to_reraise:
raise exception_to_reraise
assert response is not None, "An error occurred while generating the request"
return response
return app
@@ -313,16 +342,22 @@ def get_websocket_app(
dependant: Dependant, dependency_overrides_provider: Optional[Any] = None
) -> Callable[[WebSocket], Coroutine[Any, Any, Any]]:
async def app(websocket: WebSocket) -> None:
solved_result = await solve_dependencies(
request=websocket,
dependant=dependant,
dependency_overrides_provider=dependency_overrides_provider,
)
values, errors, _, _2, _3 = solved_result
if errors:
raise WebSocketRequestValidationError(_normalize_errors(errors))
assert dependant.call is not None, "dependant.call must be a function"
await dependant.call(**values)
async with AsyncExitStack() as async_exit_stack:
# TODO: remove this scope later, after a few releases
# This scope fastapi_astack is no longer used by FastAPI, kept for
# compatibility, just in case
websocket.scope["fastapi_astack"] = async_exit_stack
solved_result = await solve_dependencies(
request=websocket,
dependant=dependant,
dependency_overrides_provider=dependency_overrides_provider,
async_exit_stack=async_exit_stack,
)
values, errors, _, _2, _3 = solved_result
if errors:
raise WebSocketRequestValidationError(_normalize_errors(errors))
assert dependant.call is not None, "dependant.call must be a function"
await dependant.call(**values)
return app

View File

@@ -6,6 +6,5 @@ from pydantic import BaseModel
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
UnionType = getattr(types, "UnionType", Union)
NoneType = getattr(types, "UnionType", None)
ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str]
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]]

View File

@@ -84,6 +84,12 @@ module = "fastapi.tests.*"
ignore_missing_imports = true
check_untyped_defs = true
[[tool.mypy.overrides]]
module = "docs_src.*"
disallow_incomplete_defs = false
disallow_untyped_defs = false
disallow_untyped_calls = false
[tool.pytest.ini_options]
addopts = [
"--strict-config",
@@ -167,6 +173,9 @@ ignore = [
"docs_src/security/tutorial005_an_py39.py" = ["B904"]
"docs_src/security/tutorial005_py310.py" = ["B904"]
"docs_src/security/tutorial005_py39.py" = ["B904"]
"docs_src/dependencies/tutorial008b.py" = ["B904"]
"docs_src/dependencies/tutorial008b_an.py" = ["B904"]
"docs_src/dependencies/tutorial008b_an_py39.py" = ["B904"]
[tool.ruff.isort]

View File

@@ -1,6 +1,8 @@
import pytest
from fastapi import Depends, FastAPI, Path
from fastapi.param_functions import Query
from fastapi.testclient import TestClient
from fastapi.utils import PYDANTIC_V2
from typing_extensions import Annotated
app = FastAPI()
@@ -28,18 +30,13 @@ def test_no_annotated_defaults():
pass # pragma: nocover
def test_no_multiple_annotations():
def test_multiple_annotations():
async def dep():
pass # pragma: nocover
with pytest.raises(
AssertionError,
match="Cannot specify multiple `Annotated` FastAPI arguments for 'foo'",
):
@app.get("/")
async def get(foo: Annotated[int, Query(min_length=1), Query()]):
pass # pragma: nocover
@app.get("/multi-query")
async def get(foo: Annotated[int, Query(gt=2), Query(lt=10)]):
return foo
with pytest.raises(
AssertionError,
@@ -64,3 +61,15 @@ def test_no_multiple_annotations():
@app.get("/")
async def get3(foo: Annotated[int, Query(min_length=1)] = Depends(dep)):
pass # pragma: nocover
client = TestClient(app)
response = client.get("/multi-query", params={"foo": "5"})
assert response.status_code == 200
assert response.json() == 5
response = client.get("/multi-query", params={"foo": "123"})
assert response.status_code == 422
if PYDANTIC_V2:
response = client.get("/multi-query", params={"foo": "1"})
assert response.status_code == 422

View File

@@ -57,7 +57,7 @@ foo_is_short = {
{
"ctx": {"min_length": 1},
"loc": ["query", "foo"],
"msg": "String should have at least 1 characters",
"msg": "String should have at least 1 character",
"type": "string_too_short",
"input": "",
"url": match_pydantic_error_url("string_too_short"),

View File

@@ -1,7 +1,9 @@
import json
from typing import Dict
import pytest
from fastapi import BackgroundTasks, Depends, FastAPI
from fastapi.responses import StreamingResponse
from fastapi.testclient import TestClient
app = FastAPI()
@@ -200,6 +202,13 @@ async def get_sync_context_b_bg(
return state
@app.middleware("http")
async def middleware(request, call_next):
response: StreamingResponse = await call_next(request)
response.headers["x-state"] = json.dumps(state.copy())
return response
client = TestClient(app)
@@ -274,9 +283,13 @@ def test_background_tasks():
assert data["context_b"] == "started b"
assert data["context_a"] == "started a"
assert data["bg"] == "not set"
middleware_state = json.loads(response.headers["x-state"])
assert middleware_state["context_b"] == "finished b with a: started a"
assert middleware_state["context_a"] == "finished a"
assert middleware_state["bg"] == "not set"
assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a"
assert state["bg"] == "bg set - b: started b - a: started a"
assert state["bg"] == "bg set - b: finished b with a: started a - a: finished a"
def test_sync_raise_raises():
@@ -382,4 +395,7 @@ def test_sync_background_tasks():
assert data["sync_bg"] == "not set"
assert state["context_b"] == "finished b with a: started a"
assert state["context_a"] == "finished a"
assert state["sync_bg"] == "sync_bg set - b: started b - a: started a"
assert (
state["sync_bg"]
== "sync_bg set - b: finished b with a: started a - a: finished a"
)

View File

@@ -0,0 +1,23 @@
from fastapi.testclient import TestClient
from docs_src.dependencies.tutorial008b import app
client = TestClient(app)
def test_get_no_item():
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found"}
def test_owner_error():
response = client.get("/items/plumbus")
assert response.status_code == 400, response.text
assert response.json() == {"detail": "Onwer error: Rick"}
def test_get_item():
response = client.get("/items/portal-gun")
assert response.status_code == 200, response.text
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}

View File

@@ -0,0 +1,23 @@
from fastapi.testclient import TestClient
from docs_src.dependencies.tutorial008b_an import app
client = TestClient(app)
def test_get_no_item():
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found"}
def test_owner_error():
response = client.get("/items/plumbus")
assert response.status_code == 400, response.text
assert response.json() == {"detail": "Onwer error: Rick"}
def test_get_item():
response = client.get("/items/portal-gun")
assert response.status_code == 200, response.text
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}

View File

@@ -0,0 +1,23 @@
from fastapi.testclient import TestClient
from docs_src.dependencies.tutorial008b_an import app
client = TestClient(app)
def test_get_no_item():
response = client.get("/items/foo")
assert response.status_code == 404, response.text
assert response.json() == {"detail": "Item not found"}
def test_owner_error():
response = client.get("/items/plumbus")
assert response.status_code == 400, response.text
assert response.json() == {"detail": "Onwer error: Rick"}
def test_get_item():
response = client.get("/items/portal-gun")
assert response.status_code == 200, response.text
assert response.json() == {"description": "Gun to create portals", "owner": "Rick"}