Compare commits

...

34 Commits

Author SHA1 Message Date
Sebastián Ramírez
d4ddf4e62a 🔖 Release 0.49.2 2020-02-29 18:19:52 +01:00
Sebastián Ramírez
cb25dab986 📝 Update release notes 2020-02-29 18:01:40 +01:00
Sebastián Ramírez
7aa1628336 📝 Update release notes 2020-02-29 18:00:57 +01:00
sattosan
1dbd3d7aa7 ✏️ Fix broken links in release notes (#1052) 2020-02-29 18:00:40 +01:00
sattosan
24e73e01c7 ✏️ Fix typo in release notes (#1051) 2020-02-29 17:56:41 +01:00
Patrick McKenna
e26f94018c ♻️ Refactor serialize_response parameter name (#1031) 2020-02-29 17:33:02 +01:00
sm-Fifteen
1ce67887b9 ♻️ Refactor function calling a path operation function to simplify profiling (#1027) 2020-02-29 17:28:30 +01:00
Sebastián Ramírez
8e0200607f 📝 Update release notes 2020-02-29 17:16:00 +01:00
sm-Fifteen
bd407ca705 Add testing dependencies #1026 2020-02-29 14:23:41 +01:00
Sebastián Ramírez
48b5ef1681 📝 Update release notes 2020-02-29 14:06:45 +01:00
Patrick McKenna
afad59dfbb 🐛 Admit valid types for Pydantic fields as responses models (#1017) 2020-02-29 14:04:35 +01:00
Sebastián Ramírez
0f9be1d2e7 📝 Update release notes 2020-02-29 13:16:20 +01:00
Vegard Stikbakke
48c2406495 🎨 Fix markdown code section rendering error in SQL tutorial (#1015) 2020-02-29 12:40:09 +01:00
Sebastián Ramírez
9958d93120 🔖 Release 0.49.1 2020-02-28 22:48:03 +01:00
Sebastián Ramírez
b63512cd92 📝 Update release notes 2020-02-28 22:46:55 +01:00
merowinger92
74c4d1c1db 🐛 Fix declaring a single parameter per name (#994) 2020-02-28 22:36:30 +01:00
Sebastián Ramírez
7ccd81f706 🚀 Update Netlify deploy action (#1047) 2020-02-28 17:27:43 +01:00
Taneli Hukkinen
1da8d3f1e6 🔧 Move all mypy configurations to configuration file (#987) 2020-02-27 21:03:03 +01:00
Camila Gutierrez
92016da962 💚 Do not deploy preview to Netlify on external PRs while GitHub actions are solved (#1046) 2020-02-27 20:59:26 +01:00
Sebastián Ramírez
9c3c9b6e78 🔖 Release version 0.49.0 2020-02-16 21:11:28 +01:00
Sebastián Ramírez
687871e46a 📝 Update release notes 2020-02-16 21:08:58 +01:00
Patrick McKenna
3c1803897f 🐛 Fix path encoding (#978) 2020-02-16 21:06:41 +01:00
T. Tokusumi
1c3289f115 📝 Add link: JP articles related to fastapi (#974) 2020-02-16 21:00:42 +01:00
Taras Sotnikov
35d41adc7b 📝 Fix broken link in docs (#949) 2020-02-16 20:57:29 +01:00
Sebastián Ramírez
9a3dd91030 📝 Update release notes 2020-02-16 20:55:07 +01:00
Sebastián Ramírez
017eae63b1 📝 Update release notes 2020-02-16 20:53:50 +01:00
Nikita Kolesov
8af4454251 ✏️ Fix typo (#941) 2020-02-16 20:51:18 +01:00
Sebastián Ramírez
adf252768c 📝 Update docs for dependencies with yield (#986) 2020-02-16 20:49:12 +01:00
Sebastián Ramírez
3705bdefd5 📝 Update release notes 2020-02-16 19:48:52 +01:00
Sebastián Ramírez
25e94d6344 Add mermaid.js support in Markdown fenced blocks for diagrams (#985) 2020-02-16 19:48:20 +01:00
Sebastián Ramírez
26c345b766 📝 Update release notes 2020-02-15 13:26:01 +01:00
Sebastián Ramírez
9ce50b4684 👷 Add GitHub actions to deploy to Netlify (#983) 2020-02-15 13:24:34 +01:00
Sebastián Ramírez
66954c7ded 📝 Update release notes 2020-02-12 21:37:05 +01:00
Sebastián Ramírez
e0c3519b94 Allow callables (as functools.partial) in path operations (#977) 2020-02-12 21:36:14 +01:00
27 changed files with 729 additions and 211 deletions

29
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Build and Deploy to Netlify
on:
push:
pull_request:
types: [opened, synchronize]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.7"
- name: Install Flit
run: python3.7 -m pip install flit
- name: Install docs extras
run: python3.7 -m flit install --extras doc
- name: Build MkDocs
run: python3.7 -m mkdocs build
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.0.3
with:
publish-dir: './site'
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

View File

@@ -429,31 +429,31 @@ Repeat the same process with the 10 tabs. This time all of them will wait and yo
* `sql_app/database.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases_peewee/sql_app/database.py!}
```
* `sql_app/models.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases_peewee/sql_app/models.py!}
```
* `sql_app/schemas.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases_peewee/sql_app/schemas.py!}
```
* `sql_app/crud.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases_peewee/sql_app/crud.py!}
```
* `sql_app/main.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases_peewee/sql_app/main.py!}
```

View File

@@ -79,6 +79,10 @@ Here's an incomplete list of some of them.
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement" class="external-link" target="_blank">【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
* <a href="https://qiita.com/bee2/items/0ad260ab9835a2087dae" class="external-link" target="_blank">PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
* <a href="https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9" class="external-link" target="_blank">[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
### Chinese
* <a href="https://cloud.tencent.com/developer/article/1431448" class="external-link" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" class="external-link" target="_blank">逍遥散人</a>.

View File

@@ -1,4 +1,4 @@
Are you liking **FastAPI**?
Do you like **FastAPI**?
Would you like to help FastAPI, other users, and the author?

View File

@@ -256,7 +256,7 @@ Taken from the official Pydantic docs:
**FastAPI** is all based on Pydantic.
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
## Type hints in **FastAPI**
@@ -276,7 +276,7 @@ With **FastAPI** you declare parameters with type hints and you get:
* **Document** the API using OpenAPI:
* which is then used by the automatic interactive documentation user interfaces.
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you.

View File

@@ -1,5 +1,33 @@
## Latest changes
## 0.49.2
* Fix links in release notes. PR [#1052](https://github.com/tiangolo/fastapi/pull/1052) by [@sattosan](https://github.com/sattosan).
* Fix typo in release notes. PR [#1051](https://github.com/tiangolo/fastapi/pull/1051) by [@sattosan](https://github.com/sattosan).
* Refactor/clarify `serialize_response` parameter name to avoid confusion. PR [#1031](https://github.com/tiangolo/fastapi/pull/1031) by [@patrickmckenna](https://github.com/patrickmckenna).
* Refactor calling each a path operation's handler function in an isolated function, to simplify profiling. PR [#1027](https://github.com/tiangolo/fastapi/pull/1027) by [@sm-Fifteen](https://github.com/sm-Fifteen).
* Add missing dependencies for testing. PR [#1026](https://github.com/tiangolo/fastapi/pull/1026) by [@sm-Fifteen](https://github.com/sm-Fifteen).
* Fix accepting valid types for response models, including Python types like `List[int]`. PR [#1017](https://github.com/tiangolo/fastapi/pull/1017) by [@patrickmckenna](https://github.com/patrickmckenna).
* Fix format in SQL tutorial. PR [#1015](https://github.com/tiangolo/fastapi/pull/1015) by [@vegarsti](https://github.com/vegarsti).
## 0.49.1
* Fix path operation duplicated parameters when used in dependencies and the path operation function. PR [#994](https://github.com/tiangolo/fastapi/pull/994) by [@merowinger92](https://github.com/merowinger92).
* Update Netlify previews deployment GitHub action as the fix is already merged and there's a new release. PR [#1047](https://github.com/tiangolo/fastapi/pull/1047).
* Move mypy configurations to config file. PR [#987](https://github.com/tiangolo/fastapi/pull/987) by [@hukkinj1](https://github.com/hukkinj1).
* Temporary fix to Netlify previews not deployable from PRs from forks. PR [#1046](https://github.com/tiangolo/fastapi/pull/1046) by [@mariacamilagl](https://github.com/mariacamilagl).
## 0.49.0
* Fix encoding of `pathlib` paths in `jsonable_encoder`. PR [#978](https://github.com/tiangolo/fastapi/pull/978) by [@patrickmckenna](https://github.com/patrickmckenna).
* Add articles to [External Links](https://fastapi.tiangolo.com/external-links/): [PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)](https://qiita.com/bee2/items/0ad260ab9835a2087dae) and [[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する](https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9). PR [#974](https://github.com/tiangolo/fastapi/pull/974) by [@tokusumi](https://github.com/tokusumi).
* Fix broken links in docs. PR [#949](https://github.com/tiangolo/fastapi/pull/949) by [@tsotnikov](https://github.com/tsotnikov).
* Fix small typos. PR [#941](https://github.com/tiangolo/fastapi/pull/941) by [@NikitaKolesov](https://github.com/NikitaKolesov).
* Update and clarify docs for dependencies with `yield`. PR [#986](https://github.com/tiangolo/fastapi/pull/986).
* Add Mermaid JS support for diagrams in docs. Add first diagrams to [Dependencies: First Steps](https://fastapi.tiangolo.com/tutorial/dependencies/) and [Dependencies with `yield` and `HTTPException`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception). PR [#985](https://github.com/tiangolo/fastapi/pull/985).
* Update CI to run docs deployment in GitHub actions. PR [#983](https://github.com/tiangolo/fastapi/pull/983).
* Allow `callable`s in *path operation functions*, like functions modified with `functools.partial`. PR [#977](https://github.com/tiangolo/fastapi/pull/977).
## 0.48.0
* Run linters first in tests to error out faster. PR [#948](https://github.com/tiangolo/fastapi/pull/948).
@@ -30,9 +58,9 @@
* Fix handling form *path operation* parameters declared with pure classes like `list`, `tuple`, etc. PR [#856](https://github.com/tiangolo/fastapi/pull/856) by [@nsidnev](https://github.com/nsidnev).
* Add request `body` to `RequestValidationError`, new docs: [Use the `RequestValidationError` body](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body). Initial PR [#853](https://github.com/tiangolo/fastapi/pull/853) by [@aviramha](https://github.com/aviramha).
* Update [External Links](https://fastapi.tiangolo.com/external-links/) with new links and dynamic GitHub projects with `fastapi` topic. PR [#850](https://github.com/tiangolo/fastapi/pull/850).
* Fix Peewee `contextvars` handling in docs: [SQL (Relational) Databases with Peewee](https://fastapi.tiangolo.com/tutorial/sql-databases-peewee/). PR [#879](https://github.com/tiangolo/fastapi/pull/879).
* Fix Peewee `contextvars` handling in docs: [SQL (Relational) Databases with Peewee](https://fastapi.tiangolo.com/advanced/sql-databases-peewee/). PR [#879](https://github.com/tiangolo/fastapi/pull/879).
* Setup development environment with Python's Venv and Flit, instead of requiring the extra Pipenv duplicating dependencies. Updated docs: [Development - Contributing](https://fastapi.tiangolo.com/contributing/). PR [#877](https://github.com/tiangolo/fastapi/pull/877).
* Update docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/security/http-basic-auth/) to improve security against timing attacks. Initial PR [#807](https://github.com/tiangolo/fastapi/pull/807) by [@zwass](https://github.com/zwass).
* Update docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/) to improve security against timing attacks. Initial PR [#807](https://github.com/tiangolo/fastapi/pull/807) by [@zwass](https://github.com/zwass).
## 0.46.0
@@ -46,12 +74,12 @@
* Add support for subtypes of main types in `jsonable_encoder`, e.g. asyncpg's UUIDs. PR [#756](https://github.com/tiangolo/fastapi/pull/756) by [@RmStorm](https://github.com/RmStorm).
* Fix usage of Pydantic's `HttpUrl` in docs. PR [#832](https://github.com/tiangolo/fastapi/pull/832) by [@Dustyposa](https://github.com/Dustyposa).
* Fix Twitter links in docs. PR [#813](https://github.com/tiangolo/fastapi/pull/813) by [@justindujardin](https://github.com/justindujardin).
* Add docs for correctly [using FastAPI with Peewee ORM](https://fastapi.tiangolo.com/tutorial/sql-databases-peewee/). Including how to overwrite parts of Peewee to correctly handle async threads. PR [#789](https://github.com/tiangolo/fastapi/pull/789).
* Add docs for correctly [using FastAPI with Peewee ORM](https://fastapi.tiangolo.com/advanced/sql-databases-peewee/). Including how to overwrite parts of Peewee to correctly handle async threads. PR [#789](https://github.com/tiangolo/fastapi/pull/789).
## 0.45.0
* Add support for OpenAPI Callbacks:
* New docs: [OpenAPI Callbacks](https://fastapi.tiangolo.com/tutorial/openapi-callbacks/).
* New docs: [OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
* Refactor generation of `operationId`s to be valid Python names (also valid variables in most languages).
* Add `default_response_class` parameter to `APIRouter`.
* Original PR [#722](https://github.com/tiangolo/fastapi/pull/722) by [@booooh](https://github.com/booooh).
@@ -84,11 +112,11 @@
## 0.43.0
* Update docs to reduce gender bias. PR [#645](https://github.com/tiangolo/fastapi/pull/645) by [@ticosax](https://github.com/ticosax).
* Add docs about [overriding the `operationId` for all the *path operations*](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) based on their function name. PR [#642](https://github.com/tiangolo/fastapi/pull/642) by [@SKalt](https://github.com/SKalt).
* Add docs about [overriding the `operationId` for all the *path operations*](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) based on their function name. PR [#642](https://github.com/tiangolo/fastapi/pull/642) by [@SKalt](https://github.com/SKalt).
* Fix validators in models generating an incorrect key order. PR [#637](https://github.com/tiangolo/fastapi/pull/637) by [@jaddison](https://github.com/jaddison).
* Generate correct OpenAPI docs for responses with no content. PR [#621](https://github.com/tiangolo/fastapi/pull/621) by [@brotskydotcom](https://github.com/brotskydotcom).
* Remove `$` from Bash code blocks in docs for consistency. PR [#613](https://github.com/tiangolo/fastapi/pull/613) by [@nstapelbroek](https://github.com/nstapelbroek).
* Add docs for [self-serving docs' (Swagger UI) static assets](https://fastapi.tiangolo.com/tutorial/extending-openapi/#self-hosting-javascript-and-css-for-docs), e.g. to use the docs offline, or without Internet. Initial PR [#557](https://github.com/tiangolo/fastapi/pull/557) by [@svalouch](https://github.com/svalouch).
* Add docs for [self-serving docs' (Swagger UI) static assets](https://fastapi.tiangolo.com/advanced/extending-openapi/#self-hosting-javascript-and-css-for-docs), e.g. to use the docs offline, or without Internet. Initial PR [#557](https://github.com/tiangolo/fastapi/pull/557) by [@svalouch](https://github.com/svalouch).
* Fix `black` linting after upgrade. PR [#682](https://github.com/tiangolo/fastapi/pull/682) by [@frankie567](https://github.com/frankie567).
## 0.42.0
@@ -117,7 +145,7 @@
* Run middleware-like code only for a subset of *path operations*.
* Process a request before passing it to a *path operation function*. E.g. decompressing, deserializing, etc.
* Processing a response after being generated by *path operation functions* but before returning it. E.g. adding custom headers, logging, adding extra metadata.
* New docs section: [Custom Request and APIRoute class](https://fastapi.tiangolo.com/tutorial/custom-request-and-route/).
* New docs section: [Custom Request and APIRoute class](https://fastapi.tiangolo.com/advanced/custom-request-and-route/).
* PR [#589](https://github.com/tiangolo/fastapi/pull/589) by [@dmontagu](https://github.com/dmontagu).
* Fix preserving custom route class in routers when including other sub-routers. PR [#538](https://github.com/tiangolo/fastapi/pull/538) by [@dmontagu](https://github.com/dmontagu).
@@ -126,11 +154,11 @@
* Add notes to docs about installing `python-multipart` when using forms. PR [#574](https://github.com/tiangolo/fastapi/pull/574) by [@sliptonic](https://github.com/sliptonic).
* Generate OpenAPI schemas in alphabetical order. PR [#554](https://github.com/tiangolo/fastapi/pull/554) by [@dmontagu](https://github.com/dmontagu).
* Add support for truncating docstrings from *path operation functions*.
* New docs at [Advanced description from docstring](https://fastapi.tiangolo.com/tutorial/path-operation-advanced-configuration/#advanced-description-from-docstring).
* New docs at [Advanced description from docstring](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#advanced-description-from-docstring).
* PR [#556](https://github.com/tiangolo/fastapi/pull/556) by [@svalouch](https://github.com/svalouch).
* Fix `DOCTYPE` in HTML files generated for Swagger UI and ReDoc. PR [#537](https://github.com/tiangolo/fastapi/pull/537) by [@Trim21](https://github.com/Trim21).
* Fix handling `4XX` responses overriding default `422` validation error responses. PR [#517](https://github.com/tiangolo/fastapi/pull/517) by [@tsouvarev](https://github.com/tsouvarev).
* Fix typo in documentation for [Simple HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/security/http-basic-auth/#simple-http-basic-auth). PR [#514](https://github.com/tiangolo/fastapi/pull/514) by [@prostomarkeloff](https://github.com/prostomarkeloff).
* Fix typo in documentation for [Simple HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/#simple-http-basic-auth). PR [#514](https://github.com/tiangolo/fastapi/pull/514) by [@prostomarkeloff](https://github.com/prostomarkeloff).
* Fix incorrect documentation example in [first steps](https://fastapi.tiangolo.com/tutorial/first-steps/). PR [#511](https://github.com/tiangolo/fastapi/pull/511) by [@IgnatovFedor](https://github.com/IgnatovFedor).
* Add support for Swagger UI [initOauth](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md) settings with the parameter `swagger_ui_init_oauth`. PR [#499](https://github.com/tiangolo/fastapi/pull/499) by [@zamiramir](https://github.com/zamiramir).
@@ -201,7 +229,7 @@
* Fix source code `limit` for example in [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/). PR [#366](https://github.com/tiangolo/fastapi/pull/366) by [@Smashman](https://github.com/Smashman).
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
* Update docs for `Enum`s to inherit from `str` and improve Swagger UI rendering. PR [#351](https://github.com/tiangolo/fastapi/pull/351).
@@ -267,7 +295,7 @@
* Fix handling an empty-body request with a required body param. PR [#311](https://github.com/tiangolo/fastapi/pull/311).
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/advanced/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
* Fix docs discrepancy in docs for [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). PR [#288](https://github.com/tiangolo/fastapi/pull/288) by [@awiddersheim](https://github.com/awiddersheim).
@@ -275,9 +303,9 @@
* Add support for declaring a `Response` parameter:
* This allows declaring:
* [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/).
* [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/).
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/tutorial/response-change-status-code/).
* [Response Cookies](https://fastapi.tiangolo.com/advanced/response-cookies/).
* [Response Headers](https://fastapi.tiangolo.com/advanced/response-headers/).
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/advanced/response-change-status-code/).
* All of this while still being able to return arbitrary objects (`dict`, DB model, etc).
* Update attribution to Hug, for inspiring the `response` parameter pattern.
* PR [#294](https://github.com/tiangolo/fastapi/pull/294).
@@ -294,7 +322,7 @@
* Implement dependency overrides for testing.
* This allows using overrides/mocks of dependencies during tests.
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/tutorial/testing-dependencies/).
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/advanced/testing-dependencies/).
* PR [#291](https://github.com/tiangolo/fastapi/pull/291).
## 0.27.2
@@ -366,7 +394,7 @@
* `Path`
* `Query`
* ...as these are compatible with the WebSockets protocol (e.g. `Body` is not).
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/tutorial/websockets/).
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
* PR [#178](https://github.com/tiangolo/fastapi/pull/178) by [@jekirl](https://github.com/jekirl).
* Upgrade the compatible version of Pydantic to `0.26.0`.
@@ -433,16 +461,16 @@
* Upgrade OAuth2:
* Upgrade Password flow using Bearer tokens to use the correct HTTP status code 401 `UNAUTHORIZED`, with `WWW-Authenticate` headers.
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/tutorial/security/intro/).
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/advanced/security/).
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
* Update docs, images, tests.
* PR [#188](https://github.com/tiangolo/fastapi/pull/188).
* Include [Hypercorn](https://gitlab.com/pgjones/hypercorn) as an alternative ASGI server in the docs. PR [#187](https://github.com/tiangolo/fastapi/pull/187).
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/tutorial/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/advanced/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/advanced/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/advanced/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
* Fix typos in docs. PR [#176](https://github.com/tiangolo/fastapi/pull/176) by [@chdsbd](https://github.com/chdsbd).
@@ -452,13 +480,13 @@
* Add docs:
* How to use the `jsonable_encoder` in [JSON compatible encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
* How to [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/).
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/tutorial/custom-response/).
* How to [Return a Response directly](https://fastapi.tiangolo.com/advanced/response-directly/).
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/advanced/custom-response/).
* PR [#184](https://github.com/tiangolo/fastapi/pull/184).
## 0.18.0
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
* Upgrade HTTP Basic Auth handling with automatic headers (automatic browser login prompt). PR [#175](https://github.com/tiangolo/fastapi/pull/175).
@@ -476,7 +504,7 @@
## 0.16.0
* Upgrade *path operation* `doctsring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
* Upgrade *path operation* `docstring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
* Refactor internal usage of Pydantic to use correct data types. PR [#164](https://github.com/tiangolo/fastapi/pull/164).
@@ -490,7 +518,7 @@
* Add support for multiple file uploads (as a single form field). New docs at: [Multiple file uploads](https://fastapi.tiangolo.com/tutorial/request-files/#multiple-file-uploads). PR [#158](https://github.com/tiangolo/fastapi/pull/158).
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/tutorial/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/advanced/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
## 0.14.0
@@ -511,7 +539,7 @@
* Improve `Security` handling, merging scopes when declaring `SecurityScopes`.
* Allow using `SecurityBase` (like `OAuth2`) classes with `Depends` and still document them. `Security` now is needed only to declare `scopes`.
* Updated docs about: [OAuth2 with Password (and hashing), Bearer with JWT tokens](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/).
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
* PR [#141](https://github.com/tiangolo/fastapi/pull/141).
## 0.12.1
@@ -526,7 +554,7 @@
* Add additional `responses` parameter to *path operation decorators* to extend responses in OpenAPI (and API docs).
* It also allows extending existing responses generated from `response_model`, declare other media types (like images), etc.
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/tutorial/additional-responses/).
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/advanced/additional-responses/).
* `responses` can also be added to `.include_router()`, the updated docs are here: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses).
* PR [#97](https://github.com/tiangolo/fastapi/pull/97) originally initiated by [@barsi](https://github.com/barsi).
* Update `scripts/test-cov-html.sh` to allow passing extra parameters like `-vv`, for development.
@@ -543,7 +571,7 @@
* Add Gitter chat, badge, links, etc. [https://gitter.im/tiangolo/fastapi](https://gitter.im/tiangolo/fastapi) . PR [#117](https://github.com/tiangolo/fastapi/pull/117).
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/tutorial/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/advanced/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR [#92](https://github.com/tiangolo/fastapi/pull/92) by [@blueyed](https://github.com/blueyed).
@@ -563,7 +591,7 @@
## 0.10.1
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/advanced/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
## 0.10.0
@@ -571,7 +599,7 @@
* Add support for `.websocket_route()` in `APIRouter`. PR [#100](https://github.com/tiangolo/fastapi/pull/100) by [@euri10](https://github.com/euri10).
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/tutorial/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/advanced/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
## 0.9.1
@@ -625,7 +653,7 @@
* Add section about [History, Design and Future](https://fastapi.tiangolo.com/history-design-future/).
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/tutorial/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/advanced/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
## 0.6.3
@@ -641,7 +669,7 @@
## 0.6.1
* Add docs for GraphQL: [https://fastapi.tiangolo.com/tutorial/graphql/](https://fastapi.tiangolo.com/tutorial/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
* Add docs for GraphQL: [https://fastapi.tiangolo.com/advanced/graphql/](https://fastapi.tiangolo.com/advanced/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
## 0.6.0
@@ -663,7 +691,7 @@
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: [https://fastapi.tiangolo.com/tutorial/handling-errors/](https://fastapi.tiangolo.com/tutorial/handling-errors/). PR [#35](https://github.com/tiangolo/fastapi/pull/35).
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/tutorial/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/advanced/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
* Add issue templates to simplify reporting bugs, getting help, etc: [#34](https://github.com/tiangolo/fastapi/pull/34).
@@ -671,7 +699,7 @@
## 0.4.0
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/](https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/advanced/sub-applications-proxy/](https://fastapi.tiangolo.com/advanced/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
* Update [docs/tutorial for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/) including note about _DB Browser for SQLite_.

View File

@@ -102,6 +102,79 @@ You can have any combinations of dependencies that you want.
**FastAPI** uses them internally to achieve this.
## Dependencies with `yield` and `HTTPException`
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* [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. 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}.
!!! tip
You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
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.
```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 -->> handler: Raise HTTPException
handler -->> client: HTTP error response
operation -->> dep: Raise other 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
```
!!! info
Only **one response** will be sent to the client. It might be one of the error responses or it will be the response from the *path operation*.
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}. And that exception would be handled by that custom exception handler instead of the dependency exit code.
But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.
## Context Managers
### What are "Context Managers"

View File

@@ -82,6 +82,19 @@ Whenever a new request arrives, **FastAPI** will take care of:
* Get the result from your function.
* Assign that result to the parameter in your *path operation function*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
!!! check
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
@@ -154,7 +167,39 @@ Although the hierarchical dependency injection system is very simple to define a
You can define dependencies that in turn can define dependencies themselves.
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and your dependencies) and providing (injecting) the results at each step.
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
For example, let's say you have 4 API endpoints (*path operations*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integrated with **OpenAPI**

View File

@@ -44,6 +44,17 @@ Then we can use the dependency with:
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Using the same dependency multiple times
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.

View File

@@ -1,8 +1,8 @@
There are many situations in where you need to notify an error to the client that is using your API.
There are many situations in where you need to notify an error to a client that is using your API.
This client could be a browser with a frontend, the code from someone else, an IoT device, etc.
This client could be a browser with a frontend, a code from someone else, an IoT device, etc.
You could need to tell that client that:
You could need to tell the client that:
* The client doesn't have enough privileges for that operation.
* The client doesn't have access to that resource.

View File

@@ -469,6 +469,8 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in
This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank}
And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:
@@ -556,31 +558,31 @@ For example, in a background task worker with <a href="http://www.celeryproject.
* `sql_app/database.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases/sql_app/database.py!}
```
* `sql_app/models.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases/sql_app/models.py!}
```
* `sql_app/schemas.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases/sql_app/schemas.py!}
```
* `sql_app/crud.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases/sql_app/crud.py!}
```
* `sql_app/main.py`:
```Python hl_lines=""
```Python
{!./src/sql_databases/sql_app/main.py!}
```

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.48.0"
__version__ = "0.49.2"
from starlette.background import BackgroundTasks

View File

@@ -27,7 +27,12 @@ from fastapi.dependencies.models import Dependant, SecurityRequirement
from fastapi.security.base import SecurityBase
from fastapi.security.oauth2 import OAuth2, SecurityScopes
from fastapi.security.open_id_connect_url import OpenIdConnect
from fastapi.utils import PYDANTIC_1, get_field_info, get_path_param_names
from fastapi.utils import (
PYDANTIC_1,
create_response_field,
get_field_info,
get_path_param_names,
)
from pydantic import BaseConfig, BaseModel, create_model
from pydantic.error_wrappers import ErrorWrapper
from pydantic.errors import MissingError
@@ -362,31 +367,15 @@ def get_param_field(
alias = param.name.replace("_", "-")
else:
alias = field_info.alias or param.name
if PYDANTIC_1:
field = ModelField(
name=param.name,
type_=annotation,
default=None if required else default_value,
alias=alias,
required=required,
model_config=BaseConfig,
class_validators={},
field_info=field_info,
)
# TODO: remove when removing support for Pydantic < 1.2.0
field.required = required
else: # pragma: nocover
field = ModelField( # type: ignore
name=param.name,
type_=annotation,
default=None if required else default_value,
alias=alias,
required=required,
model_config=BaseConfig,
class_validators={},
schema=field_info,
)
field.required = required
field = create_response_field(
name=param.name,
type_=annotation,
default=None if required else default_value,
alias=alias,
required=required,
field_info=field_info,
)
field.required = required
if not had_schema and not is_scalar_field(field=field):
if PYDANTIC_1:
field.field_info = params.Body(field_info.default)
@@ -694,28 +683,16 @@ def get_schema_compatible_field(*, field: ModelField) -> ModelField:
use_type: type = bytes
if field.shape in sequence_shapes:
use_type = List[bytes]
if PYDANTIC_1:
out_field = ModelField(
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,
)
else: # pragma: nocover
out_field = ModelField( # type: ignore
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,
schema=field.schema, # type: ignore
)
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 if PYDANTIC_1 else field.schema, # type: ignore
)
return out_field
@@ -754,26 +731,10 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
]
if len(set(body_param_media_types)) == 1:
BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
if PYDANTIC_1:
field = ModelField(
name="body",
type_=BodyModel,
default=None,
required=required,
model_config=BaseConfig,
class_validators={},
alias="body",
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
else: # pragma: nocover
field = ModelField( # type: ignore
name="body",
type_=BodyModel,
default=None,
required=required,
model_config=BaseConfig,
class_validators={},
alias="body",
schema=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
return field
return create_response_field(
name="body",
type_=BodyModel,
required=required,
alias="body",
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)

View File

@@ -1,4 +1,5 @@
from enum import Enum
from pathlib import PurePath
from types import GeneratorType
from typing import Any, Callable, Dict, List, Set, Tuple, Union
@@ -73,6 +74,8 @@ def jsonable_encoder(
)
if isinstance(obj, Enum):
return obj.value
if isinstance(obj, PurePath):
return str(obj)
if isinstance(obj, (str, int, float, type(None))):
return obj
if isinstance(obj, dict):

View File

@@ -20,6 +20,12 @@ RequestErrorModel = create_model("Request")
WebSocketErrorModel = create_model("WebSocket")
class FastAPIError(RuntimeError):
"""
A generic, FastAPI-specific error.
"""
class RequestValidationError(ValidationError):
def __init__(self, errors: Sequence[ErrorList], *, body: Any = None) -> None:
self.body = body

View File

@@ -180,7 +180,9 @@ def get_openapi_path(
operation_parameters = get_openapi_operation_parameters(all_route_params)
parameters.extend(operation_parameters)
if parameters:
operation["parameters"] = parameters
operation["parameters"] = list(
{param["name"]: param for param in parameters}.values()
)
if method in METHODS_WITH_BODY:
request_body_oai = get_openapi_operation_request_body(
body_field=route.body_field, model_name_map=model_name_map

View File

@@ -17,13 +17,13 @@ from fastapi.openapi.constants import STATUS_CODES_WITH_NO_BODY
from fastapi.utils import (
PYDANTIC_1,
create_cloned_field,
create_response_field,
generate_operation_id_for_path,
get_field_info,
warning_response_model_skip_defaults_deprecated,
)
from pydantic import BaseConfig, BaseModel
from pydantic import BaseModel
from pydantic.error_wrappers import ErrorWrapper, ValidationError
from pydantic.utils import lenient_issubclass
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
@@ -50,7 +50,7 @@ except ImportError: # pragma: nocover
async def serialize_response(
*,
field: ModelField = None,
response: Response,
response_content: Any,
include: Union[SetIntStr, DictIntStrAny] = None,
exclude: Union[SetIntStr, DictIntStrAny] = set(),
by_alias: bool = True,
@@ -59,16 +59,18 @@ async def serialize_response(
) -> Any:
if field:
errors = []
if exclude_unset and isinstance(response, BaseModel):
if exclude_unset and isinstance(response_content, BaseModel):
if PYDANTIC_1:
response = response.dict(exclude_unset=exclude_unset)
response_content = response_content.dict(exclude_unset=exclude_unset)
else:
response = response.dict(skip_defaults=exclude_unset) # pragma: nocover
response_content = response_content.dict(
skip_defaults=exclude_unset
) # pragma: nocover
if is_coroutine:
value, errors_ = field.validate(response, {}, loc=("response",))
value, errors_ = field.validate(response_content, {}, loc=("response",))
else:
value, errors_ = await run_in_threadpool(
field.validate, response, {}, loc=("response",)
field.validate, response_content, {}, loc=("response",)
)
if isinstance(errors_, ErrorWrapper):
errors.append(errors_)
@@ -84,7 +86,20 @@ async def serialize_response(
exclude_unset=exclude_unset,
)
else:
return jsonable_encoder(response)
return jsonable_encoder(response_content)
async def run_endpoint_function(
*, dependant: Dependant, values: Dict[str, Any], is_coroutine: bool
) -> Any:
# Only called by get_request_handler. Has been split into its own function to
# facilitate profiling endpoints, since inner functions are harder to profile.
assert dependant.call is not None, "dependant.call must be a function"
if is_coroutine:
return await dependant.call(**values)
else:
return await run_in_threadpool(dependant.call, **values)
def get_request_handler(
@@ -128,18 +143,17 @@ def get_request_handler(
if errors:
raise RequestValidationError(errors, body=body)
else:
assert dependant.call is not None, "dependant.call must be a function"
if is_coroutine:
raw_response = await dependant.call(**values)
else:
raw_response = await run_in_threadpool(dependant.call, **values)
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_data = await serialize_response(
field=response_field,
response=raw_response,
response_content=raw_response,
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
@@ -243,26 +257,9 @@ class APIRoute(routing.Route):
status_code not in STATUS_CODES_WITH_NO_BODY
), f"Status code {status_code} must not have a response body"
response_name = "Response_" + self.unique_id
if PYDANTIC_1:
self.response_field: Optional[ModelField] = ModelField(
name=response_name,
type_=self.response_model,
class_validators={},
default=None,
required=False,
model_config=BaseConfig,
field_info=FieldInfo(None),
)
else:
self.response_field: Optional[ModelField] = ModelField( # type: ignore # pragma: nocover
name=response_name,
type_=self.response_model,
class_validators={},
default=None,
required=False,
model_config=BaseConfig,
schema=FieldInfo(None),
)
self.response_field = create_response_field(
name=response_name, type_=self.response_model
)
# Create a clone of the field, so that a Pydantic submodel is not returned
# as is just because it's an instance of a subclass of a more limited class
# e.g. UserInDB (containing hashed_password) could be a subclass of User
@@ -274,7 +271,7 @@ class APIRoute(routing.Route):
ModelField
] = create_cloned_field(self.response_field)
else:
self.response_field = None
self.response_field = None # type: ignore
self.secure_cloned_response_field = None
self.status_code = status_code
self.tags = tags or []
@@ -297,30 +294,8 @@ class APIRoute(routing.Route):
assert (
additional_status_code not in STATUS_CODES_WITH_NO_BODY
), f"Status code {additional_status_code} must not have a response body"
assert lenient_issubclass(
model, BaseModel
), "A response model must be a Pydantic model"
response_name = f"Response_{additional_status_code}_{self.unique_id}"
if PYDANTIC_1:
response_field = ModelField(
name=response_name,
type_=model,
class_validators=None,
default=None,
required=False,
model_config=BaseConfig,
field_info=FieldInfo(None),
)
else:
response_field = ModelField( # type: ignore # pragma: nocover
name=response_name,
type_=model,
class_validators=None,
default=None,
required=False,
model_config=BaseConfig,
schema=FieldInfo(None),
)
response_field = create_response_field(name=response_name, type_=model)
response_fields[additional_status_code] = response_field
if response_fields:
self.response_fields: Dict[Union[int, str], ModelField] = response_fields
@@ -335,9 +310,7 @@ class APIRoute(routing.Route):
self.include_in_schema = include_in_schema
self.response_class = response_class
assert inspect.isfunction(endpoint) or inspect.ismethod(
endpoint
), f"An endpoint must be a function or method"
assert callable(endpoint), f"An endpoint must be a callable"
self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
for depends in self.dependencies[::-1]:
self.dependant.dependencies.insert(

View File

@@ -1,17 +1,20 @@
import functools
import re
from dataclasses import is_dataclass
from typing import Any, Dict, List, Sequence, Set, Type, cast
from typing import Any, Dict, List, Optional, Sequence, Set, Type, Union, cast
import fastapi
from fastapi import routing
from fastapi.logger import logger
from fastapi.openapi.constants import REF_PREFIX
from pydantic import BaseConfig, BaseModel, create_model
from pydantic.class_validators import Validator
from pydantic.schema import get_flat_models_from_fields, model_process_schema
from pydantic.utils import lenient_issubclass
from starlette.routing import BaseRoute
try:
from pydantic.fields import FieldInfo, ModelField
from pydantic.fields import FieldInfo, ModelField, UndefinedType
PYDANTIC_1 = True
except ImportError: # pragma: nocover
@@ -19,6 +22,10 @@ except ImportError: # pragma: nocover
from pydantic.fields import Field as ModelField # type: ignore
from pydantic import Schema as FieldInfo # type: ignore
class UndefinedType: # type: ignore
def __repr__(self) -> str:
return "PydanticUndefined"
logger.warning(
"Pydantic versions < 1.0.0 are deprecated in FastAPI and support will be "
"removed soon."
@@ -86,6 +93,44 @@ def get_path_param_names(path: str) -> Set[str]:
return {item.strip("{}") for item in re.findall("{[^}]*}", path)}
def create_response_field(
name: str,
type_: Type[Any],
class_validators: Optional[Dict[str, Validator]] = None,
default: Optional[Any] = None,
required: Union[bool, UndefinedType] = False,
model_config: Type[BaseConfig] = BaseConfig,
field_info: Optional[FieldInfo] = None,
alias: Optional[str] = None,
) -> ModelField:
"""
Create a new response field. Raises if type_ is invalid.
"""
class_validators = class_validators or {}
field_info = field_info or FieldInfo(None)
response_field = functools.partial(
ModelField,
name=name,
type_=type_,
class_validators=class_validators,
default=default,
required=required,
model_config=model_config,
alias=alias,
)
try:
if PYDANTIC_1:
return response_field(field_info=field_info)
else: # pragma: nocover
return response_field(schema=field_info)
except RuntimeError:
raise fastapi.exceptions.FastAPIError(
f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type"
)
def create_cloned_field(field: ModelField) -> ModelField:
original_type = field.type_
if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"):
@@ -96,26 +141,8 @@ def create_cloned_field(field: ModelField) -> ModelField:
use_type = create_model(original_type.__name__, __base__=original_type)
for f in original_type.__fields__.values():
use_type.__fields__[f.name] = create_cloned_field(f)
if PYDANTIC_1:
new_field = ModelField(
name=field.name,
type_=use_type,
class_validators={},
default=None,
required=False,
model_config=BaseConfig,
field_info=FieldInfo(None),
)
else: # pragma: nocover
new_field = ModelField( # type: ignore
name=field.name,
type_=use_type,
class_validators={},
default=None,
required=False,
model_config=BaseConfig,
schema=FieldInfo(None),
)
new_field = create_response_field(name=field.name, type_=use_type)
new_field.has_alias = field.has_alias
new_field.alias = field.alias
new_field.class_validators = field.class_validators

View File

@@ -117,6 +117,11 @@ markdown_extensions:
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format
extra:
social:
@@ -137,4 +142,5 @@ extra_css:
- 'css/custom.css'
extra_javascript:
- 'https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js'
- 'js/custom.js'

View File

@@ -1,2 +1,3 @@
[mypy]
disallow_untyped_defs = True
ignore_missing_imports = True

View File

@@ -55,7 +55,10 @@ test = [
"databases[sqlite]",
"orjson",
"async_exit_stack",
"async_generator"
"async_generator",
"python-multipart",
"aiofiles",
"ujson"
]
doc = [
"mkdocs",

View File

@@ -3,6 +3,6 @@
set -e
set -x
mypy fastapi --disallow-untyped-defs
mypy fastapi
black fastapi tests --check
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only --thirdparty fastapi fastapi tests

View File

@@ -0,0 +1,24 @@
from functools import partial
from fastapi import FastAPI
from starlette.testclient import TestClient
def main(some_arg, q: str = None):
return {"some_arg": some_arg, "q": q}
endpoint = partial(main, "foo")
app = FastAPI()
app.get("/")(endpoint)
client = TestClient(app)
def test_partial():
response = client.get("/?q=bar")
data = response.json()
assert data == {"some_arg": "foo", "q": "bar"}

View File

@@ -1,9 +1,10 @@
from datetime import datetime, timezone
from enum import Enum
from pathlib import PurePath, PurePosixPath, PureWindowsPath
import pytest
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, ValidationError
from pydantic import BaseModel, ValidationError, create_model
try:
from pydantic import Field
@@ -69,6 +70,19 @@ class ModelWithAlias(BaseModel):
foo: str = Field(..., alias="Foo")
@pytest.fixture(
name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]
)
def fixture_model_with_path(request):
class Config:
arbitrary_types_allowed = True
ModelWithPath = create_model(
"ModelWithPath", path=(request.param, ...), __config__=Config
)
return ModelWithPath(path=request.param("/foo", "bar"))
def test_encode_class():
person = Person(name="Foo")
pet = Pet(owner=person, name="Firulais")
@@ -120,3 +134,11 @@ def test_custom_encoders():
instance, custom_encoder={safe_datetime: lambda o: o.isoformat()}
)
assert encoded_instance["dt_field"] == instance.dt_field.isoformat()
def test_encode_model_with_path(model_with_path):
if isinstance(model_with_path.path, PureWindowsPath):
expected = "\\foo\\bar"
else:
expected = "/foo/bar"
assert jsonable_encoder(model_with_path) == {"path": expected}

View File

@@ -0,0 +1,93 @@
from fastapi import Depends, FastAPI
from starlette.testclient import TestClient
app = FastAPI()
async def user_exists(user_id: int):
return True
@app.get("/users/{user_id}", dependencies=[Depends(user_exists)])
async def read_users(user_id: int):
pass
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/{user_id}": {
"get": {
"summary": "Read Users",
"operationId": "read_users_users__user_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
},
],
"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_reused_param():
response = client.get("/openapi.json")
data = response.json()
assert data == openapi_schema
def test_read_users():
response = client.get("/users/42")
assert response.status_code == 200

View File

@@ -0,0 +1,45 @@
from typing import List
import pytest
from fastapi import FastAPI
from fastapi.exceptions import FastAPIError
class NonPydanticModel:
pass
def test_invalid_response_model_raises():
with pytest.raises(FastAPIError):
app = FastAPI()
@app.get("/", response_model=NonPydanticModel)
def read_root():
pass # pragma: nocover
def test_invalid_response_model_sub_type_raises():
with pytest.raises(FastAPIError):
app = FastAPI()
@app.get("/", response_model=List[NonPydanticModel])
def read_root():
pass # pragma: nocover
def test_invalid_response_model_in_responses_raises():
with pytest.raises(FastAPIError):
app = FastAPI()
@app.get("/", responses={"500": {"model": NonPydanticModel}})
def read_root():
pass # pragma: nocover
def test_invalid_response_model_sub_type_in_responses_raises():
with pytest.raises(FastAPIError):
app = FastAPI()
@app.get("/", responses={"500": {"model": List[NonPydanticModel]}})
def read_root():
pass # pragma: nocover

View File

@@ -0,0 +1,160 @@
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.testclient import TestClient
class Model(BaseModel):
name: str
app = FastAPI()
@app.get("/valid1", responses={"500": {"model": int}})
def valid1():
pass
@app.get("/valid2", responses={"500": {"model": List[int]}})
def valid2():
pass
@app.get("/valid3", responses={"500": {"model": Model}})
def valid3():
pass
@app.get("/valid4", responses={"500": {"model": List[Model]}})
def valid4():
pass
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/valid1": {
"get": {
"summary": "Valid1",
"operationId": "valid1_valid1_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid1 Valid1 Get",
"type": "integer",
}
}
},
},
},
}
},
"/valid2": {
"get": {
"summary": "Valid2",
"operationId": "valid2_valid2_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid2 Valid2 Get",
"type": "array",
"items": {"type": "integer"},
}
}
},
},
},
}
},
"/valid3": {
"get": {
"summary": "Valid3",
"operationId": "valid3_valid3_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
},
},
}
},
"/valid4": {
"get": {
"summary": "Valid4",
"operationId": "valid4_valid4_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid4 Valid4 Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Model": {
"title": "Model",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
}
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_path_operations():
response = client.get("/valid1")
assert response.status_code == 200
response = client.get("/valid2")
assert response.status_code == 200
response = client.get("/valid3")
assert response.status_code == 200
response = client.get("/valid4")
assert response.status_code == 200