Browse Source

Merge branch 'master' into bugfix/subapp-custom-openapi

pull/4657/head
Lorhan Sohaky 2 years ago
committed by GitHub
parent
commit
a5c8a6e405
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/en/docs/advanced/additional-responses.md
  2. 4
      docs/en/docs/advanced/behind-a-proxy.md
  3. 16
      docs/en/docs/advanced/extending-openapi.md
  4. 4
      docs/en/docs/advanced/openapi-callbacks.md
  5. 51
      docs/en/docs/advanced/openapi-webhooks.md
  6. 2
      docs/en/docs/advanced/path-operation-advanced-configuration.md
  7. BIN
      docs/en/docs/img/tutorial/metadata/image01.png
  8. BIN
      docs/en/docs/img/tutorial/openapi-webhooks/image01.png
  9. 38
      docs/en/docs/release-notes.md
  10. 2
      docs/en/docs/tutorial/first-steps.md
  11. 15
      docs/en/docs/tutorial/metadata.md
  12. 2
      docs/en/docs/tutorial/path-params.md
  13. 128
      docs/en/docs/tutorial/schema-extra-example.md
  14. 3
      docs/en/mkdocs.maybe-insiders.yml
  15. 21
      docs/en/mkdocs.yml
  16. 3
      docs_src/extending_openapi/tutorial001.py
  17. 1
      docs_src/metadata/tutorial001.py
  18. 38
      docs_src/metadata/tutorial001_1.py
  19. 25
      docs_src/openapi_webhooks/tutorial001.py
  20. 14
      docs_src/schema_extra_example/tutorial001.py
  21. 14
      docs_src/schema_extra_example/tutorial001_py310.py
  22. 8
      docs_src/schema_extra_example/tutorial002.py
  23. 8
      docs_src/schema_extra_example/tutorial002_py310.py
  24. 14
      docs_src/schema_extra_example/tutorial003.py
  25. 14
      docs_src/schema_extra_example/tutorial003_an.py
  26. 14
      docs_src/schema_extra_example/tutorial003_an_py310.py
  27. 14
      docs_src/schema_extra_example/tutorial003_an_py39.py
  28. 14
      docs_src/schema_extra_example/tutorial003_py310.py
  29. 37
      docs_src/schema_extra_example/tutorial004.py
  30. 37
      docs_src/schema_extra_example/tutorial004_an.py
  31. 37
      docs_src/schema_extra_example/tutorial004_an_py310.py
  32. 37
      docs_src/schema_extra_example/tutorial004_an_py39.py
  33. 37
      docs_src/schema_extra_example/tutorial004_py310.py
  34. 2
      fastapi/__init__.py
  35. 8
      fastapi/applications.py
  36. 4
      fastapi/encoders.py
  37. 4
      fastapi/openapi/docs.py
  38. 101
      fastapi/openapi/models.py
  39. 38
      fastapi/openapi/utils.py
  40. 73
      fastapi/param_functions.py
  41. 108
      fastapi/params.py
  42. 1
      pyproject.toml
  43. 2
      scripts/docs.py
  44. 2
      tests/test_additional_properties.py
  45. 115
      tests/test_additional_properties_bool.py
  46. 2
      tests/test_additional_response_extra.py
  47. 2
      tests/test_additional_responses_bad.py
  48. 2
      tests/test_additional_responses_custom_model_in_callback.py
  49. 2
      tests/test_additional_responses_custom_validationerror.py
  50. 2
      tests/test_additional_responses_default_validationerror.py
  51. 2
      tests/test_additional_responses_response_class.py
  52. 2
      tests/test_additional_responses_router.py
  53. 2
      tests/test_annotated.py
  54. 2
      tests/test_application.py
  55. 2
      tests/test_custom_route_class.py
  56. 2
      tests/test_dependency_duplicates.py
  57. 2
      tests/test_deprecated_openapi_prefix.py
  58. 2
      tests/test_duplicate_models_openapi.py
  59. 2
      tests/test_enforce_once_required_parameter.py
  60. 2
      tests/test_extra_routes.py
  61. 2
      tests/test_filter_pydantic_sub_model.py
  62. 14
      tests/test_generate_unique_id_function.py
  63. 2
      tests/test_get_request_body.py
  64. 2
      tests/test_include_router_defaults_overrides.py
  65. 10
      tests/test_jsonable_encoder.py
  66. 2
      tests/test_modules_same_name_body/test_main.py
  67. 2
      tests/test_multi_body_errors.py
  68. 2
      tests/test_multi_query_errors.py
  69. 2
      tests/test_openapi_query_parameter_extension.py
  70. 2
      tests/test_openapi_route_extensions.py
  71. 2
      tests/test_openapi_servers.py
  72. 2
      tests/test_param_in_path_and_dependency.py
  73. 2
      tests/test_param_include_in_schema.py
  74. 2
      tests/test_put_no_body.py
  75. 2
      tests/test_repeated_dependency_schema.py
  76. 2
      tests/test_repeated_parameter_alias.py
  77. 2
      tests/test_reponse_set_reponse_code_empty.py
  78. 2
      tests/test_request_body_parameters_media_type.py
  79. 2
      tests/test_response_by_alias.py
  80. 2
      tests/test_response_class_no_mediatype.py
  81. 2
      tests/test_response_code_no_body.py
  82. 2
      tests/test_response_model_as_return_annotation.py
  83. 2
      tests/test_response_model_sub_types.py
  84. 572
      tests/test_schema_extra_examples.py
  85. 2
      tests/test_security_api_key_cookie.py
  86. 2
      tests/test_security_api_key_cookie_description.py
  87. 2
      tests/test_security_api_key_cookie_optional.py
  88. 2
      tests/test_security_api_key_header.py
  89. 2
      tests/test_security_api_key_header_description.py
  90. 2
      tests/test_security_api_key_header_optional.py
  91. 2
      tests/test_security_api_key_query.py
  92. 2
      tests/test_security_api_key_query_description.py
  93. 2
      tests/test_security_api_key_query_optional.py
  94. 2
      tests/test_security_http_base.py
  95. 2
      tests/test_security_http_base_description.py
  96. 2
      tests/test_security_http_base_optional.py
  97. 2
      tests/test_security_http_basic_optional.py
  98. 2
      tests/test_security_http_basic_realm.py
  99. 2
      tests/test_security_http_basic_realm_description.py
  100. 2
      tests/test_security_http_bearer.py

4
docs/en/docs/advanced/additional-responses.md

@ -236,5 +236,5 @@ For example:
To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" class="external-link" target="_blank">OpenAPI Responses Object</a>, it includes the `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" class="external-link" target="_blank">OpenAPI Response Object</a>, you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responsesObject" class="external-link" target="_blank">OpenAPI Responses Object</a>, it includes the `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responseObject" class="external-link" target="_blank">OpenAPI Response Object</a>, you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`.

4
docs/en/docs/advanced/behind-a-proxy.md

@ -46,7 +46,7 @@ The docs UI would also need the OpenAPI schema to declare that this API `server`
```JSON hl_lines="4-8"
{
"openapi": "3.0.2",
"openapi": "3.1.0",
// More stuff here
"servers": [
{
@ -298,7 +298,7 @@ Will generate an OpenAPI schema like:
```JSON hl_lines="5-7"
{
"openapi": "3.0.2",
"openapi": "3.1.0",
// More stuff here
"servers": [
{

16
docs/en/docs/advanced/extending-openapi.md

@ -29,10 +29,14 @@ And that function `get_openapi()` receives as parameters:
* `title`: The OpenAPI title, shown in the docs.
* `version`: The version of your API, e.g. `2.5.0`.
* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.0.2`.
* `description`: The description of your API.
* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`.
* `summary`: A short summary of the API.
* `description`: The description of your API, this can include markdown and will be shown in the docs.
* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`.
!!! info
The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above.
## Overriding the defaults
Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need.
@ -51,7 +55,7 @@ First, write all your **FastAPI** application as normally:
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
```Python hl_lines="2 15-20"
```Python hl_lines="2 15-21"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@ -59,7 +63,7 @@ Then, use the same utility function to generate the OpenAPI schema, inside a `cu
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
```Python hl_lines="21-23"
```Python hl_lines="22-24"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@ -71,7 +75,7 @@ That way, your application won't have to generate the schema every time a user o
It will be generated only once, and then the same cached schema will be used for the next requests.
```Python hl_lines="13-14 24-25"
```Python hl_lines="13-14 25-26"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@ -79,7 +83,7 @@ It will be generated only once, and then the same cached schema will be used for
Now you can replace the `.openapi()` method with your new function.
```Python hl_lines="28"
```Python hl_lines="29"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```

4
docs/en/docs/advanced/openapi-callbacks.md

@ -103,11 +103,11 @@ It should look just like a normal FastAPI *path operation*:
There are 2 main differences from a normal *path operation*:
* It doesn't need to have any actual code, because your app will never call this code. It's only used to document the *external API*. So, the function could just have `pass`.
* The *path* can contain an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a> (see more below) where it can use variables with parameters and parts of the original request sent to *your API*.
* The *path* can contain an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a> (see more below) where it can use variables with parameters and parts of the original request sent to *your API*.
### The callback path expression
The callback *path* can have an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a> that can contain parts of the original request sent to *your API*.
The callback *path* can have an <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 expression</a> that can contain parts of the original request sent to *your API*.
In this case, it's the `str`:

51
docs/en/docs/advanced/openapi-webhooks.md

@ -0,0 +1,51 @@
# OpenAPI Webhooks
There are cases where you want to tell your API **users** that your app could call *their* app (sending a request) with some data, normally to **notify** of some type of **event**.
This means that instead of the normal process of your users sending requests to your API, it's **your API** (or your app) that could **send requests to their system** (to their API, their app).
This is normally called a **webhook**.
## Webhooks steps
The process normally is that **you define** in your code what is the message that you will send, the **body of the request**.
You also define in some way at which **moments** your app will send those requests or events.
And **your users** define in some way (for example in a web dashboard somewhere) the **URL** where your app should send those requests.
All the **logic** about how to register the URLs for webhooks and the code to actually send those requests is up to you. You write it however you want to in **your own code**.
## Documenting webhooks with **FastAPI** and OpenAPI
With **FastAPI**, using OpenAPI, you can define the names of these webhooks, the types of HTTP operations that your app can send (e.g. `POST`, `PUT`, etc.) and the request **bodies** that your app would send.
This can make it a lot easier for your users to **implement their APIs** to receive your **webhook** requests, they might even be able to autogenerate some of their own API code.
!!! info
Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0` and above.
## An app with webhooks
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
```Python hl_lines="9-13 36-53"
{!../../../docs_src/openapi_webhooks/tutorial001.py!}
```
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.
!!! info
The `app.webhooks` object is actually just an `APIRouter`, the same type you would use when structuring your app with multiple files.
Notice that with webhooks you are actually not declaring a *path* (like `/items/`), the text you pass there is just an **identifier** of the webhook (the name of the event), for example in `@app.webhooks.post("new-subscription")`, the webhook name is `new-subscription`.
This is because it is expected that **your users** would define the actual **URL path** where they want to receive the webhook request in some other way (e.g. a web dashboard).
### Check the docs
Now you can start your app with Uvicorn and 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 your docs have the normal *path operations* and now also some **webhooks**:
<img src="/img/tutorial/openapi-webhooks/image01.png">

2
docs/en/docs/advanced/path-operation-advanced-configuration.md

@ -97,7 +97,7 @@ And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will
```JSON hl_lines="22"
{
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"

BIN
docs/en/docs/img/tutorial/metadata/image01.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 84 KiB

BIN
docs/en/docs/img/tutorial/openapi-webhooks/image01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

38
docs/en/docs/release-notes.md

@ -2,13 +2,45 @@
## Latest Changes
## 0.99.1
### Fixes
* 🐛 Fix JSON Schema accepting bools as valid JSON Schemas, e.g. `additionalProperties: false`. PR [#9781](https://github.com/tiangolo/fastapi/pull/9781) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Update source examples to use new JSON Schema examples field. PR [#9776](https://github.com/tiangolo/fastapi/pull/9776) by [@tiangolo](https://github.com/tiangolo).
## 0.99.0
### Features
* ✨ Add support for OpenAPI 3.1.0. PR [#9770](https://github.com/tiangolo/fastapi/pull/9770) by [@tiangolo](https://github.com/tiangolo).
* New support for documenting **webhooks**, read the new docs here: <a href="https://fastapi.tiangolo.com/advanced/openapi-webhooks/" class="external-link" target="_blank">Advanced User Guide: OpenAPI Webhooks</a>.
* Upgrade OpenAPI 3.1.0, this uses JSON Schema 2020-12.
* Upgrade Swagger UI to version 5.x.x, that supports OpenAPI 3.1.0.
* Updated `examples` field in `Query()`, `Cookie()`, `Body()`, etc. based on the latest JSON Schema and OpenAPI. Now it takes a list of examples and they are included directly in the JSON Schema, not outside. Read more about it (including the historical technical details) in the updated docs: <a href="https://fastapi.tiangolo.com/tutorial/schema-extra-example/" class="external-link" target="_blank">Tutorial: Declare Request Example Data</a>.
* ✨ Add support for `deque` objects and children in `jsonable_encoder`. PR [#9433](https://github.com/tiangolo/fastapi/pull/9433) by [@cranium](https://github.com/cranium).
### Docs
* 📝 Fix form for the FastAPI and friends newsletter. PR [#9749](https://github.com/tiangolo/fastapi/pull/9749) by [@tiangolo](https://github.com/tiangolo).
### Translations
* 🌐 Add Persian translation for `docs/fa/docs/advanced/sub-applications.md`. PR [#9692](https://github.com/tiangolo/fastapi/pull/9692) by [@mojtabapaso](https://github.com/mojtabapaso).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/response-model.md`. PR [#9675](https://github.com/tiangolo/fastapi/pull/9675) by [@glsglsgls](https://github.com/glsglsgls).
### Internal
* 🔨 Enable linenums in MkDocs Material during local live development to simplify highlighting code. PR [#9769](https://github.com/tiangolo/fastapi/pull/9769) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Update httpx requirement from <0.24.0,>=0.23.0 to >=0.23.0,<0.25.0. PR [#9724](https://github.com/tiangolo/fastapi/pull/9724) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump mkdocs-material from 9.1.16 to 9.1.17. PR [#9746](https://github.com/tiangolo/fastapi/pull/9746) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔥 Remove missing translation dummy pages, no longer necessary. PR [#9751](https://github.com/tiangolo/fastapi/pull/9751) by [@tiangolo](https://github.com/tiangolo).
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#9259](https://github.com/tiangolo/fastapi/pull/9259) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* 🌐 Add Persian translation for `docs/fa/docs/advanced/sub-applications.md`. PR [#9692](https://github.com/tiangolo/fastapi/pull/9692) by [@mojtabapaso](https://github.com/mojtabapaso).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/response-model.md`. PR [#9675](https://github.com/tiangolo/fastapi/pull/9675) by [@glsglsgls](https://github.com/glsglsgls).
* 📝 Fix form for the FastAPI and friends newsletter. PR [#9749](https://github.com/tiangolo/fastapi/pull/9749) by [@tiangolo](https://github.com/tiangolo).
* ✨ Add Material for MkDocs Insiders features and cards. PR [#9748](https://github.com/tiangolo/fastapi/pull/9748) by [@tiangolo](https://github.com/tiangolo).
* 🔥 Remove languages without translations. PR [#9743](https://github.com/tiangolo/fastapi/pull/9743) by [@tiangolo](https://github.com/tiangolo).
* ✨ Refactor docs for building scripts, use MkDocs hooks, simplify (remove) configs for languages. PR [#9742](https://github.com/tiangolo/fastapi/pull/9742) by [@tiangolo](https://github.com/tiangolo).

2
docs/en/docs/tutorial/first-steps.md

@ -99,7 +99,7 @@ It will show a JSON starting with something like:
```JSON
{
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"

15
docs/en/docs/tutorial/metadata.md

@ -9,15 +9,16 @@ You can set the following fields that are used in the OpenAPI specification and
| Parameter | Type | Description |
|------------|------|-------------|
| `title` | `str` | The title of the API. |
| `summary` | `str` | A short summary of the API. <small>Available since OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
| `description` | `str` | A short description of the API. It can use Markdown. |
| `version` | `string` | The version of the API. This is the version of your own application, not of OpenAPI. For example `2.5.0`. |
| `terms_of_service` | `str` | A URL to the Terms of Service for the API. If provided, this has to be a URL. |
| `contact` | `dict` | The contact information for the exposed API. It can contain several fields. <details><summary><code>contact</code> fields</summary><table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>The identifying name of the contact person/organization.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>The URL pointing to the contact information. MUST be in the format of a URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>The email address of the contact person/organization. MUST be in the format of an email address.</td></tr></tbody></table></details> |
| `license_info` | `dict` | The license information for the exposed API. It can contain several fields. <details><summary><code>license_info</code> fields</summary><table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>REQUIRED</strong> (if a <code>license_info</code> is set). The license name used for the API.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>A URL to the license used for the API. MUST be in the format of a URL.</td></tr></tbody></table></details> |
| `license_info` | `dict` | The license information for the exposed API. It can contain several fields. <details><summary><code>license_info</code> fields</summary><table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>REQUIRED</strong> (if a <code>license_info</code> is set). The license name used for the API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>An <a href="https://spdx.dev/spdx-specification-21-web-version/#h.jxpfx0ykyb60" class="external-link" target="_blank">SPDX</a> license expression for the API. The <code>identifier</code> field is mutually exclusive of the <code>url</code> field. <small>Available since OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>A URL to the license used for the API. MUST be in the format of a URL.</td></tr></tbody></table></details> |
You can set them as follows:
```Python hl_lines="3-16 19-31"
```Python hl_lines="3-16 19-32"
{!../../../docs_src/metadata/tutorial001.py!}
```
@ -28,6 +29,16 @@ With this configuration, the automatic API docs would look like:
<img src="/img/tutorial/metadata/image01.png">
## License identifier
Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the `license_info` with an `identifier` instead of a `url`.
For example:
```Python hl_lines="31"
{!../../../docs_src/metadata/tutorial001_1.py!}
```
## Metadata for tags
You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`.

2
docs/en/docs/tutorial/path-params.md

@ -83,7 +83,7 @@ And when you open your browser at <a href="http://127.0.0.1:8000/docs" class="ex
## Standards-based benefits, alternative documentation
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.
And because the generated schema is from the <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.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), 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>:

128
docs/en/docs/tutorial/schema-extra-example.md

@ -6,17 +6,17 @@ Here are several ways to do it.
## Pydantic `schema_extra`
You can declare an `example` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
You can declare `examples` for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
=== "Python 3.10+"
```Python hl_lines="13-21"
```Python hl_lines="13-23"
{!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!}
```
=== "Python 3.6+"
```Python hl_lines="15-23"
```Python hl_lines="15-25"
{!> ../../../docs_src/schema_extra_example/tutorial001.py!}
```
@ -27,11 +27,16 @@ That extra info will be added as-is to the output **JSON Schema** for that model
For example you could use it to add metadata for a frontend user interface, etc.
## `Field` additional arguments
!!! info
OpenAPI 3.1.0 (used since FastAPI 0.99.0) added support for `examples`, which is part of the **JSON Schema** standard.
Before that, it only supported the keyword `example` with a single example. That is still supported by OpenAPI 3.1.0, but is deprecated and is not part of the JSON Schema standard. So you are encouraged to migrate `example` to `examples`. 🤓
You can read more at the end of this page.
When using `Field()` with Pydantic models, you can also declare extra info for the **JSON Schema** by passing any other arbitrary arguments to the function.
## `Field` additional arguments
You can use this to add `example` for each field:
When using `Field()` with Pydantic models, you can also declare additional `examples`:
=== "Python 3.10+"
@ -45,10 +50,7 @@ You can use this to add `example` for each field:
{!> ../../../docs_src/schema_extra_example/tutorial002.py!}
```
!!! warning
Keep in mind that those extra arguments passed won't add any validation, only extra information, for documentation purposes.
## `example` and `examples` in OpenAPI
## `examples` in OpenAPI
When using any of:
@ -60,27 +62,27 @@ When using any of:
* `Form()`
* `File()`
you can also declare a data `example` or a group of `examples` with additional information that will be added to **OpenAPI**.
you can also declare a group of `examples` with additional information that will be added to **OpenAPI**.
### `Body` with `example`
### `Body` with `examples`
Here we pass an `example` of the data expected in `Body()`:
Here we pass `examples` containing one example of the data expected in `Body()`:
=== "Python 3.10+"
```Python hl_lines="22-27"
```Python hl_lines="22-29"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="22-27"
```Python hl_lines="22-29"
{!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="23-28"
```Python hl_lines="23-30"
{!> ../../../docs_src/schema_extra_example/tutorial003_an.py!}
```
@ -89,7 +91,7 @@ Here we pass an `example` of the data expected in `Body()`:
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="18-23"
```Python hl_lines="18-25"
{!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!}
```
@ -98,7 +100,7 @@ Here we pass an `example` of the data expected in `Body()`:
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="20-25"
```Python hl_lines="20-27"
{!> ../../../docs_src/schema_extra_example/tutorial003.py!}
```
@ -110,32 +112,23 @@ With any of the methods above it would look like this in the `/docs`:
### `Body` with multiple `examples`
Alternatively to the single `example`, you can pass `examples` using a `dict` with **multiple examples**, each with extra information that will be added to **OpenAPI** too.
The keys of the `dict` identify each example, and each value is another `dict`.
Each specific example `dict` in the `examples` can contain:
* `summary`: Short description for the example.
* `description`: A long description that can contain Markdown text.
* `value`: This is the actual example shown, e.g. a `dict`.
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
You can of course also pass multiple `examples`:
=== "Python 3.10+"
```Python hl_lines="23-49"
```Python hl_lines="23-38"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py310.py!}
```
=== "Python 3.9+"
```Python hl_lines="23-49"
```Python hl_lines="23-38"
{!> ../../../docs_src/schema_extra_example/tutorial004_an_py39.py!}
```
=== "Python 3.6+"
```Python hl_lines="24-50"
```Python hl_lines="24-39"
{!> ../../../docs_src/schema_extra_example/tutorial004_an.py!}
```
@ -144,7 +137,7 @@ Each specific example `dict` in the `examples` can contain:
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="19-45"
```Python hl_lines="19-34"
{!> ../../../docs_src/schema_extra_example/tutorial004_py310.py!}
```
@ -153,7 +146,7 @@ Each specific example `dict` in the `examples` can contain:
!!! tip
Prefer to use the `Annotated` version if possible.
```Python hl_lines="21-47"
```Python hl_lines="21-36"
{!> ../../../docs_src/schema_extra_example/tutorial004.py!}
```
@ -165,25 +158,76 @@ With `examples` added to `Body()` the `/docs` would look like:
## Technical Details
!!! tip
If you are already using **FastAPI** version **0.99.0 or above**, you can probably **skip** these details.
They are more relevant for older versions, before OpenAPI 3.1.0 was available.
You can consider this a brief OpenAPI and JSON Schema **history lesson**. 🤓
!!! warning
These are very technical details about the standards **JSON Schema** and **OpenAPI**.
If the ideas above already work for you, that might be enough, and you probably don't need these details, feel free to skip them.
When you add an example inside of a Pydantic model, using `schema_extra` or `Field(example="something")` that example is added to the **JSON Schema** for that Pydantic model.
Before OpenAPI 3.1.0, OpenAPI used an older and modified version of **JSON Schema**.
And that **JSON Schema** of the Pydantic model is included in the **OpenAPI** of your API, and then it's used in the docs UI.
JSON Schema didn't have `examples`, so OpenAPI added it's own `example` field to its own modified version.
OpenAPI also added `example` and `examples` fields to other parts of the specification:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object" class="external-link" target="_blank">`Parameter Object` (in the specification)</a> that was used by FastAPI's:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object" class="external-link" target="_blank">`Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification)</a> that was used by FastAPI's:
* `Body()`
* `File()`
* `Form()`
**JSON Schema** doesn't really have a field `example` in the standards. Recent versions of JSON Schema define a field <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a>, but OpenAPI 3.0.3 is based on an older version of JSON Schema that didn't have `examples`.
### OpenAPI's `examples` field
So, OpenAPI 3.0.3 defined its own <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-20" class="external-link" target="_blank">`example`</a> for the modified version of **JSON Schema** it uses, for the same purpose (but it's a single `example`, not `examples`), and that's what is used by the API docs UI (using Swagger UI).
The shape of this field `examples` from OpenAPI is a `dict` with **multiple examples**, each with extra information that will be added to **OpenAPI** too.
The keys of the `dict` identify each example, and each value is another `dict`.
Each specific example `dict` in the `examples` can contain:
* `summary`: Short description for the example.
* `description`: A long description that can contain Markdown text.
* `value`: This is the actual example shown, e.g. a `dict`.
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
This applies to those other parts of the OpenAPI specification apart from JSON Schema.
### JSON Schema's `examples` field
But then JSON Schema added an <a href="https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5" class="external-link" target="_blank">`examples`</a> field to a new version of the specification.
And then the new OpenAPI 3.1.0 was based on the latest version (JSON Schema 2020-12) that included this new field `examples`.
And now this new `examples` field takes precedence over the old single (and custom) `example` field, that is now deprecated.
This new `examples` field in JSON Schema is **just a `list`** of examples, not a dict with extra metadata as in the other places in OpenAPI (described above).
!!! info
Even after OpenAPI 3.1.0 was released with this new simpler integration with JSON Schema, for a while, Swagger UI, the tool that provides the automatic docs, didn't support OpenAPI 3.1.0 (it does since version 5.0.0 🎉).
Because of that, versions of FastAPI previous to 0.99.0 still used versions of OpenAPI lower than 3.1.0.
### Pydantic and FastAPI `examples`
When you add `examples` inside of a Pydantic model, using `schema_extra` or `Field(examples=["something"])` that example is added to the **JSON Schema** for that Pydantic model.
And that **JSON Schema** of the Pydantic model is included in the **OpenAPI** of your API, and then it's used in the docs UI.
So, although `example` is not part of JSON Schema, it is part of OpenAPI's custom version of JSON Schema, and that's what will be used by the docs UI.
In versions of FastAPI before 0.99.0 (0.99.0 and above use the newer OpenAPI 3.1.0) when you used `example` or `examples` with any of the other utilities (`Query()`, `Body()`, etc.) those examples were not added to the JSON Schema that describes that data (not even to OpenAPI's own version of JSON Schema), they were added directly to the *path operation* declaration in OpenAPI (outside the parts of OpenAPI that use JSON Schema).
But when you use `example` or `examples` with any of the other utilities (`Query()`, `Body()`, etc.) those examples are not added to the JSON Schema that describes that data (not even to OpenAPI's own version of JSON Schema), they are added directly to the *path operation* declaration in OpenAPI (outside the parts of OpenAPI that use JSON Schema).
But now that FastAPI 0.99.0 and above uses OpenAPI 3.1.0, that uses JSON Schema 2020-12, and Swagger UI 5.0.0 and above, everything is more consistent and the examples are included in JSON Schema.
For `Path()`, `Query()`, `Header()`, and `Cookie()`, the `example` or `examples` are added to the <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object" class="external-link" target="_blank">OpenAPI definition, to the `Parameter Object` (in the specification)</a>.
### Summary
And for `Body()`, `File()`, and `Form()`, the `example` or `examples` are equivalently added to the <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject" class="external-link" target="_blank">OpenAPI definition, to the `Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification)</a>.
I used to say I didn't like history that much... and look at me now giving "tech history" lessons. 😅
On the other hand, there's a newer version of OpenAPI: **3.1.0**, recently released. It is based on the latest JSON Schema and most of the modifications from OpenAPI's custom version of JSON Schema are removed, in exchange of the features from the recent versions of JSON Schema, so all these small differences are reduced. Nevertheless, Swagger UI currently doesn't support OpenAPI 3.1.0, so, for now, it's better to continue using the ideas above.
In short, **upgrade to FastAPI 0.99.0 or above**, and things are much **simpler, consistent, and intuitive**, and you don't have to know all these historic details. 😎

3
docs/en/mkdocs.maybe-insiders.yml

@ -1,3 +1,6 @@
# Define this here and not in the main mkdocs.yml file because that one is auto
# updated and written, and the script would remove the env var
INHERIT: !ENV [INSIDERS_FILE, '../en/mkdocs.no-insiders.yml']
markdown_extensions:
pymdownx.highlight:
linenums: !ENV [LINENUMS, false]

21
docs/en/mkdocs.yml

@ -147,6 +147,7 @@ nav:
- advanced/conditional-openapi.md
- advanced/extending-openapi.md
- advanced/openapi-callbacks.md
- advanced/openapi-webhooks.md
- advanced/wsgi.md
- advanced/generate-clients.md
- async.md
@ -169,24 +170,24 @@ nav:
- contributing.md
- release-notes.md
markdown_extensions:
- toc:
toc:
permalink: true
- markdown.extensions.codehilite:
markdown.extensions.codehilite:
guess_lang: false
- mdx_include:
mdx_include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
admonition:
codehilite:
extra:
pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed:
pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
attr_list:
md_in_html:
extra:
analytics:
provider: google

3
docs_src/extending_openapi/tutorial001.py

@ -15,7 +15,8 @@ def custom_openapi():
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
description="This is a very custom OpenAPI schema",
summary="This is a very custom OpenAPI schema",
description="Here's a longer description of the custom **OpenAPI** schema",
routes=app.routes,
)
openapi_schema["info"]["x-logo"] = {

1
docs_src/metadata/tutorial001.py

@ -18,6 +18,7 @@ You will be able to:
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={

38
docs_src/metadata/tutorial001_1.py

@ -0,0 +1,38 @@
from fastapi import FastAPI
description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items
You can **read items**.
## Users
You will be able to:
* **Create users** (_not implemented_).
* **Read users** (_not implemented_).
"""
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "[email protected]",
},
license_info={
"name": "Apache 2.0",
"identifier": "MIT",
},
)
@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]

25
docs_src/openapi_webhooks/tutorial001.py

@ -0,0 +1,25 @@
from datetime import datetime
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Subscription(BaseModel):
username: str
montly_fee: float
start_date: datetime
@app.webhooks.post("new-subscription")
def new_subscription(body: Subscription):
"""
When a new user subscribes to your service we'll send you a POST request with this
data to the URL that you register for the event `new-subscription` in the dashboard.
"""
@app.get("/users/")
def read_users():
return ["Rick", "Morty"]

14
docs_src/schema_extra_example/tutorial001.py

@ -14,12 +14,14 @@ class Item(BaseModel):
class Config:
schema_extra = {
"example": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}

14
docs_src/schema_extra_example/tutorial001_py310.py

@ -12,12 +12,14 @@ class Item(BaseModel):
class Config:
schema_extra = {
"example": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}

8
docs_src/schema_extra_example/tutorial002.py

@ -7,10 +7,10 @@ app = FastAPI()
class Item(BaseModel):
name: str = Field(example="Foo")
description: Union[str, None] = Field(default=None, example="A very nice Item")
price: float = Field(example=35.4)
tax: Union[float, None] = Field(default=None, example=3.2)
name: str = Field(examples=["Foo"])
description: Union[str, None] = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: Union[float, None] = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")

8
docs_src/schema_extra_example/tutorial002_py310.py

@ -5,10 +5,10 @@ app = FastAPI()
class Item(BaseModel):
name: str = Field(example="Foo")
description: str | None = Field(default=None, example="A very nice Item")
price: float = Field(example=35.4)
tax: float | None = Field(default=None, example=3.2)
name: str = Field(examples=["Foo"])
description: str | None = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: float | None = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")

14
docs_src/schema_extra_example/tutorial003.py

@ -17,12 +17,14 @@ class Item(BaseModel):
async def update_item(
item_id: int,
item: Item = Body(
example={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}

14
docs_src/schema_extra_example/tutorial003_an.py

@ -20,12 +20,14 @@ async def update_item(
item: Annotated[
Item,
Body(
example={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):

14
docs_src/schema_extra_example/tutorial003_an_py310.py

@ -19,12 +19,14 @@ async def update_item(
item: Annotated[
Item,
Body(
example={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):

14
docs_src/schema_extra_example/tutorial003_an_py39.py

@ -19,12 +19,14 @@ async def update_item(
item: Annotated[
Item,
Body(
example={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):

14
docs_src/schema_extra_example/tutorial003_py310.py

@ -15,12 +15,14 @@ class Item(BaseModel):
async def update_item(
item_id: int,
item: Item = Body(
example={
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}

37
docs_src/schema_extra_example/tutorial004.py

@ -18,33 +18,22 @@ async def update_item(
*,
item_id: int,
item: Item = Body(
examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
{
"name": "Bar",
"price": "35.4",
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
{
"name": "Baz",
"price": "thirty five point four",
},
},
],
),
):
results = {"item_id": item_id, "item": item}

37
docs_src/schema_extra_example/tutorial004_an.py

@ -21,33 +21,22 @@ async def update_item(
item: Annotated[
Item,
Body(
examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
{
"name": "Bar",
"price": "35.4",
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
{
"name": "Baz",
"price": "thirty five point four",
},
},
],
),
],
):

37
docs_src/schema_extra_example/tutorial004_an_py310.py

@ -20,33 +20,22 @@ async def update_item(
item: Annotated[
Item,
Body(
examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
{
"name": "Bar",
"price": "35.4",
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
{
"name": "Baz",
"price": "thirty five point four",
},
},
],
),
],
):

37
docs_src/schema_extra_example/tutorial004_an_py39.py

@ -20,33 +20,22 @@ async def update_item(
item: Annotated[
Item,
Body(
examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
{
"name": "Bar",
"price": "35.4",
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
{
"name": "Baz",
"price": "thirty five point four",
},
},
],
),
],
):

37
docs_src/schema_extra_example/tutorial004_py310.py

@ -16,33 +16,22 @@ async def update_item(
*,
item_id: int,
item: Item = Body(
examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
{
"name": "Bar",
"price": "35.4",
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
{
"name": "Baz",
"price": "thirty five point four",
},
},
],
),
):
results = {"item_id": item_id, "item": item}

2
fastapi/__init__.py

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

8
fastapi/applications.py

@ -55,6 +55,7 @@ class FastAPI(Starlette):
debug: bool = False,
routes: Optional[List[BaseRoute]] = None,
title: str = "FastAPI",
summary: Optional[str] = None,
description: str = "",
version: str = "0.1.0",
openapi_url: Optional[str] = "/openapi.json",
@ -85,6 +86,7 @@ class FastAPI(Starlette):
root_path_in_servers: bool = True,
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
callbacks: Optional[List[BaseRoute]] = None,
webhooks: Optional[routing.APIRouter] = None,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
swagger_ui_parameters: Optional[Dict[str, Any]] = None,
@ -95,6 +97,7 @@ class FastAPI(Starlette):
) -> None:
self.debug = debug
self.title = title
self.summary = summary
self.description = description
self.version = version
self.terms_of_service = terms_of_service
@ -110,7 +113,7 @@ class FastAPI(Starlette):
self.swagger_ui_parameters = swagger_ui_parameters
self.servers = servers or []
self.extra = extra
self.openapi_version = "3.0.2"
self.openapi_version = "3.1.0"
self.openapi_schema: Optional[Dict[str, Any]] = None
if self.openapi_url:
assert self.title, "A title must be provided for OpenAPI, e.g.: 'My API'"
@ -123,6 +126,7 @@ class FastAPI(Starlette):
"automatic. Check the docs at "
"https://fastapi.tiangolo.com/advanced/sub-applications/"
)
self.webhooks = webhooks or routing.APIRouter()
self.root_path = root_path or openapi_prefix
self.state: State = State()
self.dependency_overrides: Dict[Callable[..., Any], Callable[..., Any]] = {}
@ -215,11 +219,13 @@ class FastAPI(Starlette):
title=self.title,
version=self.version,
openapi_version=self.openapi_version,
summary=self.summary,
description=self.description,
terms_of_service=self.terms_of_service,
contact=self.contact,
license_info=self.license_info,
routes=self.routes,
webhooks=self.webhooks.routes,
tags=self.openapi_tags,
servers=self.servers,
)

4
fastapi/encoders.py

@ -1,5 +1,5 @@
import dataclasses
from collections import defaultdict
from collections import defaultdict, deque
from enum import Enum
from pathlib import PurePath
from types import GeneratorType
@ -124,7 +124,7 @@ def jsonable_encoder(
)
encoded_dict[encoded_key] = encoded_value
return encoded_dict
if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)):
if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)):
encoded_list = []
for item in obj:
encoded_list.append(

4
fastapi/openapi/docs.py

@ -17,8 +17,8 @@ def get_swagger_ui_html(
*,
openapi_url: str,
title: str,
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js",
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css",
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
oauth2_redirect_url: Optional[str] = None,
init_oauth: Optional[Dict[str, Any]] = None,

101
fastapi/openapi/models.py

@ -1,9 +1,10 @@
from enum import Enum
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Union
from fastapi.logger import logger
from pydantic import AnyUrl, BaseModel, Field
from typing_extensions import Literal
from typing_extensions import Annotated, Literal
from typing_extensions import deprecated as typing_deprecated
try:
import email_validator # type: ignore
@ -37,6 +38,7 @@ class Contact(BaseModel):
class License(BaseModel):
name: str
identifier: Optional[str] = None
url: Optional[AnyUrl] = None
class Config:
@ -45,6 +47,7 @@ class License(BaseModel):
class Info(BaseModel):
title: str
summary: Optional[str] = None
description: Optional[str] = None
termsOfService: Optional[str] = None
contact: Optional[Contact] = None
@ -56,7 +59,7 @@ class Info(BaseModel):
class ServerVariable(BaseModel):
enum: Optional[List[str]] = None
enum: Annotated[Optional[List[str]], Field(min_items=1)] = None
default: str
description: Optional[str] = None
@ -102,9 +105,45 @@ class ExternalDocumentation(BaseModel):
class Schema(BaseModel):
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
# Core Vocabulary
schema_: Optional[str] = Field(default=None, alias="$schema")
vocabulary: Optional[str] = Field(default=None, alias="$vocabulary")
id: Optional[str] = Field(default=None, alias="$id")
anchor: Optional[str] = Field(default=None, alias="$anchor")
dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
ref: Optional[str] = Field(default=None, alias="$ref")
title: Optional[str] = None
multipleOf: Optional[float] = None
dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
comment: Optional[str] = Field(default=None, alias="$comment")
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
# A Vocabulary for Applying Subschemas
allOf: Optional[List["SchemaOrBool"]] = None
anyOf: Optional[List["SchemaOrBool"]] = None
oneOf: Optional[List["SchemaOrBool"]] = None
not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
then: Optional["SchemaOrBool"] = None
else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None
prefixItems: Optional[List["SchemaOrBool"]] = None
# TODO: uncomment and remove below when deprecating Pydantic v1
# It generales a list of schemas for tuples, before prefixItems was available
# items: Optional["SchemaOrBool"] = None
items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None
contains: Optional["SchemaOrBool"] = None
properties: Optional[Dict[str, "SchemaOrBool"]] = None
patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None
additionalProperties: Optional["SchemaOrBool"] = None
propertyNames: Optional["SchemaOrBool"] = None
unevaluatedItems: Optional["SchemaOrBool"] = None
unevaluatedProperties: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
# A Vocabulary for Structural Validation
type: Optional[str] = None
enum: Optional[List[Any]] = None
const: Optional[Any] = None
multipleOf: Optional[float] = Field(default=None, gt=0)
maximum: Optional[float] = None
exclusiveMaximum: Optional[float] = None
minimum: Optional[float] = None
@ -115,34 +154,51 @@ class Schema(BaseModel):
maxItems: Optional[int] = Field(default=None, ge=0)
minItems: Optional[int] = Field(default=None, ge=0)
uniqueItems: Optional[bool] = None
maxContains: Optional[int] = Field(default=None, ge=0)
minContains: Optional[int] = Field(default=None, ge=0)
maxProperties: Optional[int] = Field(default=None, ge=0)
minProperties: Optional[int] = Field(default=None, ge=0)
required: Optional[List[str]] = None
enum: Optional[List[Any]] = None
type: Optional[str] = None
allOf: Optional[List["Schema"]] = None
oneOf: Optional[List["Schema"]] = None
anyOf: Optional[List["Schema"]] = None
not_: Optional["Schema"] = Field(default=None, alias="not")
items: Optional[Union["Schema", List["Schema"]]] = None
properties: Optional[Dict[str, "Schema"]] = None
additionalProperties: Optional[Union["Schema", Reference, bool]] = None
description: Optional[str] = None
dependentRequired: Optional[Dict[str, Set[str]]] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
# Vocabularies for Semantic Content With "format"
format: Optional[str] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
# A Vocabulary for the Contents of String-Encoded Data
contentEncoding: Optional[str] = None
contentMediaType: Optional[str] = None
contentSchema: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
# A Vocabulary for Basic Meta-Data Annotations
title: Optional[str] = None
description: Optional[str] = None
default: Optional[Any] = None
nullable: Optional[bool] = None
discriminator: Optional[Discriminator] = None
deprecated: Optional[bool] = None
readOnly: Optional[bool] = None
writeOnly: Optional[bool] = None
examples: Optional[List[Any]] = None
# Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
# Schema Object
discriminator: Optional[Discriminator] = None
xml: Optional[XML] = None
externalDocs: Optional[ExternalDocumentation] = None
example: Optional[Any] = None
deprecated: Optional[bool] = None
example: Annotated[
Optional[Any],
typing_deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = None
class Config:
extra: str = "allow"
# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
# A JSON Schema MUST be an object or a boolean.
SchemaOrBool = Union[Schema, bool]
class Example(BaseModel):
summary: Optional[str] = None
description: Optional[str] = None
@ -248,7 +304,7 @@ class Operation(BaseModel):
parameters: Optional[List[Union[Parameter, Reference]]] = None
requestBody: Optional[Union[RequestBody, Reference]] = None
# Using Any for Specification Extensions
responses: Dict[str, Union[Response, Any]]
responses: Optional[Dict[str, Union[Response, Any]]] = None
callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None
deprecated: Optional[bool] = None
security: Optional[List[Dict[str, List[str]]]] = None
@ -375,6 +431,7 @@ class Components(BaseModel):
links: Optional[Dict[str, Union[Link, Reference]]] = None
# Using Any for Specification Extensions
callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
class Config:
extra = "allow"
@ -392,9 +449,11 @@ class Tag(BaseModel):
class OpenAPI(BaseModel):
openapi: str
info: Info
jsonSchemaDialect: Optional[str] = None
servers: Optional[List[Server]] = None
# Using Any for Specification Extensions
paths: Dict[str, Union[PathItem, Any]]
paths: Optional[Dict[str, Union[PathItem, Any]]] = None
webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
components: Optional[Components] = None
security: Optional[List[Dict[str, List[str]]]] = None
tags: Optional[List[Tag]] = None

38
fastapi/openapi/utils.py

@ -106,9 +106,7 @@ def get_openapi_operation_parameters(
}
if field_info.description:
parameter["description"] = field_info.description
if field_info.examples:
parameter["examples"] = jsonable_encoder(field_info.examples)
elif field_info.example != Undefined:
if field_info.example != Undefined:
parameter["example"] = jsonable_encoder(field_info.example)
if field_info.deprecated:
parameter["deprecated"] = field_info.deprecated
@ -134,9 +132,7 @@ def get_openapi_operation_request_body(
if required:
request_body_oai["required"] = required
request_media_content: Dict[str, Any] = {"schema": body_schema}
if field_info.examples:
request_media_content["examples"] = jsonable_encoder(field_info.examples)
elif field_info.example != Undefined:
if field_info.example != Undefined:
request_media_content["example"] = jsonable_encoder(field_info.example)
request_body_oai["content"] = {request_media_type: request_media_content}
return request_body_oai
@ -392,9 +388,11 @@ def get_openapi(
*,
title: str,
version: str,
openapi_version: str = "3.0.2",
openapi_version: str = "3.1.0",
summary: Optional[str] = None,
description: Optional[str] = None,
routes: Sequence[BaseRoute],
webhooks: Optional[Sequence[BaseRoute]] = None,
tags: Optional[List[Dict[str, Any]]] = None,
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
terms_of_service: Optional[str] = None,
@ -403,6 +401,8 @@ def get_openapi(
prefix: str = "",
) -> Dict[str, Any]:
info: Dict[str, Any] = {"title": title, "version": version}
if summary:
info["summary"] = summary
if description:
info["description"] = description
if terms_of_service:
@ -416,13 +416,14 @@ def get_openapi(
output["servers"] = servers
components: Dict[str, Dict[str, Any]] = {}
paths: Dict[str, Dict[str, Any]] = {}
webhook_paths: Dict[str, Dict[str, Any]] = {}
operation_ids: Set[str] = set()
flat_models = get_flat_models_from_routes(routes)
flat_models = get_flat_models_from_routes(list(routes or []) + list(webhooks or []))
model_name_map = get_model_name_map(flat_models)
definitions = get_model_definitions(
flat_models=flat_models, model_name_map=model_name_map
)
for route in routes:
for route in routes or []:
if isinstance(route, routing.APIRoute):
result = get_openapi_path(
route=route, model_name_map=model_name_map, operation_ids=operation_ids
@ -438,11 +439,30 @@ def get_openapi(
)
if path_definitions:
definitions.update(path_definitions)
for webhook in webhooks or []:
if isinstance(webhook, routing.APIRoute):
result = get_openapi_path(
route=webhook,
model_name_map=model_name_map,
operation_ids=operation_ids,
)
if result:
path, security_schemes, path_definitions = result
if path:
webhook_paths.setdefault(webhook.path_format, {}).update(path)
if security_schemes:
components.setdefault("securitySchemes", {}).update(
security_schemes
)
if path_definitions:
definitions.update(path_definitions)
if definitions:
components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
if components:
output["components"] = components
output["paths"] = paths
if webhook_paths:
output["webhooks"] = webhook_paths
if tags:
output["tags"] = tags
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore

73
fastapi/param_functions.py

@ -1,7 +1,8 @@
from typing import Any, Callable, Dict, Optional, Sequence
from typing import Any, Callable, List, Optional, Sequence
from fastapi import params
from pydantic.fields import Undefined
from typing_extensions import Annotated, deprecated
def Path( # noqa: N802
@ -17,8 +18,14 @@ def Path( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -56,8 +63,14 @@ def Query( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -96,8 +109,14 @@ def Header( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -136,8 +155,14 @@ def Cookie( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -177,8 +202,14 @@ def Body( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
) -> Any:
return params.Body(
@ -215,8 +246,14 @@ def Form( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
) -> Any:
return params.Form(
@ -252,8 +289,14 @@ def File( # noqa: N802
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
) -> Any:
return params.File(

108
fastapi/params.py

@ -1,7 +1,9 @@
import warnings
from enum import Enum
from typing import Any, Callable, Dict, Optional, Sequence
from typing import Any, Callable, List, Optional, Sequence
from pydantic.fields import FieldInfo, Undefined
from typing_extensions import Annotated, deprecated
class ParamTypes(Enum):
@ -28,16 +30,30 @@ class Param(FieldInfo):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
):
self.deprecated = deprecated
if example is not Undefined:
warnings.warn(
"`example` has been depreacated, please use `examples` instead",
category=DeprecationWarning,
stacklevel=1,
)
self.example = example
self.examples = examples
self.include_in_schema = include_in_schema
extra_kwargs = {**extra}
if examples:
extra_kwargs["examples"] = examples
super().__init__(
default=default,
alias=alias,
@ -50,7 +66,7 @@ class Param(FieldInfo):
min_length=min_length,
max_length=max_length,
regex=regex,
**extra,
**extra_kwargs,
)
def __repr__(self) -> str:
@ -74,8 +90,14 @@ class Path(Param):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -119,8 +141,14 @@ class Query(Param):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -163,8 +191,14 @@ class Header(Param):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -207,8 +241,14 @@ class Cookie(Param):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
@ -250,14 +290,28 @@ class Body(FieldInfo):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
):
self.embed = embed
self.media_type = media_type
if example is not Undefined:
warnings.warn(
"`example` has been depreacated, please use `examples` instead",
category=DeprecationWarning,
stacklevel=1,
)
self.example = example
self.examples = examples
extra_kwargs = {**extra}
if examples is not None:
extra_kwargs["examples"] = examples
super().__init__(
default=default,
alias=alias,
@ -270,7 +324,7 @@ class Body(FieldInfo):
min_length=min_length,
max_length=max_length,
regex=regex,
**extra,
**extra_kwargs,
)
def __repr__(self) -> str:
@ -293,8 +347,14 @@ class Form(Body):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
):
super().__init__(
@ -333,8 +393,14 @@ class File(Form):
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
examples: Optional[List[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = Undefined,
**extra: Any,
):
super().__init__(

1
pyproject.toml

@ -43,6 +43,7 @@ classifiers = [
dependencies = [
"starlette>=0.27.0,<0.28.0",
"pydantic>=1.7.4,!=1.8,!=1.8.1,<2.0.0",
"typing-extensions>=4.5.0"
]
dynamic = ["version"]

2
scripts/docs.py

@ -258,6 +258,8 @@ def live(
Takes an optional LANG argument with the name of the language to serve, by default
en.
"""
# Enable line numbers during local development to make it easier to highlight
os.environ["LINENUMS"] = "true"
if lang is None:
lang = "en"
lang_path: Path = docs_path / lang

2
tests/test_additional_properties.py

@ -29,7 +29,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/foo": {

115
tests/test_additional_properties_bool.py

@ -0,0 +1,115 @@
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
class FooBaseModel(BaseModel):
class Config:
extra = "forbid"
class Foo(FooBaseModel):
pass
app = FastAPI()
@app.post("/")
async def post(
foo: Union[Foo, None] = None,
):
return foo
client = TestClient(app)
def test_call_invalid():
response = client.post("/", json={"foo": {"bar": "baz"}})
assert response.status_code == 422
def test_call_valid():
response = client.post("/", json={})
assert response.status_code == 200
assert response.json() == {}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"post": {
"summary": "Post",
"operationId": "post__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Foo"}
}
}
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"Foo": {
"properties": {},
"additionalProperties": False,
"type": "object",
"title": "Foo",
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {"$ref": "#/components/schemas/ValidationError"},
"type": "array",
"title": "Detail",
}
},
"type": "object",
"title": "HTTPValidationError",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
"type": "array",
"title": "Location",
},
"msg": {"type": "string", "title": "Message"},
"type": {"type": "string", "title": "Error Type"},
},
"type": "object",
"required": ["loc", "msg", "type"],
"title": "ValidationError",
},
}
},
}

2
tests/test_additional_response_extra.py

@ -30,7 +30,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {

2
tests/test_additional_responses_bad.py

@ -11,7 +11,7 @@ async def a():
openapi_schema = {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {

2
tests/test_additional_responses_custom_model_in_callback.py

@ -32,7 +32,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {

2
tests/test_additional_responses_custom_validationerror.py

@ -37,7 +37,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {

2
tests/test_additional_responses_default_validationerror.py

@ -16,7 +16,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {

2
tests/test_additional_responses_response_class.py

@ -42,7 +42,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {

2
tests/test_additional_responses_router.py

@ -85,7 +85,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {

2
tests/test_annotated.py

@ -118,7 +118,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/default": {

2
tests/test_application.py

@ -55,7 +55,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/api_route": {

2
tests/test_custom_route_class.py

@ -75,7 +75,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/": {

2
tests/test_dependency_duplicates.py

@ -86,7 +86,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/with-duplicates": {

2
tests/test_deprecated_openapi_prefix.py

@ -22,7 +22,7 @@ def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/app": {

2
tests/test_duplicate_models_openapi.py

@ -36,7 +36,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {

2
tests/test_enforce_once_required_parameter.py

@ -57,7 +57,7 @@ expected_schema = {
}
},
"info": {"title": "FastAPI", "version": "0.1.0"},
"openapi": "3.0.2",
"openapi": "3.1.0",
"paths": {
"/foo": {
"get": {

2
tests/test_extra_routes.py

@ -99,7 +99,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {

2
tests/test_filter_pydantic_sub_model.py

@ -66,7 +66,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/model/{name}": {

14
tests/test_generate_unique_id_function.py

@ -48,7 +48,7 @@ def test_top_level_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -249,7 +249,7 @@ def test_router_overrides_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -450,7 +450,7 @@ def test_router_include_overrides_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -661,7 +661,7 @@ def test_subrouter_top_level_include_overrides_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -928,7 +928,7 @@ def test_router_path_operation_overrides_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -1136,7 +1136,7 @@ def test_app_path_operation_overrides_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
@ -1353,7 +1353,7 @@ def test_callback_override_generate_unique_id():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {

2
tests/test_get_request_body.py

@ -29,7 +29,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/product": {

2
tests/test_include_router_defaults_overrides.py

@ -443,7 +443,7 @@ def test_openapi():
assert issubclass(w[-1].category, UserWarning)
assert "Duplicate Operation ID" in str(w[-1].message)
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/override1": {

10
tests/test_jsonable_encoder.py

@ -1,3 +1,4 @@
from collections import deque
from dataclasses import dataclass
from datetime import datetime, timezone
from enum import Enum
@ -237,3 +238,12 @@ def test_encode_model_with_path(model_with_path):
def test_encode_root():
model = ModelWithRoot(__root__="Foo")
assert jsonable_encoder(model) == "Foo"
def test_encode_deque_encodes_child_models():
class Model(BaseModel):
test: str
dq = deque([Model(test="test")])
assert jsonable_encoder(dq)[0]["test"] == "test"

2
tests/test_modules_same_name_body/test_main.py

@ -35,7 +35,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/compute": {

2
tests/test_multi_body_errors.py

@ -80,7 +80,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {

2
tests/test_multi_query_errors.py

@ -46,7 +46,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {

2
tests/test_openapi_query_parameter_extension.py

@ -42,7 +42,7 @@ def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {

2
tests/test_openapi_route_extensions.py

@ -22,7 +22,7 @@ def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {

2
tests/test_openapi_servers.py

@ -30,7 +30,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"servers": [
{"url": "/", "description": "Default, relative server"},

2
tests/test_param_in_path_and_dependency.py

@ -25,7 +25,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/{user_id}": {

2
tests/test_param_include_in_schema.py

@ -34,7 +34,7 @@ async def hidden_query(
openapi_schema = {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/hidden_cookie": {

2
tests/test_put_no_body.py

@ -28,7 +28,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {

2
tests/test_repeated_dependency_schema.py

@ -50,7 +50,7 @@ schema = {
}
},
"info": {"title": "FastAPI", "version": "0.1.0"},
"openapi": "3.0.2",
"openapi": "3.1.0",
"paths": {
"/": {
"get": {

2
tests/test_repeated_parameter_alias.py

@ -58,7 +58,7 @@ def test_openapi_schema():
}
},
"info": {"title": "FastAPI", "version": "0.1.0"},
"openapi": "3.0.2",
"openapi": "3.1.0",
"paths": {
"/{repeated_alias}": {
"get": {

2
tests/test_reponse_set_reponse_code_empty.py

@ -32,7 +32,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/{id}": {

2
tests/test_request_body_parameters_media_type.py

@ -41,7 +41,7 @@ def test_openapi_schema():
assert response.status_code == 200, response.text
# insert_assert(response.json())
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/products": {

2
tests/test_response_by_alias.py

@ -138,7 +138,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/dict": {

2
tests/test_response_class_no_mediatype.py

@ -42,7 +42,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {

2
tests/test_response_code_no_body.py

@ -50,7 +50,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {

2
tests/test_response_model_as_return_annotation.py

@ -507,7 +507,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/no_response_model-no_annotation-return_model": {

2
tests/test_response_model_sub_types.py

@ -50,7 +50,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/valid1": {

572
tests/test_schema_extra_examples.py

@ -1,240 +1,220 @@
from typing import Union
import pytest
from fastapi import Body, Cookie, FastAPI, Header, Path, Query
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
data: str
class Config:
schema_extra = {"example": {"data": "Data in schema_extra"}}
@app.post("/schema_extra/")
def schema_extra(item: Item):
return item
@app.post("/example/")
def example(item: Item = Body(example={"data": "Data in Body example"})):
return item
@app.post("/examples/")
def examples(
item: Item = Body(
examples={
"example1": {
"summary": "example1 summary",
"value": {"data": "Data in Body examples, example1"},
},
"example2": {"value": {"data": "Data in Body examples, example2"}},
},
)
):
return item
@app.post("/example_examples/")
def example_examples(
item: Item = Body(
example={"data": "Overridden example"},
examples={
"example1": {"value": {"data": "examples example_examples 1"}},
"example2": {"value": {"data": "examples example_examples 2"}},
},
)
):
return item
# TODO: enable these tests once/if Form(embed=False) is supported
# TODO: In that case, define if File() should support example/examples too
# @app.post("/form_example")
# def form_example(firstname: str = Form(example="John")):
# return firstname
# @app.post("/form_examples")
# def form_examples(
# lastname: str = Form(
# ...,
# examples={
# "example1": {"summary": "last name summary", "value": "Doe"},
# "example2": {"value": "Doesn't"},
# },
# ),
# ):
# return lastname
# @app.post("/form_example_examples")
# def form_example_examples(
# lastname: str = Form(
# ...,
# example="Doe overridden",
# examples={
# "example1": {"summary": "last name summary", "value": "Doe"},
# "example2": {"value": "Doesn't"},
# },
# ),
# ):
# return lastname
@app.get("/path_example/{item_id}")
def path_example(
item_id: str = Path(
example="item_1",
),
):
return item_id
@app.get("/path_examples/{item_id}")
def path_examples(
item_id: str = Path(
examples={
"example1": {"summary": "item ID summary", "value": "item_1"},
"example2": {"value": "item_2"},
},
),
):
return item_id
@app.get("/path_example_examples/{item_id}")
def path_example_examples(
item_id: str = Path(
example="item_overridden",
examples={
"example1": {"summary": "item ID summary", "value": "item_1"},
"example2": {"value": "item_2"},
},
),
):
return item_id
@app.get("/query_example/")
def query_example(
data: Union[str, None] = Query(
default=None,
example="query1",
),
):
return data
@app.get("/query_examples/")
def query_examples(
data: Union[str, None] = Query(
default=None,
examples={
"example1": {"summary": "Query example 1", "value": "query1"},
"example2": {"value": "query2"},
},
),
):
return data
@app.get("/query_example_examples/")
def query_example_examples(
data: Union[str, None] = Query(
default=None,
example="query_overridden",
examples={
"example1": {"summary": "Query example 1", "value": "query1"},
"example2": {"value": "query2"},
},
),
):
return data
@app.get("/header_example/")
def header_example(
data: Union[str, None] = Header(
default=None,
example="header1",
),
):
return data
@app.get("/header_examples/")
def header_examples(
data: Union[str, None] = Header(
default=None,
examples={
"example1": {"summary": "header example 1", "value": "header1"},
"example2": {"value": "header2"},
},
),
):
return data
@app.get("/header_example_examples/")
def header_example_examples(
data: Union[str, None] = Header(
default=None,
example="header_overridden",
examples={
"example1": {"summary": "Query example 1", "value": "header1"},
"example2": {"value": "header2"},
},
),
):
return data
@app.get("/cookie_example/")
def cookie_example(
data: Union[str, None] = Cookie(
default=None,
example="cookie1",
),
):
return data
@app.get("/cookie_examples/")
def cookie_examples(
data: Union[str, None] = Cookie(
default=None,
examples={
"example1": {"summary": "cookie example 1", "value": "cookie1"},
"example2": {"value": "cookie2"},
},
),
):
return data
@app.get("/cookie_example_examples/")
def cookie_example_examples(
data: Union[str, None] = Cookie(
default=None,
example="cookie_overridden",
examples={
"example1": {"summary": "Query example 1", "value": "cookie1"},
"example2": {"value": "cookie2"},
},
),
):
return data
client = TestClient(app)
def create_app():
app = FastAPI()
class Item(BaseModel):
data: str
class Config:
schema_extra = {"example": {"data": "Data in schema_extra"}}
@app.post("/schema_extra/")
def schema_extra(item: Item):
return item
with pytest.warns(DeprecationWarning):
@app.post("/example/")
def example(item: Item = Body(example={"data": "Data in Body example"})):
return item
@app.post("/examples/")
def examples(
item: Item = Body(
examples=[
{"data": "Data in Body examples, example1"},
{"data": "Data in Body examples, example2"},
],
)
):
return item
with pytest.warns(DeprecationWarning):
@app.post("/example_examples/")
def example_examples(
item: Item = Body(
example={"data": "Overridden example"},
examples=[
{"data": "examples example_examples 1"},
{"data": "examples example_examples 2"},
],
)
):
return item
# TODO: enable these tests once/if Form(embed=False) is supported
# TODO: In that case, define if File() should support example/examples too
# @app.post("/form_example")
# def form_example(firstname: str = Form(example="John")):
# return firstname
# @app.post("/form_examples")
# def form_examples(
# lastname: str = Form(
# ...,
# examples={
# "example1": {"summary": "last name summary", "value": "Doe"},
# "example2": {"value": "Doesn't"},
# },
# ),
# ):
# return lastname
# @app.post("/form_example_examples")
# def form_example_examples(
# lastname: str = Form(
# ...,
# example="Doe overridden",
# examples={
# "example1": {"summary": "last name summary", "value": "Doe"},
# "example2": {"value": "Doesn't"},
# },
# ),
# ):
# return lastname
with pytest.warns(DeprecationWarning):
@app.get("/path_example/{item_id}")
def path_example(
item_id: str = Path(
example="item_1",
),
):
return item_id
@app.get("/path_examples/{item_id}")
def path_examples(
item_id: str = Path(
examples=["item_1", "item_2"],
),
):
return item_id
with pytest.warns(DeprecationWarning):
@app.get("/path_example_examples/{item_id}")
def path_example_examples(
item_id: str = Path(
example="item_overridden",
examples=["item_1", "item_2"],
),
):
return item_id
with pytest.warns(DeprecationWarning):
@app.get("/query_example/")
def query_example(
data: Union[str, None] = Query(
default=None,
example="query1",
),
):
return data
@app.get("/query_examples/")
def query_examples(
data: Union[str, None] = Query(
default=None,
examples=["query1", "query2"],
),
):
return data
with pytest.warns(DeprecationWarning):
@app.get("/query_example_examples/")
def query_example_examples(
data: Union[str, None] = Query(
default=None,
example="query_overridden",
examples=["query1", "query2"],
),
):
return data
with pytest.warns(DeprecationWarning):
@app.get("/header_example/")
def header_example(
data: Union[str, None] = Header(
default=None,
example="header1",
),
):
return data
@app.get("/header_examples/")
def header_examples(
data: Union[str, None] = Header(
default=None,
examples=[
"header1",
"header2",
],
),
):
return data
with pytest.warns(DeprecationWarning):
@app.get("/header_example_examples/")
def header_example_examples(
data: Union[str, None] = Header(
default=None,
example="header_overridden",
examples=["header1", "header2"],
),
):
return data
with pytest.warns(DeprecationWarning):
@app.get("/cookie_example/")
def cookie_example(
data: Union[str, None] = Cookie(
default=None,
example="cookie1",
),
):
return data
@app.get("/cookie_examples/")
def cookie_examples(
data: Union[str, None] = Cookie(
default=None,
examples=["cookie1", "cookie2"],
),
):
return data
with pytest.warns(DeprecationWarning):
@app.get("/cookie_example_examples/")
def cookie_example_examples(
data: Union[str, None] = Cookie(
default=None,
example="cookie_overridden",
examples=["cookie1", "cookie2"],
),
):
return data
return app
def test_call_api():
app = create_app()
client = TestClient(app)
response = client.post("/schema_extra/", json={"data": "Foo"})
assert response.status_code == 200, response.text
response = client.post("/example/", json={"data": "Foo"})
@ -277,10 +257,12 @@ def test_openapi_schema():
* Body(example={}) overrides schema_extra in pydantic model
* Body(examples{}) overrides Body(example={}) and schema_extra in pydantic model
"""
app = create_app()
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/schema_extra/": {
@ -351,20 +333,14 @@ def test_openapi_schema():
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"},
"examples": {
"example1": {
"summary": "example1 summary",
"value": {
"data": "Data in Body examples, example1"
},
},
"example2": {
"value": {
"data": "Data in Body examples, example2"
}
},
},
"schema": {
"allOf": [{"$ref": "#/components/schemas/Item"}],
"title": "Item",
"examples": [
{"data": "Data in Body examples, example1"},
{"data": "Data in Body examples, example2"},
],
}
}
},
"required": True,
@ -394,15 +370,15 @@ def test_openapi_schema():
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"},
"examples": {
"example1": {
"value": {"data": "examples example_examples 1"}
},
"example2": {
"value": {"data": "examples example_examples 2"}
},
"schema": {
"allOf": [{"$ref": "#/components/schemas/Item"}],
"title": "Item",
"examples": [
{"data": "examples example_examples 1"},
{"data": "examples example_examples 2"},
],
},
"example": {"data": "Overridden example"},
}
},
"required": True,
@ -463,13 +439,10 @@ def test_openapi_schema():
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"examples": {
"example1": {
"summary": "item ID summary",
"value": "item_1",
},
"example2": {"value": "item_2"},
"schema": {
"title": "Item Id",
"type": "string",
"examples": ["item_1", "item_2"],
},
"name": "item_id",
"in": "path",
@ -500,14 +473,12 @@ def test_openapi_schema():
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"examples": {
"example1": {
"summary": "item ID summary",
"value": "item_1",
},
"example2": {"value": "item_2"},
"schema": {
"title": "Item Id",
"type": "string",
"examples": ["item_1", "item_2"],
},
"example": "item_overridden",
"name": "item_id",
"in": "path",
}
@ -568,13 +539,10 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "Query example 1",
"value": "query1",
},
"example2": {"value": "query2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["query1", "query2"],
},
"name": "data",
"in": "query",
@ -605,14 +573,12 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "Query example 1",
"value": "query1",
},
"example2": {"value": "query2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["query1", "query2"],
},
"example": "query_overridden",
"name": "data",
"in": "query",
}
@ -642,7 +608,7 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"schema": {"type": "string", "title": "Data"},
"example": "header1",
"name": "data",
"in": "header",
@ -673,13 +639,10 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "header example 1",
"value": "header1",
},
"example2": {"value": "header2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["header1", "header2"],
},
"name": "data",
"in": "header",
@ -710,14 +673,12 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "Query example 1",
"value": "header1",
},
"example2": {"value": "header2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["header1", "header2"],
},
"example": "header_overridden",
"name": "data",
"in": "header",
}
@ -747,7 +708,7 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"schema": {"type": "string", "title": "Data"},
"example": "cookie1",
"name": "data",
"in": "cookie",
@ -778,13 +739,10 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "cookie example 1",
"value": "cookie1",
},
"example2": {"value": "cookie2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["cookie1", "cookie2"],
},
"name": "data",
"in": "cookie",
@ -815,14 +773,12 @@ def test_openapi_schema():
"parameters": [
{
"required": False,
"schema": {"title": "Data", "type": "string"},
"examples": {
"example1": {
"summary": "Query example 1",
"value": "cookie1",
},
"example2": {"value": "cookie2"},
"schema": {
"type": "string",
"title": "Data",
"examples": ["cookie1", "cookie2"],
},
"example": "cookie_overridden",
"name": "data",
"in": "cookie",
}

2
tests/test_security_api_key_cookie.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_cookie_description.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_cookie_optional.py

@ -48,7 +48,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_header.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_header_description.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_header_optional.py

@ -47,7 +47,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_query.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_query_description.py

@ -41,7 +41,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_api_key_query_optional.py

@ -47,7 +47,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_base.py

@ -31,7 +31,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_base_description.py

@ -31,7 +31,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_base_optional.py

@ -37,7 +37,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_basic_optional.py

@ -54,7 +54,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_basic_realm.py

@ -52,7 +52,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_basic_realm_description.py

@ -52,7 +52,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

2
tests/test_security_http_bearer.py

@ -37,7 +37,7 @@ def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save