Compare commits

..

53 Commits

Author SHA1 Message Date
Sebastián Ramírez
b87072bc12 🔖 Release version 0.58.0 2020-06-15 13:18:36 +02:00
Sebastián Ramírez
04e2bfafbc 📝 Update release notes 2020-06-15 13:13:53 +02:00
Sebastián Ramírez
181a32236a Deep merge OpenAPI responses (#1577)
* override successful response

*  Add deep_dict_udpate

*  Merge additional responses with generated responses

* 🍱 Update docs screenshot

Co-authored-by: rkbeatss <rkaus053@uottawa.ca>
2020-06-15 13:12:12 +02:00
Sebastián Ramírez
1f54a8e0a1 📝 Update release notes 2020-06-15 12:42:48 +02:00
Andrew
d63475bb7d 📝 Mention in docs that subapps don't fire events (#1554)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-14 18:25:10 +02:00
Sebastián Ramírez
5a3c5f1523 📝 Update release notes 2020-06-14 18:12:51 +02:00
Andrew
12bc9285f7 🐛 Fix body validation error response, remove variable name when it is not embedded (#1553) 2020-06-14 18:07:39 +02:00
Sebastián Ramírez
31df2ea940 📝 Update release notes 2020-06-14 17:56:12 +02:00
Sebastián Ramírez
50b90dd6a4 📝 Update release notes 2020-06-14 17:55:13 +02:00
Andrew
7dd881334d 🐛 Fix testing security scopes when using dependency overrides (#1549)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-14 17:54:46 +02:00
Vinny Do
530fc8ff3f 🐛 Fix JSON Schema "not" keyword (#1548) 2020-06-14 15:46:49 +02:00
Sebastián Ramírez
ef460b4d23 📝 Update release notes 2020-06-14 15:40:18 +02:00
mikaello
b591de2ace Add support for OpenAPI servers metadata (#1547)
* feat: add servers option for OpenAPI

Closes #872

*  Use dicts for OpenAPI servers

* ♻️ Update OpenAPI Server model to support relative URLs

*  Add tests for OpenAPI servers

* ♻️ Re-order parameter location of servers for OpenAPI

* 🎨 Format code

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-14 15:38:29 +02:00
Sebastián Ramírez
34c857b7cb 🔖 Release version 0.57.0 2020-06-13 23:13:25 +02:00
Sebastián Ramírez
c78bc0c82d 📝 Update release notes 2020-06-13 23:08:20 +02:00
JAYATI SHRIVASTAVA
194446e51a 🔥 Remove broken external link (#1565) 2020-06-13 23:07:11 +02:00
Sebastián Ramírez
777e2151e6 📝 Update release notes 2020-06-13 22:55:30 +02:00
Sebastián Ramírez
5ce5bdba0b 📝 Update release notes 2020-06-13 22:52:59 +02:00
Chih Sean Hsu
e4300769ac 📝 Update tutorial for WebSockets with dependencies (#1540)
* fix websockets/tutorial002.py

* fix tutorial002 in ws to correspond with test case

* reformat websocket tutorial002

* fix websocket tutorial002 coverage

* 📝 Update example for WebSockets with Depends

*  Update and refactor tests for WebSockets with dependencies

* 👷 Trigger Travis, as it's not reporting to Codecov

*  Update WebSocket tests to raise coverage

Co-authored-by: Chih Sean Hsu <Sean@Sean-Mac.local>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 22:51:34 +02:00
retnikt
c6dd627bdd Add support for Python's http.HTTPStatus in status_code (#1534)
* Normalise IntEnums to ints for route status codes

Closes #1349

* add tests for status code enum support

* add docs for status code enum support

* add endpoint test for enum status code

* 📝 Update note about http.HTTPStatus

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 19:40:10 +02:00
Sebastián Ramírez
6576f724bb 📝 Update release notes 2020-06-13 19:22:08 +02:00
Sebastián Ramírez
91a6736d0e 📝 Update release notes 2020-06-13 19:20:48 +02:00
Patrick Wang
8fb755703d When using Pydantic models with __root__ use the internal value in jsonable_encoder (#1524) 2020-06-13 19:20:11 +02:00
Yankee
748bedd37c 📝 Updated docs for path-params (#1521)
* Added response example; URL for quick access; typo fixes

* Added line breaks for readability

* Fix typo on redoc url

* 📝 Update format, links, rewordings

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 19:14:23 +02:00
Sebastián Ramírez
bf58788f29 📝 Update release notes 2020-06-13 18:54:47 +02:00
Yankee
5f78ba4a31 📝 Update docs for first-steps, links, rewordings (#1518)
* ✏️ Typo/readability fixes for first-steps documentation

* 📝 Update link and small rewordings

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 18:53:31 +02:00
Sebastián Ramírez
db9f827263 📝 Update release notes 2020-06-13 18:18:27 +02:00
TiewKH
dd9e94cf21 Enable showCommonExtensions and showExtensions in SwaggerUI (#1466)
* Set showExtensions and showCommonExtensions to true

* Clean up comma

Co-authored-by: tiewkeehui <keehuitiew@airasia.com>
2020-06-13 18:16:34 +02:00
Sebastián Ramírez
e482d74241 📝 Update release notes 2020-06-13 18:13:06 +02:00
Richard Hoekstra
bd2acbcabb Export OAuth2PasswordRequestFormStrict from security (#1462)
* Update __init__.py

Fixes an import error:     

from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestFormStrict
ImportError: cannot import name 'OAuth2PasswordRequestFormStrict'

* Simplify import of OAuth2PasswordRequestFormStrict

* Simplify import of OAuth2PasswordRequestFormStrict
2020-06-13 18:08:08 +02:00
Sebastián Ramírez
f913d469a8 📝 Update release notes 2020-06-13 18:05:22 +02:00
Roman Tezikov
66cb266641 📝 Add docs for default_response_class (#1455)
*  Add docs to default_response_class

*  create a tip

*  fixing the tip

* 🚑 grammar

* 📝 Update docs for default response class

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 18:02:45 +02:00
Sebastián Ramírez
74954894c5 📝 Update release notes 2020-06-13 15:26:42 +02:00
William Hayes
ceedfccde0 📝 Document additional parameters for response_model (#1427)
* Documented additional parameters

These are included in a recent PR (https://github.com/tiangolo/fastapi/pull/1166) but not in the docs yet.
* response_model_exclude_none
* response_model_exclude_defaults

* 📝 Update note about response_model_exclude_defaults and response_model_exclude_none

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 15:23:29 +02:00
Sebastián Ramírez
2ee0eedf23 📝 Update release notes 2020-06-13 15:08:10 +02:00
Koudai Aono
c0f3019764 📝 Add PyCharm Pydantic plugin to docs (#1420)
* add pydantic pycharm plugin in document

* 📝 Update PyCharm Pydantic plugin note

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 15:05:59 +02:00
Sebastián Ramírez
dd6d0cb23c 📝 Update release notes 2020-06-13 14:51:21 +02:00
Chen Rotem Levy
fe15620df3 🎨 Update and clarify testing function name (#1395)
test_create_existing_token -> test_create_existing_item
2020-06-13 14:50:14 +02:00
Sebastián Ramírez
6af857f206 📝 Update release notes 2020-06-13 14:47:03 +02:00
obataku
7ce756f9dd 🐛 Fix duplicated headers set by indirect dependencies (#1386)
* Added test for repeating cookies in response headers

* update `response` headers, status code to match `sub_response` in `solve_dependencies` only if necessary; fix formatting of scottsmith2gmail's test

* restore code coverage, remove dead code from `solve_dependencies`

Co-authored-by: Scott Smith <scott.smith.2@gmail.com>
2020-06-13 14:44:51 +02:00
Sebastián Ramírez
c0b1fddb31 📝 Update release notes 2020-06-13 14:39:58 +02:00
Nick Rushton
9aea85a84e ⬆ Upgrade Starlette dependency to 0.13.4 (#1361) 2020-06-13 14:38:08 +02:00
Sebastián Ramírez
fddd1c12de 📝 Update release notes 2020-06-13 14:36:27 +02:00
Aviram Hassan
b13a4baf32 Add better JSON decode error handling, improve feedback for client after invalid JSON requests (#1354)
* Request body error, raise RequestValidationError instead of HTTPException in case JSON decode failure

* add missing test case for body general exception
2020-06-13 14:33:27 +02:00
Sebastián Ramírez
5ffa18f10f 📝 Update release notes 2020-06-13 14:06:12 +02:00
Sebastián Ramírez
828915baf5 📝 Update Tags metadata title 2020-06-13 14:02:58 +02:00
Thomas Maschler
a071ddf3cd Add support for tag metadata in OpenAPI (#1348)
* Allow to add OpenAPI tag descriptions

* fix type hint

* fix type hint 2

* refactor test to assure 100% coverage

* 📝 Update tags metadata example

* 📝 Update docs for tags metadata

*  Move tags metadata test to tutorial subdir

* 🎨 Update format in applications

* 🍱 Update docs UI image based on new example

* 🎨 Apply formatting after solving conflicts

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2020-06-13 13:58:06 +02:00
Sebastián Ramírez
3651b8a30f 📝 Update release notes 2020-06-13 12:27:00 +02:00
Sebastián Ramírez
0d73b9ff1c 🔧 Add basic setup for Russian translations (#1566) 2020-06-13 12:26:15 +02:00
Sebastián Ramírez
43235cf236 📝 Update release notes 2020-06-13 09:47:36 +02:00
Xie Wei
269a155583 🔥 Remove obsolete Chinese articles after translations (#1510) 2020-06-13 09:45:48 +02:00
Sebastián Ramírez
12433d51dd 📝 Update release notes 2020-06-13 01:25:29 +02:00
Rupsi Kaushik
3699e17212 Implement __repr__ methods for path parameters to simplify debugging (#1560)
* repr description added to Depends class

* repr description added to Security subclass

* get rid of __repr__ in security since it will inherit from super

* make code format consistent with rest

* add desc for rest of the classes

* Update fastapi/params.py

remove trailing whitespace

Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>

* Implement __repr__

* fix formatting

* formatting again

* ran formatting

* added basic testing

* basic tests added to rest of the classes

* added more test coverage and simplified test file

Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Jayati Shrivastava <gaurijove@gmail.com>
2020-06-13 01:22:30 +02:00
57 changed files with 1260 additions and 166 deletions

View File

@@ -203,6 +203,21 @@ File responses will include appropriate `Content-Length`, `Last-Modified` and `E
{!../../../docs_src/custom_response/tutorial009.py!}
```
## Default response class
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
```Python hl_lines="2 4"
{!../../../docs_src/custom_response/tutorial010.py!}
```
!!! tip
You can still override `response_class` in *path operations* as before.
## Additional documentation
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.

View File

@@ -4,6 +4,9 @@ You can define event handlers (functions) that need to be executed before the ap
These functions can be declared with `async def` or normal `def`.
!!! warning
Only event handlers for the main application will be executed, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}.
## `startup` event
To add a function that should be run before the application starts, declare it with the event `"startup"`:
@@ -41,4 +44,4 @@ Here, the `shutdown` event handler function will write a text line `"Application
So, we declare the event handler function with standard `def` instead of `async def`.
!!! info
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.

View File

@@ -51,38 +51,7 @@ In your WebSocket route you can `await` for messages and send messages.
You can receive and send binary, text, and JSON data.
## Using `Depends` and others
In WebSocket endpoints you can import from `fastapi` and use:
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
They work the same way as for other FastAPI endpoints/*path operations*:
```Python hl_lines="53 54 55 56 57 58 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76"
{!../../../docs_src/websockets/tutorial002.py!}
```
!!! info
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
## More info
To learn more about the options, check Starlette's documentation for:
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
## Test it
## Try it
If your file is named `main.py`, run your application with:
@@ -115,3 +84,62 @@ You can send (and receive) many messages:
<img src="/img/tutorial/websockets/image04.png">
And all of them will use the same WebSocket connection.
## Using `Depends` and others
In WebSocket endpoints you can import from `fastapi` and use:
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
They work the same way as for other FastAPI endpoints/*path operations*:
```Python hl_lines="56 57 58 59 60 61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79"
{!../../../docs_src/websockets/tutorial002.py!}
```
!!! info
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
### Try the WebSockets with dependencies
If your file is named `main.py`, run your application with:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Open your browser at <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
There you can set:
* The "Item ID", used in the path.
* The "Token" used as a query parameter.
!!! tip
Notice that the query `token` will be handled by a dependency.
With that you can connect the WebSocket and then send and receive messages:
<img src="/img/tutorial/websockets/image05.png">
## More info
To learn more about the options, check Starlette's documentation for:
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.

View File

@@ -25,8 +25,6 @@ Here's an incomplete list of some of them.
* <a href="https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680" class="external-link" target="_blank">FastAPI authentication revisited: Enabling API key authentication</a> by <a href="https://medium.com/@nils_29588" class="external-link" target="_blank">Nils de Bruin</a>.
* <a href="https://blog.bartab.fr/fastapi-logging-on-the-fly/" class="external-link" target="_blank">FastAPI, a simple use case on logging</a> by <a href="https://blog.bartab.fr/" class="external-link" target="_blank">@euri10</a>.
* <a href="https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915" class="external-link" target="_blank">Deploying a scikit-learn model with ONNX and FastAPI</a> by <a href="https://www.linkedin.com/in/nico-axtmann" class="external-link" target="_blank">Nico Axtmann</a>.
* <a href="https://geekflare.com/python-asynchronous-web-frameworks/" class="external-link" target="_blank">Top 5 Asynchronous Web Frameworks for Python</a> by <a href="https://geekflare.com/author/ankush/" class="external-link" target="_blank">Ankush Thakur</a> on <a href="https://geekflare.com" class="external-link" target="_blank">GeekFlare</a>.
@@ -104,12 +102,6 @@ Here's an incomplete list of some of them.
* <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>.
* <a href="https://wxq0309.github.io/" class="external-link" target="_blank">FastAPI框架中文文档</a> by <a href="https://wxq0309.github.io/" class="external-link" target="_blank">何大仙</a>.
### Vietnamese
* <a href="https://fullstackstation.com/fastapi-trien-khai-bang-docker/" class="external-link" target="_blank">FASTAPI: TRIỂN KHAI BẰNG DOCKER</a> by <a href="https://fullstackstation.com/author/figonking/" class="external-link" target="_blank">Nguyễn Nhân</a>.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -2,6 +2,37 @@
## Latest changes
## 0.58.0
* Deep merge OpenAPI responses to preserve all the additional metadata. PR [#1577](https://github.com/tiangolo/fastapi/pull/1577).
* Mention in docs that only main app events are run (not sub-apps). PR [#1554](https://github.com/tiangolo/fastapi/pull/1554) by [@amacfie](https://github.com/amacfie).
* Fix body validation error response, do not include body variable when it is not embedded. PR [#1553](https://github.com/tiangolo/fastapi/pull/1553) by [@amacfie](https://github.com/amacfie).
* Fix testing OAuth2 security scopes when using dependency overrides. PR [#1549](https://github.com/tiangolo/fastapi/pull/1549) by [@amacfie](https://github.com/amacfie).
* Fix Model for JSON Schema keyword `not` as a JSON Schema instead of a list. PR [#1548](https://github.com/tiangolo/fastapi/pull/1548) by [@v-do](https://github.com/v-do).
* Add support for OpenAPI `servers`. PR [#1547](https://github.com/tiangolo/fastapi/pull/1547) by [@mikaello](https://github.com/mikaello).
## 0.57.0
* Remove broken link from "External Links". PR [#1565](https://github.com/tiangolo/fastapi/pull/1565) by [@victorphoenix3](https://github.com/victorphoenix3).
* Update/fix docs for [WebSockets with dependencies](https://fastapi.tiangolo.com/advanced/websockets/#using-depends-and-others). Original PR [#1540](https://github.com/tiangolo/fastapi/pull/1540) by [@ChihSeanHsu](https://github.com/ChihSeanHsu).
* Add support for Python's `http.HTTPStatus` in `status_code` parameters. PR [#1534](https://github.com/tiangolo/fastapi/pull/1534) by [@retnikt](https://github.com/retnikt).
* When using Pydantic models with `__root__`, use the internal value in `jsonable_encoder`. PR [#1524](https://github.com/tiangolo/fastapi/pull/1524) by [@patrickkwang](https://github.com/patrickkwang).
* Update docs for path parameters. PR [#1521](https://github.com/tiangolo/fastapi/pull/1521) by [@yankeexe](https://github.com/yankeexe).
* Update docs for first steps, links and rewording. PR [#1518](https://github.com/tiangolo/fastapi/pull/1518) by [@yankeexe](https://github.com/yankeexe).
* Enable `showCommonExtensions` in Swagger UI to show additional validations like `maxLength`, etc. PR [#1466](https://github.com/tiangolo/fastapi/pull/1466) by [@TiewKH](https://github.com/TiewKH).
* Make `OAuth2PasswordRequestFormStrict` importable directly from `fastapi.security`. PR [#1462](https://github.com/tiangolo/fastapi/pull/1462) by [@RichardHoekstra](https://github.com/RichardHoekstra).
* Add docs about [Default response class](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). PR [#1455](https://github.com/tiangolo/fastapi/pull/1455) by [@TezRomacH](https://github.com/TezRomacH).
* Add note in docs about additional parameters `response_model_exclude_defaults` and `response_model_exclude_none` in [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). PR [#1427](https://github.com/tiangolo/fastapi/pull/1427) by [@wshayes](https://github.com/wshayes).
* Add note about [PyCharm Pydantic plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin) to docs. PR [#1420](https://github.com/tiangolo/fastapi/pull/1420) by [@koxudaxi](https://github.com/koxudaxi).
* Update and clarify testing function name. PR [#1395](https://github.com/tiangolo/fastapi/pull/1395) by [@chenl](https://github.com/chenl).
* Fix duplicated headers created by indirect dependencies that use the request directly. PR [#1386](https://github.com/tiangolo/fastapi/pull/1386) by [@obataku](https://github.com/obataku) from tests by [@scottsmith2gmail](https://github.com/scottsmith2gmail).
* Upgrade Starlette version to `0.13.4`. PR [#1361](https://github.com/tiangolo/fastapi/pull/1361) by [@rushton](https://github.com/rushton).
* Improve error handling and feedback for requests with invalid JSON. PR [#1354](https://github.com/tiangolo/fastapi/pull/1354) by [@aviramha](https://github.com/aviramha).
* Add support for declaring metadata for tags in OpenAPI. New docs at [Tutorial - Metadata and Docs URLs - Metadata for tags](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-tags). PR [#1348](https://github.com/tiangolo/fastapi/pull/1348) by [@thomas-maschler](https://github.com/thomas-maschler).
* Add basic setup for Russian translations. PR [#1566](https://github.com/tiangolo/fastapi/pull/1566).
* Remove obsolete Chinese articles after adding official community translations. PR [#1510](https://github.com/tiangolo/fastapi/pull/1510) by [@waynerv](https://github.com/waynerv).
* Add `__repr__` for *path operation function* parameter helpers (like `Query`, `Depends`, etc) to simplify debugging. PR [#1560](https://github.com/tiangolo/fastapi/pull/1560) by [@rkbeatss](https://github.com/rkbeatss) and [@victorphoenix3](https://github.com/victorphoenix3).
## 0.56.1
* Add link to advanced docs from tutorial. PR [#1512](https://github.com/tiangolo/fastapi/pull/1512) by [@kx-chen](https://github.com/kx-chen).

View File

@@ -108,6 +108,17 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
<img src="/img/tutorial/body/image05.png">
!!! tip
If you use <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> as your editor, you can use the <a href="https://github.com/koxudaxi/pydantic-pycharm-plugin/" class="external-link" target="_blank">Pydantic PyCharm Plugin</a>.
It improves editor support for Pydantic models, with:
* auto-completion
* type checks
* refactoring
* searching
* inspections
## Use the model
Inside of the function, you can access all the attributes of the model object directly:

View File

@@ -71,13 +71,13 @@ You will see the alternative automatic documentation (provided by <a href="https
#### "Schema"
A "schema" is a definition or description of something. Not the code that implements it, but just the abstract description.
A "schema" is a definition or description of something. Not the code that implements it, but just an abstract description.
#### API "schema"
In this case, OpenAPI is a specification that dictates how to define a schema of your API.
In this case, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> is a specification that dictates how to define a schema of your API.
This OpenAPI schema would include your API paths, the possible parameters they take, etc.
This schema definition includes your API paths, the possible parameters they take, etc.
#### Data "schema"
@@ -91,7 +91,7 @@ OpenAPI defines an API schema for your API. And that schema includes definitions
#### Check the `openapi.json`
If you are curious about how the raw OpenAPI schema looks like, it is just an automatically generated JSON with the descriptions of all your API.
If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON (schema) with the descriptions of all your API.
You can see it directly at: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
@@ -120,7 +120,7 @@ It will show a JSON starting with something like:
#### What is OpenAPI for
This OpenAPI schema is what powers the 2 interactive documentation systems included.
The OpenAPI schema is what powers the two interactive documentation systems included.
And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives to your application built with **FastAPI**.
@@ -139,7 +139,7 @@ You could also use it to generate code automatically, for clients that communica
!!! note "Technical Details"
`FastAPI` is a class that inherits directly from `Starlette`.
You can use all the Starlette functionality with `FastAPI` too.
You can use all the <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> functionality with `FastAPI` too.
### Step 2: create a `FastAPI` "instance"
@@ -202,7 +202,7 @@ https://example.com/items/foo
!!! info
A "path" is also commonly called an "endpoint" or a "route".
Building an API, the "path" is the main way to separate "concerns" and "resources".
While building an API, the "path" is the main way to separate "concerns" and "resources".
#### Operation
@@ -281,7 +281,7 @@ And the more exotic ones:
The information here is presented as a guideline, not a requirement.
For example, when using GraphQL you normally perform all the actions using only `post`.
For example, when using GraphQL you normally perform all the actions using only `POST` operations.
### Step 4: define the **path operation function**
@@ -297,7 +297,7 @@ This is our "**path operation function**":
This is a Python function.
It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using `GET`.
It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using a `GET` operation.
In this case, it is an `async` function.

View File

@@ -215,7 +215,6 @@ You will receive a response telling you that the data is invalid containing the
{
"loc": [
"body",
"item",
"size"
],
"msg": "value is not a valid integer",

View File

@@ -21,6 +21,58 @@ With this configuration, the automatic API docs would look like:
<img src="/img/tutorial/metadata/image01.png">
## Metadata for tags
You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`.
It takes a list containing one dictionary for each tag.
Each dictionary can contain:
* `name` (**required**): a `str` with the same tag name you use in the `tags` parameter in your *path operations* and `APIRouter`s.
* `description`: a `str` with a short description for the tag. It can have Markdown and will be shown in the docs UI.
* `externalDocs`: a `dict` describing external documentation with:
* `description`: a `str` with a short description for the external docs.
* `url` (**required**): a `str` with the URL for the external documentation.
### Create metadata for tags
Let's try that in an example with tags for `users` and `items`.
Create metadata for your tags and pass it to the `openapi_tags` parameter:
```Python hl_lines="3 4 5 6 7 8 9 10 11 12 13 14 15 16 18"
{!../../../docs_src/metadata/tutorial004.py!}
```
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
!!! tip
You don't have to add metadata for all the tags that you use.
### Use your tags
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
```Python hl_lines="21 26"
{!../../../docs_src/metadata/tutorial004.py!}
```
!!! info
Read more about tags in [Path Operation Configuration](../path-operation-configuration/#tags){.internal-link target=_blank}.
### Check the docs
Now, if you check the docs, they will show all the additional metadata:
<img src="/img/tutorial/metadata/image02.png">
### Order of tags
The order of each tag metadata dictionary also defines the order shown in the docs UI.
For example, even though `users` would go after `items` in alphabetical order, it is shown before them, because we added their metadata as the first dictionary in the list.
## OpenAPI URL
By default, the OpenAPI schema is served at `/openapi.json`.

View File

@@ -85,7 +85,7 @@ And when you open your browser at <a href="http://127.0.0.1:8000/docs" class="ex
And because the generated schema is from the <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md" class="external-link" target="_blank">OpenAPI</a> standard, there are many compatible tools.
Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc):
Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc), which you can access at <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>:
<img src="/img/tutorial/path-params/image02.png">
@@ -125,7 +125,7 @@ Import `Enum` and create a sub-class that inherits from `str` and from `Enum`.
By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly.
And create class attributes with fixed values, those fixed values will be the available valid values:
Then create class attributes with fixed values, which will be the available valid values:
```Python hl_lines="1 6 7 8 9"
{!../../../docs_src/path_params/tutorial005.py!}
@@ -147,7 +147,7 @@ Then create a *path parameter* with a type annotation using the enum class you c
### Check the docs
Because the available values for the *path parameter* are specified, the interactive docs can show them nicely:
Because the available values for the *path parameter* are predefined, the interactive docs can show them nicely:
<img src="/img/tutorial/path-params/image03.png">
@@ -167,7 +167,7 @@ You can compare it with the *enumeration member* in your created enum `ModelName
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
```Python hl_lines="19"
```Python hl_lines="20"
{!../../../docs_src/path_params/tutorial005.py!}
```
@@ -178,12 +178,21 @@ You can get the actual value (a `str` in this case) using `model_name.value`, or
You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`).
They will be converted to their corresponding values before returning them to the client:
They will be converted to their corresponding values (strings in this case) before returning them to the client:
```Python hl_lines="18 20 21"
```Python hl_lines="18 21 23"
{!../../../docs_src/path_params/tutorial005.py!}
```
In your client you will get a JSON response like:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Path parameters containing paths
Let's say you have a *path operation* with a path `/files/{file_path}`.

View File

@@ -124,6 +124,14 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
!!! info
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">its `exclude_unset` parameter</a> to achieve this.
!!! info
You can also use:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
as described in <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" class="external-link" target="_blank">the Pydantic docs</a> for `exclude_defaults` and `exclude_none`.
#### Data with values for fields with defaults
But if your data has values for the model's fields with default values, like the item with ID `bar`:

View File

@@ -17,6 +17,9 @@ The same way you can specify a response model, you can also declare the HTTP sta
The `status_code` parameter receives a number with the HTTP status code.
!!! info
`status_code` can alternatively also receive an `IntEnum`, such as Python's <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>.
It will:
* Return that status code in the response.

View File

@@ -24,6 +24,7 @@ nav:
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
- features.md
- python-types.md

View File

@@ -24,6 +24,7 @@ nav:
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
- features.md
- python-types.md

View File

@@ -24,6 +24,7 @@ nav:
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
markdown_extensions:
- toc:

View File

@@ -24,6 +24,7 @@ nav:
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
- features.md
- Tutorial - Guia de Usuário:

447
docs/ru/docs/index.md Normal file
View File

@@ -0,0 +1,447 @@
{!../../../docs/missing-translation.md!}
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
</p>
<p align="center">
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
</a>
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
</a>
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
</a>
</p>
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
Python 3.6+
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
<div class="termy">
```console
$ pip install fastapi
---> 100%
```
</div>
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
<div class="termy">
```console
$ pip install uvicorn
---> 100%
```
</div>
## Example
### Create it
* Create a file `main.py` with:
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
</details>
### Run it
Run the server with:
<div class="termy">
```console
$ uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
</details>
### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="2 7 8 9 10 23 24 25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.6+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install fastapi[all]`.
## License
This project is licensed under the terms of the MIT license.

67
docs/ru/mkdocs.yml Normal file
View File

@@ -0,0 +1,67 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/ru/
theme:
name: material
palette:
primary: teal
accent: amber
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: ru
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
google_analytics:
- UA-133183413-1
- auto
nav:
- FastAPI: index.md
- Languages:
- en: /
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- markdown_include.include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format ''
- pymdownx.tabbed
extra:
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/typer
- icon: fontawesome/brands/twitter
link: https://twitter.com/tiangolo
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/tiangolo
- icon: fontawesome/brands/dev
link: https://dev.to/tiangolo
- icon: fontawesome/brands/medium
link: https://medium.com/@tiangolo
- icon: fontawesome/solid/globe
link: https://tiangolo.com
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js
- https://fastapi.tiangolo.com/js/chat.js
- https://sidecar.gitter.im/dist/sidecar.v1.js

View File

@@ -24,6 +24,7 @@ nav:
- es: /es/
- it: /it/
- pt: /pt/
- ru: /ru/
- zh: /zh/
- features.md
- python-types.md

View File

@@ -51,7 +51,7 @@ def test_create_item_bad_token():
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_token():
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},

View File

@@ -0,0 +1,9 @@
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]

View File

@@ -0,0 +1,28 @@
from fastapi import FastAPI
tags_metadata = [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}, {"name": "Ron"}]
@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "wand"}, {"name": "flying broom"}]

View File

@@ -16,6 +16,8 @@ app = FastAPI()
async def get_model(model_name: ModelName):
if model_name == ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}

View File

@@ -1,4 +1,4 @@
from fastapi import Cookie, Depends, FastAPI, Header, WebSocket, status
from fastapi import Cookie, Depends, FastAPI, Query, WebSocket, status
from fastapi.responses import HTMLResponse
app = FastAPI()
@@ -13,8 +13,9 @@ html = """
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
<label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
<button onclick="connect(event)">Connect</button>
<br>
<hr>
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
<button>Send</button>
</form>
@@ -23,8 +24,9 @@ html = """
<script>
var ws = null;
function connect(event) {
var input = document.getElementById("itemId")
ws = new WebSocket("ws://localhost:8000/items/" + input.value + "/ws");
var itemId = document.getElementById("itemId")
var token = document.getElementById("token")
ws = new WebSocket("ws://localhost:8000/items/" + itemId.value + "/ws?token=" + token.value);
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
@@ -32,6 +34,7 @@ html = """
message.appendChild(content)
messages.appendChild(message)
};
event.preventDefault()
}
function sendMessage(event) {
var input = document.getElementById("messageText")
@@ -50,26 +53,26 @@ async def get():
return HTMLResponse(html)
async def get_cookie_or_client(
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
async def get_cookie_or_token(
websocket: WebSocket, session: str = Cookie(None), token: str = Query(None)
):
if session is None and x_client is None:
if session is None and token is None:
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
return session or x_client
return session or token
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(
websocket: WebSocket,
item_id: int,
q: str = None,
cookie_or_client: str = Depends(get_cookie_or_client),
item_id: str,
q: int = None,
cookie_or_token: str = Depends(get_cookie_or_token),
):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(
f"Session Cookie or X-Client Header value is: {cookie_or_client}"
f"Session cookie or query token value is: {cookie_or_token}"
)
if q is not None:
await websocket.send_text(f"Query parameter q is: {q}")

View File

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

View File

@@ -37,6 +37,8 @@ class FastAPI(Starlette):
description: str = "",
version: str = "0.1.0",
openapi_url: Optional[str] = "/openapi.json",
openapi_tags: Optional[List[Dict[str, Any]]] = None,
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
default_response_class: Type[Response] = JSONResponse,
docs_url: Optional[str] = "/docs",
redoc_url: Optional[str] = "/redoc",
@@ -69,7 +71,9 @@ class FastAPI(Starlette):
self.title = title
self.description = description
self.version = version
self.servers = servers
self.openapi_url = openapi_url
self.openapi_tags = openapi_tags
# TODO: remove when discarding the openapi_prefix parameter
if openapi_prefix:
logger.warning(
@@ -103,6 +107,8 @@ class FastAPI(Starlette):
description=self.description,
routes=self.routes,
openapi_prefix=openapi_prefix,
tags=self.openapi_tags,
servers=self.servers,
)
return self.openapi_schema

View File

@@ -500,6 +500,7 @@ async def solve_dependencies(
name=sub_dependant.name,
security_scopes=sub_dependant.security_scopes,
)
use_sub_dependant.security_scopes = sub_dependant.security_scopes
solved_result = await solve_dependencies(
request=request,
@@ -514,13 +515,9 @@ async def solve_dependencies(
sub_values,
sub_errors,
background_tasks,
sub_response,
_, # the subdependency returns the same response we have
sub_dependency_cache,
) = solved_result
sub_response = cast(Response, sub_response)
response.headers.raw.extend(sub_response.headers.raw)
if sub_response.status_code:
response.status_code = sub_response.status_code
dependency_cache.update(sub_dependency_cache)
if sub_errors:
errors.extend(sub_errors)
@@ -645,9 +642,17 @@ async def request_body_to_args(
field = required_params[0]
field_info = get_field_info(field)
embed = getattr(field_info, "embed", None)
if len(required_params) == 1 and not embed:
field_alias_omitted = len(required_params) == 1 and not embed
if field_alias_omitted:
received_body = {field.alias: received_body}
for field in required_params:
loc: Tuple[str, ...]
if field_alias_omitted:
loc = ("body",)
else:
loc = ("body", field.alias)
value: Any = None
if received_body is not None:
if (
@@ -658,7 +663,7 @@ async def request_body_to_args(
try:
value = received_body.get(field.alias)
except AttributeError:
errors.append(get_missing_field_error(field.alias))
errors.append(get_missing_field_error(loc))
continue
if (
value is None
@@ -670,7 +675,7 @@ async def request_body_to_args(
)
):
if field.required:
errors.append(get_missing_field_error(field.alias))
errors.append(get_missing_field_error(loc))
else:
values[field.name] = deepcopy(field.default)
continue
@@ -689,7 +694,9 @@ async def request_body_to_args(
awaitables = [sub_value.read() for sub_value in value]
contents = await asyncio.gather(*awaitables)
value = sequence_shape_to_type[field.shape](contents)
v_, errors_ = field.validate(value, values, loc=("body", field.alias))
v_, errors_ = field.validate(value, values, loc=loc)
if isinstance(errors_, ErrorWrapper):
errors.append(errors_)
elif isinstance(errors_, list):
@@ -699,12 +706,12 @@ async def request_body_to_args(
return values, errors
def get_missing_field_error(field_alias: str) -> ErrorWrapper:
def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
if PYDANTIC_1:
missing_field_error = ErrorWrapper(MissingError(), loc=("body", field_alias))
missing_field_error = ErrorWrapper(MissingError(), loc=loc)
else: # pragma: no cover
missing_field_error = ErrorWrapper( # type: ignore
MissingError(), loc=("body", field_alias), config=BaseConfig,
MissingError(), loc=loc, config=BaseConfig,
)
return missing_field_error

View File

@@ -71,6 +71,8 @@ def jsonable_encoder(
by_alias=by_alias,
skip_defaults=bool(exclude_unset or skip_defaults),
)
if "__root__" in obj_dict:
obj_dict = obj_dict["__root__"]
return jsonable_encoder(
obj_dict,
exclude_none=exclude_none,

View File

@@ -44,7 +44,9 @@ def get_swagger_ui_html(
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
deepLinking: true
deepLinking: true,
showExtensions: true,
showCommonExtensions: true
})"""
if init_oauth:

View File

@@ -63,7 +63,7 @@ class ServerVariable(BaseModel):
class Server(BaseModel):
url: AnyUrl
url: Union[AnyUrl, str]
description: Optional[str] = None
variables: Optional[Dict[str, ServerVariable]] = None
@@ -112,7 +112,7 @@ class SchemaBase(BaseModel):
allOf: Optional[List[Any]] = None
oneOf: Optional[List[Any]] = None
anyOf: Optional[List[Any]] = None
not_: Optional[List[Any]] = Field(None, alias="not")
not_: Optional[Any] = Field(None, alias="not")
items: Optional[Any] = None
properties: Optional[Dict[str, Any]] = None
additionalProperties: Optional[Union[Dict[str, Any], bool]] = None
@@ -133,7 +133,7 @@ class Schema(SchemaBase):
allOf: Optional[List[SchemaBase]] = None
oneOf: Optional[List[SchemaBase]] = None
anyOf: Optional[List[SchemaBase]] = None
not_: Optional[List[SchemaBase]] = Field(None, alias="not")
not_: Optional[SchemaBase] = Field(None, alias="not")
items: Optional[SchemaBase] = None
properties: Optional[Dict[str, SchemaBase]] = None
additionalProperties: Optional[Union[Dict[str, Any], bool]] = None

View File

@@ -14,6 +14,7 @@ from fastapi.openapi.constants import (
from fastapi.openapi.models import OpenAPI
from fastapi.params import Body, Param
from fastapi.utils import (
deep_dict_update,
generate_operation_id_for_path,
get_field_info,
get_model_definitions,
@@ -86,7 +87,7 @@ def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, L
def get_openapi_operation_parameters(
*,
all_route_params: Sequence[ModelField],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> List[Dict[str, Any]]:
parameters = []
for param in all_route_params:
@@ -112,7 +113,7 @@ def get_openapi_operation_parameters(
def get_openapi_operation_request_body(
*,
body_field: Optional[ModelField],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str]
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> Optional[Dict]:
if not body_field:
return None
@@ -201,33 +202,6 @@ def get_openapi_path(
)
callbacks[callback.name] = {callback.path: cb_path}
operation["callbacks"] = callbacks
if route.responses:
for (additional_status_code, response) in route.responses.items():
process_response = response.copy()
assert isinstance(
process_response, dict
), "An additional response must be a dict"
field = route.response_fields.get(additional_status_code)
if field:
response_schema, _, _ = field_schema(
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
)
process_response.setdefault("content", {}).setdefault(
route_response_media_type or "application/json", {}
)["schema"] = response_schema
status_text: Optional[str] = status_code_ranges.get(
str(additional_status_code).upper()
) or http.client.responses.get(int(additional_status_code))
process_response.setdefault(
"description", status_text or "Additional Response"
)
status_code_key = str(additional_status_code).upper()
if status_code_key == "DEFAULT":
status_code_key = "default"
process_response.pop("model", None)
operation.setdefault("responses", {})[
status_code_key
] = process_response
status_code = str(route.status_code)
operation.setdefault("responses", {}).setdefault(status_code, {})[
"description"
@@ -251,7 +225,47 @@ def get_openapi_path(
).setdefault("content", {}).setdefault(route_response_media_type, {})[
"schema"
] = response_schema
if route.responses:
operation_responses = operation.setdefault("responses", {})
for (
additional_status_code,
additional_response,
) in route.responses.items():
process_response = additional_response.copy()
process_response.pop("model", None)
status_code_key = str(additional_status_code).upper()
if status_code_key == "DEFAULT":
status_code_key = "default"
openapi_response = operation_responses.setdefault(
status_code_key, {}
)
assert isinstance(
process_response, dict
), "An additional response must be a dict"
field = route.response_fields.get(additional_status_code)
additional_field_schema: Optional[Dict[str, Any]] = None
if field:
additional_field_schema, _, _ = field_schema(
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
)
media_type = route_response_media_type or "application/json"
additional_schema = (
process_response.setdefault("content", {})
.setdefault(media_type, {})
.setdefault("schema", {})
)
deep_dict_update(additional_schema, additional_field_schema)
status_text: Optional[str] = status_code_ranges.get(
str(additional_status_code).upper()
) or http.client.responses.get(int(additional_status_code))
description = (
process_response.get("description")
or openapi_response.get("description")
or status_text
or "Additional Response"
)
deep_dict_update(openapi_response, process_response)
openapi_response["description"] = description
http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
if (all_route_params or route.body_field) and not any(
[
@@ -317,12 +331,16 @@ def get_openapi(
openapi_version: str = "3.0.2",
description: str = None,
routes: Sequence[BaseRoute],
openapi_prefix: str = ""
openapi_prefix: str = "",
tags: Optional[List[Dict[str, Any]]] = None,
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
) -> Dict:
info = {"title": title, "version": version}
if description:
info["description"] = description
output = {"openapi": openapi_version, "info": info}
output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
if servers:
output["servers"] = servers
components: Dict[str, Dict] = {}
paths: Dict[str, Dict] = {}
flat_models = get_flat_models_from_routes(routes)
@@ -352,4 +370,6 @@ def get_openapi(
if components:
output["components"] = components
output["paths"] = paths
if tags:
output["tags"] = tags
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)

View File

@@ -51,6 +51,9 @@ class Param(FieldInfo):
**extra,
)
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.default})"
class Path(Param):
in_ = ParamTypes.path
@@ -239,6 +242,9 @@ class Body(FieldInfo):
**extra,
)
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.default})"
class Form(Body):
def __init__(
@@ -316,6 +322,11 @@ class Depends:
self.dependency = dependency
self.use_cache = use_cache
def __repr__(self) -> str:
attr = getattr(self.dependency, "__name__", type(self.dependency).__name__)
cache = "" if self.use_cache else ", use_cache=False"
return f"{self.__class__.__name__}({attr}{cache})"
class Security(Depends):
def __init__(

View File

@@ -1,5 +1,7 @@
import asyncio
import enum
import inspect
import json
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Type, Union
from fastapi import params
@@ -12,7 +14,6 @@ from fastapi.dependencies.utils import (
)
from fastapi.encoders import DictIntStrAny, SetIntStr, jsonable_encoder
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
from fastapi.logger import logger
from fastapi.openapi.constants import STATUS_CODES_WITH_NO_BODY
from fastapi.utils import (
PYDANTIC_1,
@@ -178,6 +179,8 @@ def get_request_handler(
body_bytes = await request.body()
if body_bytes:
body = await request.json()
except json.JSONDecodeError as e:
raise RequestValidationError([ErrorWrapper(e, ("body", e.pos))], body=e.doc)
except Exception as e:
raise HTTPException(
status_code=400, detail="There was an error parsing the body"
@@ -294,6 +297,9 @@ class APIRoute(routing.Route):
dependency_overrides_provider: Any = None,
callbacks: Optional[List["APIRoute"]] = None,
) -> None:
# normalise enums e.g. http.HTTPStatus
if isinstance(status_code, enum.IntEnum):
status_code = int(status_code)
self.path = path
self.endpoint = endpoint
self.name = get_name(endpoint) if name is None else name

View File

@@ -11,6 +11,7 @@ from .oauth2 import (
OAuth2AuthorizationCodeBearer,
OAuth2PasswordBearer,
OAuth2PasswordRequestForm,
OAuth2PasswordRequestFormStrict,
SecurityScopes,
)
from .open_id_connect_url import OpenIdConnect

View File

@@ -172,3 +172,15 @@ def generate_operation_id_for_path(*, name: str, path: str, method: str) -> str:
operation_id = re.sub("[^0-9a-zA-Z_]", "_", operation_id)
operation_id = operation_id + "_" + method.lower()
return operation_id
def deep_dict_update(main_dict: dict, update_dict: dict) -> None:
for key in update_dict:
if (
key in main_dict
and isinstance(main_dict[key], dict)
and isinstance(update_dict[key], dict)
):
deep_dict_update(main_dict[key], update_dict[key])
else:
main_dict[key] = update_dict[key]

View File

@@ -32,7 +32,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
requires = [
"starlette ==0.13.2",
"starlette ==0.13.4",
"pydantic >=0.32.2,<2.0.0"
]
description-file = "README.md"

View File

@@ -1,3 +1,5 @@
import http
from fastapi import FastAPI, Path, Query
app = FastAPI()
@@ -184,3 +186,8 @@ def get_query_param_required(query=Query(...)):
@app.get("/query/param-required/int")
def get_query_param_required_type(query: int = Query(...)):
return f"foo bar {query}"
@app.get("/enum-status-code", status_code=http.HTTPStatus.CREATED)
def get_enum_status_code():
return "foo bar"

View File

@@ -1078,6 +1078,18 @@ openapi_schema = {
],
}
},
"/enum-status-code": {
"get": {
"responses": {
"201": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "Get Enum Status Code",
"operationId": "get_enum_status_code_enum_status_code_get",
}
},
},
"components": {
"schemas": {
@@ -1149,3 +1161,9 @@ def test_redoc():
assert response.status_code == 200, response.text
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert "redoc@next" in response.text
def test_enum_status_code_response():
response = client.get("/enum-status-code")
assert response.status_code == 201, response.text
assert response.json() == "foo bar"

View File

@@ -0,0 +1,65 @@
from typing import List, Tuple
from fastapi import Depends, FastAPI, Security
from fastapi.security import SecurityScopes
from fastapi.testclient import TestClient
app = FastAPI()
def get_user(required_scopes: SecurityScopes):
return "john", required_scopes.scopes
def get_user_override(required_scopes: SecurityScopes):
return "alice", required_scopes.scopes
def get_data():
return [1, 2, 3]
def get_data_override():
return [3, 4, 5]
@app.get("/user")
def read_user(
user_data: Tuple[str, List[str]] = Security(get_user, scopes=["foo", "bar"]),
data: List[int] = Depends(get_data),
):
return {"user": user_data[0], "scopes": user_data[1], "data": data}
client = TestClient(app)
def test_normal():
response = client.get("/user")
assert response.json() == {
"user": "john",
"scopes": ["foo", "bar"],
"data": [1, 2, 3],
}
def test_override_data():
app.dependency_overrides[get_data] = get_data_override
response = client.get("/user")
assert response.json() == {
"user": "john",
"scopes": ["foo", "bar"],
"data": [3, 4, 5],
}
app.dependency_overrides = {}
def test_override_security():
app.dependency_overrides[get_user] = get_user_override
response = client.get("/user")
assert response.json() == {
"user": "alice",
"scopes": ["foo", "bar"],
"data": [1, 2, 3],
}
app.dependency_overrides = {}

View File

@@ -76,6 +76,10 @@ class ModelWithDefault(BaseModel):
bla: str = "bla"
class ModelWithRoot(BaseModel):
__root__: str
@pytest.fixture(
name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]
)
@@ -158,3 +162,8 @@ def test_encode_model_with_path(model_with_path):
else:
expected = "/foo/bar"
assert jsonable_encoder(model_with_path) == {"path": expected}
def test_encode_root():
model = ModelWithRoot(__root__="Foo")
assert jsonable_encoder(model) == "Foo"

View File

@@ -104,7 +104,7 @@ single_error = {
"detail": [
{
"ctx": {"limit_value": 0.0},
"loc": ["body", "item", 0, "age"],
"loc": ["body", 0, "age"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt",
}
@@ -114,22 +114,22 @@ single_error = {
multiple_errors = {
"detail": [
{
"loc": ["body", "item", 0, "name"],
"loc": ["body", 0, "name"],
"msg": "field required",
"type": "value_error.missing",
},
{
"loc": ["body", "item", 0, "age"],
"loc": ["body", 0, "age"],
"msg": "value is not a valid decimal",
"type": "type_error.decimal",
},
{
"loc": ["body", "item", 1, "name"],
"loc": ["body", 1, "name"],
"msg": "field required",
"type": "value_error.missing",
},
{
"loc": ["body", "item", 1, "age"],
"loc": ["body", 1, "age"],
"msg": "value is not a valid decimal",
"type": "type_error.decimal",
},

View File

@@ -0,0 +1,60 @@
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI(
servers=[
{"url": "/", "description": "Default, relative server"},
{
"url": "http://staging.localhost.tiangolo.com:8000",
"description": "Staging but actually localhost still",
},
{"url": "https://prod.example.com"},
]
)
@app.get("/foo")
def foo():
return {"message": "Hello World"}
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"servers": [
{"url": "/", "description": "Default, relative server"},
{
"url": "http://staging.localhost.tiangolo.com:8000",
"description": "Staging but actually localhost still",
},
{"url": "https://prod.example.com"},
],
"paths": {
"/foo": {
"get": {
"summary": "Foo",
"operationId": "foo_foo_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}
def test_openapi_servers():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_app():
response = client.get("/foo")
assert response.status_code == 200, response.text

46
tests/test_params_repr.py Normal file
View File

@@ -0,0 +1,46 @@
import pytest
from fastapi.params import Body, Cookie, Depends, Header, Param, Path, Query
test_data = ["teststr", None, ..., 1, []]
def get_user():
return {} # pragma: no cover
@pytest.fixture(scope="function", params=test_data)
def params(request):
return request.param
def test_param_repr(params):
assert repr(Param(params)) == "Param(" + str(params) + ")"
def test_path_repr(params):
assert repr(Path(params)) == "Path(Ellipsis)"
def test_query_repr(params):
assert repr(Query(params)) == "Query(" + str(params) + ")"
def test_header_repr(params):
assert repr(Header(params)) == "Header(" + str(params) + ")"
def test_cookie_repr(params):
assert repr(Cookie(params)) == "Cookie(" + str(params) + ")"
def test_body_repr(params):
assert repr(Body(params)) == "Body(" + str(params) + ")"
def test_depends_repr():
assert repr(Depends()) == "Depends(NoneType)"
assert repr(Depends(get_user)) == "Depends(get_user)"
assert repr(Depends(use_cache=False)) == "Depends(NoneType, use_cache=False)"
assert (
repr(Depends(get_user, use_cache=False)) == "Depends(get_user, use_cache=False)"
)

View File

@@ -0,0 +1,34 @@
from fastapi import Depends, FastAPI, Response
from fastapi.testclient import TestClient
app = FastAPI()
def set_cookie(*, response: Response):
response.set_cookie("cookie-name", "cookie-value")
return {}
def set_indirect_cookie(*, dep: str = Depends(set_cookie)):
return dep
@app.get("/directCookie")
def get_direct_cookie(dep: str = Depends(set_cookie)):
return {"dep": dep}
@app.get("/indirectCookie")
def get_indirect_cookie(dep: str = Depends(set_indirect_cookie)):
return {"dep": dep}
client = TestClient(app)
def test_cookie_is_set_once():
direct_response = client.get("/directCookie")
indirect_response = client.get("/indirectCookie")
assert (
direct_response.headers["set-cookie"] == indirect_response.headers["set-cookie"]
)

View File

@@ -1,7 +1,6 @@
import pytest
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2
from fastapi.security.oauth2 import OAuth2PasswordRequestFormStrict
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
from fastapi.testclient import TestClient
from pydantic import BaseModel

View File

@@ -2,8 +2,7 @@ from typing import Optional
import pytest
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2
from fastapi.security.oauth2 import OAuth2PasswordRequestFormStrict
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
from fastapi.testclient import TestClient
from pydantic import BaseModel

View File

@@ -15,7 +15,7 @@ openapi_schema = {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"description": "Return the JSON item or an image.",
"content": {
"image/png": {},
"application/json": {

View File

@@ -20,7 +20,7 @@ openapi_schema = {
},
},
"200": {
"description": "Successful Response",
"description": "Item requested by ID",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"},

View File

@@ -1,3 +1,5 @@
from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
@@ -92,7 +94,7 @@ def test_openapi_schema():
price_missing = {
"detail": [
{
"loc": ["body", "item", "price"],
"loc": ["body", "price"],
"msg": "field required",
"type": "value_error.missing",
}
@@ -102,7 +104,7 @@ price_missing = {
price_not_float = {
"detail": [
{
"loc": ["body", "item", "price"],
"loc": ["body", "price"],
"msg": "value is not a valid float",
"type": "type_error.float",
}
@@ -112,12 +114,12 @@ price_not_float = {
name_price_missing = {
"detail": [
{
"loc": ["body", "item", "name"],
"loc": ["body", "name"],
"msg": "field required",
"type": "value_error.missing",
},
{
"loc": ["body", "item", "price"],
"loc": ["body", "price"],
"msg": "field required",
"type": "value_error.missing",
},
@@ -126,11 +128,7 @@ name_price_missing = {
body_missing = {
"detail": [
{
"loc": ["body", "item"],
"msg": "field required",
"type": "value_error.missing",
}
{"loc": ["body"], "msg": "field required", "type": "value_error.missing",}
]
}
@@ -176,5 +174,24 @@ def test_post_body(path, body, expected_status, expected_response):
def test_post_broken_body():
response = client.post("/items/", data={"name": "Foo", "price": 50.5})
assert response.status_code == 400, response.text
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
"ctx": {
"colno": 1,
"doc": "name=Foo&price=50.5",
"lineno": 1,
"msg": "Expecting value",
"pos": 0,
},
"loc": ["body", 0],
"msg": "Expecting value: line 1 column 1 (char 0)",
"type": "value_error.jsondecode",
}
]
}
with patch("json.loads", side_effect=Exception):
response = client.post("/items/", json={"test": "test2"})
assert response.status_code == 400, response.text
assert response.json() == {"detail": "There was an error parsing the body"}

View File

@@ -95,7 +95,7 @@ def test_post_invalid_body():
assert response.json() == {
"detail": [
{
"loc": ["body", "weights", "__key__"],
"loc": ["body", "__key__"],
"msg": "value is not a valid integer",
"type": "type_error.integer",
}

View File

@@ -18,7 +18,7 @@ def test_exception_handler_body_access():
"body": '{"numbers": [1, 2, 3]}',
"errors": [
{
"loc": ["body", "numbers"],
"loc": ["body"],
"msg": "value is not a valid list",
"type": "type_error.list",
}

View File

@@ -92,7 +92,7 @@ def test_post_validation_error():
assert response.json() == {
"detail": [
{
"loc": ["body", "item", "size"],
"loc": ["body", "size"],
"msg": "value is not a valid integer",
"type": "type_error.integer",
}

View File

@@ -0,0 +1,65 @@
from fastapi.testclient import TestClient
from metadata.tutorial004 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/": {
"get": {
"tags": ["users"],
"summary": "Get Users",
"operationId": "get_users_users__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
},
"/items/": {
"get": {
"tags": ["items"],
"summary": "Get Items",
"operationId": "get_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
},
},
"tags": [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
],
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_path_operations():
response = client.get("/items/")
assert response.status_code == 200, response.text
response = client.get("/users/")
assert response.status_code == 200, response.text

View File

@@ -15,69 +15,65 @@ def test_main():
def test_websocket_with_cookie():
with pytest.raises(WebSocketDisconnect):
with client.websocket_connect(
"/items/1/ws", cookies={"session": "fakesession"}
"/items/foo/ws", cookies={"session": "fakesession"}
) as websocket:
message = "Message one"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: fakesession"
assert data == "Session cookie or query token value is: fakesession"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 1"
assert data == f"Message text was: {message}, for item ID: foo"
message = "Message two"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: fakesession"
assert data == "Session cookie or query token value is: fakesession"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 1"
assert data == f"Message text was: {message}, for item ID: foo"
def test_websocket_with_header():
with pytest.raises(WebSocketDisconnect):
with client.websocket_connect(
"/items/2/ws", headers={"X-Client": "xmen"}
) as websocket:
with client.websocket_connect("/items/bar/ws?token=some-token") as websocket:
message = "Message one"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: xmen"
assert data == "Session cookie or query token value is: some-token"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 2"
assert data == f"Message text was: {message}, for item ID: bar"
message = "Message two"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: xmen"
assert data == "Session cookie or query token value is: some-token"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 2"
assert data == f"Message text was: {message}, for item ID: bar"
def test_websocket_with_header_and_query():
with pytest.raises(WebSocketDisconnect):
with client.websocket_connect(
"/items/2/ws?q=baz", headers={"X-Client": "xmen"}
) as websocket:
with client.websocket_connect("/items/2/ws?q=3&token=some-token") as websocket:
message = "Message one"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: xmen"
assert data == "Session cookie or query token value is: some-token"
data = websocket.receive_text()
assert data == "Query parameter q is: baz"
assert data == "Query parameter q is: 3"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 2"
message = "Message two"
websocket.send_text(message)
data = websocket.receive_text()
assert data == "Session Cookie or X-Client Header value is: xmen"
assert data == "Session cookie or query token value is: some-token"
data = websocket.receive_text()
assert data == "Query parameter q is: baz"
assert data == "Query parameter q is: 3"
data = websocket.receive_text()
assert data == f"Message text was: {message}, for item ID: 2"
def test_websocket_no_credentials():
with pytest.raises(WebSocketDisconnect):
client.websocket_connect("/items/2/ws")
client.websocket_connect("/items/foo/ws")
def test_websocket_invalid_data():
with pytest.raises(WebSocketDisconnect):
client.websocket_connect("/items/foo/ws", headers={"X-Client": "xmen"})
client.websocket_connect("/items/foo/ws?q=bar&token=some-token")