From 6c99e90a6b808a1d819ecc45bcf25751dd30b22b Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Thu, 27 Jul 2023 19:22:23 +0100 Subject: [PATCH 001/317] =?UTF-8?q?=F0=9F=90=9B=20Replace=20`MultHostUrl`?= =?UTF-8?q?=20to=20`AnyUrl`=20for=20compatibility=20with=20older=20version?= =?UTF-8?q?s=20of=20Pydantic=20v1=20(#9852)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/_compat.py | 4 ---- fastapi/encoders.py | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/fastapi/_compat.py b/fastapi/_compat.py index 2233fe33c..9ffcaf409 100644 --- a/fastapi/_compat.py +++ b/fastapi/_compat.py @@ -56,7 +56,6 @@ if PYDANTIC_V2: from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue from pydantic_core import CoreSchema as CoreSchema - from pydantic_core import MultiHostUrl as MultiHostUrl from pydantic_core import PydanticUndefined, PydanticUndefinedType from pydantic_core import Url as Url from pydantic_core.core_schema import ( @@ -294,9 +293,6 @@ else: from pydantic.fields import ( # type: ignore[no-redef, attr-defined] UndefinedType as UndefinedType, # noqa: F401 ) - from pydantic.networks import ( # type: ignore[no-redef] - MultiHostDsn as MultiHostUrl, # noqa: F401 - ) from pydantic.schema import ( field_schema, get_flat_models_from_fields, diff --git a/fastapi/encoders.py b/fastapi/encoders.py index b542749f2..30493697e 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -20,10 +20,10 @@ from uuid import UUID from fastapi.types import IncEx from pydantic import BaseModel from pydantic.color import Color -from pydantic.networks import NameEmail +from pydantic.networks import AnyUrl, NameEmail from pydantic.types import SecretBytes, SecretStr -from ._compat import PYDANTIC_V2, MultiHostUrl, Url, _model_dump +from ._compat import PYDANTIC_V2, Url, _model_dump # Taken from Pydantic v1 as is @@ -80,7 +80,7 @@ ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { set: list, UUID: str, Url: str, - MultiHostUrl: str, + AnyUrl: str, } From 7cdea41431a6b2abcb07cf8507592cb39f5eade1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:23:13 +0000 Subject: [PATCH 002/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 72fa346d1..84e3c4855 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). * 👷 Update FastAPI People token. PR [#9844](https://github.com/tiangolo/fastapi/pull/9844) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9775](https://github.com/tiangolo/fastapi/pull/9775) by [@tiangolo](https://github.com/tiangolo). * 👷 Update MkDocs Material token. PR [#9843](https://github.com/tiangolo/fastapi/pull/9843) by [@tiangolo](https://github.com/tiangolo). From 703a1f200aa2c7dbba13e52407338ec1d691c1b5 Mon Sep 17 00:00:00 2001 From: Creat55 <64245796+Creat55@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:42:48 +0800 Subject: [PATCH 003/317] =?UTF-8?q?=F0=9F=8C=90=20Update=20Chinese=20trans?= =?UTF-8?q?lation=20for=20`docs/zh/docs/tutorial/handling-errors.md`=20(#9?= =?UTF-8?q?485)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez --- docs/zh/docs/tutorial/handling-errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh/docs/tutorial/handling-errors.md b/docs/zh/docs/tutorial/handling-errors.md index 9b066bc2c..a0d66e557 100644 --- a/docs/zh/docs/tutorial/handling-errors.md +++ b/docs/zh/docs/tutorial/handling-errors.md @@ -145,7 +145,7 @@ ``` -访问 `/items/foo`,可以看到以下内容替换了默认 JSON 错误信息: +访问 `/items/foo`,可以看到默认的 JSON 错误信息: ```JSON { @@ -163,7 +163,7 @@ ``` -以下是文本格式的错误信息: +被替换为了以下文本格式的错误信息: ``` 1 validation error From 39318a39f40bec0d4919e09b397cfb44f0f623a0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:43:30 +0000 Subject: [PATCH 004/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 84e3c4855..7219156b8 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/handling-errors.md`. PR [#9485](https://github.com/tiangolo/fastapi/pull/9485) by [@Creat55](https://github.com/Creat55). * 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). * 👷 Update FastAPI People token. PR [#9844](https://github.com/tiangolo/fastapi/pull/9844) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9775](https://github.com/tiangolo/fastapi/pull/9775) by [@tiangolo](https://github.com/tiangolo). From 608cc4fea31130dca45806da06dd50e4d0f007dd Mon Sep 17 00:00:00 2001 From: dedkot Date: Thu, 27 Jul 2023 21:47:42 +0300 Subject: [PATCH 005/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Russian=20translat?= =?UTF-8?q?ion=20for=20`docs/tutorial/request-forms.md`=20(#9841)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ru/docs/tutorial/request-forms.md | 92 ++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/ru/docs/tutorial/request-forms.md diff --git a/docs/ru/docs/tutorial/request-forms.md b/docs/ru/docs/tutorial/request-forms.md new file mode 100644 index 000000000..a20cf78e0 --- /dev/null +++ b/docs/ru/docs/tutorial/request-forms.md @@ -0,0 +1,92 @@ +# Данные формы + +Когда вам нужно получить поля формы вместо JSON, вы можете использовать `Form`. + +!!! info "Дополнительная информация" + Чтобы использовать формы, сначала установите `python-multipart`. + + Например, выполните команду `pip install python-multipart`. + +## Импорт `Form` + +Импортируйте `Form` из `fastapi`: + +=== "Python 3.9+" + + ```Python hl_lines="3" + {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms/tutorial001_an.py!} + ``` + +=== "Python 3.6+ без Annotated" + + !!! tip "Подсказка" + Рекомендуется использовать 'Annotated' версию, если это возможно. + + ```Python hl_lines="1" + {!> ../../../docs_src/request_forms/tutorial001.py!} + ``` + +## Определение параметров `Form` + +Создайте параметры формы так же, как это делается для `Body` или `Query`: + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/request_forms/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="8" + {!> ../../../docs_src/request_forms/tutorial001_an.py!} + ``` + +=== "Python 3.6+ без Annotated" + + !!! tip "Подсказка" + Рекомендуется использовать 'Annotated' версию, если это возможно. + + ```Python hl_lines="7" + {!> ../../../docs_src/request_forms/tutorial001.py!} + ``` + +Например, в одном из способов использования спецификации OAuth2 (называемом "потоком пароля") требуется отправить `username` и `password` в виде полей формы. + +Данный способ требует отправку данных для авторизации посредством формы (а не JSON) и обязательного наличия в форме строго именованных полей `username` и `password`. + +Вы можете настроить `Form` точно так же, как настраиваете и `Body` ( `Query`, `Path`, `Cookie`), включая валидации, примеры, псевдонимы (например, `user-name` вместо `username`) и т.д. + +!!! info "Дополнительная информация" + `Form` - это класс, который наследуется непосредственно от `Body`. + +!!! tip "Подсказка" + Вам необходимо явно указывать параметр `Form` при объявлении каждого поля, иначе поля будут интерпретироваться как параметры запроса или параметры тела (JSON). + +## О "полях формы" + +Обычно способ, которым HTML-формы (`
`) отправляют данные на сервер, использует "специальное" кодирование для этих данных, отличное от JSON. + +**FastAPI** гарантирует правильное чтение этих данных из соответствующего места, а не из JSON. + +!!! note "Технические детали" + Данные из форм обычно кодируются с использованием "типа медиа" `application/x-www-form-urlencoded`. + + Но когда форма содержит файлы, она кодируется как `multipart/form-data`. Вы узнаете о работе с файлами в следующей главе. + + Если вы хотите узнать больше про кодировки и поля формы, ознакомьтесь с документацией MDN для `POST` на веб-сайте. + +!!! warning "Предупреждение" + Вы можете объявлять несколько параметров `Form` в *операции пути*, но вы не можете одновременно с этим объявлять поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с использованием `application/x-www-form-urlencoded`, а не `application/json`. + + Это не ограничение **FastAPI**, это часть протокола HTTP. + +## Резюме + +Используйте `Form` для объявления входных параметров данных формы. From 6a95a3a8e74a9373f830246570d23f8d7d593da9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:48:28 +0000 Subject: [PATCH 006/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7219156b8..ad01f3c5b 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Russian translation for `docs/ru/docs/tutorial/request-forms.md`. PR [#9841](https://github.com/tiangolo/fastapi/pull/9841) by [@dedkot01](https://github.com/dedkot01). * 🌐 Update Chinese translation for `docs/zh/docs/tutorial/handling-errors.md`. PR [#9485](https://github.com/tiangolo/fastapi/pull/9485) by [@Creat55](https://github.com/Creat55). * 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). * 👷 Update FastAPI People token. PR [#9844](https://github.com/tiangolo/fastapi/pull/9844) by [@tiangolo](https://github.com/tiangolo). From 943baa387f52084f80a3db2c7417093325b19917 Mon Sep 17 00:00:00 2001 From: mahone3297 <329730566@qq.com> Date: Fri, 28 Jul 2023 02:49:03 +0800 Subject: [PATCH 007/317] =?UTF-8?q?=F0=9F=8C=90=20Update=20Chinese=20trans?= =?UTF-8?q?lations=20with=20new=20source=20files=20(#9738)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: mkdir700 --- docs/zh/docs/tutorial/body-fields.md | 76 +++++++- docs/zh/docs/tutorial/body-multiple-params.md | 166 ++++++++++++++-- docs/zh/docs/tutorial/body-nested-models.md | 184 +++++++++++++++--- docs/zh/docs/tutorial/body.md | 84 ++++++-- docs/zh/docs/tutorial/cookie-params.md | 76 +++++++- docs/zh/docs/tutorial/extra-data-types.md | 76 +++++++- docs/zh/docs/tutorial/extra-models.md | 70 +++++-- docs/zh/docs/tutorial/header-params.md | 161 +++++++++++++-- .../path-params-numeric-validations.md | 87 ++++++++- .../tutorial/query-params-str-validations.md | 14 +- docs/zh/docs/tutorial/response-model.md | 62 ++++-- docs/zh/docs/tutorial/schema-extra-example.md | 66 ++++++- docs/zh/docs/tutorial/security/first-steps.md | 23 ++- 13 files changed, 1000 insertions(+), 145 deletions(-) diff --git a/docs/zh/docs/tutorial/body-fields.md b/docs/zh/docs/tutorial/body-fields.md index 053cae71c..c153784dc 100644 --- a/docs/zh/docs/tutorial/body-fields.md +++ b/docs/zh/docs/tutorial/body-fields.md @@ -6,9 +6,41 @@ 首先,你必须导入它: -```Python hl_lines="2" -{!../../../docs_src/body_fields/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="2" + {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="4" + {!> ../../../docs_src/body_fields/tutorial001.py!} + ``` !!! warning 注意,`Field` 是直接从 `pydantic` 导入的,而不是像其他的(`Query`,`Path`,`Body` 等)都从 `fastapi` 导入。 @@ -17,9 +49,41 @@ 然后,你可以对模型属性使用 `Field`: -```Python hl_lines="9-10" -{!../../../docs_src/body_fields/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12-15" + {!> ../../../docs_src/body_fields/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="9-12" + {!> ../../../docs_src/body_fields/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="11-14" + {!> ../../../docs_src/body_fields/tutorial001.py!} + ``` `Field` 的工作方式和 `Query`、`Path` 和 `Body` 相同,包括它们的参数等等也完全相同。 diff --git a/docs/zh/docs/tutorial/body-multiple-params.md b/docs/zh/docs/tutorial/body-multiple-params.md index 34fa5b638..ee2cba6df 100644 --- a/docs/zh/docs/tutorial/body-multiple-params.md +++ b/docs/zh/docs/tutorial/body-multiple-params.md @@ -8,9 +8,41 @@ 你还可以通过将默认值设置为 `None` 来将请求体参数声明为可选参数: -```Python hl_lines="17-19" -{!../../../docs_src/body_multiple_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="18-20" + {!> ../../../docs_src/body_multiple_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="18-20" + {!> ../../../docs_src/body_multiple_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="19-21" + {!> ../../../docs_src/body_multiple_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="17-19" + {!> ../../../docs_src/body_multiple_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="19-21" + {!> ../../../docs_src/body_multiple_params/tutorial001.py!} + ``` !!! note 请注意,在这种情况下,将从请求体获取的 `item` 是可选的。因为它的默认值为 `None`。 @@ -30,9 +62,17 @@ 但是你也可以声明多个请求体参数,例如 `item` 和 `user`: -```Python hl_lines="20" -{!../../../docs_src/body_multiple_params/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_multiple_params/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="22" + {!> ../../../docs_src/body_multiple_params/tutorial002.py!} + ``` 在这种情况下,**FastAPI** 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。 @@ -72,9 +112,41 @@ 但是你可以使用 `Body` 指示 **FastAPI** 将其作为请求体的另一个键进行处理。 -```Python hl_lines="22" -{!../../../docs_src/body_multiple_params/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="23" + {!> ../../../docs_src/body_multiple_params/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="23" + {!> ../../../docs_src/body_multiple_params/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/body_multiple_params/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="20" + {!> ../../../docs_src/body_multiple_params/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="22" + {!> ../../../docs_src/body_multiple_params/tutorial003.py!} + ``` 在这种情况下,**FastAPI** 将期望像这样的请求体: @@ -109,9 +181,41 @@ q: str = None 比如: -```Python hl_lines="25" -{!../../../docs_src/body_multiple_params/tutorial004.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="28" + {!> ../../../docs_src/body_multiple_params/tutorial004_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="25" + {!> ../../../docs_src/body_multiple_params/tutorial004_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="27" + {!> ../../../docs_src/body_multiple_params/tutorial004.py!} + ``` !!! info `Body` 同样具有与 `Query`、`Path` 以及其他后面将看到的类完全相同的额外校验和元数据参数。 @@ -131,9 +235,41 @@ item: Item = Body(embed=True) 比如: -```Python hl_lines="15" -{!../../../docs_src/body_multiple_params/tutorial005.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_multiple_params/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="15" + {!> ../../../docs_src/body_multiple_params/tutorial005_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="17" + {!> ../../../docs_src/body_multiple_params/tutorial005.py!} + ``` 在这种情况下,**FastAPI** 将期望像这样的请求体: diff --git a/docs/zh/docs/tutorial/body-nested-models.md b/docs/zh/docs/tutorial/body-nested-models.md index 7649ee6fe..7704d2624 100644 --- a/docs/zh/docs/tutorial/body-nested-models.md +++ b/docs/zh/docs/tutorial/body-nested-models.md @@ -6,9 +6,17 @@ 你可以将一个属性定义为拥有子元素的类型。例如 Python `list`: -```Python hl_lines="12" -{!../../../docs_src/body_nested_models/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial001.py!} + ``` 这将使 `tags` 成为一个由元素组成的列表。不过它没有声明每个元素的类型。 @@ -21,7 +29,7 @@ 首先,从 Python 的标准库 `typing` 模块中导入 `List`: ```Python hl_lines="1" -{!../../../docs_src/body_nested_models/tutorial002.py!} +{!> ../../../docs_src/body_nested_models/tutorial002.py!} ``` ### 声明具有子类型的 List @@ -43,9 +51,23 @@ my_list: List[str] 因此,在我们的示例中,我们可以将 `tags` 明确地指定为一个「字符串列表」: -```Python hl_lines="14" -{!../../../docs_src/body_nested_models/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial002_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial002_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial002.py!} + ``` ## Set 类型 @@ -55,9 +77,23 @@ Python 具有一种特殊的数据类型来保存一组唯一的元素,即 `se 然后我们可以导入 `Set` 并将 `tag` 声明为一个由 `str` 组成的 `set`: -```Python hl_lines="1 14" -{!../../../docs_src/body_nested_models/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="12" + {!> ../../../docs_src/body_nested_models/tutorial003_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="14" + {!> ../../../docs_src/body_nested_models/tutorial003_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 14" + {!> ../../../docs_src/body_nested_models/tutorial003.py!} + ``` 这样,即使你收到带有重复数据的请求,这些数据也会被转换为一组唯一项。 @@ -79,17 +115,45 @@ Pydantic 模型的每个属性都具有类型。 例如,我们可以定义一个 `Image` 模型: -```Python hl_lines="9 10 11" -{!../../../docs_src/body_nested_models/tutorial004.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7-9" + {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9-11" + {!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9-11" + {!> ../../../docs_src/body_nested_models/tutorial004.py!} + ``` ### 将子模型用作类型 然后我们可以将其用作一个属性的类型: -```Python hl_lines="20" -{!../../../docs_src/body_nested_models/tutorial004.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial004.py!} + ``` 这意味着 **FastAPI** 将期望类似于以下内容的请求体: @@ -122,9 +186,23 @@ Pydantic 模型的每个属性都具有类型。 例如,在 `Image` 模型中我们有一个 `url` 字段,我们可以把它声明为 Pydantic 的 `HttpUrl`,而不是 `str`: -```Python hl_lines="4 10" -{!../../../docs_src/body_nested_models/tutorial005.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="2 8" + {!> ../../../docs_src/body_nested_models/tutorial005_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="4 10" + {!> ../../../docs_src/body_nested_models/tutorial005_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4 10" + {!> ../../../docs_src/body_nested_models/tutorial005.py!} + ``` 该字符串将被检查是否为有效的 URL,并在 JSON Schema / OpenAPI 文档中进行记录。 @@ -132,9 +210,23 @@ Pydantic 模型的每个属性都具有类型。 你还可以将 Pydantic 模型用作 `list`、`set` 等的子类型: -```Python hl_lines="20" -{!../../../docs_src/body_nested_models/tutorial006.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body_nested_models/tutorial006_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial006_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="20" + {!> ../../../docs_src/body_nested_models/tutorial006.py!} + ``` 这将期望(转换,校验,记录文档等)下面这样的 JSON 请求体: @@ -169,9 +261,23 @@ Pydantic 模型的每个属性都具有类型。 你可以定义任意深度的嵌套模型: -```Python hl_lines="9 14 20 23 27" -{!../../../docs_src/body_nested_models/tutorial007.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 12 18 21 25" + {!> ../../../docs_src/body_nested_models/tutorial007_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9 14 20 23 27" + {!> ../../../docs_src/body_nested_models/tutorial007_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 14 20 23 27" + {!> ../../../docs_src/body_nested_models/tutorial007.py!} + ``` !!! info 请注意 `Offer` 拥有一组 `Item` 而反过来 `Item` 又是一个可选的 `Image` 列表是如何发生的。 @@ -186,9 +292,17 @@ images: List[Image] 例如: -```Python hl_lines="15" -{!../../../docs_src/body_nested_models/tutorial008.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="13" + {!> ../../../docs_src/body_nested_models/tutorial008_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="15" + {!> ../../../docs_src/body_nested_models/tutorial008.py!} + ``` ## 无处不在的编辑器支持 @@ -218,9 +332,17 @@ images: List[Image] 在下面的例子中,你将接受任意键为 `int` 类型并且值为 `float` 类型的 `dict`: -```Python hl_lines="15" -{!../../../docs_src/body_nested_models/tutorial009.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="7" + {!> ../../../docs_src/body_nested_models/tutorial009_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/body_nested_models/tutorial009.py!} + ``` !!! tip 请记住 JSON 仅支持将 `str` 作为键。 diff --git a/docs/zh/docs/tutorial/body.md b/docs/zh/docs/tutorial/body.md index f80ab5bf5..d00c96dc3 100644 --- a/docs/zh/docs/tutorial/body.md +++ b/docs/zh/docs/tutorial/body.md @@ -17,9 +17,17 @@ 首先,你需要从 `pydantic` 中导入 `BaseModel`: -```Python hl_lines="2" -{!../../../docs_src/body/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="2" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4" + {!> ../../../docs_src/body/tutorial001.py!} + ``` ## 创建数据模型 @@ -27,9 +35,17 @@ 使用标准的 Python 类型来声明所有属性: -```Python hl_lines="5-9" -{!../../../docs_src/body/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="5-9" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="7-11" + {!> ../../../docs_src/body/tutorial001.py!} + ``` 和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。否则它是一个必需属性。将默认值设为 `None` 可使其成为可选属性。 @@ -57,9 +73,17 @@ 使用与声明路径和查询参数的相同方式声明请求体,即可将其添加到「路径操作」中: -```Python hl_lines="16" -{!../../../docs_src/body/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial001.py!} + ``` ...并且将它的类型声明为你创建的 `Item` 模型。 @@ -112,9 +136,17 @@ Pydantic 本身甚至也进行了一些更改以支持此功能。 在函数内部,你可以直接访问模型对象的所有属性: -```Python hl_lines="19" -{!../../../docs_src/body/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="19" + {!> ../../../docs_src/body/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="21" + {!> ../../../docs_src/body/tutorial002.py!} + ``` ## 请求体 + 路径参数 @@ -122,9 +154,17 @@ Pydantic 本身甚至也进行了一些更改以支持此功能。 **FastAPI** 将识别出与路径参数匹配的函数参数应**从路径中获取**,而声明为 Pydantic 模型的函数参数应**从请求体中获取**。 -```Python hl_lines="15-16" -{!../../../docs_src/body/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="15-16" + {!> ../../../docs_src/body/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17-18" + {!> ../../../docs_src/body/tutorial003.py!} + ``` ## 请求体 + 路径参数 + 查询参数 @@ -132,9 +172,17 @@ Pydantic 本身甚至也进行了一些更改以支持此功能。 **FastAPI** 会识别它们中的每一个,并从正确的位置获取数据。 -```Python hl_lines="16" -{!../../../docs_src/body/tutorial004.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial004_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial004.py!} + ``` 函数参数将依次按如下规则进行识别: diff --git a/docs/zh/docs/tutorial/cookie-params.md b/docs/zh/docs/tutorial/cookie-params.md index d67daf0f9..470fd8e82 100644 --- a/docs/zh/docs/tutorial/cookie-params.md +++ b/docs/zh/docs/tutorial/cookie-params.md @@ -6,9 +6,41 @@ 首先,导入 `Cookie`: -```Python hl_lines="3" -{!../../../docs_src/cookie_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="1" + {!> ../../../docs_src/cookie_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="3" + {!> ../../../docs_src/cookie_params/tutorial001.py!} + ``` ## 声明 `Cookie` 参数 @@ -17,9 +49,41 @@ 第一个值是参数的默认值,同时也可以传递所有验证参数或注释参数,来校验参数: -```Python hl_lines="9" -{!../../../docs_src/cookie_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/cookie_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="7" + {!> ../../../docs_src/cookie_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="9" + {!> ../../../docs_src/cookie_params/tutorial001.py!} + ``` !!! note "技术细节" `Cookie` 、`Path` 、`Query`是兄弟类,它们都继承自公共的 `Param` 类 diff --git a/docs/zh/docs/tutorial/extra-data-types.md b/docs/zh/docs/tutorial/extra-data-types.md index ac3e07654..76d606903 100644 --- a/docs/zh/docs/tutorial/extra-data-types.md +++ b/docs/zh/docs/tutorial/extra-data-types.md @@ -55,12 +55,76 @@ 下面是一个*路径操作*的示例,其中的参数使用了上面的一些类型。 -```Python hl_lines="1 3 12-16" -{!../../../docs_src/extra_data_types/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="1 3 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="1 3 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 3 13-17" + {!> ../../../docs_src/extra_data_types/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="1 2 11-15" + {!> ../../../docs_src/extra_data_types/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="1 2 12-16" + {!> ../../../docs_src/extra_data_types/tutorial001.py!} + ``` 注意,函数内的参数有原生的数据类型,你可以,例如,执行正常的日期操作,如: -```Python hl_lines="18-19" -{!../../../docs_src/extra_data_types/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="19-20" + {!> ../../../docs_src/extra_data_types/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="17-18" + {!> ../../../docs_src/extra_data_types/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="18-19" + {!> ../../../docs_src/extra_data_types/tutorial001.py!} + ``` diff --git a/docs/zh/docs/tutorial/extra-models.md b/docs/zh/docs/tutorial/extra-models.md index 1fbe77be8..32f8f9df1 100644 --- a/docs/zh/docs/tutorial/extra-models.md +++ b/docs/zh/docs/tutorial/extra-models.md @@ -17,9 +17,17 @@ 下面是应该如何根据它们的密码字段以及使用位置去定义模型的大概思路: -```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" -{!../../../docs_src/extra_models/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39" + {!> ../../../docs_src/extra_models/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" + {!> ../../../docs_src/extra_models/tutorial001.py!} + ``` ### 关于 `**user_in.dict()` @@ -150,9 +158,17 @@ UserInDB( 这样,我们可以仅声明模型之间的差异部分(具有明文的 `password`、具有 `hashed_password` 以及不包括密码)。 -```Python hl_lines="9 15-16 19-20 23-24" -{!../../../docs_src/extra_models/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7 13-14 17-18 21-22" + {!> ../../../docs_src/extra_models/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 15-16 19-20 23-24" + {!> ../../../docs_src/extra_models/tutorial002.py!} + ``` ## `Union` 或者 `anyOf` @@ -166,9 +182,17 @@ UserInDB( !!! note 定义一个 `Union` 类型时,首先包括最详细的类型,然后是不太详细的类型。在下面的示例中,更详细的 `PlaneItem` 位于 `Union[PlaneItem,CarItem]` 中的 `CarItem` 之前。 -```Python hl_lines="1 14-15 18-20 33" -{!../../../docs_src/extra_models/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003.py!} + ``` ## 模型列表 @@ -176,9 +200,17 @@ UserInDB( 为此,请使用标准的 Python `typing.List`: -```Python hl_lines="1 20" -{!../../../docs_src/extra_models/tutorial004.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="18" + {!> ../../../docs_src/extra_models/tutorial004_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 20" + {!> ../../../docs_src/extra_models/tutorial004.py!} + ``` ## 任意 `dict` 构成的响应 @@ -188,9 +220,17 @@ UserInDB( 在这种情况下,你可以使用 `typing.Dict`: -```Python hl_lines="1 8" -{!../../../docs_src/extra_models/tutorial005.py!} -``` +=== "Python 3.9+" + + ```Python hl_lines="6" + {!> ../../../docs_src/extra_models/tutorial005_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="1 8" + {!> ../../../docs_src/extra_models/tutorial005.py!} + ``` ## 总结 diff --git a/docs/zh/docs/tutorial/header-params.md b/docs/zh/docs/tutorial/header-params.md index c4b1c38ce..22ff6dc27 100644 --- a/docs/zh/docs/tutorial/header-params.md +++ b/docs/zh/docs/tutorial/header-params.md @@ -6,9 +6,41 @@ 首先导入 `Header`: -```Python hl_lines="3" -{!../../../docs_src/header_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="1" + {!> ../../../docs_src/header_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="3" + {!> ../../../docs_src/header_params/tutorial001.py!} + ``` ## 声明 `Header` 参数 @@ -16,9 +48,41 @@ 第一个值是默认值,你可以传递所有的额外验证或注释参数: -```Python hl_lines="9" -{!../../../docs_src/header_params/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="7" + {!> ../../../docs_src/header_params/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial001.py!} + ``` !!! note "技术细节" `Header` 是 `Path`, `Query` 和 `Cookie` 的兄弟类型。它也继承自通用的 `Param` 类. @@ -44,9 +108,41 @@ 如果出于某些原因,你需要禁用下划线到连字符的自动转换,设置`Header`的参数 `convert_underscores` 为 `False`: -```Python hl_lines="10" -{!../../../docs_src/header_params/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="11" + {!> ../../../docs_src/header_params/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="12" + {!> ../../../docs_src/header_params/tutorial002_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="8" + {!> ../../../docs_src/header_params/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial002.py!} + ``` !!! warning 在设置 `convert_underscores` 为 `False` 之前,请记住,一些HTTP代理和服务器不允许使用带有下划线的headers。 @@ -62,9 +158,50 @@ 比如, 为了声明一个 `X-Token` header 可以出现多次,你可以这样写: -```Python hl_lines="9" -{!../../../docs_src/header_params/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="10" + {!> ../../../docs_src/header_params/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="7" + {!> ../../../docs_src/header_params/tutorial003_py310.py!} + ``` + +=== "Python 3.9+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003_py39.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="9" + {!> ../../../docs_src/header_params/tutorial003.py!} + ``` 如果你与*路径操作*通信时发送两个HTTP headers,就像: diff --git a/docs/zh/docs/tutorial/path-params-numeric-validations.md b/docs/zh/docs/tutorial/path-params-numeric-validations.md index 13512a08e..78fa922b4 100644 --- a/docs/zh/docs/tutorial/path-params-numeric-validations.md +++ b/docs/zh/docs/tutorial/path-params-numeric-validations.md @@ -6,9 +6,41 @@ 首先,从 `fastapi` 导入 `Path`: -```Python hl_lines="1" -{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="1 3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="1 3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="3-4" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="1" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="3" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!} + ``` ## 声明元数据 @@ -16,9 +48,41 @@ 例如,要声明路径参数 `item_id`的 `title` 元数据值,你可以输入: -```Python hl_lines="8" -{!../../../docs_src/path_params_numeric_validations/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="11" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="8" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="10" + {!> ../../../docs_src/path_params_numeric_validations/tutorial001.py!} + ``` !!! note 路径参数总是必需的,因为它必须是路径的一部分。 @@ -43,9 +107,14 @@ 因此,你可以将函数声明为: -```Python hl_lines="7" -{!../../../docs_src/path_params_numeric_validations/tutorial002.py!} -``` +=== "Python 3.6 non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="7" + {!> ../../../docs_src/path_params_numeric_validations/tutorial002.py!} + ``` ## 按需对参数排序的技巧 diff --git a/docs/zh/docs/tutorial/query-params-str-validations.md b/docs/zh/docs/tutorial/query-params-str-validations.md index 070074839..7244aeade 100644 --- a/docs/zh/docs/tutorial/query-params-str-validations.md +++ b/docs/zh/docs/tutorial/query-params-str-validations.md @@ -4,9 +4,17 @@ 让我们以下面的应用程序为例: -```Python hl_lines="7" -{!../../../docs_src/query_params_str_validations/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="7" + {!> ../../../docs_src/query_params_str_validations/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9" + {!> ../../../docs_src/query_params_str_validations/tutorial001.py!} + ``` 查询参数 `q` 的类型为 `str`,默认值为 `None`,因此它是可选的。 diff --git a/docs/zh/docs/tutorial/response-model.md b/docs/zh/docs/tutorial/response-model.md index ea3d0666d..f529cb0d8 100644 --- a/docs/zh/docs/tutorial/response-model.md +++ b/docs/zh/docs/tutorial/response-model.md @@ -8,9 +8,23 @@ * `@app.delete()` * 等等。 -```Python hl_lines="17" -{!../../../docs_src/response_model/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="17 22 24-27" + {!> ../../../docs_src/response_model/tutorial001.py!} + ``` !!! note 注意,`response_model`是「装饰器」方法(`get`,`post` 等)的一个参数。不像之前的所有参数和请求体,它不属于*路径操作函数*。 @@ -58,21 +72,45 @@ FastAPI 将使用此 `response_model` 来: 相反,我们可以创建一个有明文密码的输入模型和一个没有明文密码的输出模型: -```Python hl_lines="9 11 16" -{!../../../docs_src/response_model/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="9 11 16" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="9 11 16" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` 这样,即便我们的*路径操作函数*将会返回包含密码的相同输入用户: -```Python hl_lines="24" -{!../../../docs_src/response_model/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` ...我们已经将 `response_model` 声明为了不包含密码的 `UserOut` 模型: -```Python hl_lines="22" -{!../../../docs_src/response_model/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="22" + {!> ../../../docs_src/response_model/tutorial003_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="22" + {!> ../../../docs_src/response_model/tutorial003.py!} + ``` 因此,**FastAPI** 将会负责过滤掉未在输出模型中声明的所有数据(使用 Pydantic)。 diff --git a/docs/zh/docs/tutorial/schema-extra-example.md b/docs/zh/docs/tutorial/schema-extra-example.md index 8f5fbfe70..816e8f68e 100644 --- a/docs/zh/docs/tutorial/schema-extra-example.md +++ b/docs/zh/docs/tutorial/schema-extra-example.md @@ -10,9 +10,17 @@ 您可以使用 `Config` 和 `schema_extra` 为Pydantic模型声明一个示例,如Pydantic 文档:定制 Schema 中所述: -```Python hl_lines="15-23" -{!../../../docs_src/schema_extra_example/tutorial001.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="13-21" + {!> ../../../docs_src/schema_extra_example/tutorial001_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="15-23" + {!> ../../../docs_src/schema_extra_example/tutorial001.py!} + ``` 这些额外的信息将按原样添加到输出的JSON模式中。 @@ -20,9 +28,17 @@ 在 `Field`, `Path`, `Query`, `Body` 和其他你之后将会看到的工厂函数,你可以为JSON 模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息,比如增加 `example`: -```Python hl_lines="4 10-13" -{!../../../docs_src/schema_extra_example/tutorial002.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="2 8-11" + {!> ../../../docs_src/schema_extra_example/tutorial002_py310.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="4 10-13" + {!> ../../../docs_src/schema_extra_example/tutorial002.py!} + ``` !!! warning 请记住,传递的那些额外参数不会添加任何验证,只会添加注释,用于文档的目的。 @@ -33,9 +49,41 @@ 比如,你可以将请求体的一个 `example` 传递给 `Body`: -```Python hl_lines="20-25" -{!../../../docs_src/schema_extra_example/tutorial003.py!} -``` +=== "Python 3.10+" + + ```Python hl_lines="22-27" + {!> ../../../docs_src/schema_extra_example/tutorial003_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="22-27" + {!> ../../../docs_src/schema_extra_example/tutorial003_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="23-28" + {!> ../../../docs_src/schema_extra_example/tutorial003_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="18-23" + {!> ../../../docs_src/schema_extra_example/tutorial003_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="20-25" + {!> ../../../docs_src/schema_extra_example/tutorial003.py!} + ``` ## 文档 UI 中的例子 diff --git a/docs/zh/docs/tutorial/security/first-steps.md b/docs/zh/docs/tutorial/security/first-steps.md index 86c3320ce..7b1052e12 100644 --- a/docs/zh/docs/tutorial/security/first-steps.md +++ b/docs/zh/docs/tutorial/security/first-steps.md @@ -20,9 +20,26 @@ 把下面的示例代码复制到 `main.py`: -```Python -{!../../../docs_src/security/tutorial001.py!} -``` +=== "Python 3.9+" + + ```Python + {!> ../../../docs_src/security/tutorial001_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python + {!> ../../../docs_src/security/tutorial001_an.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python + {!> ../../../docs_src/security/tutorial001.py!} + ``` ## 运行 From 3ffebbcf0165faf79fe8105c7a6c878f4ba2d9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Sauvage?= Date: Thu, 27 Jul 2023 20:49:56 +0200 Subject: [PATCH 008/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20French=20translati?= =?UTF-8?q?on=20for=20`docs/fr/docs/benchmarks.md`=20(#2155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Courtemanche Co-authored-by: Ruidy --- docs/fr/docs/benchmarks.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/fr/docs/benchmarks.md diff --git a/docs/fr/docs/benchmarks.md b/docs/fr/docs/benchmarks.md new file mode 100644 index 000000000..d33c263a2 --- /dev/null +++ b/docs/fr/docs/benchmarks.md @@ -0,0 +1,34 @@ +# Test de performance + +Les tests de performance de TechEmpower montrent que les applications **FastAPI** tournant sous Uvicorn comme étant l'un des frameworks Python les plus rapides disponibles, seulement inférieur à Starlette et Uvicorn (tous deux utilisés au cœur de FastAPI). (*) + +Mais en prêtant attention aux tests de performance et aux comparaisons, il faut tenir compte de ce qu'il suit. + +## Tests de performance et rapidité + +Lorsque vous vérifiez les tests de performance, il est commun de voir plusieurs outils de différents types comparés comme équivalents. + +En particulier, on voit Uvicorn, Starlette et FastAPI comparés (parmi de nombreux autres outils). + +Plus le problème résolu par un outil est simple, mieux seront les performances obtenues. Et la plupart des tests de performance ne prennent pas en compte les fonctionnalités additionnelles fournies par les outils. + +La hiérarchie est la suivante : + +* **Uvicorn** : un serveur ASGI + * **Starlette** : (utilise Uvicorn) un micro-framework web + * **FastAPI**: (utilise Starlette) un micro-framework pour API disposant de fonctionnalités additionnelles pour la création d'API, avec la validation des données, etc. + +* **Uvicorn** : + * A les meilleures performances, étant donné qu'il n'a pas beaucoup de code mis-à-part le serveur en lui-même. + * On n'écrit pas une application avec uniquement Uvicorn. Cela signifie que le code devrait inclure plus ou moins, au minimum, tout le code offert par Starlette (ou **FastAPI**). Et si on fait cela, l'application finale apportera les mêmes complications que si on avait utilisé un framework et que l'on avait minimisé la quantité de code et de bugs. + * Si on compare Uvicorn, il faut le comparer à d'autre applications de serveurs comme Daphne, Hypercorn, uWSGI, etc. +* **Starlette** : + * A les seconde meilleures performances après Uvicorn. Starlette utilise en réalité Uvicorn. De ce fait, il ne peut qu’être plus "lent" qu'Uvicorn car il requiert l'exécution de plus de code. + * Cependant il nous apporte les outils pour construire une application web simple, avec un routage basé sur des chemins, etc. + * Si on compare Starlette, il faut le comparer à d'autres frameworks web (ou micorframework) comme Sanic, Flask, Django, etc. +* **FastAPI** : + * Comme Starlette, FastAPI utilise Uvicorn et ne peut donc pas être plus rapide que ce dernier. + * FastAPI apporte des fonctionnalités supplémentaires à Starlette. Des fonctionnalités qui sont nécessaires presque systématiquement lors de la création d'une API, comme la validation des données, la sérialisation. En utilisant FastAPI, on obtient une documentation automatiquement (qui ne requiert aucune manipulation pour être mise en place). + * Si on n'utilisait pas FastAPI mais directement Starlette (ou un outil équivalent comme Sanic, Flask, Responder, etc) il faudrait implémenter la validation des données et la sérialisation par nous-même. Le résultat serait donc le même dans les deux cas mais du travail supplémentaire serait à réaliser avec Starlette, surtout en considérant que la validation des données et la sérialisation représentent la plus grande quantité de code à écrire dans une application. + * De ce fait, en utilisant FastAPI on minimise le temps de développement, les bugs, le nombre de lignes de code, et on obtient les mêmes performances (si ce n'est de meilleurs performances) que l'on aurait pu avoir sans ce framework (en ayant à implémenter de nombreuses fonctionnalités importantes par nous-mêmes). + * Si on compare FastAPI, il faut le comparer à d'autres frameworks web (ou ensemble d'outils) qui fournissent la validation des données, la sérialisation et la documentation, comme Flask-apispec, NestJS, Molten, etc. From d7c6894b8bd1584cbf29cc11fb6148c40d03a52d Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:50:16 +0000 Subject: [PATCH 009/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ad01f3c5b..b203afe33 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Update Chinese translations with new source files. PR [#9738](https://github.com/tiangolo/fastapi/pull/9738) by [@mahone3297](https://github.com/mahone3297). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/request-forms.md`. PR [#9841](https://github.com/tiangolo/fastapi/pull/9841) by [@dedkot01](https://github.com/dedkot01). * 🌐 Update Chinese translation for `docs/zh/docs/tutorial/handling-errors.md`. PR [#9485](https://github.com/tiangolo/fastapi/pull/9485) by [@Creat55](https://github.com/Creat55). * 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). From 2dcf78f29526eec2d96f5312a13ea9956e62fc56 Mon Sep 17 00:00:00 2001 From: Julian Maurin Date: Thu, 27 Jul 2023 20:51:07 +0200 Subject: [PATCH 010/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20French=20translati?= =?UTF-8?q?on=20for=20`docs/fr/docs/contributing.md`=20(#2132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ruidy Co-authored-by: Sebastián Ramírez --- docs/fr/docs/contributing.md | 501 +++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 docs/fr/docs/contributing.md diff --git a/docs/fr/docs/contributing.md b/docs/fr/docs/contributing.md new file mode 100644 index 000000000..8292f14bb --- /dev/null +++ b/docs/fr/docs/contributing.md @@ -0,0 +1,501 @@ +# Développement - Contribuer + +Tout d'abord, vous voudrez peut-être voir les moyens de base pour [aider FastAPI et obtenir de l'aide](help-fastapi.md){.internal-link target=_blank}. + +## Développement + +Si vous avez déjà cloné le dépôt et que vous savez que vous devez vous plonger dans le code, voici quelques directives pour mettre en place votre environnement. + +### Environnement virtuel avec `venv` + +Vous pouvez créer un environnement virtuel dans un répertoire en utilisant le module `venv` de Python : + +
+ +```console +$ python -m venv env +``` + +
+ +Cela va créer un répertoire `./env/` avec les binaires Python et vous pourrez alors installer des paquets pour cet environnement isolé. + +### Activer l'environnement + +Activez le nouvel environnement avec : + +=== "Linux, macOS" + +
+ + ```console + $ source ./env/bin/activate + ``` + +
+ +=== "Windows PowerShell" + +
+ + ```console + $ .\env\Scripts\Activate.ps1 + ``` + +
+ +=== "Windows Bash" + + Ou si vous utilisez Bash pour Windows (par exemple Git Bash): + +
+ + ```console + $ source ./env/Scripts/activate + ``` + +
+ +Pour vérifier que cela a fonctionné, utilisez : + +=== "Linux, macOS, Windows Bash" + +
+ + ```console + $ which pip + + some/directory/fastapi/env/bin/pip + ``` + +
+ +=== "Windows PowerShell" + +
+ + ```console + $ Get-Command pip + + some/directory/fastapi/env/bin/pip + ``` + +
+ +Si celui-ci montre le binaire `pip` à `env/bin/pip`, alors ça a fonctionné. 🎉 + + + +!!! tip + Chaque fois que vous installez un nouveau paquet avec `pip` sous cet environnement, activez à nouveau l'environnement. + + Cela permet de s'assurer que si vous utilisez un programme terminal installé par ce paquet (comme `flit`), vous utilisez celui de votre environnement local et pas un autre qui pourrait être installé globalement. + +### Flit + +**FastAPI** utilise Flit pour build, packager et publier le projet. + +Après avoir activé l'environnement comme décrit ci-dessus, installez `flit` : + +
+ +```console +$ pip install flit + +---> 100% +``` + +
+ +Réactivez maintenant l'environnement pour vous assurer que vous utilisez le "flit" que vous venez d'installer (et non un environnement global). + +Et maintenant, utilisez `flit` pour installer les dépendances de développement : + +=== "Linux, macOS" + +
+ + ```console + $ flit install --deps develop --symlink + + ---> 100% + ``` + +
+ +=== "Windows" + + Si vous êtes sous Windows, utilisez `--pth-file` au lieu de `--symlink` : + +
+ + ```console + $ flit install --deps develop --pth-file + + ---> 100% + ``` + +
+ +Il installera toutes les dépendances et votre FastAPI local dans votre environnement local. + +#### Utiliser votre FastAPI local + +Si vous créez un fichier Python qui importe et utilise FastAPI, et que vous l'exécutez avec le Python de votre environnement local, il utilisera votre code source FastAPI local. + +Et si vous mettez à jour le code source local de FastAPI, tel qu'il est installé avec `--symlink` (ou `--pth-file` sous Windows), lorsque vous exécutez à nouveau ce fichier Python, il utilisera la nouvelle version de FastAPI que vous venez d'éditer. + +De cette façon, vous n'avez pas à "installer" votre version locale pour pouvoir tester chaque changement. + +### Formatage + +Il existe un script que vous pouvez exécuter qui formatera et nettoiera tout votre code : + +
+ +```console +$ bash scripts/format.sh +``` + +
+ +Il effectuera également un tri automatique de touts vos imports. + +Pour qu'il puisse les trier correctement, vous devez avoir FastAPI installé localement dans votre environnement, avec la commande dans la section ci-dessus en utilisant `--symlink` (ou `--pth-file` sous Windows). + +### Formatage des imports + +Il existe un autre script qui permet de formater touts les imports et de s'assurer que vous n'avez pas d'imports inutilisés : + +
+ +```console +$ bash scripts/format-imports.sh +``` + +
+ +Comme il exécute une commande après l'autre et modifie et inverse de nombreux fichiers, il prend un peu plus de temps à s'exécuter, il pourrait donc être plus facile d'utiliser fréquemment `scripts/format.sh` et `scripts/format-imports.sh` seulement avant de commit. + +## Documentation + +Tout d'abord, assurez-vous que vous configurez votre environnement comme décrit ci-dessus, qui installera toutes les exigences. + +La documentation utilise MkDocs. + +Et il y a des outils/scripts supplémentaires en place pour gérer les traductions dans `./scripts/docs.py`. + +!!! tip + Vous n'avez pas besoin de voir le code dans `./scripts/docs.py`, vous l'utilisez simplement dans la ligne de commande. + +Toute la documentation est au format Markdown dans le répertoire `./docs/fr/`. + +De nombreux tutoriels comportent des blocs de code. + +Dans la plupart des cas, ces blocs de code sont de véritables applications complètes qui peuvent être exécutées telles quelles. + +En fait, ces blocs de code ne sont pas écrits à l'intérieur du Markdown, ce sont des fichiers Python dans le répertoire `./docs_src/`. + +Et ces fichiers Python sont inclus/injectés dans la documentation lors de la génération du site. + +### Documentation pour les tests + +La plupart des tests sont en fait effectués par rapport aux exemples de fichiers sources dans la documentation. + +Cela permet de s'assurer que : + +* La documentation est à jour. +* Les exemples de documentation peuvent être exécutés tels quels. +* La plupart des fonctionnalités sont couvertes par la documentation, assurées par la couverture des tests. + +Au cours du développement local, un script build le site et vérifie les changements éventuels, puis il est rechargé en direct : + +
+ +```console +$ python ./scripts/docs.py live + +[INFO] Serving on http://127.0.0.1:8008 +[INFO] Start watching changes +[INFO] Start detecting changes +``` + +
+ +Il servira la documentation sur `http://127.0.0.1:8008`. + +De cette façon, vous pouvez modifier la documentation/les fichiers sources et voir les changements en direct. + +#### Typer CLI (facultatif) + +Les instructions ici vous montrent comment utiliser le script à `./scripts/docs.py` avec le programme `python` directement. + +Mais vous pouvez également utiliser Typer CLI, et vous obtiendrez l'auto-complétion dans votre terminal pour les commandes après l'achèvement de l'installation. + +Si vous installez Typer CLI, vous pouvez installer la complétion avec : + +
+ +```console +$ typer --install-completion + +zsh completion installed in /home/user/.bashrc. +Completion will take effect once you restart the terminal. +``` + +
+ +### Apps et documentation en même temps + +Si vous exécutez les exemples avec, par exemple : + +
+ +```console +$ uvicorn tutorial001:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Comme Uvicorn utilisera par défaut le port `8000`, la documentation sur le port `8008` n'entrera pas en conflit. + +### Traductions + +L'aide aux traductions est TRÈS appréciée ! Et cela ne peut se faire sans l'aide de la communauté. 🌎 🚀 + +Voici les étapes à suivre pour aider à la traduction. + +#### Conseils et lignes directrices + +* Vérifiez les pull requests existantes pour votre langue et ajouter des reviews demandant des changements ou les approuvant. + +!!! tip + Vous pouvez ajouter des commentaires avec des suggestions de changement aux pull requests existantes. + + Consultez les documents concernant l'ajout d'un review de pull request pour l'approuver ou demander des modifications. + +* Vérifiez dans issues pour voir s'il y a une personne qui coordonne les traductions pour votre langue. + +* Ajoutez une seule pull request par page traduite. Il sera ainsi beaucoup plus facile pour les autres de l'examiner. + +Pour les langues que je ne parle pas, je vais attendre plusieurs autres reviews de la traduction avant de merge. + +* Vous pouvez également vérifier s'il existe des traductions pour votre langue et y ajouter une review, ce qui m'aidera à savoir si la traduction est correcte et je pourrai la fusionner. + +* Utilisez les mêmes exemples en Python et ne traduisez que le texte des documents. Vous n'avez pas besoin de changer quoi que ce soit pour que cela fonctionne. + +* Utilisez les mêmes images, noms de fichiers et liens. Vous n'avez pas besoin de changer quoi que ce soit pour que cela fonctionne. + +* Pour vérifier le code à 2 lettres de la langue que vous souhaitez traduire, vous pouvez utiliser le tableau Liste des codes ISO 639-1. + +#### Langue existante + +Disons que vous voulez traduire une page pour une langue qui a déjà des traductions pour certaines pages, comme l'espagnol. + +Dans le cas de l'espagnol, le code à deux lettres est `es`. Ainsi, le répertoire des traductions espagnoles se trouve à l'adresse `docs/es/`. + +!!! tip + La langue principale ("officielle") est l'anglais, qui se trouve à l'adresse "docs/en/". + +Maintenant, lancez le serveur en live pour les documents en espagnol : + +
+ +```console +// Use the command "live" and pass the language code as a CLI argument +$ python ./scripts/docs.py live es + +[INFO] Serving on http://127.0.0.1:8008 +[INFO] Start watching changes +[INFO] Start detecting changes +``` + +
+ +Vous pouvez maintenant aller sur http://127.0.0.1:8008 et voir vos changements en direct. + +Si vous regardez le site web FastAPI docs, vous verrez que chaque langue a toutes les pages. Mais certaines pages ne sont pas traduites et sont accompagnées d'une notification concernant la traduction manquante. + +Mais si vous le gérez localement de cette manière, vous ne verrez que les pages déjà traduites. + +Disons maintenant que vous voulez ajouter une traduction pour la section [Features](features.md){.internal-link target=_blank}. + +* Copiez le fichier à : + +``` +docs/en/docs/features.md +``` + +* Collez-le exactement au même endroit mais pour la langue que vous voulez traduire, par exemple : + +``` +docs/es/docs/features.md +``` + +!!! tip + Notez que le seul changement dans le chemin et le nom du fichier est le code de langue, qui passe de `en` à `es`. + +* Ouvrez maintenant le fichier de configuration de MkDocs pour l'anglais à + +``` +docs/en/docs/mkdocs.yml +``` + +* Trouvez l'endroit où cette `docs/features.md` se trouve dans le fichier de configuration. Quelque part comme : + +```YAML hl_lines="8" +site_name: FastAPI +# More stuff +nav: +- FastAPI: index.md +- Languages: + - en: / + - es: /es/ +- features.md +``` + +* Ouvrez le fichier de configuration MkDocs pour la langue que vous éditez, par exemple : + +``` +docs/es/docs/mkdocs.yml +``` + +* Ajoutez-le à l'endroit exact où il se trouvait pour l'anglais, par exemple : + +```YAML hl_lines="8" +site_name: FastAPI +# More stuff +nav: +- FastAPI: index.md +- Languages: + - en: / + - es: /es/ +- features.md +``` + +Assurez-vous que s'il y a d'autres entrées, la nouvelle entrée avec votre traduction est exactement dans le même ordre que dans la version anglaise. + +Si vous allez sur votre navigateur, vous verrez que maintenant les documents montrent votre nouvelle section. 🎉 + +Vous pouvez maintenant tout traduire et voir à quoi cela ressemble au fur et à mesure que vous enregistrez le fichier. + +#### Nouvelle langue + +Disons que vous voulez ajouter des traductions pour une langue qui n'est pas encore traduite, pas même quelques pages. + +Disons que vous voulez ajouter des traductions pour le Créole, et que ce n'est pas encore dans les documents. + +En vérifiant le lien ci-dessus, le code pour "Créole" est `ht`. + +L'étape suivante consiste à exécuter le script pour générer un nouveau répertoire de traduction : + +
+ +```console +// Use the command new-lang, pass the language code as a CLI argument +$ python ./scripts/docs.py new-lang ht + +Successfully initialized: docs/ht +Updating ht +Updating en +``` + +
+ +Vous pouvez maintenant vérifier dans votre éditeur de code le répertoire nouvellement créé `docs/ht/`. + +!!! tip + Créez une première demande d'extraction à l'aide de cette fonction, afin de configurer la nouvelle langue avant d'ajouter des traductions. + + Ainsi, d'autres personnes peuvent vous aider à rédiger d'autres pages pendant que vous travaillez sur la première. 🚀 + +Commencez par traduire la page principale, `docs/ht/index.md`. + +Vous pouvez ensuite continuer avec les instructions précédentes, pour une "langue existante". + +##### Nouvelle langue non prise en charge + +Si, lors de l'exécution du script du serveur en direct, vous obtenez une erreur indiquant que la langue n'est pas prise en charge, quelque chose comme : + +``` + raise TemplateNotFound(template) +jinja2.exceptions.TemplateNotFound: partials/language/xx.html +``` + +Cela signifie que le thème ne supporte pas cette langue (dans ce cas, avec un faux code de 2 lettres de `xx`). + +Mais ne vous inquiétez pas, vous pouvez définir la langue du thème en anglais et ensuite traduire le contenu des documents. + +Si vous avez besoin de faire cela, modifiez le fichier `mkdocs.yml` pour votre nouvelle langue, il aura quelque chose comme : + +```YAML hl_lines="5" +site_name: FastAPI +# More stuff +theme: + # More stuff + language: xx +``` + +Changez cette langue de `xx` (de votre code de langue) à `fr`. + +Vous pouvez ensuite relancer le serveur live. + +#### Prévisualisez le résultat + +Lorsque vous utilisez le script à `./scripts/docs.py` avec la commande `live`, il n'affiche que les fichiers et les traductions disponibles pour la langue courante. + +Mais une fois que vous avez terminé, vous pouvez tester le tout comme il le ferait en ligne. + +Pour ce faire, il faut d'abord construire tous les documents : + +
+ +```console +// Use the command "build-all", this will take a bit +$ python ./scripts/docs.py build-all + +Updating es +Updating en +Building docs for: en +Building docs for: es +Successfully built docs for: es +Copying en index.md to README.md +``` + +
+ +Cela génère tous les documents à `./docs_build/` pour chaque langue. Cela inclut l'ajout de tout fichier dont la traduction est manquante, avec une note disant que "ce fichier n'a pas encore de traduction". Mais vous n'avez rien à faire avec ce répertoire. + +Ensuite, il construit tous ces sites MkDocs indépendants pour chaque langue, les combine, et génère le résultat final à `./site/`. + +Ensuite, vous pouvez servir cela avec le commandement `serve`: + +
+ +```console +// Use the command "serve" after running "build-all" +$ python ./scripts/docs.py serve + +Warning: this is a very simple server. For development, use mkdocs serve instead. +This is here only to preview a site with translations already built. +Make sure you run the build-all command first. +Serving at: http://127.0.0.1:8008 +``` + +
+ +## Tests + +Il existe un script que vous pouvez exécuter localement pour tester tout le code et générer des rapports de couverture en HTML : + +
+ +```console +$ bash scripts/test-cov-html.sh +``` + +
+ +Cette commande génère un répertoire `./htmlcov/`, si vous ouvrez le fichier `./htmlcov/index.html` dans votre navigateur, vous pouvez explorer interactivement les régions de code qui sont couvertes par les tests, et remarquer s'il y a une région manquante. From e79dc9697ceefcc88c7ac06fe564bfec9afb008b Mon Sep 17 00:00:00 2001 From: Julian Maurin Date: Thu, 27 Jul 2023 20:51:55 +0200 Subject: [PATCH 011/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20French=20translati?= =?UTF-8?q?on=20for=20`docs/fr/docs/tutorial/index.md`=20(#2234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ruidy Co-authored-by: Sebastián Ramírez --- docs/fr/docs/tutorial/index.md | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/fr/docs/tutorial/index.md diff --git a/docs/fr/docs/tutorial/index.md b/docs/fr/docs/tutorial/index.md new file mode 100644 index 000000000..4dc202b33 --- /dev/null +++ b/docs/fr/docs/tutorial/index.md @@ -0,0 +1,80 @@ +# Tutoriel - Guide utilisateur - Introduction + +Ce tutoriel vous montre comment utiliser **FastAPI** avec la plupart de ses fonctionnalités, étape par étape. + +Chaque section s'appuie progressivement sur les précédentes, mais elle est structurée de manière à séparer les sujets, afin que vous puissiez aller directement à l'un d'entre eux pour résoudre vos besoins spécifiques en matière d'API. + +Il est également conçu pour fonctionner comme une référence future. + +Vous pouvez donc revenir et voir exactement ce dont vous avez besoin. + +## Exécuter le code + +Tous les blocs de code peuvent être copiés et utilisés directement (il s'agit en fait de fichiers Python testés). + +Pour exécuter l'un de ces exemples, copiez le code dans un fichier `main.py`, et commencez `uvicorn` avec : + +
+ +```console +$ uvicorn main:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +INFO: Started reloader process [28720] +INFO: Started server process [28722] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +
+ +Il est **FORTEMENT encouragé** que vous écriviez ou copiez le code, l'éditiez et l'exécutiez localement. + +L'utiliser dans votre éditeur est ce qui vous montre vraiment les avantages de FastAPI, en voyant le peu de code que vous avez à écrire, toutes les vérifications de type, l'autocomplétion, etc. + +--- + +## Installer FastAPI + +La première étape consiste à installer FastAPI. + +Pour le tutoriel, vous voudrez peut-être l'installer avec toutes les dépendances et fonctionnalités optionnelles : + +
+ +```console +$ pip install fastapi[all] + +---> 100% +``` + +
+ +... qui comprend également `uvicorn`, que vous pouvez utiliser comme serveur pour exécuter votre code. + +!!! note + Vous pouvez également l'installer pièce par pièce. + + C'est ce que vous feriez probablement une fois que vous voudrez déployer votre application en production : + + ``` + pip install fastapi + ``` + + Installez également `uvicorn` pour qu'il fonctionne comme serveur : + + ``` + pip install uvicorn + ``` + + Et la même chose pour chacune des dépendances facultatives que vous voulez utiliser. + +## Guide utilisateur avancé + +Il existe également un **Guide d'utilisation avancé** que vous pouvez lire plus tard après ce **Tutoriel - Guide d'utilisation**. + +Le **Guide d'utilisation avancé**, qui s'appuie sur cette base, utilise les mêmes concepts et vous apprend quelques fonctionnalités supplémentaires. + +Mais vous devez d'abord lire le **Tutoriel - Guide d'utilisation** (ce que vous êtes en train de lire en ce moment). + +Il est conçu pour que vous puissiez construire une application complète avec seulement le **Tutoriel - Guide d'utilisation**, puis l'étendre de différentes manières, en fonction de vos besoins, en utilisant certaines des idées supplémentaires du **Guide d'utilisation avancé**. From 35707a1b2913e3f3d2f478e2752178d71357563a Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:51:59 +0000 Subject: [PATCH 012/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b203afe33..fda474978 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add French translation for `docs/fr/docs/benchmarks.md`. PR [#2155](https://github.com/tiangolo/fastapi/pull/2155) by [@clemsau](https://github.com/clemsau). * 🌐 Update Chinese translations with new source files. PR [#9738](https://github.com/tiangolo/fastapi/pull/9738) by [@mahone3297](https://github.com/mahone3297). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/request-forms.md`. PR [#9841](https://github.com/tiangolo/fastapi/pull/9841) by [@dedkot01](https://github.com/dedkot01). * 🌐 Update Chinese translation for `docs/zh/docs/tutorial/handling-errors.md`. PR [#9485](https://github.com/tiangolo/fastapi/pull/9485) by [@Creat55](https://github.com/Creat55). From 02ed00cc471862588479ffc364c1c908ef0b35f5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:53:03 +0000 Subject: [PATCH 013/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fda474978..f8837f2d0 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add French translation for `docs/fr/docs/contributing.md`. PR [#2132](https://github.com/tiangolo/fastapi/pull/2132) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/benchmarks.md`. PR [#2155](https://github.com/tiangolo/fastapi/pull/2155) by [@clemsau](https://github.com/clemsau). * 🌐 Update Chinese translations with new source files. PR [#9738](https://github.com/tiangolo/fastapi/pull/9738) by [@mahone3297](https://github.com/mahone3297). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/request-forms.md`. PR [#9841](https://github.com/tiangolo/fastapi/pull/9841) by [@dedkot01](https://github.com/dedkot01). From 04b9a67cbb12553b0011f240c4d3ef39913ab0df Mon Sep 17 00:00:00 2001 From: Sam Courtemanche Date: Thu, 27 Jul 2023 20:53:21 +0200 Subject: [PATCH 014/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20French=20translati?= =?UTF-8?q?on=20for=20`docs/fr/docs/tutorial/query-params-str-validations.?= =?UTF-8?q?md`=20(#4075)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julian Maurin Co-authored-by: Sebastián Ramírez --- .../tutorial/query-params-str-validations.md | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 docs/fr/docs/tutorial/query-params-str-validations.md diff --git a/docs/fr/docs/tutorial/query-params-str-validations.md b/docs/fr/docs/tutorial/query-params-str-validations.md new file mode 100644 index 000000000..f5248fe8b --- /dev/null +++ b/docs/fr/docs/tutorial/query-params-str-validations.md @@ -0,0 +1,305 @@ +# Paramètres de requête et validations de chaînes de caractères + +**FastAPI** vous permet de déclarer des informations et des validateurs additionnels pour vos paramètres de requêtes. + +Commençons avec cette application pour exemple : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial001.py!} +``` + +Le paramètre de requête `q` a pour type `Union[str, None]` (ou `str | None` en Python 3.10), signifiant qu'il est de type `str` mais pourrait aussi être égal à `None`, et bien sûr, la valeur par défaut est `None`, donc **FastAPI** saura qu'il n'est pas requis. + +!!! note + **FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`. + + Le `Union` dans `Union[str, None]` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs. + +## Validation additionnelle + +Nous allons imposer que bien que `q` soit un paramètre optionnel, dès qu'il est fourni, **sa longueur n'excède pas 50 caractères**. + +## Importer `Query` + +Pour cela, importez d'abord `Query` depuis `fastapi` : + +```Python hl_lines="3" +{!../../../docs_src/query_params_str_validations/tutorial002.py!} +``` + +## Utiliser `Query` comme valeur par défaut + +Construisez ensuite la valeur par défaut de votre paramètre avec `Query`, en choisissant 50 comme `max_length` : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial002.py!} +``` + +Comme nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous pouvons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, il sert le même objectif qui est de définir cette valeur par défaut. + +Donc : + +```Python +q: Union[str, None] = Query(default=None) +``` + +... rend le paramètre optionnel, et est donc équivalent à : + +```Python +q: Union[str, None] = None +``` + +Mais déclare explicitement `q` comme étant un paramètre de requête. + +!!! info + Gardez à l'esprit que la partie la plus importante pour rendre un paramètre optionnel est : + + ```Python + = None + ``` + + ou : + + ```Python + = Query(None) + ``` + + et utilisera ce `None` pour détecter que ce paramètre de requête **n'est pas requis**. + + Le `Union[str, None]` est uniquement là pour permettre à votre éditeur un meilleur support. + +Ensuite, nous pouvons passer d'autres paramètres à `Query`. Dans cet exemple, le paramètre `max_length` qui s'applique aux chaînes de caractères : + +```Python +q: Union[str, None] = Query(default=None, max_length=50) +``` + +Cela va valider les données, montrer une erreur claire si ces dernières ne sont pas valides, et documenter le paramètre dans le schéma `OpenAPI` de cette *path operation*. + +## Rajouter plus de validation + +Vous pouvez aussi rajouter un second paramètre `min_length` : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial003.py!} +``` + +## Ajouter des validations par expressions régulières + +On peut définir une expression régulière à laquelle le paramètre doit correspondre : + +```Python hl_lines="10" +{!../../../docs_src/query_params_str_validations/tutorial004.py!} +``` + +Cette expression régulière vérifie que la valeur passée comme paramètre : + +* `^` : commence avec les caractères qui suivent, avec aucun caractère avant ceux-là. +* `fixedquery` : a pour valeur exacte `fixedquery`. +* `$` : se termine directement ensuite, n'a pas d'autres caractères après `fixedquery`. + +Si vous vous sentez perdu avec le concept d'**expression régulière**, pas d'inquiétudes. Il s'agit d'une notion difficile pour beaucoup, et l'on peut déjà réussir à faire beaucoup sans jamais avoir à les manipuler. + +Mais si vous décidez d'apprendre à les utiliser, sachez qu'ensuite vous pouvez les utiliser directement dans **FastAPI**. + +## Valeurs par défaut + +De la même façon que vous pouvez passer `None` comme premier argument pour l'utiliser comme valeur par défaut, vous pouvez passer d'autres valeurs. + +Disons que vous déclarez le paramètre `q` comme ayant une longueur minimale de `3`, et une valeur par défaut étant `"fixedquery"` : + +```Python hl_lines="7" +{!../../../docs_src/query_params_str_validations/tutorial005.py!} +``` + +!!! note "Rappel" + Avoir une valeur par défaut rend le paramètre optionnel. + +## Rendre ce paramètre requis + +Quand on ne déclare ni validation, ni métadonnée, on peut rendre le paramètre `q` requis en ne lui déclarant juste aucune valeur par défaut : + +```Python +q: str +``` + +à la place de : + +```Python +q: Union[str, None] = None +``` + +Mais maintenant, on déclare `q` avec `Query`, comme ceci : + +```Python +q: Union[str, None] = Query(default=None, min_length=3) +``` + +Donc pour déclarer une valeur comme requise tout en utilisant `Query`, il faut utiliser `...` comme premier argument : + +```Python hl_lines="7" +{!../../../docs_src/query_params_str_validations/tutorial006.py!} +``` + +!!! info + Si vous n'avez jamais vu ce `...` auparavant : c'est une des constantes natives de Python appelée "Ellipsis". + +Cela indiquera à **FastAPI** que la présence de ce paramètre est obligatoire. + +## Liste de paramètres / valeurs multiples via Query + +Quand on définit un paramètre de requête explicitement avec `Query` on peut aussi déclarer qu'il reçoit une liste de valeur, ou des "valeurs multiples". + +Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans une URL, on écrit : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial011.py!} +``` + +Ce qui fait qu'avec une URL comme : + +``` +http://localhost:8000/items/?q=foo&q=bar +``` + +vous recevriez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python au sein de votre fonction de **path operation**, dans le paramètre de fonction `q`. + +Donc la réponse de cette URL serait : + +```JSON +{ + "q": [ + "foo", + "bar" + ] +} +``` + +!!! tip "Astuce" + Pour déclarer un paramètre de requête de type `list`, comme dans l'exemple ci-dessus, il faut explicitement utiliser `Query`, sinon cela sera interprété comme faisant partie du corps de la requête. + +La documentation sera donc mise à jour automatiquement pour autoriser plusieurs valeurs : + + + +### Combiner liste de paramètres et valeurs par défaut + +Et l'on peut aussi définir une liste de valeurs par défaut si aucune n'est fournie : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial012.py!} +``` + +Si vous allez à : + +``` +http://localhost:8000/items/ +``` + +la valeur par défaut de `q` sera : `["foo", "bar"]` + +et la réponse sera : + +```JSON +{ + "q": [ + "foo", + "bar" + ] +} +``` + +#### Utiliser `list` + +Il est aussi possible d'utiliser directement `list` plutôt que `List[str]` : + +```Python hl_lines="7" +{!../../../docs_src/query_params_str_validations/tutorial013.py!} +``` + +!!! note + Dans ce cas-là, **FastAPI** ne vérifiera pas le contenu de la liste. + + Par exemple, `List[int]` vérifiera (et documentera) que la liste est bien entièrement composée d'entiers. Alors qu'un simple `list` ne ferait pas cette vérification. + +## Déclarer des métadonnées supplémentaires + +On peut aussi ajouter plus d'informations sur le paramètre. + +Ces informations seront incluses dans le schéma `OpenAPI` généré et utilisées par la documentation interactive ou les outils externes utilisés. + +!!! note + Gardez en tête que les outils externes utilisés ne supportent pas forcément tous parfaitement OpenAPI. + + Il se peut donc que certains d'entre eux n'utilisent pas toutes les métadonnées que vous avez déclarées pour le moment, bien que dans la plupart des cas, les fonctionnalités manquantes ont prévu d'être implémentées. + +Vous pouvez ajouter un `title` : + +```Python hl_lines="10" +{!../../../docs_src/query_params_str_validations/tutorial007.py!} +``` + +Et une `description` : + +```Python hl_lines="13" +{!../../../docs_src/query_params_str_validations/tutorial008.py!} +``` + +## Alias de paramètres + +Imaginez que vous vouliez que votre paramètre se nomme `item-query`. + +Comme dans la requête : + +``` +http://127.0.0.1:8000/items/?item-query=foobaritems +``` + +Mais `item-query` n'est pas un nom de variable valide en Python. + +Le nom le plus proche serait `item_query`. + +Mais vous avez vraiment envie que ce soit exactement `item-query`... + +Pour cela vous pouvez déclarer un `alias`, et cet alias est ce qui sera utilisé pour trouver la valeur du paramètre : + +```Python hl_lines="9" +{!../../../docs_src/query_params_str_validations/tutorial009.py!} +``` + +## Déprécier des paramètres + +Disons que vous ne vouliez plus utiliser ce paramètre désormais. + +Il faut qu'il continue à exister pendant un certain temps car vos clients l'utilisent, mais vous voulez que la documentation mentionne clairement que ce paramètre est déprécié. + +On utilise alors l'argument `deprecated=True` de `Query` : + +```Python hl_lines="18" +{!../../../docs_src/query_params_str_validations/tutorial010.py!} +``` + +La documentation le présentera comme il suit : + + + +## Pour résumer + +Il est possible d'ajouter des validateurs et métadonnées pour vos paramètres. + +Validateurs et métadonnées génériques: + +* `alias` +* `title` +* `description` +* `deprecated` + +Validateurs spécifiques aux chaînes de caractères : + +* `min_length` +* `max_length` +* `regex` + +Parmi ces exemples, vous avez pu voir comment déclarer des validateurs pour les chaînes de caractères. + +Dans les prochains chapitres, vous verrez comment déclarer des validateurs pour d'autres types, comme les nombres. From 570ca011f91346fa0d79bf3d9ae5ba629165f25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 27 Jul 2023 20:53:51 +0200 Subject: [PATCH 015/317] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors,=20add?= =?UTF-8?q?=20Fern=20(#9956)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/en/data/sponsors.yml | 3 +++ docs/en/docs/img/sponsors/fern-banner.png | Bin 0 -> 8801 bytes docs/en/docs/img/sponsors/fern.png | Bin 0 -> 10924 bytes docs/en/overrides/main.html | 6 ++++++ 5 files changed, 10 insertions(+) create mode 100644 docs/en/docs/img/sponsors/fern-banner.png create mode 100644 docs/en/docs/img/sponsors/fern.png diff --git a/README.md b/README.md index 36c71081e..f0e76c4b6 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ The key features are: + diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 1b5240b5e..33d57c873 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -5,6 +5,9 @@ gold: - url: https://platform.sh/try-it-now/?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023 title: "Build, run and scale your apps on a modern, reliable, and secure PaaS." img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png + - url: https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge + title: Fern | SDKs and API docs + img: https://fastapi.tiangolo.com/img/sponsors/fern.png silver: - url: https://www.deta.sh/?ref=fastapi title: The launchpad for all your (team's) ideas diff --git a/docs/en/docs/img/sponsors/fern-banner.png b/docs/en/docs/img/sponsors/fern-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..1b70ab96d9548145028a8667013843cc810a5ee4 GIT binary patch literal 8801 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 literal 0 HcmV?d00001 diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index 4608880f2..fcd1704b9 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -34,6 +34,12 @@ + {% endblock %} From a52875c6563ccf17596287170d42d27676947891 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:54:32 +0000 Subject: [PATCH 016/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index f8837f2d0..2f9aada99 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add French translation for `docs/fr/docs/tutorial/index.md`. PR [#2234](https://github.com/tiangolo/fastapi/pull/2234) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/contributing.md`. PR [#2132](https://github.com/tiangolo/fastapi/pull/2132) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/benchmarks.md`. PR [#2155](https://github.com/tiangolo/fastapi/pull/2155) by [@clemsau](https://github.com/clemsau). * 🌐 Update Chinese translations with new source files. PR [#9738](https://github.com/tiangolo/fastapi/pull/9738) by [@mahone3297](https://github.com/mahone3297). From e081145c7dc1218b4fee08f61295f76549aa0156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=AE=9A=E7=84=95?= <108172295+wdh99@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:55:40 +0800 Subject: [PATCH 017/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Chinese=20translat?= =?UTF-8?q?ion=20for=20`docs/zh/docs/tutorial/background-tasks.md`=20(#981?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/zh/docs/tutorial/background-tasks.md | 126 ++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/zh/docs/tutorial/background-tasks.md diff --git a/docs/zh/docs/tutorial/background-tasks.md b/docs/zh/docs/tutorial/background-tasks.md new file mode 100644 index 000000000..c8568298b --- /dev/null +++ b/docs/zh/docs/tutorial/background-tasks.md @@ -0,0 +1,126 @@ +# 后台任务 + +你可以定义在返回响应后运行的后台任务。 + +这对需要在请求之后执行的操作很有用,但客户端不必在接收响应之前等待操作完成。 + +包括这些例子: + +* 执行操作后发送的电子邮件通知: + * 由于连接到电子邮件服务器并发送电子邮件往往很“慢”(几秒钟),您可以立即返回响应并在后台发送电子邮件通知。 +* 处理数据: + * 例如,假设您收到的文件必须经过一个缓慢的过程,您可以返回一个"Accepted"(HTTP 202)响应并在后台处理它。 + +## 使用 `BackgroundTasks` + +首先导入 `BackgroundTasks` 并在 *路径操作函数* 中使用类型声明 `BackgroundTasks` 定义一个参数: + +```Python hl_lines="1 13" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +**FastAPI** 会创建一个 `BackgroundTasks` 类型的对象并作为该参数传入。 + +## 创建一个任务函数 + +创建要作为后台任务运行的函数。 + +它只是一个可以接收参数的标准函数。 + +它可以是 `async def` 或普通的 `def` 函数,**FastAPI** 知道如何正确处理。 + +在这种情况下,任务函数将写入一个文件(模拟发送电子邮件)。 + +由于写操作不使用 `async` 和 `await`,我们用普通的 `def` 定义函数: + +```Python hl_lines="6-9" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +## 添加后台任务 + +在你的 *路径操作函数* 里,用 `.add_task()` 方法将任务函数传到 *后台任务* 对象中: + +```Python hl_lines="14" +{!../../../docs_src/background_tasks/tutorial001.py!} +``` + +`.add_task()` 接收以下参数: + +* 在后台运行的任务函数(`write_notification`)。 +* 应按顺序传递给任务函数的任意参数序列(`email`)。 +* 应传递给任务函数的任意关键字参数(`message="some notification"`)。 + +## 依赖注入 + +使用 `BackgroundTasks` 也适用于依赖注入系统,你可以在多个级别声明 `BackgroundTasks` 类型的参数:在 *路径操作函数* 里,在依赖中(可依赖),在子依赖中,等等。 + +**FastAPI** 知道在每种情况下该做什么以及如何复用同一对象,因此所有后台任务被合并在一起并且随后在后台运行: + +=== "Python 3.10+" + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="14 16 23 26" + {!> ../../../docs_src/background_tasks/tutorial002_an.py!} + ``` + +=== "Python 3.10+ 没Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="11 13 20 23" + {!> ../../../docs_src/background_tasks/tutorial002_py310.py!} + ``` + +=== "Python 3.6+ 没Annotated" + + !!! tip + 尽可能选择使用 `Annotated` 的版本。 + + ```Python hl_lines="13 15 22 25" + {!> ../../../docs_src/background_tasks/tutorial002.py!} + ``` + +该示例中,信息会在响应发出 *之后* 被写到 `log.txt` 文件。 + +如果请求中有查询,它将在后台任务中写入日志。 + +然后另一个在 *路径操作函数* 生成的后台任务会使用路径参数 `email` 写入一条信息。 + +## 技术细节 + +`BackgroundTasks` 类直接来自 `starlette.background`。 + +它被直接导入/包含到FastAPI以便你可以从 `fastapi` 导入,并避免意外从 `starlette.background` 导入备用的 `BackgroundTask` (后面没有 `s`)。 + +通过仅使用 `BackgroundTasks` (而不是 `BackgroundTask`),使得能将它作为 *路径操作函数* 的参数 ,并让**FastAPI**为您处理其余部分, 就像直接使用 `Request` 对象。 + +在FastAPI中仍然可以单独使用 `BackgroundTask`,但您必须在代码中创建对象,并返回包含它的Starlette `Response`。 + +更多细节查看 Starlette's official docs for Background Tasks. + +## 告诫 + +如果您需要执行繁重的后台计算,并且不一定需要由同一进程运行(例如,您不需要共享内存、变量等),那么使用其他更大的工具(如 Celery)可能更好。 + +它们往往需要更复杂的配置,即消息/作业队列管理器,如RabbitMQ或Redis,但它们允许您在多个进程中运行后台任务,甚至是在多个服务器中。 + +要查看示例,查阅 [Project Generators](../project-generation.md){.internal-link target=_blank},它们都包括已经配置的Celery。 + +但是,如果您需要从同一个**FastAPI**应用程序访问变量和对象,或者您需要执行小型后台任务(如发送电子邮件通知),您只需使用 `BackgroundTasks` 即可。 + +## 回顾 + +导入并使用 `BackgroundTasks` 通过 *路径操作函数* 中的参数和依赖项来添加后台任务。 From 5d3f51c8bc7b902204b1b55ea7044ca5f10a7256 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:56:19 +0000 Subject: [PATCH 018/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 2f9aada99..d295826fc 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add French translation for `docs/fr/docs/tutorial/query-params-str-validations.md`. PR [#4075](https://github.com/tiangolo/fastapi/pull/4075) by [@Smlep](https://github.com/Smlep). * 🌐 Add French translation for `docs/fr/docs/tutorial/index.md`. PR [#2234](https://github.com/tiangolo/fastapi/pull/2234) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/contributing.md`. PR [#2132](https://github.com/tiangolo/fastapi/pull/2132) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/benchmarks.md`. PR [#2155](https://github.com/tiangolo/fastapi/pull/2155) by [@clemsau](https://github.com/clemsau). From e334065d1055e27773271c00ba8830c7176f975d Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:57:37 +0000 Subject: [PATCH 019/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d295826fc..f6097bedb 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). * 🌐 Add French translation for `docs/fr/docs/tutorial/query-params-str-validations.md`. PR [#4075](https://github.com/tiangolo/fastapi/pull/4075) by [@Smlep](https://github.com/Smlep). * 🌐 Add French translation for `docs/fr/docs/tutorial/index.md`. PR [#2234](https://github.com/tiangolo/fastapi/pull/2234) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/contributing.md`. PR [#2132](https://github.com/tiangolo/fastapi/pull/2132) by [@JulianMaurin](https://github.com/JulianMaurin). From 55871036dbd85531f042f1e6b87a14fe8556ccd0 Mon Sep 17 00:00:00 2001 From: Nina Hwang <79563565+NinaHwang@users.noreply.github.com> Date: Fri, 28 Jul 2023 03:59:18 +0900 Subject: [PATCH 020/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Korean=20translati?= =?UTF-8?q?on=20for=20`docs/ko/docs/async.md`=20(#4179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/ko/docs/async.md | 404 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 docs/ko/docs/async.md diff --git a/docs/ko/docs/async.md b/docs/ko/docs/async.md new file mode 100644 index 000000000..47dbaa1b0 --- /dev/null +++ b/docs/ko/docs/async.md @@ -0,0 +1,404 @@ +# 동시성과 async / await + +*경로 작동 함수*에서의 `async def` 문법에 대한 세부사항과 비동기 코드, 동시성 및 병렬성에 대한 배경 + +## 바쁘신 경우 + +요약 + +다음과 같이 `await`를 사용해 호출하는 제3의 라이브러리를 사용하는 경우: + +```Python +results = await some_library() +``` + +다음처럼 *경로 작동 함수*를 `async def`를 사용해 선언하십시오: + +```Python hl_lines="2" +@app.get('/') +async def read_results(): + results = await some_library() + return results +``` + +!!! note "참고" + `async def`로 생성된 함수 내부에서만 `await`를 사용할 수 있습니다. + +--- + +데이터베이스, API, 파일시스템 등과 의사소통하는 제3의 라이브러리를 사용하고, 그것이 `await`를 지원하지 않는 경우(현재 거의 모든 데이터베이스 라이브러리가 그러합니다), *경로 작동 함수*를 일반적인 `def`를 사용해 선언하십시오: + +```Python hl_lines="2" +@app.get('/') +def results(): + results = some_library() + return results +``` + +--- + +만약 당신의 응용프로그램이 (어째서인지) 다른 무엇과 의사소통하고 그것이 응답하기를 기다릴 필요가 없다면 `async def`를 사용하십시오. + +--- + +모르겠다면, 그냥 `def`를 사용하십시오. + +--- + +**참고**: *경로 작동 함수*에서 필요한만큼 `def`와 `async def`를 혼용할 수 있고, 가장 알맞은 것을 선택해서 정의할 수 있습니다. FastAPI가 자체적으로 알맞은 작업을 수행할 것입니다. + +어찌되었든, 상기 어떠한 경우라도, FastAPI는 여전히 비동기적으로 작동하고 매우 빠릅니다. + +그러나 상기 작업을 수행함으로써 어느 정도의 성능 최적화가 가능합니다. + +## 기술적 세부사항 + +최신 파이썬 버전은 `async`와 `await` 문법과 함께 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 지원합니다. + +아래 섹션들에서 해당 문장을 부분별로 살펴보겠습니다: + +* **비동기 코드** +* **`async`와 `await`** +* **코루틴** + +## 비동기 코드 + +비동기 코드란 언어 💬 가 코드의 어느 한 부분에서, 컴퓨터 / 프로그램🤖에게 *다른 무언가*가 어딘가에서 끝날 때까지 기다려야한다고 말하는 방식입니다. *다른 무언가*가 “느린-파일" 📝 이라고 불린다고 가정해봅시다. + +따라서 “느린-파일” 📝이 끝날때까지 컴퓨터는 다른 작업을 수행할 수 있습니다. + +그 다음 컴퓨터 / 프로그램 🤖 은 다시 기다리고 있기 때문에 기회가 있을 때마다 다시 돌아오거나, 혹은 당시에 수행해야하는 작업들이 완료될 때마다 다시 돌아옵니다. 그리고 그것 🤖 은 기다리고 있던 작업 중 어느 것이 이미 완료되었는지, 그것 🤖 이 해야하는 모든 작업을 수행하면서 확인합니다. + +다음으로, 그것 🤖 은 완료할 첫번째 작업에 착수하고(우리의 "느린-파일" 📝 이라고 가정합시다) 그에 대해 수행해야하는 작업을 계속합니다. + +"다른 무언가를 기다리는 것"은 일반적으로 비교적 "느린" (프로세서와 RAM 메모리 속도에 비해) I/O 작업을 의미합니다. 예를 들면 다음의 것들을 기다리는 것입니다: + +* 네트워크를 통해 클라이언트로부터 전송되는 데이터 +* 네트워크를 통해 클라이언트가 수신할, 당신의 프로그램으로부터 전송되는 데이터 +* 시스템이 읽고 프로그램에 전달할 디스크 내의 파일 내용 +* 당신의 프로그램이 시스템에 전달하는, 디스크에 작성될 내용 +* 원격 API 작업 +* 완료될 데이터베이스 작업 +* 결과를 반환하는 데이터베이스 쿼리 +* 기타 + +수행 시간의 대부분이 I/O 작업을 기다리는데에 소요되기 때문에, "I/O에 묶인" 작업이라고 불립니다. + +이것은 "비동기"라고 불리는데 컴퓨터 / 프로그램이 작업 결과를 가지고 일을 수행할 수 있도록, 느린 작업에 "동기화"되어 아무것도 하지 않으면서 작업이 완료될 정확한 시점만을 기다릴 필요가 없기 때문입니다. + +이 대신에, "비동기" 시스템에서는, 작업은 일단 완료되면, 컴퓨터 / 프로그램이 수행하고 있는 일을 완료하고 이후 다시 돌아와서 그것의 결과를 받아 이를 사용해 작업을 지속할 때까지 잠시 (몇 마이크로초) 대기할 수 있습니다. + +"동기"("비동기"의 반대)는 컴퓨터 / 프로그램이 상이한 작업들간 전환을 하기 전에 그것이 대기를 동반하게 될지라도 모든 순서를 따르기 때문에 "순차"라는 용어로도 흔히 불립니다. + +### 동시성과 버거 + +위에서 설명한 **비동기** 코드에 대한 개념은 종종 **"동시성"**이라고도 불립니다. 이것은 **"병렬성"**과는 다릅니다. + +**동시성**과 **병렬성**은 모두 "동시에 일어나는 서로 다른 일들"과 관련이 있습니다. + +하지만 *동시성*과 *병렬성*의 세부적인 개념에는 꽤 차이가 있습니다. + +차이를 확인하기 위해, 다음의 버거에 대한 이야기를 상상해보십시오: + +### 동시 버거 + +당신은 짝사랑 상대 😍 와 패스트푸드 🍔 를 먹으러 갔습니다. 당신은 점원 💁 이 당신 앞에 있는 사람들의 주문을 받을 동안 줄을 서서 기다리고 있습니다. + +이제 당신의 순서가 되어서, 당신은 당신과 짝사랑 상대 😍 를 위한 두 개의 고급스러운 버거 🍔 를 주문합니다. + +당신이 돈을 냅니다 💸. + +점원 💁 은 주방 👨‍🍳 에 요리를 하라고 전달하고, 따라서 그들은 당신의 버거 🍔 를 준비해야한다는 사실을 알게됩니다(그들이 지금은 당신 앞 고객들의 주문을 준비하고 있을지라도 말입니다). + +점원 💁 은 당신의 순서가 적힌 번호표를 줍니다. + +기다리는 동안, 당신은 짝사랑 상대 😍 와 함께 테이블을 고르고, 자리에 앉아 오랫동안 (당신이 주문한 버거는 꽤나 고급스럽기 때문에 준비하는데 시간이 조금 걸립니다 ✨🍔✨) 대화를 나눕니다. + +짝사랑 상대 😍 와 테이블에 앉아서 버거 🍔 를 기다리는 동안, 그 사람 😍 이 얼마나 멋지고, 사랑스럽고, 똑똑한지 감탄하며 시간을 보냅니다 ✨😍✨. + +짝사랑 상대 😍 와 기다리면서 얘기하는 동안, 때때로, 당신은 당신의 차례가 되었는지 보기 위해 카운터의 번호를 확인합니다. + +그러다 어느 순간, 당신의 차례가 됩니다. 카운터에 가서, 버거 🍔 를 받고, 테이블로 다시 돌아옵니다. + +당신과 짝사랑 상대 😍 는 버거 🍔 를 먹으며 좋은 시간을 보냅니다 ✨. + +--- + +당신이 이 이야기에서 컴퓨터 / 프로그램 🤖 이라고 상상해보십시오. + +줄을 서서 기다리는 동안, 당신은 아무것도 하지 않고 😴 당신의 차례를 기다리며, 어떠한 "생산적인" 일도 하지 않습니다. 하지만 점원 💁 이 (음식을 준비하지는 않고) 주문을 받기만 하기 때문에 줄이 빨리 줄어들어서 괜찮습니다. + +그다음, 당신이 차례가 오면, 당신은 실제로 "생산적인" 일 🤓 을 합니다. 당신은 메뉴를 보고, 무엇을 먹을지 결정하고, 짝사랑 상대 😍 의 선택을 묻고, 돈을 내고 💸 , 맞는 카드를 냈는지 확인하고, 비용이 제대로 지불되었는지 확인하고, 주문이 제대로 들어갔는지 확인을 하는 작업 등등을 수행합니다. + +하지만 이후에는, 버거 🍔 를 아직 받지 못했음에도, 버거가 준비될 때까지 기다려야 🕙 하기 때문에 점원 💁 과의 작업은 "일시정지" ⏸ 상태입니다. + +하지만 번호표를 받고 카운터에서 나와 테이블에 앉으면, 당신은 짝사랑 상대 😍 와 그 "작업" ⏯ 🤓 에 번갈아가며 🔀 집중합니다. 그러면 당신은 다시 짝사랑 상대 😍 에게 작업을 거는 매우 "생산적인" 일 🤓 을 합니다. + +점원 💁 이 카운터 화면에 당신의 번호를 표시함으로써 "버거 🍔 가 준비되었습니다"라고 해도, 당신은 즉시 뛰쳐나가지는 않을 것입니다. 당신은 당신의 번호를 갖고있고, 다른 사람들은 그들의 번호를 갖고있기 때문에, 아무도 당신의 버거 🍔 를 훔쳐가지 않는다는 사실을 알기 때문입니다. + +그래서 당신은 짝사랑 상대 😍 가 이야기를 끝낼 때까지 기다린 후 (현재 작업 완료 ⏯ / 진행 중인 작업 처리 🤓 ), 정중하게 미소짓고 버거를 가지러 가겠다고 말합니다 ⏸. + +그다음 당신은 카운터에 가서 🔀 , 초기 작업을 이제 완료하고 ⏯ , 버거 🍔 를 받고, 감사하다고 말하고 테이블로 가져옵니다. 이로써 카운터와의 상호작용 단계 / 작업이 종료됩니다 ⏹. + +이전 작업인 "버거 받기"가 종료되면 ⏹ "버거 먹기"라는 새로운 작업이 생성됩니다 🔀 ⏯. + +### 병렬 버거 + +이제 "동시 버거"가 아닌 "병렬 버거"를 상상해보십시오. + +당신은 짝사랑 상대 😍 와 함께 병렬 패스트푸드 🍔 를 먹으러 갔습니다. + +당신은 여러명(8명이라고 가정합니다)의 점원이 당신 앞 사람들의 주문을 받으며 동시에 요리 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 도 하는 동안 줄을 서서 기다립니다. + +당신 앞 모든 사람들이 버거가 준비될 때까지 카운터에서 떠나지 않고 기다립니다 🕙 . 왜냐하면 8명의 직원들이 다음 주문을 받기 전에 버거를 준비하러 가기 때문입니다. + +마침내 당신의 차례가 왔고, 당신은 당신과 짝사랑 상대 😍 를 위한 두 개의 고급스러운 버거 🍔 를 주문합니다. + +당신이 비용을 지불합니다 💸 . + +점원이 주방에 갑니다 👨‍🍳 . + +당신은 번호표가 없기 때문에 누구도 당신의 버거 🍔 를 대신 가져갈 수 없도록 카운터에 서서 기다립니다 🕙 . + +당신과 짝사랑 상대 😍 는 다른 사람이 새치기해서 버거를 가져가지 못하게 하느라 바쁘기 때문에 🕙 , 짝사랑 상대에게 주의를 기울일 수 없습니다 😞 . + +이것은 "동기" 작업이고, 당신은 점원/요리사 👨‍🍳 와 "동기화" 되었습니다. 당신은 기다리고 🕙 , 점원/요리사 👨‍🍳 가 버거 🍔 준비를 완료한 후 당신에게 주거나, 누군가가 그것을 가져가는 그 순간에 그 곳에 있어야합니다. + +카운터 앞에서 오랫동안 기다린 후에 🕙 , 점원/요리사 👨‍🍳 가 당신의 버거 🍔 를 가지고 돌아옵니다. + +당신은 버거를 받고 짝사랑 상대와 함께 테이블로 돌아옵니다. + +단지 먹기만 하다가, 다 먹었습니다 🍔 ⏹. + +카운터 앞에서 기다리면서 🕙 너무 많은 시간을 허비했기 때문에 대화를 하거나 작업을 걸 시간이 거의 없었습니다 😞 . + +--- + +이 병렬 버거 시나리오에서, 당신은 기다리고 🕙 , 오랜 시간동안 "카운터에서 기다리는" 🕙 데에 주의를 기울이는 ⏯ 두 개의 프로세서(당신과 짝사랑 상대😍)를 가진 컴퓨터 / 프로그램 🤖 입니다. + +패스트푸드점에는 8개의 프로세서(점원/요리사) 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 가 있습니다. 동시 버거는 단 두 개(한 명의 직원과 한 명의 요리사) 💁 👨‍🍳 만을 가지고 있었습니다. + +하지만 여전히, 병렬 버거 예시가 최선은 아닙니다 😞 . + +--- + +이 예시는 버거🍔 이야기와 결이 같습니다. + +더 "현실적인" 예시로, 은행을 상상해보십시오. + +최근까지, 대다수의 은행에는 다수의 은행원들 👨‍💼👨‍💼👨‍💼👨‍💼 과 긴 줄 🕙🕙🕙🕙🕙🕙🕙🕙 이 있습니다. + +모든 은행원들은 한 명 한 명의 고객들을 차례로 상대합니다 👨‍💼⏯ . + +그리고 당신은 오랫동안 줄에서 기다려야하고 🕙 , 그렇지 않으면 당신의 차례를 잃게 됩니다. + +아마 당신은 은행 🏦 심부름에 짝사랑 상대 😍 를 데려가고 싶지는 않을 것입니다. + +### 버거 예시의 결론 + +"짝사랑 상대와의 패스트푸드점 버거" 시나리오에서, 오랜 기다림 🕙 이 있기 때문에 동시 시스템 ⏸🔀⏯ 을 사용하는 것이 더 합리적입니다. + +대다수의 웹 응용프로그램의 경우가 그러합니다. + +매우 많은 수의 유저가 있지만, 서버는 그들의 요청을 전송하기 위해 그닥-좋지-않은 연결을 기다려야 합니다 🕙 . + +그리고 응답이 돌아올 때까지 다시 기다려야 합니다 🕙 . + +이 "기다림" 🕙 은 마이크로초 단위이지만, 모두 더해지면, 결국에는 매우 긴 대기시간이 됩니다. + +따라서 웹 API를 위해 비동기 ⏸🔀⏯ 코드를 사용하는 것이 합리적입니다. + +대부분의 존재하는 유명한 파이썬 프레임워크 (Flask와 Django 등)은 새로운 비동기 기능들이 파이썬에 존재하기 전에 만들어졌습니다. 그래서, 그들의 배포 방식은 병렬 실행과 새로운 기능만큼 강력하지는 않은 예전 버전의 비동기 실행을 지원합니다. + +비동기 웹 파이썬(ASGI)에 대한 주요 명세가 웹소켓을 지원하기 위해 Django에서 개발 되었음에도 그렇습니다. + +이러한 종류의 비동기성은 (NodeJS는 병렬적이지 않음에도) NodeJS가 사랑받는 이유이고, 프로그래밍 언어로서의 Go의 강점입니다. + +그리고 **FastAPI**를 사용함으로써 동일한 성능을 낼 수 있습니다. + +또한 병렬성과 비동기성을 동시에 사용할 수 있기 때문에, 대부분의 테스트가 완료된 NodeJS 프레임워크보다 더 높은 성능을 얻고 C에 더 가까운 컴파일 언어인 Go와 동등한 성능을 얻을 수 있습니다(모두 Starlette 덕분입니다). + +### 동시성이 병렬성보다 더 나은가? + +그렇지 않습니다! 그것이 이야기의 교훈은 아닙니다. + +동시성은 병렬성과 다릅니다. 그리고 그것은 많은 대기를 필요로하는 **특정한** 시나리오에서는 더 낫습니다. 이로 인해, 웹 응용프로그램 개발에서 동시성이 병렬성보다 일반적으로 훨씬 낫습니다. 하지만 모든 경우에 그런 것은 아닙니다. + +따라서, 균형을 맞추기 위해, 다음의 짧은 이야기를 상상해보십시오: + +> 당신은 크고, 더러운 집을 청소해야합니다. + +*네, 이게 전부입니다*. + +--- + +어디에도 대기 🕙 는 없고, 집안 곳곳에서 해야하는 많은 작업들만 있습니다. + +버거 예시처럼 처음에는 거실, 그 다음은 부엌과 같은 식으로 순서를 정할 수도 있으나, 무엇도 기다리지 🕙 않고 계속해서 청소 작업만 수행하기 때문에, 순서는 아무런 영향을 미치지 않습니다. + +순서가 있든 없든 동일한 시간이 소요될 것이고(동시성) 동일한 양의 작업을 하게 될 것입니다. + +하지만 이 경우에서, 8명의 전(前)-점원/요리사이면서-현(現)-청소부 👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳👩‍🍳👨‍🍳 를 고용할 수 있고, 그들 각자(그리고 당신)가 집의 한 부분씩 맡아 청소를 한다면, 당신은 **병렬적**으로 작업을 수행할 수 있고, 조금의 도움이 있다면, 훨씬 더 빨리 끝낼 수 있습니다. + +이 시나리오에서, (당신을 포함한) 각각의 청소부들은 프로세서가 될 것이고, 각자의 역할을 수행합니다. + +실행 시간의 대부분이 대기가 아닌 실제 작업에 소요되고, 컴퓨터에서 작업은 CPU에서 이루어지므로, 이러한 문제를 "CPU에 묶였"다고 합니다. + +--- + +CPU에 묶인 연산에 관한 흔한 예시는 복잡한 수학 처리를 필요로 하는 경우입니다. + +예를 들어: + +* **오디오** 또는 **이미지** 처리. +* **컴퓨터 비전**: 하나의 이미지는 수백개의 픽셀로 구성되어있고, 각 픽셀은 3개의 값 / 색을 갖고 있으며, 일반적으로 해당 픽셀들에 대해 동시에 무언가를 계산해야하는 처리. +* **머신러닝**: 일반적으로 많은 "행렬"과 "벡터" 곱셈이 필요합니다. 거대한 스프레드 시트에 수들이 있고 그 수들을 동시에 곱해야 한다고 생각해보십시오. +* **딥러닝**: 머신러닝의 하위영역으로, 동일한 예시가 적용됩니다. 단지 이 경우에는 하나의 스프레드 시트에 곱해야할 수들이 있는 것이 아니라, 거대한 세트의 스프레드 시트들이 있고, 많은 경우에, 이 모델들을 만들고 사용하기 위해 특수한 프로세서를 사용합니다. + +### 동시성 + 병렬성: 웹 + 머신러닝 + +**FastAPI**를 사용하면 웹 개발에서는 매우 흔한 동시성의 이점을 (NodeJS의 주된 매력만큼) 얻을 수 있습니다. + +뿐만 아니라 머신러닝 시스템과 같이 **CPU에 묶인** 작업을 위해 병렬성과 멀티프로세싱(다수의 프로세스를 병렬적으로 동작시키는 것)을 이용하는 것도 가능합니다. + +파이썬이 **데이터 사이언스**, 머신러닝과 특히 딥러닝에 의 주된 언어라는 간단한 사실에 더해서, 이것은 FastAPI를 데이터 사이언스 / 머신러닝 웹 API와 응용프로그램에 (다른 것들보다) 좋은 선택지가 되게 합니다. + +배포시 병렬을 어떻게 가능하게 하는지 알고싶다면, [배포](/ko/deployment){.internal-link target=_blank}문서를 참고하십시오. + +## `async`와 `await` + +최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이는 이것을 평범한 "순차적" 코드로 보이게 하고, 적절한 순간에 당신을 위해 "대기"합니다. + +연산이 결과를 전달하기 전에 대기를 해야하고 새로운 파이썬 기능들을 지원한다면, 이렇게 코드를 작성할 수 있습니다: + +```Python +burgers = await get_burgers(2) +``` + +여기서 핵심은 `await`입니다. 이것은 파이썬에게 `burgers` 결과를 저장하기 이전에 `get_burgers(2)`의 작업이 완료되기를 🕙 기다리라고 ⏸ 말합니다. 이로 인해, 파이썬은 그동안 (다른 요청을 받는 것과 같은) 다른 작업을 수행해도 된다는 것을 🔀 ⏯ 알게될 것입니다. + +`await`가 동작하기 위해, 이것은 비동기를 지원하는 함수 내부에 있어야 합니다. 이를 위해서 함수를 `async def`를 사용해 정의하기만 하면 됩니다: + +```Python hl_lines="1" +async def get_burgers(number: int): + # Do some asynchronous stuff to create the burgers + return burgers +``` + +...`def`를 사용하는 대신: + +```Python hl_lines="2" +# This is not asynchronous +def get_sequential_burgers(number: int): + # Do some sequential stuff to create the burgers + return burgers +``` + +`async def`를 사용하면, 파이썬은 해당 함수 내에서 `await` 표현에 주의해야한다는 사실과, 해당 함수의 실행을 "일시정지"⏸하고 다시 돌아오기 전까지 다른 작업을 수행🔀할 수 있다는 것을 알게됩니다. + +`async def`f 함수를 호출하고자 할 때, "대기"해야합니다. 따라서, 아래는 동작하지 않습니다. + +```Python +# This won't work, because get_burgers was defined with: async def +burgers = get_burgers(2) +``` + +--- + +따라서, `await`f를 사용해서 호출할 수 있는 라이브러리를 사용한다면, 다음과 같이 `async def`를 사용하는 *경로 작동 함수*를 생성해야 합니다: + +```Python hl_lines="2-3" +@app.get('/burgers') +async def read_burgers(): + burgers = await get_burgers(2) + return burgers +``` + +### 더 세부적인 기술적 사항 + +`await`가 `async def`를 사용하는 함수 내부에서만 사용이 가능하다는 것을 눈치채셨을 것입니다. + +하지만 동시에, `async def`로 정의된 함수들은 "대기"되어야만 합니다. 따라서, `async def`를 사용한 함수들은 역시 `async def`를 사용한 함수 내부에서만 호출될 수 있습니다. + +그렇다면 닭이 먼저냐, 달걀이 먼저냐, 첫 `async` 함수를 어떻게 호출할 수 있겠습니까? + +**FastAPI**를 사용해 작업한다면 이것을 걱정하지 않아도 됩니다. 왜냐하면 그 "첫" 함수는 당신의 *경로 작동 함수*가 될 것이고, FastAPI는 어떻게 올바르게 처리할지 알고있기 때문입니다. + +하지만 FastAPI를 사용하지 않고 `async` / `await`를 사용하고 싶다면, 이 역시 가능합니다. + +### 당신만의 비동기 코드 작성하기 + +Starlette(그리고 FastAPI)는 AnyIO를 기반으로 하고있고, 따라서 파이썬 표준 라이브러리인 asyncioTrio와 호환됩니다. + +특히, 코드에서 고급 패턴이 필요한 고급 동시성을 사용하는 경우 직접적으로 AnyIO를 사용할 수 있습니다. + +FastAPI를 사용하지 않더라도, 높은 호환성 및 AnyIO의 이점(예: *구조화된 동시성*)을 취하기 위해 AnyIO를 사용해 비동기 응용프로그램을 작성할 수 있습니다. + +### 비동기 코드의 다른 형태 + +파이썬에서 `async`와 `await`를 사용하게 된 것은 비교적 최근의 일입니다. + +하지만 이로 인해 비동기 코드 작업이 훨씬 간단해졌습니다. + +같은 (또는 거의 유사한) 문법은 최신 버전의 자바스크립트(브라우저와 NodeJS)에도 추가되었습니다. + +하지만 그 이전에, 비동기 코드를 처리하는 것은 꽤 복잡하고 어려운 일이었습니다. + +파이썬의 예전 버전이라면, 스레드 또는 Gevent를 사용할 수 있을 것입니다. 하지만 코드를 이해하고, 디버깅하고, 이에 대해 생각하는게 훨씬 복잡합니다. + +예전 버전의 NodeJS / 브라우저 자바스크립트라면, "콜백 함수"를 사용했을 것입니다. 그리고 이로 인해 콜백 지옥에 빠지게 될 수 있습니다. + +## 코루틴 + +**코루틴**은 `async def` 함수가 반환하는 것을 칭하는 매우 고급스러운 용어일 뿐입니다. 파이썬은 그것이 시작되고 어느 시점에서 완료되지만 내부에 `await`가 있을 때마다 내부적으로 일시정지⏸될 수도 있는 함수와 유사한 것이라는 사실을 알고있습니다. + +그러나 `async` 및 `await`와 함께 비동기 코드를 사용하는 이 모든 기능들은 "코루틴"으로 간단히 요약됩니다. 이것은 Go의 주된 핵심 기능인 "고루틴"에 견줄 수 있습니다. + +## 결론 + +상기 문장을 다시 한 번 봅시다: + +> 최신 파이썬 버전은 **`async` 및 `await`** 문법과 함께 **“코루틴”**이라고 하는 것을 사용하는 **“비동기 코드”**를 지원합니다. + +이제 이 말을 조금 더 이해할 수 있을 것입니다. ✨ + +이것이 (Starlette을 통해) FastAPI를 강하게 하면서 그것이 인상적인 성능을 낼 수 있게 합니다. + +## 매우 세부적인 기술적 사항 + +!!! warning "경고" + 이 부분은 넘어가도 됩니다. + + 이것들은 **FastAPI**가 내부적으로 어떻게 동작하는지에 대한 매우 세부적인 기술사항입니다. + + 만약 기술적 지식(코루틴, 스레드, 블록킹 등)이 있고 FastAPI가 어떻게 `async def` vs `def`를 다루는지 궁금하다면, 계속하십시오. + +### 경로 작동 함수 + +경로 작동 함수를 `async def` 대신 일반적인 `def`로 선언하는 경우, (서버를 차단하는 것처럼) 그것을 직접 호출하는 대신 대기중인 외부 스레드풀에서 실행됩니다. + +만약 상기에 묘사된대로 동작하지 않는 비동기 프로그램을 사용해왔고 약간의 성능 향상 (약 100 나노초)을 위해 `def`를 사용해서 계산만을 위한 사소한 *경로 작동 함수*를 정의해왔다면, **FastAPI**는 이와는 반대라는 것에 주의하십시오. 이러한 경우에, *경로 작동 함수*가 블로킹 I/O를 수행하는 코드를 사용하지 않는 한 `async def`를 사용하는 편이 더 낫습니다. + +하지만 두 경우 모두, FastAPI가 당신이 전에 사용하던 프레임워크보다 [더 빠를](/#performance){.internal-link target=_blank} (최소한 비견될) 확률이 높습니다. + +### 의존성 + +의존성에도 동일하게 적용됩니다. 의존성이 `async def`가 아닌 표준 `def` 함수라면, 외부 스레드풀에서 실행됩니다. + +### 하위-의존성 + +함수 정의시 매개변수로 서로를 필요로하는 다수의 의존성과 하위-의존성을 가질 수 있고, 그 중 일부는 `async def`로, 다른 일부는 일반적인 `def`로 생성되었을 수 있습니다. 이것은 여전히 잘 동작하고, 일반적인 `def`로 생성된 것들은 "대기"되는 대신에 (스레드풀로부터) 외부 스레드에서 호출됩니다. + +### 다른 유틸리티 함수 + +직접 호출되는 다른 모든 유틸리티 함수는 일반적인 `def`나 `async def`로 생성될 수 있고 FastAPI는 이를 호출하는 방식에 영향을 미치지 않습니다. + +이것은 FastAPI가 당신을 위해 호출하는 함수와는 반대입니다: *경로 작동 함수*와 의존성 + +만약 당신의 유틸리티 함수가 `def`를 사용한 일반적인 함수라면, 스레드풀에서가 아니라 직접 호출(당신이 코드에 작성한 대로)될 것이고, `async def`로 생성된 함수라면 코드에서 호출할 때 그 함수를 `await` 해야 합니다. + +--- + +다시 말하지만, 이것은 당신이 이것에 대해 찾고있던 경우에 한해 유용할 매우 세부적인 기술사항입니다. + +그렇지 않은 경우, 상기의 가이드라인만으로도 충분할 것입니다: [바쁘신 경우](#in-a-hurry). From 77cfb3c822c6e58b68a8694f4b01bb1bb9c87255 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 18:59:56 +0000 Subject: [PATCH 021/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index f6097bedb..ce28cb029 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Chinese translation for `docs/zh/docs/tutorial/background-tasks.md`. PR [#9812](https://github.com/tiangolo/fastapi/pull/9812) by [@wdh99](https://github.com/wdh99). * 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). * 🌐 Add French translation for `docs/fr/docs/tutorial/query-params-str-validations.md`. PR [#4075](https://github.com/tiangolo/fastapi/pull/4075) by [@Smlep](https://github.com/Smlep). * 🌐 Add French translation for `docs/fr/docs/tutorial/index.md`. PR [#2234](https://github.com/tiangolo/fastapi/pull/2234) by [@JulianMaurin](https://github.com/JulianMaurin). From 1d088eaf18b096b0ded50c80d33aa5e603add49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Kh=E1=BA=AFc=20Th=C3=A0nh?= Date: Fri, 28 Jul 2023 02:01:57 +0700 Subject: [PATCH 022/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Vietnamese=20trans?= =?UTF-8?q?lation=20for=20`docs/vi/docs/features.md`=20and=20`docs/vi/docs?= =?UTF-8?q?/index.md`=20(#3006)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nguyen Khac Thanh Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez --- docs/vi/docs/features.md | 197 ++++++++++++++++ docs/vi/docs/index.md | 476 +++++++++++++++++++++++++++++++++++++++ docs/vi/mkdocs.yml | 1 + 3 files changed, 674 insertions(+) create mode 100644 docs/vi/docs/features.md create mode 100644 docs/vi/docs/index.md create mode 100644 docs/vi/mkdocs.yml diff --git a/docs/vi/docs/features.md b/docs/vi/docs/features.md new file mode 100644 index 000000000..0599530e8 --- /dev/null +++ b/docs/vi/docs/features.md @@ -0,0 +1,197 @@ +# Tính năng + +## Tính năng của FastAPI + +**FastAPI** cho bạn những tính năng sau: + +### Dựa trên những tiêu chuẩn mở + +* OpenAPI cho việc tạo API, bao gồm những khai báo về đường dẫn các toán tử, tham số, body requests, cơ chế bảo mật, etc. +* Tự động tài liệu hóa data model theo JSON Schema (OpenAPI bản thân nó được dựa trên JSON Schema). +* Được thiết kế xung quanh các tiêu chuẩn này sau khi nghiên cứu tỉ mỉ thay vì chỉ suy nghĩ đơn giản và sơ xài. +* Điều này cho phép tự động hóa **trình sinh code client** cho nhiều ngôn ngữ lập trình khác nhau. + +### Tự động hóa tài liệu + + +Tài liệu tương tác API và web giao diện người dùng. Là một framework được dựa trên OpenAPI do đó có nhiều tùy chọn giao diện cho tài liệu API, 2 giao diện bên dưới là mặc định. + +* Swagger UI, với giao diện khám phá, gọi và kiểm thử API trực tiếp từ trình duyệt. + +![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) + +* Thay thế với tài liệu API với ReDoc. + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) + +### Chỉ cần phiên bản Python hiện đại + +Tất cả được dựa trên khai báo kiểu dữ liệu chuẩn của **Python 3.6** (cảm ơn Pydantic). Bạn không cần học cú pháp mới, chỉ cần biết chuẩn Python hiện đại. + +Nếu bạn cần 2 phút để làm mới lại cách sử dụng các kiểu dữ liệu mới của Python (thậm chí nếu bạn không sử dụng FastAPI), xem hướng dẫn ngắn: [Kiểu dữ liệu Python](python-types.md){.internal-link target=_blank}. + +Bạn viết chuẩn Python với kiểu dữ liệu như sau: + +```Python +from datetime import date + +from pydantic import BaseModel + +# Declare a variable as a str +# and get editor support inside the function +def main(user_id: str): + return user_id + + +# A Pydantic model +class User(BaseModel): + id: int + name: str + joined: date +``` + +Sau đó có thể được sử dụng: + +```Python +my_user: User = User(id=3, name="John Doe", joined="2018-07-19") + +second_user_data = { + "id": 4, + "name": "Mary", + "joined": "2018-11-30", +} + +my_second_user: User = User(**second_user_data) +``` + +!!! info + `**second_user_data` nghĩa là: + + Truyền các khóa và giá trị của dict `second_user_data` trực tiếp như các tham số kiểu key-value, tương đương với: `User(id=4, name="Mary", joined="2018-11-30")` + +### Được hỗ trợ từ các trình soạn thảo + + +Toàn bộ framework được thiết kế để sử dụng dễ dàng và trực quan, toàn bộ quyết định đã được kiểm thử trên nhiều trình soạn thảo thậm chí trước khi bắt đầu quá trình phát triển, để chắc chắn trải nghiệm phát triển là tốt nhất. + +Trong lần khảo sát cuối cùng dành cho các lập trình viên Python, đã rõ ràng rằng đa số các lập trình viên sử dụng tính năng "autocompletion". + +Toàn bộ framework "FastAPI" phải đảm bảo rằng: autocompletion hoạt động ở mọi nơi. Bạn sẽ hiếm khi cần quay lại để đọc tài liệu. + +Đây là các trình soạn thảo có thể giúp bạn: + +* trong Visual Studio Code: + +![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) + +* trong PyCharm: + +![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) + +Bạn sẽ có được auto-completion trong code, thậm chí trước đó là không thể. Như trong ví dụ, khóa `price` bên trong một JSON (đó có thể được lồng nhau) đến từ một request. + +Không còn nhập sai tên khóa, quay đi quay lại giữa các tài liệu hoặc cuộn lên cuộn xuống để tìm xem cuối cùng bạn đã sử dụng `username` hay `user_name`. + +### Ngắn gọn + +FastAPI có các giá trị mặc định hợp lý cho mọi thứ, với các cấu hình tùy chọn ở mọi nơi. Tất cả các tham số có thể được tinh chỉnh để thực hiện những gì bạn cần và để định nghĩa API bạn cần. + +Nhưng mặc định, tất cả **đều hoạt động**. + +### Validation + +* Validation cho đa số (hoặc tất cả?) **các kiểu dữ liệu** Python, bao gồm: + * JSON objects (`dict`). + * Mảng JSON (`list`) định nghĩa kiểu dữ liệu từng phần tử. + * Xâu (`str`), định nghĩa độ dài lớn nhất, nhỏ nhất. + * Số (`int`, `float`) với các giá trị lớn nhất, nhỏ nhất, etc. + +* Validation cho nhiều kiểu dữ liệu bên ngoài như: + * URL. + * Email. + * UUID. + * ...và nhiều cái khác. + +Tất cả validation được xử lí bằng những thiết lập tốt và mạnh mẽ của **Pydantic**. + +### Bảo mật và xác thực + +Bảo mật và xác thực đã tích hợp mà không làm tổn hại tới cơ sở dữ liệu hoặc data models. + +Tất cả cơ chế bảo mật định nghĩa trong OpenAPI, bao gồm: + +* HTTP Basic. +* **OAuth2** (với **JWT tokens**). Xem hướng dẫn [OAuth2 with JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. +* API keys in: + * Headers. + * Các tham số trong query string. + * Cookies, etc. + +Cộng với tất cả các tính năng bảo mật từ Starlette (bao gồm **session cookies**). + +Tất cả được xây dựng dưới dạng các công cụ và thành phần có thể tái sử dụng, dễ dàng tích hợp với hệ thống, kho lưu trữ dữ liệu, cơ sở dữ liệu quan hệ và NoSQL của bạn,... + +### Dependency Injection + +FastAPI bao gồm một hệ thống Dependency Injection vô cùng dễ sử dụng nhưng vô cùng mạnh mẽ. + +* Thậm chí, các dependency có thể có các dependency khác, tạo thành một phân cấp hoặc **"một đồ thị" của các dependency**. +* Tất cả **được xử lí tự động** bởi framework. +* Tất cả các dependency có thể yêu cầu dữ liệu từ request và **tăng cường các ràng buộc từ đường dẫn** và tự động tài liệu hóa. +* **Tự động hóa validation**, thậm chí với các tham số *đường dẫn* định nghĩa trong các dependency. +* Hỗ trợ hệ thống xác thực người dùng phức tạp, **các kết nối cơ sở dữ liệu**,... +* **Không làm tổn hại** cơ sở dữ liệu, frontends,... Nhưng dễ dàng tích hợp với tất cả chúng. + +### Không giới hạn "plug-ins" + +Hoặc theo một cách nào khác, không cần chúng, import và sử dụng code bạn cần. + +Bất kì tích hợp nào được thiết kế để sử dụng đơn giản (với các dependency), đến nỗi bạn có thể tạo một "plug-in" cho ứng dụng của mình trong 2 dòng code bằng cách sử dụng cùng một cấu trúc và cú pháp được sử dụng cho *path operations* của bạn. + +### Đã được kiểm thử + +* 100% test coverage. +* 100% type annotated code base. +* Được sử dụng cho các ứng dụng sản phẩm. + +## Tính năng của Starlette + +`FastAPI` is thực sự là một sub-class của `Starlette`. Do đó, nếu bạn đã biết hoặc đã sử dụng Starlette, đa số các chức năng sẽ làm việc giống như vậy. + +Với **FastAPI**, bạn có được tất cả những tính năng của **Starlette**: + +* Hiệu năng thực sự ấn tượng. Nó là một trong nhưng framework Python nhanh nhất, khi so sánh với **NodeJS** và **Go**. +* Hỗ trợ **WebSocket**. +* In-process background tasks. +* Startup and shutdown events. +* Client cho kiểm thử xây dựng trên HTTPX. +* **CORS**, GZip, Static Files, Streaming responses. +* Hỗ trợ **Session and Cookie**. +* 100% test coverage. +* 100% type annotated codebase. + +## Tính năng của Pydantic + +**FastAPI** tương thích đầy đủ với (và dựa trên) Pydantic. Do đó, bất kì code Pydantic nào bạn thêm vào cũng sẽ hoạt động. + +Bao gồm các thư viện bên ngoài cũng dựa trên Pydantic, như ORMs, ODMs cho cơ sở dữ liệu. + +Nó cũng có nghĩa là trong nhiều trường hợp, bạn có thể truyền cùng object bạn có từ một request **trực tiếp cho cơ sở dữ liệu**, vì mọi thứ được validate tự động. + +Điều tương tự áp dụng cho các cách khác nhau, trong nhiều trường hợp, bạn có thể chỉ truyền object từ cơ sở dữ liêu **trực tiếp tới client**. + +Với **FastAPI**, bạn có tất cả những tính năng của **Pydantic** (FastAPI dựa trên Pydantic cho tất cả những xử lí về dữ liệu): + +* **Không gây rối não**: + * Không cần học ngôn ngữ mô tả cấu trúc mới. + * Nếu bạn biết kiểu dữ liệu Python, bạn biết cách sử dụng Pydantic. +* Sử dụng tốt với **IDE/linter/não của bạn**: + + * Bởi vì các cấu trúc dữ liệu của Pydantic chỉ là các instances của class bạn định nghĩa; auto-completion, linting, mypy và trực giác của bạn nên làm việc riêng biệt với những dữ liệu mà bạn đã validate. +* Validate **các cấu trúc phức tạp**: + * Sử dụng các models Pydantic phân tầng, `List` và `Dict` của Python `typing`,... + * Và các validators cho phép các cấu trúc dữ liệu phức tạp trở nên rõ ràng và dễ dàng để định nghĩa, kiểm tra và tài liệu hóa thành JSON Schema. + * Bạn có thể có các object **JSON lồng nhau** và tất cả chúng đã validate và annotated. +* **Có khả năng mở rộng**: + * Pydantic cho phép bạn tùy chỉnh kiểu dữ liệu bằng việc định nghĩa hoặc bạn có thể mở rộng validation với các decorator trong model. +* 100% test coverage. diff --git a/docs/vi/docs/index.md b/docs/vi/docs/index.md new file mode 100644 index 000000000..ba5d68161 --- /dev/null +++ b/docs/vi/docs/index.md @@ -0,0 +1,476 @@ + +{!../../../docs/missing-translation.md!} + + +

+ FastAPI +

+

+ FastAPI framework, hiệu năng cao, dễ học, dễ code, sẵn sàng để tạo ra sản phẩm +

+

+ + Test + + + Coverage + + + Package version + + + Supported Python versions + +

+ +--- + +**Tài liệu**: https://fastapi.tiangolo.com + +**Mã nguồn**: https://github.com/tiangolo/fastapi + +--- + +FastAPI là một web framework hiện đại, hiệu năng cao để xây dựng web APIs với Python 3.7+ dựa trên tiêu chuẩn Python type hints. + +Những tính năng như: + +* **Nhanh**: Hiệu năng rất cao khi so sánh với **NodeJS** và **Go** (cảm ơn Starlette và Pydantic). [Một trong những Python framework nhanh nhất](#performance). +* **Code nhanh**: Tăng tốc độ phát triển tính năng từ 200% tới 300%. * +* **Ít lỗi hơn**: Giảm khoảng 40% những lỗi phát sinh bởi con người (nhà phát triển). * +* **Trực giác tốt hơn**: Được các trình soạn thảo hỗ tuyệt vời. Completion mọi nơi. Ít thời gian gỡ lỗi. +* **Dễ dàng**: Được thiết kế để dễ dàng học và sử dụng. Ít thời gian đọc tài liệu. +* **Ngắn**: Tối thiểu code bị trùng lặp. Nhiều tính năng được tích hợp khi định nghĩa tham số. Ít lỗi hơn. +* **Tăng tốc**: Có được sản phẩm cùng với tài liệu (được tự động tạo) có thể tương tác. +* **Được dựa trên các tiêu chuẩn**: Dựa trên (và hoàn toàn tương thích với) các tiêu chuẩn mở cho APIs : OpenAPI (trước đó được biết đến là Swagger) và JSON Schema. + +* ước tính được dựa trên những kiểm chứng trong nhóm phát triển nội bộ, xây dựng các ứng dụng sản phẩm. + +## Nhà tài trợ + + + +{% if sponsors %} +{% for sponsor in sponsors.gold -%} + +{% endfor -%} +{%- for sponsor in sponsors.silver -%} + +{% endfor %} +{% endif %} + + + +Những nhà tài trợ khác + +## Ý kiến đánh giá + +"_[...] Tôi đang sử dụng **FastAPI** vô cùng nhiều vào những ngày này. [...] Tôi thực sự đang lên kế hoạch sử dụng nó cho tất cả các nhóm **dịch vụ ML tại Microsoft**. Một vài trong số đó đang tích hợp vào sản phẩm lõi của **Window** và một vài sản phẩm cho **Office**._" + +
Kabir Khan - Microsoft (ref)
+ +--- + +"_Chúng tôi tích hợp thư viện **FastAPI** để sinh ra một **REST** server, nó có thể được truy vấn để thu được những **dự đoán**._ [bởi Ludwid] " + +
Piero Molino, Yaroslav Dudin, và Sai Sumanth Miryala - Uber (ref)
+ +--- + +"_**Netflix** vui mừng thông báo việc phát hành framework mã nguồn mở của chúng tôi cho *quản lí khủng hoảng* tập trung: **Dispatch**! [xây dựng với **FastAPI**]_" + +
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
+ +--- + +"_Tôi vô cùng hào hứng về **FastAPI**. Nó rất thú vị_" + +
Brian Okken - Python Bytes podcast host (ref)
+ +--- + +"_Thành thật, những gì bạn đã xây dựng nhìn siêu chắc chắn và bóng bẩy. Theo nhiều cách, nó là những gì tôi đã muốn Hug trở thành - thật sự truyền cảm hứng để thấy ai đó xây dựng nó._" + +
Timothy Crosley - người tạo ra Hug (ref)
+ +--- + +"_Nếu bạn đang tìm kiếm một **framework hiện đại** để xây dựng một REST APIs, thử xem xét **FastAPI** [...] Nó nhanh, dễ dùng và dễ học [...]_" + +"_Chúng tôi đã chuyển qua **FastAPI cho **APIs** của chúng tôi [...] Tôi nghĩ bạn sẽ thích nó [...]_" + +
Ines Montani - Matthew Honnibal - Explosion AI founders - spaCy creators (ref) - (ref)
+
Ines Montani - Matthew Honnibal - nhà sáng lập Explosion AI - người tạo ra spaCy (ref) - (ref)
+ +--- + +"_Nếu ai đó đang tìm cách xây dựng sản phẩm API bằng Python, tôi sẽ đề xuất **FastAPI**. Nó **được thiết kế đẹp đẽ**, **sử dụng đơn giản** và **có khả năng mở rộng cao**, nó đã trở thành một **thành phần quan trọng** trong chiến lược phát triển API của chúng tôi và đang thúc đẩy nhiều dịch vụ và mảng tự động hóa như Kỹ sư TAC ảo của chúng tôi._" + +
Deon Pillsbury - Cisco (ref)
+ +--- + +## **Typer**, giao diện dòng lệnh của FastAPI + + + +Nếu bạn đang xây dựng một CLI - ứng dụng được sử dụng trong giao diện dòng lệnh, xem về **Typer**. + +**Typer** là một người anh em của FastAPI. Và nó được dự định trở thành **giao diện dòng lệnh cho FastAPI**. ⌨️ 🚀 + +## Yêu cầu + +Python 3.7+ + +FastAPI đứng trên vai những người khổng lồ: + +* Starlette cho phần web. +* Pydantic cho phần data. + +## Cài đặt + +
+ +```console +$ pip install fastapi + +---> 100% +``` + +
+ +Bạn cũng sẽ cần một ASGI server cho production như Uvicorn hoặc Hypercorn. + +
+ +```console +$ pip install "uvicorn[standard]" + +---> 100% +``` + +
+ +## Ví dụ + +### Khởi tạo + +* Tạo một tệp tin `main.py` như sau: + +```Python +from typing import Union + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} +``` + +
+Hoặc sử dụng async def... + +Nếu code của bạn sử dụng `async` / `await`, hãy sử dụng `async def`: + +```Python hl_lines="9 14" +from typing import Union + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +async def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +async def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} +``` + +**Lưu ý**: + +Nếu bạn không biết, xem phần _"In a hurry?"_ về `async` và `await` trong tài liệu này. + +
+ +### Chạy ứng dụng + +Chạy server như sau: + +
+ +```console +$ uvicorn main:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +INFO: Started reloader process [28720] +INFO: Started server process [28722] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +
+ +
+Về lệnh uvicorn main:app --reload... + +Lệnh `uvicorn main:app` tham chiếu tới những thành phần sau: + +* `main`: tệp tin `main.py` (một Python "module"). +* `app`: object được tạo trong tệp tin `main.py` tại dòng `app = FastAPI()`. +* `--reload`: chạy lại server sau khi code thay đổi. Chỉ sử dụng trong quá trình phát triển. + +
+ +### Kiểm tra + +Mở trình duyệt của bạn tại http://127.0.0.1:8000/items/5?q=somequery. + +Bạn sẽ thấy một JSON response: + +```JSON +{"item_id": 5, "q": "somequery"} +``` + +Bạn đã sẵn sàng để tạo một API như sau: + +* Nhận HTTP request với _đường dẫn_ `/` và `/items/{item_id}`. +* Cả hai _đường dẫn_ sử dụng toán tử `GET` (cũng đươc biết đến là _phương thức_ HTTP). +* _Đường dẫn_ `/items/{item_id}` có một _tham số đường dẫn_ `item_id`, nó là một tham số kiểu `int`. +* _Đường dẫn_ `/items/{item_id}` có một _tham số query string_ `q`, nó là một tham số tùy chọn kiểu `str`. + +### Tài liệu tương tác API + +Truy cập http://127.0.0.1:8000/docs. + +Bạn sẽ thấy tài liệu tương tác API được tạo tự động (cung cấp bởi Swagger UI): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +### Tài liệu API thay thế + +Và bây giờ, hãy truy cập tới http://127.0.0.1:8000/redoc. + +Bạn sẽ thấy tài liệu được thay thế (cung cấp bởi ReDoc): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +## Nâng cấp ví dụ + +Bây giờ sửa tệp tin `main.py` để nhận body từ một request `PUT`. + +Định nghĩa của body sử dụng kiểu dữ liệu chuẩn của Python, cảm ơn Pydantic. + +```Python hl_lines="4 9-12 25-27" +from typing import Union + +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + price: float + is_offer: Union[bool, None] = None + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} + + +@app.put("/items/{item_id}") +def update_item(item_id: int, item: Item): + return {"item_name": item.name, "item_id": item_id} +``` + +Server nên tự động chạy lại (bởi vì bạn đã thêm `--reload` trong lệnh `uvicorn` ở trên). + +### Nâng cấp tài liệu API + +Bây giờ truy cập tới http://127.0.0.1:8000/docs. + +* Tài liệu API sẽ được tự động cập nhật, bao gồm body mới: + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) + +* Click vào nút "Try it out", nó cho phép bạn điền những tham số và tương tác trực tiếp với API: + +![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png) + +* Sau khi click vào nút "Execute", giao diện người dùng sẽ giao tiếp với API của bạn bao gồm: gửi các tham số, lấy kết quả và hiển thị chúng trên màn hình: + +![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png) + +### Nâng cấp tài liệu API thay thế + +Và bây giờ truy cập tới http://127.0.0.1:8000/redoc. + +* Tài liệu thay thế cũng sẽ phản ánh tham số và body mới: + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) + +### Tóm lại + +Bạn khai báo **một lần** kiểu dữ liệu của các tham số, body, etc là các tham số của hàm số. + +Bạn định nghĩa bằng cách sử dụng các kiểu dữ liệu chuẩn của Python. + +Bạn không phải học một cú pháp mới, các phương thức và class của một thư viện cụ thể nào. + +Chỉ cần sử dụng các chuẩn của **Python 3.7+**. + +Ví dụ, với một tham số kiểu `int`: + +```Python +item_id: int +``` + +hoặc với một model `Item` phức tạp hơn: + +```Python +item: Item +``` + +...và với định nghĩa đơn giản đó, bạn có được: + +* Sự hỗ trợ từ các trình soạn thảo, bao gồm: + * Completion. + * Kiểm tra kiểu dữ liệu. +* Kiểm tra kiểu dữ liệu: + * Tự động sinh lỗi rõ ràng khi dữ liệu không hợp lệ . + * Kiểm tra JSON lồng nhau . +* Chuyển đổi dữ liệu đầu vào: tới từ network sang dữ liệu kiểu Python. Đọc từ: + * JSON. + * Các tham số trong đường dẫn. + * Các tham số trong query string. + * Cookies. + * Headers. + * Forms. + * Files. +* Chuyển đổi dữ liệu đầu ra: chuyển đổi từ kiểu dữ liệu Python sang dữ liệu network (như JSON): + * Chuyển đổi kiểu dữ liệu Python (`str`, `int`, `float`, `bool`, `list`,...). + * `datetime` objects. + * `UUID` objects. + * Database models. + * ...và nhiều hơn thế. +* Tự động tạo tài liệu tương tác API, bao gồm 2 giao diện người dùng: + * Swagger UI. + * ReDoc. + +--- + +Quay trở lại ví dụ trước, **FastAPI** sẽ thực hiện: + +* Kiểm tra xem có một `item_id` trong đường dẫn với các request `GET` và `PUT` không? +* Kiểm tra xem `item_id` có phải là kiểu `int` trong các request `GET` và `PUT` không? + * Nếu không, client sẽ thấy một lỗi rõ ràng và hữu ích. +* Kiểm tra xem nếu có một tham số `q` trong query string (ví dụ như `http://127.0.0.1:8000/items/foo?q=somequery`) cho request `GET`. + * Tham số `q` được định nghĩa `= None`, nó là tùy chọn. + * Nếu không phải `None`, nó là bắt buộc (như body trong trường hợp của `PUT`). +* Với request `PUT` tới `/items/{item_id}`, đọc body như JSON: + * Kiểm tra xem nó có một thuộc tính bắt buộc kiểu `str` là `name` không? + * Kiểm tra xem nó có một thuộc tính bắt buộc kiểu `float` là `price` không? + * Kiểm tra xem nó có một thuộc tính tùy chọn là `is_offer` không? Nếu có, nó phải có kiểu `bool`. + * Tất cả những kiểm tra này cũng được áp dụng với các JSON lồng nhau. +* Chuyển đổi tự động các JSON object đến và JSON object đi. +* Tài liệu hóa mọi thứ với OpenAPI, tài liệu đó có thể được sử dụng bởi: + + * Các hệ thống tài liệu có thể tương tác. + * Hệ thống sinh code tự động, cho nhiều ngôn ngữ lập trình. +* Cung cấp trực tiếp 2 giao diện web cho tài liệu tương tác + +--- + +Chúng tôi chỉ trình bày những thứ cơ bản bên ngoài, nhưng bạn đã hiểu cách thức hoạt động của nó. + +Thử thay đổi dòng này: + +```Python + return {"item_name": item.name, "item_id": item_id} +``` + +...từ: + +```Python + ... "item_name": item.name ... +``` + +...sang: + +```Python + ... "item_price": item.price ... +``` + +...và thấy trình soạn thảo của bạn nhận biết kiểu dữ liệu và gợi ý hoàn thiện các thuộc tính. + +![trình soạn thảo hỗ trợ](https://fastapi.tiangolo.com/img/vscode-completion.png) + +Ví dụ hoàn chỉnh bao gồm nhiều tính năng hơn, xem Tutorial - User Guide. + + +**Cảnh báo tiết lỗ**: Tutorial - User Guide: + +* Định nghĩa **tham số** từ các nguồn khác nhau như: **headers**, **cookies**, **form fields** và **files**. +* Cách thiết lập **các ràng buộc cho validation** như `maximum_length` hoặc `regex`. +* Một hệ thống **Dependency Injection vô cùng mạnh mẽ và dễ dàng sử dụng. +* Bảo mật và xác thực, hỗ trợ **OAuth2**(với **JWT tokens**) và **HTTP Basic**. +* Những kĩ thuật nâng cao hơn (nhưng tương đối dễ) để định nghĩa **JSON models lồng nhau** (cảm ơn Pydantic). +* Tích hợp **GraphQL** với Strawberry và các thư viện khác. +* Nhiều tính năng mở rộng (cảm ơn Starlette) như: + * **WebSockets** + * kiểm thử vô cùng dễ dàng dựa trên HTTPX và `pytest` + * **CORS** + * **Cookie Sessions** + * ...và nhiều hơn thế. + +## Hiệu năng + +Independent TechEmpower benchmarks cho thấy các ứng dụng **FastAPI** chạy dưới Uvicorn là một trong những Python framework nhanh nhất, chỉ đứng sau Starlette và Uvicorn (được sử dụng bên trong FastAPI). (*) + +Để hiểu rõ hơn, xem phần Benchmarks. + +## Các dependency tùy chọn + +Sử dụng bởi Pydantic: + +* ujson - "Parse" JSON nhanh hơn. +* email_validator - cho email validation. + +Sử dụng Starlette: + +* httpx - Bắt buộc nếu bạn muốn sử dụng `TestClient`. +* jinja2 - Bắt buộc nếu bạn muốn sử dụng cấu hình template engine mặc định. +* python-multipart - Bắt buộc nếu bạn muốn hỗ trợ "parsing", form với `request.form()`. +* itsdangerous - Bắt buộc để hỗ trợ `SessionMiddleware`. +* pyyaml - Bắt buộc để hỗ trợ `SchemaGenerator` cho Starlette (bạn có thể không cần nó trong FastAPI). +* ujson - Bắt buộc nếu bạn muốn sử dụng `UJSONResponse`. + +Sử dụng bởi FastAPI / Starlette: + +* uvicorn - Server để chạy ứng dụng của bạn. +* orjson - Bắt buộc nếu bạn muốn sử dụng `ORJSONResponse`. + +Bạn có thể cài đặt tất cả những dependency trên với `pip install "fastapi[all]"`. + +## Giấy phép + +Dự án này được cấp phép dưới những điều lệ của giấy phép MIT. diff --git a/docs/vi/mkdocs.yml b/docs/vi/mkdocs.yml new file mode 100644 index 000000000..de18856f4 --- /dev/null +++ b/docs/vi/mkdocs.yml @@ -0,0 +1 @@ +INHERIT: ../en/mkdocs.yml From c52c94006650dbd53f6d4bd75ef0a7459ad7e3d8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 19:04:24 +0000 Subject: [PATCH 023/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ce28cb029..b69b6d594 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Korean translation for `docs/ko/docs/async.md`. PR [#4179](https://github.com/tiangolo/fastapi/pull/4179) by [@NinaHwang](https://github.com/NinaHwang). * 🌐 Add Chinese translation for `docs/zh/docs/tutorial/background-tasks.md`. PR [#9812](https://github.com/tiangolo/fastapi/pull/9812) by [@wdh99](https://github.com/wdh99). * 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). * 🌐 Add French translation for `docs/fr/docs/tutorial/query-params-str-validations.md`. PR [#4075](https://github.com/tiangolo/fastapi/pull/4075) by [@Smlep](https://github.com/Smlep). From 643d8e41c498d7575e0e5947ca5bef99e0f2804e Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 19:08:23 +0000 Subject: [PATCH 024/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b69b6d594..537e57fc7 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Vietnamese translation for `docs/vi/docs/features.md` and `docs/vi/docs/index.md`. PR [#3006](https://github.com/tiangolo/fastapi/pull/3006) by [@magiskboy](https://github.com/magiskboy). * 🌐 Add Korean translation for `docs/ko/docs/async.md`. PR [#4179](https://github.com/tiangolo/fastapi/pull/4179) by [@NinaHwang](https://github.com/NinaHwang). * 🌐 Add Chinese translation for `docs/zh/docs/tutorial/background-tasks.md`. PR [#9812](https://github.com/tiangolo/fastapi/pull/9812) by [@wdh99](https://github.com/wdh99). * 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). From 7b3d770d65eeaef9720b5140c18f994d82e0bbbc Mon Sep 17 00:00:00 2001 From: Orest Furda <56111536+ss-o-furda@users.noreply.github.com> Date: Thu, 27 Jul 2023 22:09:34 +0300 Subject: [PATCH 025/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Ukrainian=20transl?= =?UTF-8?q?ation=20for=20`docs/uk/docs/tutorial/body.md`=20(#4574)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/uk/docs/tutorial/body.md | 213 ++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 docs/uk/docs/tutorial/body.md diff --git a/docs/uk/docs/tutorial/body.md b/docs/uk/docs/tutorial/body.md new file mode 100644 index 000000000..e78c5de0e --- /dev/null +++ b/docs/uk/docs/tutorial/body.md @@ -0,0 +1,213 @@ +# Тіло запиту + +Коли вам потрібно надіслати дані з клієнта (скажімо, браузера) до вашого API, ви надсилаєте їх як **тіло запиту**. + +Тіло **запиту** — це дані, надіслані клієнтом до вашого API. Тіло **відповіді** — це дані, які ваш API надсилає клієнту. + +Ваш API майже завжди має надсилати тіло **відповіді**. Але клієнтам не обов’язково потрібно постійно надсилати тіла **запитів**. + +Щоб оголосити тіло **запиту**, ви використовуєте Pydantic моделі з усією їх потужністю та перевагами. + +!!! info + Щоб надіслати дані, ви повинні використовувати один із: `POST` (більш поширений), `PUT`, `DELETE` або `PATCH`. + + Надсилання тіла із запитом `GET` має невизначену поведінку в специфікаціях, проте воно підтримується FastAPI лише для дуже складних/екстремальних випадків використання. + + Оскільки це не рекомендується, інтерактивна документація з Swagger UI не відображатиме документацію для тіла запиту під час використання `GET`, і проксі-сервери в середині можуть не підтримувати її. + +## Імпортуйте `BaseModel` від Pydantic + +Спочатку вам потрібно імпортувати `BaseModel` з `pydantic`: + +=== "Python 3.6 і вище" + + ```Python hl_lines="4" + {!> ../../../docs_src/body/tutorial001.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="2" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +## Створіть свою модель даних + +Потім ви оголошуєте свою модель даних як клас, який успадковується від `BaseModel`. + +Використовуйте стандартні типи Python для всіх атрибутів: + +=== "Python 3.6 і вище" + + ```Python hl_lines="7-11" + {!> ../../../docs_src/body/tutorial001.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="5-9" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +Так само, як і при оголошенні параметрів запиту, коли атрибут моделі має значення за замовчуванням, він не є обов’язковим. В іншому випадку це потрібно. Використовуйте `None`, щоб зробити його необов'язковим. + +Наприклад, ця модель вище оголошує JSON "`об'єкт`" (або Python `dict`), як: + +```JSON +{ + "name": "Foo", + "description": "An optional description", + "price": 45.2, + "tax": 3.5 +} +``` + +...оскільки `description` і `tax` є необов'язковими (зі значенням за замовчуванням `None`), цей JSON "`об'єкт`" також буде дійсним: + +```JSON +{ + "name": "Foo", + "price": 45.2 +} +``` + +## Оголоси її як параметр + +Щоб додати модель даних до вашої *операції шляху*, оголосіть її так само, як ви оголосили параметри шляху та запиту: + +=== "Python 3.6 і вище" + + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial001.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial001_py310.py!} + ``` + +...і вкажіть її тип як модель, яку ви створили, `Item`. + +## Результати + +Лише з цим оголошенням типу Python **FastAPI** буде: + +* Читати тіло запиту як JSON. +* Перетворювати відповідні типи (якщо потрібно). +* Валідувати дані. + * Якщо дані недійсні, він поверне гарну та чітку помилку, вказуючи, де саме і які дані були неправильними. +* Надавати отримані дані у параметрі `item`. + * Оскільки ви оголосили його у функції як тип `Item`, ви також матимете всю підтримку редактора (автозаповнення, тощо) для всіх атрибутів та їх типів. +* Генерувати JSON Schema визначення для вашої моделі, ви також можете використовувати їх де завгодно, якщо це має сенс для вашого проекту. +* Ці схеми будуть частиною згенерованої схеми OpenAPI і використовуватимуться автоматичною документацією інтерфейсу користувача. + +## Автоматична документація + +Схеми JSON ваших моделей будуть частиною вашої схеми, згенерованої OpenAPI, і будуть показані в інтерактивній API документації: + + + +А також використовуватимуться в API документації всередині кожної *операції шляху*, якій вони потрібні: + + + +## Підтримка редактора + +У вашому редакторі, всередині вашої функції, ви будете отримувати підказки типу та завершення скрізь (це б не сталося, якби ви отримали `dict` замість моделі Pydantic): + + + +Ви також отримуєте перевірку помилок на наявність операцій з неправильним типом: + + + +Це не випадково, весь каркас був побудований навколо цього дизайну. + +І він був ретельно перевірений на етапі проектування, перед будь-яким впровадженням, щоб переконатися, що він працюватиме з усіма редакторами. + +Були навіть деякі зміни в самому Pydantic, щоб підтримати це. + +Попередні скріншоти були зроблені у Visual Studio Code. + +Але ви отримаєте ту саму підтримку редактора у PyCharm та більшість інших редакторів Python: + + + +!!! tip + Якщо ви використовуєте PyCharm як ваш редактор, ви можете використати Pydantic PyCharm Plugin. + + Він покращує підтримку редакторів для моделей Pydantic за допомогою: + + * автозаповнення + * перевірки типу + * рефакторингу + * пошуку + * інспекції + +## Використовуйте модель + +Усередині функції ви можете отримати прямий доступ до всіх атрибутів об’єкта моделі: + +=== "Python 3.6 і вище" + + ```Python hl_lines="21" + {!> ../../../docs_src/body/tutorial002.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="19" + {!> ../../../docs_src/body/tutorial002_py310.py!} + ``` + +## Тіло запиту + параметри шляху + +Ви можете одночасно оголошувати параметри шляху та тіло запиту. + +**FastAPI** розпізнає, що параметри функції, які відповідають параметрам шляху, мають бути **взяті з шляху**, а параметри функції, які оголошуються як моделі Pydantic, **взяті з тіла запиту**. + +=== "Python 3.6 і вище" + + ```Python hl_lines="17-18" + {!> ../../../docs_src/body/tutorial003.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="15-16" + {!> ../../../docs_src/body/tutorial003_py310.py!} + ``` + +## Тіло запиту + шлях + параметри запиту + +Ви також можете оголосити параметри **тіло**, **шлях** і **запит** одночасно. + +**FastAPI** розпізнає кожен з них і візьме дані з потрібного місця. + +=== "Python 3.6 і вище" + + ```Python hl_lines="18" + {!> ../../../docs_src/body/tutorial004.py!} + ``` + +=== "Python 3.10 і вище" + + ```Python hl_lines="16" + {!> ../../../docs_src/body/tutorial004_py310.py!} + ``` + +Параметри функції будуть розпізнаватися наступним чином: + +* Якщо параметр також оголошено в **шляху**, він використовуватиметься як параметр шляху. +* Якщо параметр має **сингулярний тип** (наприклад, `int`, `float`, `str`, `bool` тощо), він буде інтерпретуватися як параметр **запиту**. +* Якщо параметр оголошується як тип **Pydantic моделі**, він інтерпретується як **тіло** запиту. + +!!! note + FastAPI буде знати, що значення "q" не є обов'язковим через значення за замовчуванням "= None". + + `Optional` у `Optional[str]` не використовується FastAPI, але дозволить вашому редактору надати вам кращу підтримку та виявляти помилки. + +## Без Pydantic + +Якщо ви не хочете використовувати моделі Pydantic, ви також можете використовувати параметри **Body**. Перегляньте документацію для [Тіло – Кілька параметрів: сингулярні значення в тілі](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}. From bec5530ac849f9c52d35d5282628333a278b6a26 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 Jul 2023 19:14:48 +0000 Subject: [PATCH 026/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 537e57fc7..42d240f28 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/body.md`. PR [#4574](https://github.com/tiangolo/fastapi/pull/4574) by [@ss-o-furda](https://github.com/ss-o-furda). * 🌐 Add Vietnamese translation for `docs/vi/docs/features.md` and `docs/vi/docs/index.md`. PR [#3006](https://github.com/tiangolo/fastapi/pull/3006) by [@magiskboy](https://github.com/magiskboy). * 🌐 Add Korean translation for `docs/ko/docs/async.md`. PR [#4179](https://github.com/tiangolo/fastapi/pull/4179) by [@NinaHwang](https://github.com/NinaHwang). * 🌐 Add Chinese translation for `docs/zh/docs/tutorial/background-tasks.md`. PR [#9812](https://github.com/tiangolo/fastapi/pull/9812) by [@wdh99](https://github.com/wdh99). From effa578b8df27b7d4afa627a4508413811fcf72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 27 Jul 2023 21:15:16 +0200 Subject: [PATCH 027/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 42d240f28..7211cc3fc 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,11 +2,20 @@ ## Latest Changes +### Fixes + +* 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). + +### Docs + +* 📝 Update links for self-hosted Swagger UI, point to v5, for OpenAPI 31.0. PR [#9834](https://github.com/tiangolo/fastapi/pull/9834) by [@tiangolo](https://github.com/tiangolo). + +### Translations + * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/body.md`. PR [#4574](https://github.com/tiangolo/fastapi/pull/4574) by [@ss-o-furda](https://github.com/ss-o-furda). * 🌐 Add Vietnamese translation for `docs/vi/docs/features.md` and `docs/vi/docs/index.md`. PR [#3006](https://github.com/tiangolo/fastapi/pull/3006) by [@magiskboy](https://github.com/magiskboy). * 🌐 Add Korean translation for `docs/ko/docs/async.md`. PR [#4179](https://github.com/tiangolo/fastapi/pull/4179) by [@NinaHwang](https://github.com/NinaHwang). * 🌐 Add Chinese translation for `docs/zh/docs/tutorial/background-tasks.md`. PR [#9812](https://github.com/tiangolo/fastapi/pull/9812) by [@wdh99](https://github.com/wdh99). -* 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). * 🌐 Add French translation for `docs/fr/docs/tutorial/query-params-str-validations.md`. PR [#4075](https://github.com/tiangolo/fastapi/pull/4075) by [@Smlep](https://github.com/Smlep). * 🌐 Add French translation for `docs/fr/docs/tutorial/index.md`. PR [#2234](https://github.com/tiangolo/fastapi/pull/2234) by [@JulianMaurin](https://github.com/JulianMaurin). * 🌐 Add French translation for `docs/fr/docs/contributing.md`. PR [#2132](https://github.com/tiangolo/fastapi/pull/2132) by [@JulianMaurin](https://github.com/JulianMaurin). @@ -14,12 +23,14 @@ * 🌐 Update Chinese translations with new source files. PR [#9738](https://github.com/tiangolo/fastapi/pull/9738) by [@mahone3297](https://github.com/mahone3297). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/request-forms.md`. PR [#9841](https://github.com/tiangolo/fastapi/pull/9841) by [@dedkot01](https://github.com/dedkot01). * 🌐 Update Chinese translation for `docs/zh/docs/tutorial/handling-errors.md`. PR [#9485](https://github.com/tiangolo/fastapi/pull/9485) by [@Creat55](https://github.com/Creat55). -* 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). + +### Internal + +* 🔧 Update sponsors, add Fern. PR [#9956](https://github.com/tiangolo/fastapi/pull/9956) by [@tiangolo](https://github.com/tiangolo). * 👷 Update FastAPI People token. PR [#9844](https://github.com/tiangolo/fastapi/pull/9844) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9775](https://github.com/tiangolo/fastapi/pull/9775) by [@tiangolo](https://github.com/tiangolo). * 👷 Update MkDocs Material token. PR [#9843](https://github.com/tiangolo/fastapi/pull/9843) by [@tiangolo](https://github.com/tiangolo). * 👷 Update token for latest changes. PR [#9842](https://github.com/tiangolo/fastapi/pull/9842) by [@tiangolo](https://github.com/tiangolo). -* 📝 Update links for self-hosted Swagger UI, point to v5, for OpenAPI 31.0. PR [#9834](https://github.com/tiangolo/fastapi/pull/9834) by [@tiangolo](https://github.com/tiangolo). ## 0.100.0 From 8d2723664801c32e6174cd87812fb92e850cfa4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 27 Jul 2023 21:16:01 +0200 Subject: [PATCH 028/317] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.10?= =?UTF-8?q?0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 3 +++ fastapi/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 7211cc3fc..b56941ec7 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,9 @@ ## Latest Changes + +## 0.100.1 + ### Fixes * 🐛 Replace `MultHostUrl` to `AnyUrl` for compatibility with older versions of Pydantic v1. PR [#9852](https://github.com/tiangolo/fastapi/pull/9852) by [@Kludex](https://github.com/Kludex). diff --git a/fastapi/__init__.py b/fastapi/__init__.py index e9c3abe01..7dfeca0d4 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.100.0" +__version__ = "0.100.1" from starlette import status as status From 076bdea6716a5fbfc84d2227066dea6bd91ffb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 28 Jul 2023 14:15:29 +0200 Subject: [PATCH 029/317] =?UTF-8?q?=F0=9F=8C=90=20Remove=20Vietnamese=20no?= =?UTF-8?q?te=20about=20missing=20translation=20(#9957)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/mkdocs.yml | 6 ++++++ docs/uk/mkdocs.yml | 1 + docs/vi/docs/index.md | 4 ---- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 docs/uk/mkdocs.yml diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 030bbe5d3..a18613185 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -59,6 +59,8 @@ nav: - pt: /pt/ - ru: /ru/ - tr: /tr/ + - uk: /uk/ + - vi: /vi/ - zh: /zh/ - features.md - fastapi-people.md @@ -236,6 +238,10 @@ extra: name: ru - русский язык - link: /tr/ name: tr - Türkçe + - link: /uk/ + name: uk + - link: /vi/ + name: vi - link: /zh/ name: zh - 汉语 extra_css: diff --git a/docs/uk/mkdocs.yml b/docs/uk/mkdocs.yml new file mode 100644 index 000000000..de18856f4 --- /dev/null +++ b/docs/uk/mkdocs.yml @@ -0,0 +1 @@ +INHERIT: ../en/mkdocs.yml diff --git a/docs/vi/docs/index.md b/docs/vi/docs/index.md index ba5d68161..0e773a011 100644 --- a/docs/vi/docs/index.md +++ b/docs/vi/docs/index.md @@ -1,7 +1,3 @@ - -{!../../../docs/missing-translation.md!} - -

FastAPI

From cd6d75e451cd998cb511c20a72055110035b63ed Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 28 Jul 2023 12:16:16 +0000 Subject: [PATCH 030/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b56941ec7..4addb83cc 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). ## 0.100.1 From a0b987224aa0f80689568b3d38e211b23a7d7e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 31 Jul 2023 21:54:07 +0200 Subject: [PATCH 031/317] =?UTF-8?q?=F0=9F=91=B7=20Update=20CI=20debug=20mo?= =?UTF-8?q?de=20with=20Tmate=20(#9977)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/latest-changes.yml | 6 +++--- .github/workflows/notify-translations.yml | 11 ++++++++++- .github/workflows/people.yml | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 1f7ac7b28..0461f3dd3 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -14,7 +14,7 @@ on: debug_enabled: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false - default: false + default: 'false' jobs: latest-changes: @@ -22,12 +22,12 @@ jobs: steps: - uses: actions/checkout@v3 with: - # To allow latest-changes to commit to master + # To allow latest-changes to commit to the main branch token: ${{ secrets.FASTAPI_LATEST_CHANGES }} # Allow debugging with tmate - name: Setup tmate session uses: mxschmitt/action-tmate@v3 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }} + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} with: limit-access-to-actor: true - uses: docker://tiangolo/latest-changes:0.0.3 diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml index 0926486e9..cd7affbc3 100644 --- a/.github/workflows/notify-translations.yml +++ b/.github/workflows/notify-translations.yml @@ -5,6 +5,15 @@ on: types: - labeled - closed + workflow_dispatch: + inputs: + number: + description: PR number + required: true + debug_enabled: + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: 'false' jobs: notify-translations: @@ -14,7 +23,7 @@ jobs: # Allow debugging with tmate - name: Setup tmate session uses: mxschmitt/action-tmate@v3 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }} + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} with: limit-access-to-actor: true - uses: ./.github/actions/notify-translations diff --git a/.github/workflows/people.yml b/.github/workflows/people.yml index dac526a6c..aa7f34464 100644 --- a/.github/workflows/people.yml +++ b/.github/workflows/people.yml @@ -8,7 +8,7 @@ on: debug_enabled: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false - default: false + default: 'false' jobs: fastapi-people: @@ -22,7 +22,7 @@ jobs: # Allow debugging with tmate - name: Setup tmate session uses: mxschmitt/action-tmate@v3 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }} + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} with: limit-access-to-actor: true - uses: ./.github/actions/people From d38e86ef203f5a1c710452737b32ad63466f23d7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 31 Jul 2023 19:54:46 +0000 Subject: [PATCH 032/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 4addb83cc..14ce41374 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo). * 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). ## 0.100.1 From 1da0a7afbd53c66eefddc2455501159bf50f55ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 31 Jul 2023 23:49:19 +0200 Subject: [PATCH 033/317] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsor=20Fern?= =?UTF-8?q?=20(#9979)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/en/data/sponsors.yml | 2 +- docs/en/docs/advanced/generate-clients.md | 5 +++++ docs/en/docs/alternatives.md | 2 ++ docs/en/docs/img/sponsors/fern-banner.svg | 1 + docs/en/docs/img/sponsors/fern.svg | 1 + docs/en/overrides/main.html | 2 +- 7 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 docs/en/docs/img/sponsors/fern-banner.svg create mode 100644 docs/en/docs/img/sponsors/fern.svg diff --git a/README.md b/README.md index f0e76c4b6..50f80ded6 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The key features are: - + diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 33d57c873..53cdb9bad 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -7,7 +7,7 @@ gold: img: https://fastapi.tiangolo.com/img/sponsors/platform-sh.png - url: https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge title: Fern | SDKs and API docs - img: https://fastapi.tiangolo.com/img/sponsors/fern.png + img: https://fastapi.tiangolo.com/img/sponsors/fern.svg silver: - url: https://www.deta.sh/?ref=fastapi title: The launchpad for all your (team's) ideas diff --git a/docs/en/docs/advanced/generate-clients.md b/docs/en/docs/advanced/generate-clients.md index f62c0b57c..3fed48b0b 100644 --- a/docs/en/docs/advanced/generate-clients.md +++ b/docs/en/docs/advanced/generate-clients.md @@ -12,6 +12,11 @@ A common tool is openapi-typescript-codegen. +Another option you could consider for several languages is Fern. + +!!! info + Fern is also a FastAPI sponsor. 😎🎉 + ## Generate a TypeScript Frontend Client Let's start with a simple FastAPI application: diff --git a/docs/en/docs/alternatives.md b/docs/en/docs/alternatives.md index 0f074ccf3..a777ddb98 100644 --- a/docs/en/docs/alternatives.md +++ b/docs/en/docs/alternatives.md @@ -119,6 +119,8 @@ That's why when talking about version 2.0 it's common to say "Swagger", and for These two were chosen for being fairly popular and stable, but doing a quick search, you could find dozens of additional alternative user interfaces for OpenAPI (that you can use with **FastAPI**). + For example, you could try Fern which is also a FastAPI sponsor. 😎🎉 + ### Flask REST frameworks There are several Flask REST frameworks, but after investing the time and work into investigating them, I found that many are discontinued or abandoned, with several standing issues that made them unfit. diff --git a/docs/en/docs/img/sponsors/fern-banner.svg b/docs/en/docs/img/sponsors/fern-banner.svg new file mode 100644 index 000000000..bb3a389f3 --- /dev/null +++ b/docs/en/docs/img/sponsors/fern-banner.svg @@ -0,0 +1 @@ + diff --git a/docs/en/docs/img/sponsors/fern.svg b/docs/en/docs/img/sponsors/fern.svg new file mode 100644 index 000000000..ad3842fe0 --- /dev/null +++ b/docs/en/docs/img/sponsors/fern.svg @@ -0,0 +1 @@ + diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index fcd1704b9..7e6c0f763 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -37,7 +37,7 @@ From 74de15d0df975f7da4b29e9ad49142d7da172b6c Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 31 Jul 2023 21:49:56 +0000 Subject: [PATCH 034/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 14ce41374..ead095d8d 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo). * 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo). * 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). From d2169fbad9eb1b2537292996ce8d26e7584e9e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 1 Aug 2023 11:19:44 +0200 Subject: [PATCH 035/317] =?UTF-8?q?=F0=9F=91=B7=20Deploy=20docs=20to=20Clo?= =?UTF-8?q?udflare=20Pages=20(#9978)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-docs.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 312d835af..dcd6d7107 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -29,21 +29,20 @@ jobs: run_id: ${{ github.event.workflow_run.id }} name: docs-site path: ./site/ - - name: Deploy to Netlify + - name: Deploy to Cloudflare Pages if: steps.download.outputs.found_artifact == 'true' - id: netlify - uses: nwtgck/actions-netlify@v2.0.0 + id: deploy + uses: cloudflare/pages-action@v1 with: - publish-dir: './site' - production-deploy: ${{ github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' }} - github-token: ${{ secrets.FASTAPI_PREVIEW_DOCS_NETLIFY }} - enable-commit-comment: false - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: fastapitiangolo + directory: './site' + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }} - name: Comment Deploy - if: steps.netlify.outputs.deploy-url != '' + if: steps.deploy.outputs.url != '' uses: ./.github/actions/comment-docs-preview-in-pr with: token: ${{ secrets.FASTAPI_PREVIEW_DOCS_COMMENT_DEPLOY }} - deploy_url: "${{ steps.netlify.outputs.deploy-url }}" + deploy_url: "${{ steps.deploy.outputs.url }}" From 6c8c3b788bb7c3338f1105493833c9815ba243a4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 1 Aug 2023 09:20:23 +0000 Subject: [PATCH 036/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ead095d8d..2aed9859a 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo). * 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo). * 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). From c2a33f1087b5843054de839b47e7fe7200a62504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 1 Aug 2023 23:39:22 +0200 Subject: [PATCH 037/317] =?UTF-8?q?=F0=9F=8D=B1=20Update=20sponsors,=20Fer?= =?UTF-8?q?n=20badge=20(#9982)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/img/sponsors/fern-banner.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs/img/sponsors/fern-banner.svg b/docs/en/docs/img/sponsors/fern-banner.svg index bb3a389f3..e05ccc3a4 100644 --- a/docs/en/docs/img/sponsors/fern-banner.svg +++ b/docs/en/docs/img/sponsors/fern-banner.svg @@ -1 +1 @@ - + From 01f91fdb57e55b4540363ef5749480dc3f3e8bf6 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 1 Aug 2023 21:40:00 +0000 Subject: [PATCH 038/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 2aed9859a..895926cab 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo). * 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo). * 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo). From 88d96799b15e530c068f749626595c739bce3fcf Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 2 Aug 2023 18:14:19 +0300 Subject: [PATCH 039/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Russian=20translat?= =?UTF-8?q?ion=20for=20`docs/ru/docs/tutorial/security/index.md`=20(#9963)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dedkot Co-authored-by: Vladislav Kramorenko <85196001+Xewus@users.noreply.github.com> --- docs/ru/docs/tutorial/security/index.md | 101 ++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/ru/docs/tutorial/security/index.md diff --git a/docs/ru/docs/tutorial/security/index.md b/docs/ru/docs/tutorial/security/index.md new file mode 100644 index 000000000..d5fe4e76f --- /dev/null +++ b/docs/ru/docs/tutorial/security/index.md @@ -0,0 +1,101 @@ +# Настройка авторизации + +Существует множество способов обеспечения безопасности, аутентификации и авторизации. + +Обычно эта тема является достаточно сложной и трудной. + +Во многих фреймворках и системах только работа с определением доступов к приложению и аутентификацией требует значительных затрат усилий и написания множества кода (во многих случаях его объём может составлять более 50% от всего написанного кода). + +**FastAPI** предоставляет несколько инструментов, которые помогут вам настроить **Авторизацию** легко, быстро, стандартным способом, без необходимости изучать все её тонкости. + +Но сначала давайте рассмотрим некоторые небольшие концепции. + +## Куда-то торопишься? + +Если вам не нужна информация о каких-либо из следующих терминов и вам просто нужно добавить защиту с аутентификацией на основе логина и пароля *прямо сейчас*, переходите к следующим главам. + +## OAuth2 + +OAuth2 - это протокол, который определяет несколько способов обработки аутентификации и авторизации. + +Он довольно обширен и охватывает несколько сложных вариантов использования. + +OAuth2 включает в себя способы аутентификации с использованием "третьей стороны". + +Это то, что используют под собой все кнопки "вход с помощью Facebook, Google, Twitter, GitHub" на страницах авторизации. + +### OAuth 1 + +Ранее использовался протокол OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые описания того, как шифровать сообщение. + +В настоящее время он не очень популярен и не используется. + +OAuth2 не указывает, как шифровать сообщение, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS. + +!!! tip "Подсказка" + В разделе **Развертывание** вы увидите [как настроить протокол HTTPS бесплатно, используя Traefik и Let's Encrypt.](https://fastapi.tiangolo.com/ru/deployment/https/) + + +## OpenID Connect + +OpenID Connect - это еще один протокол, основанный на **OAuth2**. + +Он просто расширяет OAuth2, уточняя некоторые вещи, не имеющие однозначного определения в OAuth2, в попытке сделать его более совместимым. + +Например, для входа в Google используется OpenID Connect (который под собой использует OAuth2). + +Но вход в Facebook не поддерживает OpenID Connect. У него есть собственная вариация OAuth2. + +### OpenID (не "OpenID Connect") + +Также ранее использовался стандарт "OpenID", который пытался решить ту же проблему, что и **OpenID Connect**, но не был основан на OAuth2. + +Таким образом, это была полноценная дополнительная система. + +В настоящее время не очень популярен и не используется. + +## OpenAPI + +OpenAPI (ранее известный как Swagger) - это открытая спецификация для создания API (в настоящее время является частью Linux Foundation). + +**FastAPI** основан на **OpenAPI**. + +Это то, что делает возможным наличие множества автоматических интерактивных интерфейсов документирования, сгенерированного кода и т.д. + +В OpenAPI есть способ использовать несколько "схем" безопасности. + +Таким образом, вы можете воспользоваться преимуществами Всех этих стандартных инструментов, включая интерактивные системы документирования. + +OpenAPI может использовать следующие схемы авторизации: + +* `apiKey`: уникальный идентификатор для приложения, который может быть получен из: + * Параметров запроса. + * Заголовка. + * Cookies. +* `http`: стандартные системы аутентификации по протоколу HTTP, включая: + * `bearer`: заголовок `Authorization` со значением `Bearer {уникальный токен}`. Это унаследовано от OAuth2. + * Базовая аутентификация по протоколу HTTP. + * HTTP Digest и т.д. +* `oauth2`: все способы обеспечения безопасности OAuth2 называемые "потоки" (англ. "flows"). + * Некоторые из этих "потоков" подходят для реализации аутентификации через сторонний сервис использующий OAuth 2.0 (например, Google, Facebook, Twitter, GitHub и т.д.): + * `implicit` + * `clientCredentials` + * `authorizationCode` + * Но есть один конкретный "поток", который может быть идеально использован для обработки аутентификации непосредственно в том же приложении: + * `password`: в некоторых следующих главах будут рассмотрены примеры этого. +* `openIdConnect`: способ определить, как автоматически обнаруживать данные аутентификации OAuth2. + * Это автоматическое обнаружение определено в спецификации OpenID Connect. + + +!!! tip "Подсказка" + Интеграция сторонних сервисов для аутентификации/авторизации таких как Google, Facebook, Twitter, GitHub и т.д. осуществляется достаточно легко. + + Самой сложной проблемой является создание такого провайдера аутентификации/авторизации, но **FastAPI** предоставляет вам инструменты, позволяющие легко это сделать, выполняя при этом всю тяжелую работу за вас. + +## Преимущества **FastAPI** + +Fast API предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности. + +В следующих главах вы увидите, как обезопасить свой API, используя инструменты, предоставляемые **FastAPI**. + +И вы также увидите, как он автоматически интегрируется в систему интерактивной документации. From 2d8a776836e1363e021b2a1233a72584f57b5d7a Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Aug 2023 15:15:10 +0000 Subject: [PATCH 040/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 895926cab..fde398be5 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). * 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo). * 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo). From 37818f553ddb21b7adaaf16d18b95f9710065ad4 Mon Sep 17 00:00:00 2001 From: Irfanuddin Shafi Ahmed Date: Wed, 2 Aug 2023 20:58:34 +0530 Subject: [PATCH 041/317] =?UTF-8?q?=E2=9C=85=20Fix=20test=20error=20in=20W?= =?UTF-8?q?indows=20for=20`jsonable=5Fencoder`=20(#9840)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marcelo Trylesinski --- tests/test_jsonable_encoder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index ff3033ecd..7c8338ff3 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -247,8 +247,9 @@ def test_encode_model_with_pure_path(): class Config: arbitrary_types_allowed = True - obj = ModelWithPath(path=PurePath("/foo", "bar")) - assert jsonable_encoder(obj) == {"path": "/foo/bar"} + test_path = PurePath("/foo", "bar") + obj = ModelWithPath(path=test_path) + assert jsonable_encoder(obj) == {"path": str(test_path)} def test_encode_model_with_pure_posix_path(): From b473cdd88d878b6657f0c80edb384b71971841a8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Aug 2023 15:29:13 +0000 Subject: [PATCH 042/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fde398be5..5065295e6 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). * 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo). * 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo). From 1e6bfa1f3931b841f3c2e173a5e673e854130a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 2 Aug 2023 17:57:20 +0200 Subject: [PATCH 043/317] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20FastAPI?= =?UTF-8?q?=20People=20logic=20with=20new=20Pydantic=20(#9985)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/actions/people/Dockerfile | 2 +- .github/actions/people/app/main.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/actions/people/Dockerfile b/.github/actions/people/Dockerfile index fa4197e6a..6d65f1c2b 100644 --- a/.github/actions/people/Dockerfile +++ b/.github/actions/people/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.7 -RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0" +RUN pip install httpx PyGithub "pydantic==2.0.2" "pyyaml>=5.3.1,<6.0.0" COPY ./app /app diff --git a/.github/actions/people/app/main.py b/.github/actions/people/app/main.py index b11e3456d..cb6b229e8 100644 --- a/.github/actions/people/app/main.py +++ b/.github/actions/people/app/main.py @@ -9,7 +9,8 @@ from typing import Any, Container, DefaultDict, Dict, List, Set, Union import httpx import yaml from github import Github -from pydantic import BaseModel, BaseSettings, SecretStr +from pydantic import BaseModel, SecretStr +from pydantic_settings import BaseSettings github_graphql_url = "https://api.github.com/graphql" questions_category_id = "MDE4OkRpc2N1c3Npb25DYXRlZ29yeTMyMDAxNDM0" @@ -382,6 +383,7 @@ def get_graphql_response( data = response.json() if "errors" in data: logging.error(f"Errors in response, after: {after}, category_id: {category_id}") + logging.error(data["errors"]) logging.error(response.text) raise RuntimeError(response.text) return data @@ -389,7 +391,7 @@ def get_graphql_response( def get_graphql_issue_edges(*, settings: Settings, after: Union[str, None] = None): data = get_graphql_response(settings=settings, query=issues_query, after=after) - graphql_response = IssuesResponse.parse_obj(data) + graphql_response = IssuesResponse.model_validate(data) return graphql_response.data.repository.issues.edges @@ -404,19 +406,19 @@ def get_graphql_question_discussion_edges( after=after, category_id=questions_category_id, ) - graphql_response = DiscussionsResponse.parse_obj(data) + graphql_response = DiscussionsResponse.model_validate(data) return graphql_response.data.repository.discussions.edges def get_graphql_pr_edges(*, settings: Settings, after: Union[str, None] = None): data = get_graphql_response(settings=settings, query=prs_query, after=after) - graphql_response = PRsResponse.parse_obj(data) + graphql_response = PRsResponse.model_validate(data) return graphql_response.data.repository.pullRequests.edges def get_graphql_sponsor_edges(*, settings: Settings, after: Union[str, None] = None): data = get_graphql_response(settings=settings, query=sponsors_query, after=after) - graphql_response = SponsorsResponse.parse_obj(data) + graphql_response = SponsorsResponse.model_validate(data) return graphql_response.data.user.sponsorshipsAsMaintainer.edges @@ -607,7 +609,7 @@ def get_top_users( if __name__ == "__main__": logging.basicConfig(level=logging.INFO) settings = Settings() - logging.info(f"Using config: {settings.json()}") + logging.info(f"Using config: {settings.model_dump_json()}") g = Github(settings.input_token.get_secret_value()) repo = g.get_repo(settings.github_repository) question_commentors, question_last_month_commentors, question_authors = get_experts( From 165f29fe5ec6be1d42b82cdef8b3d144300005f9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Aug 2023 15:57:57 +0000 Subject: [PATCH 044/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 5065295e6..d0b7c6a93 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). * 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo). From 53220b983227ef6147a43dfdc0528ff4e6c31f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 2 Aug 2023 20:57:48 +0200 Subject: [PATCH 045/317] =?UTF-8?q?=E2=9E=95=20Add=20pydantic-settings=20t?= =?UTF-8?q?o=20FastAPI=20People=20dependencies=20(#9988)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/actions/people/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/people/Dockerfile b/.github/actions/people/Dockerfile index 6d65f1c2b..f0f389c64 100644 --- a/.github/actions/people/Dockerfile +++ b/.github/actions/people/Dockerfile @@ -1,6 +1,6 @@ -FROM python:3.7 +FROM python:3.11 -RUN pip install httpx PyGithub "pydantic==2.0.2" "pyyaml>=5.3.1,<6.0.0" +RUN pip install httpx PyGithub "pydantic==2.0.2" pydantic-settings "pyyaml>=5.3.1,<6.0.0" COPY ./app /app From 38291292451481ca9004a5031464fac036de0161 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Aug 2023 18:58:29 +0000 Subject: [PATCH 046/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d0b7c6a93..920886e5d 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo). * ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). From 89537a0497ef3ccacbe2f4959c3b4f31414319ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 3 Aug 2023 16:12:28 +0200 Subject: [PATCH 047/317] =?UTF-8?q?=F0=9F=90=B3=20Update=20Dockerfile=20wi?= =?UTF-8?q?th=20compatibility=20versions,=20to=20upgrade=20later=20(#9998)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/actions/people/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/people/Dockerfile b/.github/actions/people/Dockerfile index f0f389c64..1455106bd 100644 --- a/.github/actions/people/Dockerfile +++ b/.github/actions/people/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11 +FROM python:3.9 RUN pip install httpx PyGithub "pydantic==2.0.2" pydantic-settings "pyyaml>=5.3.1,<6.0.0" From ad1d7f539ea53c3b75e9c23051d66663eac6146f Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Aug 2023 14:13:59 +0000 Subject: [PATCH 048/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 920886e5d..91745453c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo). * ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo). * ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). From 3fa6cfbcc5d37cecfc6a936c42c832978ecbfba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 3 Aug 2023 16:25:11 +0200 Subject: [PATCH 049/317] =?UTF-8?q?=F0=9F=91=A5=20Update=20FastAPI=20Peopl?= =?UTF-8?q?e=20(#9999)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions --- docs/en/data/github_sponsors.yml | 93 +++++++-------- docs/en/data/people.yml | 188 +++++++++++++++---------------- 2 files changed, 142 insertions(+), 139 deletions(-) diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml index 56a886c68..3a68ba62b 100644 --- a/docs/en/data/github_sponsors.yml +++ b/docs/en/data/github_sponsors.yml @@ -2,6 +2,9 @@ sponsors: - - login: cryptapi avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4 url: https://github.com/cryptapi + - login: fern-api + avatarUrl: https://avatars.githubusercontent.com/u/102944815?v=4 + url: https://github.com/fern-api - login: nanram22 avatarUrl: https://avatars.githubusercontent.com/u/116367316?v=4 url: https://github.com/nanram22 @@ -29,15 +32,18 @@ sponsors: - login: VincentParedes avatarUrl: https://avatars.githubusercontent.com/u/103889729?v=4 url: https://github.com/VincentParedes +- - login: arcticfly + avatarUrl: https://avatars.githubusercontent.com/u/41524992?u=03c88529a86cf51f7a380e890d84d84c71468848&v=4 + url: https://github.com/arcticfly - - login: getsentry avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4 url: https://github.com/getsentry -- - login: takashi-yoneya +- - login: acsone + avatarUrl: https://avatars.githubusercontent.com/u/7601056?v=4 + url: https://github.com/acsone + - login: takashi-yoneya avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4 url: https://github.com/takashi-yoneya - - login: mercedes-benz - avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4 - url: https://github.com/mercedes-benz - login: xoflare avatarUrl: https://avatars.githubusercontent.com/u/74335107?v=4 url: https://github.com/xoflare @@ -50,12 +56,12 @@ sponsors: - login: BoostryJP avatarUrl: https://avatars.githubusercontent.com/u/57932412?v=4 url: https://github.com/BoostryJP + - login: jina-ai + avatarUrl: https://avatars.githubusercontent.com/u/60539444?v=4 + url: https://github.com/jina-ai - - login: HiredScore avatarUrl: https://avatars.githubusercontent.com/u/3908850?v=4 url: https://github.com/HiredScore - - login: petebachant - avatarUrl: https://avatars.githubusercontent.com/u/4604869?u=b17a5a4ac82f77b7efff864d439e8068d2a36593&v=4 - url: https://github.com/petebachant - login: Trivie avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4 url: https://github.com/Trivie @@ -74,9 +80,6 @@ sponsors: - login: RodneyU215 avatarUrl: https://avatars.githubusercontent.com/u/3329665?u=ec6a9adf8e7e8e306eed7d49687c398608d1604f&v=4 url: https://github.com/RodneyU215 - - login: tizz98 - avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4 - url: https://github.com/tizz98 - login: americanair avatarUrl: https://avatars.githubusercontent.com/u/12281813?v=4 url: https://github.com/americanair @@ -92,11 +95,17 @@ sponsors: - - login: indeedeng avatarUrl: https://avatars.githubusercontent.com/u/2905043?v=4 url: https://github.com/indeedeng + - login: iguit0 + avatarUrl: https://avatars.githubusercontent.com/u/12905770?u=63a1a96d1e6c27d85c4f946b84836599de047f65&v=4 + url: https://github.com/iguit0 + - login: JacobKochems + avatarUrl: https://avatars.githubusercontent.com/u/41692189?u=a75f62ddc0d060ee6233a91e19c433d2687b8eb6&v=4 + url: https://github.com/JacobKochems - - login: Kludex avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: samuelcolvin - avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4 url: https://github.com/samuelcolvin - login: jefftriplett avatarUrl: https://avatars.githubusercontent.com/u/50527?u=af1ddfd50f6afd6d99f333ba2ac8d0a5b245ea74&v=4 @@ -104,9 +113,6 @@ sponsors: - login: jstanden avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4 url: https://github.com/jstanden - - login: kamalgill - avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4 - url: https://github.com/kamalgill - login: dekoza avatarUrl: https://avatars.githubusercontent.com/u/210980?u=c03c78a8ae1039b500dfe343665536ebc51979b2&v=4 url: https://github.com/dekoza @@ -149,6 +155,9 @@ sponsors: - login: zsinx6 avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4 url: https://github.com/zsinx6 + - login: kennywakeland + avatarUrl: https://avatars.githubusercontent.com/u/3631417?u=7c8f743f1ae325dfadea7c62bbf1abd6a824fc55&v=4 + url: https://github.com/kennywakeland - login: aacayaco avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4 url: https://github.com/aacayaco @@ -194,9 +203,6 @@ sponsors: - login: Shackelford-Arden avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4 url: https://github.com/Shackelford-Arden - - login: savannahostrowski - avatarUrl: https://avatars.githubusercontent.com/u/8949415?u=c3177aa099fb2b8c36aeba349278b77f9a8df211&v=4 - url: https://github.com/savannahostrowski - login: wdwinslow avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4 url: https://github.com/wdwinslow @@ -236,9 +242,6 @@ sponsors: - login: ygorpontelo avatarUrl: https://avatars.githubusercontent.com/u/32963605?u=35f7103f9c4c4c2589ae5737ee882e9375ef072e&v=4 url: https://github.com/ygorpontelo - - login: AlrasheedA - avatarUrl: https://avatars.githubusercontent.com/u/33544979?u=7fe66bf62b47682612b222e3e8f4795ef3be769b&v=4 - url: https://github.com/AlrasheedA - login: ProteinQure avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4 url: https://github.com/ProteinQure @@ -281,9 +284,6 @@ sponsors: - login: DelfinaCare avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4 url: https://github.com/DelfinaCare - - login: khoadaniel - avatarUrl: https://avatars.githubusercontent.com/u/84840546?v=4 - url: https://github.com/khoadaniel - login: osawa-koki avatarUrl: https://avatars.githubusercontent.com/u/94336223?u=59c6fe6945bcbbaff87b2a794238671b060620d2&v=4 url: https://github.com/osawa-koki @@ -327,14 +327,11 @@ sponsors: avatarUrl: https://avatars.githubusercontent.com/u/861044?u=5abfca5588f3e906b31583d7ee62f6de4b68aa24&v=4 url: https://github.com/browniebroke - login: janfilips - avatarUrl: https://avatars.githubusercontent.com/u/870699?u=96df18ad355e58b9397accc55f4eeb7a86e959b0&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/870699?u=80702ec63f14e675cd4cdcc6ce3821d2ed207fd7&v=4 url: https://github.com/janfilips - login: WillHogan avatarUrl: https://avatars.githubusercontent.com/u/1661551?u=7036c064cf29781470573865264ec8e60b6b809f&v=4 url: https://github.com/WillHogan - - login: NateShoffner - avatarUrl: https://avatars.githubusercontent.com/u/1712163?u=b43cc2fa3fd8bec54b7706e4b98b72543c7bfea8&v=4 - url: https://github.com/NateShoffner - login: my3 avatarUrl: https://avatars.githubusercontent.com/u/1825270?v=4 url: https://github.com/my3 @@ -344,12 +341,6 @@ sponsors: - login: cbonoz avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4 url: https://github.com/cbonoz - - login: Patechoc - avatarUrl: https://avatars.githubusercontent.com/u/2376641?u=23b49e9eda04f078cb74fa3f93593aa6a57bb138&v=4 - url: https://github.com/Patechoc - - login: larsvik - avatarUrl: https://avatars.githubusercontent.com/u/3442226?v=4 - url: https://github.com/larsvik - login: anthonycorletti avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4 url: https://github.com/anthonycorletti @@ -359,6 +350,9 @@ sponsors: - login: Alisa-lisa avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4 url: https://github.com/Alisa-lisa + - login: piotrgredowski + avatarUrl: https://avatars.githubusercontent.com/u/4294480?v=4 + url: https://github.com/piotrgredowski - login: danielunderwood avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4 url: https://github.com/danielunderwood @@ -383,6 +377,9 @@ sponsors: - login: mattwelke avatarUrl: https://avatars.githubusercontent.com/u/7719209?u=80f02a799323b1472b389b836d95957c93a6d856&v=4 url: https://github.com/mattwelke + - login: harsh183 + avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4 + url: https://github.com/harsh183 - login: hcristea avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4 url: https://github.com/hcristea @@ -428,9 +425,6 @@ sponsors: - login: shuheng-liu avatarUrl: https://avatars.githubusercontent.com/u/22414322?u=813c45f30786c6b511b21a661def025d8f7b609e&v=4 url: https://github.com/shuheng-liu - - login: ghandic - avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4 - url: https://github.com/ghandic - login: pers0n4 avatarUrl: https://avatars.githubusercontent.com/u/24864600?u=f211a13a7b572cbbd7779b9c8d8cb428cc7ba07e&v=4 url: https://github.com/pers0n4 @@ -458,9 +452,6 @@ sponsors: - login: bnkc avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=527044d90b5ebb7f8dad517db5da1f45253b774b&v=4 url: https://github.com/bnkc - - login: devbruce - avatarUrl: https://avatars.githubusercontent.com/u/35563380?u=ca4e811ac7f7b3eb1600fa63285119fcdee01188&v=4 - url: https://github.com/devbruce - login: declon avatarUrl: https://avatars.githubusercontent.com/u/36180226?v=4 url: https://github.com/declon @@ -476,6 +467,9 @@ sponsors: - login: ArtyomVancyan avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4 url: https://github.com/ArtyomVancyan + - login: josehenriqueroveda + avatarUrl: https://avatars.githubusercontent.com/u/46685746?u=2e672057a7dbe1dba47e57c378fc0cac336022eb&v=4 + url: https://github.com/josehenriqueroveda - login: hgalytoby avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=f4888c2c54929bd86eed0d3971d09fcb306e5088&v=4 url: https://github.com/hgalytoby @@ -485,27 +479,36 @@ sponsors: - login: conservative-dude avatarUrl: https://avatars.githubusercontent.com/u/55538308?u=f250c44942ea6e73a6bd90739b381c470c192c11&v=4 url: https://github.com/conservative-dude - - login: leo-jp-edwards - avatarUrl: https://avatars.githubusercontent.com/u/58213433?u=2c128e8b0794b7a66211cd7d8ebe05db20b7e9c0&v=4 - url: https://github.com/leo-jp-edwards - login: 0417taehyun avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4 url: https://github.com/0417taehyun - - login: ssbarnea avatarUrl: https://avatars.githubusercontent.com/u/102495?u=b4bf6818deefe59952ac22fec6ed8c76de1b8f7c&v=4 url: https://github.com/ssbarnea - - login: tomast1337 - avatarUrl: https://avatars.githubusercontent.com/u/15125899?u=2c2f2907012d820499e2c43632389184923513fe&v=4 - url: https://github.com/tomast1337 + - login: Patechoc + avatarUrl: https://avatars.githubusercontent.com/u/2376641?u=23b49e9eda04f078cb74fa3f93593aa6a57bb138&v=4 + url: https://github.com/Patechoc + - login: LanceMoe + avatarUrl: https://avatars.githubusercontent.com/u/18505474?u=7fd3ead4364bdf215b6d75cb122b3811c391ef6b&v=4 + url: https://github.com/LanceMoe - login: sadikkuzu avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=d179c06bb9f65c4167fcab118526819f8e0dac17&v=4 url: https://github.com/sadikkuzu - login: ruizdiazever avatarUrl: https://avatars.githubusercontent.com/u/29817086?u=2df54af55663d246e3a4dc8273711c37f1adb117&v=4 url: https://github.com/ruizdiazever + - login: samnimoh + avatarUrl: https://avatars.githubusercontent.com/u/33413170?u=147bc516be6cb647b28d7e3b3fea3a018a331145&v=4 + url: https://github.com/samnimoh - login: danburonline avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=2cad4388c1544e539ecb732d656e42fb07b4ff2d&v=4 url: https://github.com/danburonline + - login: iharshgor + avatarUrl: https://avatars.githubusercontent.com/u/35490011?u=2dea054476e752d9e92c9d71a9a7cc919b1c2f8e&v=4 + url: https://github.com/iharshgor - login: rwxd avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4 url: https://github.com/rwxd + - login: ThomasPalma1 + avatarUrl: https://avatars.githubusercontent.com/u/66331874?u=5763f7402d784ba189b60d704ff5849b4d0a63fb&v=4 + url: https://github.com/ThomasPalma1 diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml index dd2dbe5ea..89d189564 100644 --- a/docs/en/data/people.yml +++ b/docs/en/data/people.yml @@ -1,16 +1,16 @@ maintainers: - login: tiangolo - answers: 1844 - prs: 430 + answers: 1849 + prs: 466 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4 url: https://github.com/tiangolo experts: - login: Kludex - count: 434 + count: 463 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: dmontagu - count: 237 + count: 239 avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4 url: https://github.com/dmontagu - login: Mause @@ -25,34 +25,34 @@ experts: count: 193 avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4 url: https://github.com/JarroVGIT -- login: euri10 - count: 152 - avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4 - url: https://github.com/euri10 - login: jgould22 - count: 139 + count: 157 avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 url: https://github.com/jgould22 +- login: euri10 + count: 153 + avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4 + url: https://github.com/euri10 - login: phy25 count: 126 - avatarUrl: https://avatars.githubusercontent.com/u/331403?u=191cd73f0c936497c8d1931a217bb3039d050265&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4 url: https://github.com/phy25 - login: iudeen - count: 118 + count: 121 avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 url: https://github.com/iudeen - login: raphaelauv count: 83 avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 url: https://github.com/raphaelauv -- login: ghandic - count: 71 - avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4 - url: https://github.com/ghandic - login: ArcLightSlavik count: 71 avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4 url: https://github.com/ArcLightSlavik +- login: ghandic + count: 71 + avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4 + url: https://github.com/ghandic - login: falkben count: 57 avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4 @@ -61,10 +61,10 @@ experts: count: 49 avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4 url: https://github.com/sm-Fifteen -- login: adriangb +- login: insomnes count: 45 - avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4 - url: https://github.com/adriangb + avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4 + url: https://github.com/insomnes - login: yinziyan1206 count: 45 avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4 @@ -73,22 +73,22 @@ experts: count: 45 avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4 url: https://github.com/acidjunk -- login: insomnes - count: 45 - avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4 - url: https://github.com/insomnes - login: Dustyposa count: 45 avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 url: https://github.com/Dustyposa -- login: odiseo0 - count: 43 - avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=2da05dab6cc8e1ade557801634760a56e4101796&v=4 - url: https://github.com/odiseo0 +- login: adriangb + count: 44 + avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4 + url: https://github.com/adriangb - login: frankie567 count: 43 avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4 url: https://github.com/frankie567 +- login: odiseo0 + count: 43 + avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4 + url: https://github.com/odiseo0 - login: includeamin count: 40 avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4 @@ -97,14 +97,14 @@ experts: count: 37 avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4 url: https://github.com/STeveShary -- login: chbndrhnns - count: 35 - avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4 - url: https://github.com/chbndrhnns - login: krishnardt count: 35 avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4 url: https://github.com/krishnardt +- login: chbndrhnns + count: 35 + avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4 + url: https://github.com/chbndrhnns - login: panla count: 32 avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4 @@ -121,38 +121,38 @@ experts: count: 25 avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 url: https://github.com/wshayes +- login: acnebs + count: 23 + avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4 + url: https://github.com/acnebs - login: SirTelemak count: 23 avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4 url: https://github.com/SirTelemak -- login: acnebs - count: 22 - avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4 - url: https://github.com/acnebs - login: rafsaf count: 21 avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4 url: https://github.com/rafsaf -- login: chris-allnutt +- login: n8sty count: 20 - avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4 - url: https://github.com/chris-allnutt + avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4 + url: https://github.com/n8sty - login: nsidnev count: 20 avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4 url: https://github.com/nsidnev -- login: n8sty +- login: chris-allnutt + count: 20 + avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4 + url: https://github.com/chris-allnutt +- login: zoliknemet count: 18 - avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4 - url: https://github.com/n8sty + avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4 + url: https://github.com/zoliknemet - login: retnikt count: 18 avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4 url: https://github.com/retnikt -- login: zoliknemet - count: 18 - avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4 - url: https://github.com/zoliknemet - login: Hultner count: 17 avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4 @@ -198,38 +198,30 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4 url: https://github.com/jorgerpo last_month_active: -- login: jgould22 - count: 15 - avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 - url: https://github.com/jgould22 - login: Kludex - count: 13 + count: 24 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex -- login: abhint - count: 5 - avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4 - url: https://github.com/abhint -- login: chrisK824 - count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4 - url: https://github.com/chrisK824 +- login: jgould22 + count: 17 + avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 + url: https://github.com/jgould22 - login: arjwilliams - count: 3 + count: 8 avatarUrl: https://avatars.githubusercontent.com/u/22227620?v=4 url: https://github.com/arjwilliams -- login: wu-clan - count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/52145145?u=f8c9e5c8c259d248e1683fedf5027b4ee08a0967&v=4 - url: https://github.com/wu-clan - login: Ahmed-Abdou14 - count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=d1e1c064d57c3ad5b6481716928da840f6d5a492&v=4 + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4 url: https://github.com/Ahmed-Abdou14 -- login: esrefzeki +- login: iudeen count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/54935247?u=193cf5a169ca05fc54995a4dceabc82c7dc6e5ea&v=4 - url: https://github.com/esrefzeki + avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 + url: https://github.com/iudeen +- login: mikeedjones + count: 3 + avatarUrl: https://avatars.githubusercontent.com/u/4087139?u=cc4a242896ac2fcf88a53acfaf190d0fe0a1f0c9&v=4 + url: https://github.com/mikeedjones top_contributors: - login: waynerv count: 25 @@ -240,7 +232,7 @@ top_contributors: avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 url: https://github.com/tokusumi - login: Kludex - count: 20 + count: 21 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: jaystone776 @@ -264,7 +256,7 @@ top_contributors: avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 url: https://github.com/mariacamilagl - login: Smlep - count: 10 + count: 11 avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4 url: https://github.com/Smlep - login: Serrones @@ -287,6 +279,10 @@ top_contributors: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4 url: https://github.com/Alexandrhub +- login: NinaHwang + count: 6 + avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4 + url: https://github.com/NinaHwang - login: batlopes count: 6 avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4 @@ -297,7 +293,7 @@ top_contributors: url: https://github.com/wshayes - login: samuelcolvin count: 5 - avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4 url: https://github.com/samuelcolvin - login: SwftAlpc count: 5 @@ -311,10 +307,6 @@ top_contributors: count: 5 avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4 url: https://github.com/ComicShrimp -- login: NinaHwang - count: 5 - avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=eee6bfe9224c71193025ab7477f4f96ceaa05c62&v=4 - url: https://github.com/NinaHwang - login: jekirl count: 4 avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4 @@ -335,10 +327,18 @@ top_contributors: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4 url: https://github.com/hitrust +- login: JulianMaurin + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4 + url: https://github.com/JulianMaurin - login: lsglucas count: 4 avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4 url: https://github.com/lsglucas +- login: iudeen + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 + url: https://github.com/iudeen - login: axel584 count: 4 avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4 @@ -349,7 +349,7 @@ top_contributors: url: https://github.com/ivan-abc top_reviewers: - login: Kludex - count: 122 + count: 136 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: BilalAlpaslan @@ -357,7 +357,7 @@ top_reviewers: avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4 url: https://github.com/BilalAlpaslan - login: yezz123 - count: 77 + count: 78 avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=d7062cbc6eb7671d5dc9cc0e32a24ae335e0f225&v=4 url: https://github.com/yezz123 - login: tokusumi @@ -372,20 +372,20 @@ top_reviewers: count: 47 avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4 url: https://github.com/Laineyzhang55 +- login: iudeen + count: 46 + avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 + url: https://github.com/iudeen - login: ycd count: 45 avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4 url: https://github.com/ycd -- login: iudeen - count: 44 - avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 - url: https://github.com/iudeen - login: cikay count: 41 avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4 url: https://github.com/cikay - login: Xewus - count: 35 + count: 38 avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4 url: https://github.com/Xewus - login: JarroVGIT @@ -412,6 +412,10 @@ top_reviewers: count: 26 avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4 url: https://github.com/lsglucas +- login: LorhanSohaky + count: 24 + avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4 + url: https://github.com/LorhanSohaky - login: Ryandaydev count: 24 avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=809f3d1074d04bbc28012a7f17f06ea56f5bd71a&v=4 @@ -420,10 +424,6 @@ top_reviewers: count: 23 avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4 url: https://github.com/dmontagu -- login: LorhanSohaky - count: 23 - avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4 - url: https://github.com/LorhanSohaky - login: rjNemo count: 21 avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4 @@ -434,7 +434,7 @@ top_reviewers: url: https://github.com/hard-coders - login: odiseo0 count: 20 - avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=2da05dab6cc8e1ade557801634760a56e4101796&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=241a71f6b7068738b81af3e57f45ffd723538401&v=4 url: https://github.com/odiseo0 - login: 0417taehyun count: 19 @@ -456,6 +456,10 @@ top_reviewers: count: 16 avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4 url: https://github.com/SwftAlpc +- login: axel584 + count: 16 + avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4 + url: https://github.com/axel584 - login: DevDae count: 16 avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4 @@ -488,10 +492,6 @@ top_reviewers: count: 12 avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4 url: https://github.com/RunningIkkyu -- login: axel584 - count: 12 - avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4 - url: https://github.com/axel584 - login: ivan-abc count: 12 avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4 @@ -500,6 +500,10 @@ top_reviewers: count: 11 avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4 url: https://github.com/solomein-sv +- login: wdh99 + count: 11 + avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4 + url: https://github.com/wdh99 - login: mariacamilagl count: 10 avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 @@ -540,7 +544,3 @@ top_reviewers: count: 9 avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4 url: https://github.com/bezaca -- login: oandersonmagalhaes - count: 9 - avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4 - url: https://github.com/oandersonmagalhaes From a73cdaed35d6d9a120ba4831c512b3a7cb25b697 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Aug 2023 14:25:48 +0000 Subject: [PATCH 050/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 91745453c..ab8c6e68c 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo). * 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo). * ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo). * ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo). From 4ab0363ad794fd60e1ebead224f57a1d01e6bd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 3 Aug 2023 17:24:31 +0200 Subject: [PATCH 051/317] =?UTF-8?q?=E2=9E=96=20Remove=20direct=20dependenc?= =?UTF-8?q?y=20on=20MkDocs,=20Material=20for=20MkDocs=20defines=20its=20ow?= =?UTF-8?q?n=20dependency=20(#9986)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docs.yml | 8 ++++---- requirements-docs.txt | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 19009447b..eb816b72f 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -44,14 +44,14 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v05 + key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06 - name: Install docs extras if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-docs.txt # Install MkDocs Material Insiders here just to put it in the cache for the rest of the steps - name: Install Material for MkDocs Insiders if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true' - run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git + run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git@9.1.21-insiders-4.38.0 - name: Export Language Codes id: show-langs run: | @@ -80,13 +80,13 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v05 + key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06 - name: Install docs extras if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-docs.txt - name: Install Material for MkDocs Insiders if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true' - run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git + run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git@9.1.21-insiders-4.38.0 - name: Update Languages run: python ./scripts/docs.py update-languages - uses: actions/cache@v3 diff --git a/requirements-docs.txt b/requirements-docs.txt index df60ca4df..7152ebf7b 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,5 +1,4 @@ -e . -mkdocs==1.4.3 mkdocs-material==9.1.17 mdx-include >=1.4.1,<2.0.0 mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0 From 94c48cfc8cb5987113c6a8558a7a428d27888cce Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Aug 2023 15:25:10 +0000 Subject: [PATCH 052/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ab8c6e68c..caf74c353 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo). * 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo). * ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo). From 25694f5ae100741c9f7ee0d409e298609e71366e Mon Sep 17 00:00:00 2001 From: David Montague <35119617+dmontagu@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:46:57 +0100 Subject: [PATCH 053/317] =?UTF-8?q?=E2=9C=85=20Fix=20tests=20for=20compati?= =?UTF-8?q?bility=20with=20pydantic=202.1.1=20(#9943)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez --- .github/workflows/test.yml | 4 +-- tests/test_filter_pydantic_sub_model_pv2.py | 4 +-- tests/test_multi_body_errors.py | 34 +++++++++++++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b95358d01..fbaf759c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v03 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-tests.txt @@ -54,7 +54,7 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v03 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-tests.txt diff --git a/tests/test_filter_pydantic_sub_model_pv2.py b/tests/test_filter_pydantic_sub_model_pv2.py index 656332a01..ae12179bd 100644 --- a/tests/test_filter_pydantic_sub_model_pv2.py +++ b/tests/test_filter_pydantic_sub_model_pv2.py @@ -1,7 +1,7 @@ from typing import Optional import pytest -from dirty_equals import IsDict +from dirty_equals import HasRepr, IsDict from fastapi import Depends, FastAPI from fastapi.exceptions import ResponseValidationError from fastapi.testclient import TestClient @@ -66,7 +66,7 @@ def test_validator_is_cloned(client: TestClient): "loc": ("response", "name"), "msg": "Value error, name must end in A", "input": "modelX", - "ctx": {"error": "name must end in A"}, + "ctx": {"error": HasRepr("ValueError('name must end in A')")}, "url": match_pydantic_error_url("value_error"), } ) diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py index aa989c612..931f08fc1 100644 --- a/tests/test_multi_body_errors.py +++ b/tests/test_multi_body_errors.py @@ -51,7 +51,7 @@ def test_jsonable_encoder_requiring_error(): "loc": ["body", 0, "age"], "msg": "Input should be greater than 0", "input": -1.0, - "ctx": {"gt": 0.0}, + "ctx": {"gt": "0"}, "url": match_pydantic_error_url("greater_than"), } ] @@ -84,9 +84,23 @@ def test_put_incorrect_body_multiple(): "input": {"age": "five"}, "url": match_pydantic_error_url("missing"), }, + { + "ctx": {"class": "Decimal"}, + "input": "five", + "loc": ["body", 0, "age", "is-instance[Decimal]"], + "msg": "Input should be an instance of Decimal", + "type": "is_instance_of", + "url": match_pydantic_error_url("is_instance_of"), + }, { "type": "decimal_parsing", - "loc": ["body", 0, "age"], + "loc": [ + "body", + 0, + "age", + "function-after[to_decimal(), " + "union[int,constrained-str,function-plain[str()]]]", + ], "msg": "Input should be a valid decimal", "input": "five", }, @@ -97,9 +111,23 @@ def test_put_incorrect_body_multiple(): "input": {"age": "six"}, "url": match_pydantic_error_url("missing"), }, + { + "ctx": {"class": "Decimal"}, + "input": "six", + "loc": ["body", 1, "age", "is-instance[Decimal]"], + "msg": "Input should be an instance of Decimal", + "type": "is_instance_of", + "url": match_pydantic_error_url("is_instance_of"), + }, { "type": "decimal_parsing", - "loc": ["body", 1, "age"], + "loc": [ + "body", + 1, + "age", + "function-after[to_decimal(), " + "union[int,constrained-str,function-plain[str()]]]", + ], "msg": "Input should be a valid decimal", "input": "six", }, From 10b4c31f063d9a098e69dfc48a3a62028fab1261 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Aug 2023 15:47:35 +0000 Subject: [PATCH 054/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index caf74c353..91574addd 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). * 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo). * 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo). From 059fb128926ae06a52af472bd91b216ce45a5997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 3 Aug 2023 17:59:41 +0200 Subject: [PATCH 055/317] =?UTF-8?q?=F0=9F=94=A7=20Update=20the=20Question?= =?UTF-8?q?=20template=20to=20ask=20for=20the=20Pydantic=20version=20(#100?= =?UTF-8?q?00)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/DISCUSSION_TEMPLATE/questions.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml index 3726b7d18..98424a341 100644 --- a/.github/DISCUSSION_TEMPLATE/questions.yml +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -123,6 +123,20 @@ body: ``` validations: required: true + - type: input + id: pydantic-version + attributes: + label: Pydantic Version + description: | + What Pydantic version are you using? + + You can find the Pydantic version with: + + ```bash + python -c "import pydantic; print(pydantic.version.VERSION)" + ``` + validations: + required: true - type: input id: python-version attributes: From 3af7265a435ebb1016dfa190c196afb045451d4f Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Aug 2023 16:00:19 +0000 Subject: [PATCH 056/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 91574addd..c85ef0ad2 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). * 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo). From 86e4e9f8f9266e471192aefaac7ef55cee957a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 19:47:42 +0200 Subject: [PATCH 057/317] =?UTF-8?q?=F0=9F=94=A7=20Restore=20MkDocs=20Mater?= =?UTF-8?q?ial=20pin=20after=20the=20fix=20(#10001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index eb816b72f..dedf23fb9 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -51,7 +51,7 @@ jobs: # Install MkDocs Material Insiders here just to put it in the cache for the rest of the steps - name: Install Material for MkDocs Insiders if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true' - run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git@9.1.21-insiders-4.38.0 + run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git - name: Export Language Codes id: show-langs run: | @@ -86,7 +86,7 @@ jobs: run: pip install -r requirements-docs.txt - name: Install Material for MkDocs Insiders if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true' - run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git@9.1.21-insiders-4.38.0 + run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git - name: Update Languages run: python ./scripts/docs.py update-languages - uses: actions/cache@v3 From b3a1f910048ea60a108a8f40ee5643778047b344 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 4 Aug 2023 17:48:24 +0000 Subject: [PATCH 058/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c85ef0ad2..dc61f76f2 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🔧 Restore MkDocs Material pin after the fix. PR [#10001](https://github.com/tiangolo/fastapi/pull/10001) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). * 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo). From ebdf952545cbd95a16d21d48421fea7aa07e3b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 20:18:38 +0200 Subject: [PATCH 059/317] =?UTF-8?q?=F0=9F=91=B7=20Add=20GitHub=20Actions?= =?UTF-8?q?=20step=20dump=20context=20to=20debug=20external=20failures=20(?= =?UTF-8?q?#10008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 4 ++++ .github/workflows/label-approved.yml | 4 ++++ .github/workflows/latest-changes.yml | 4 ++++ .github/workflows/notify-translations.yml | 4 ++++ .github/workflows/people.yml | 4 ++++ .github/workflows/smokeshow.yml | 4 ++++ .github/workflows/test.yml | 16 ++++++++++++++++ 7 files changed, 40 insertions(+) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 324623103..bb967fa11 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -19,6 +19,10 @@ jobs: if: github.repository_owner == 'tiangolo' runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: tiangolo/issue-manager@0.4.0 with: token: ${{ secrets.FASTAPI_ISSUE_MANAGER }} diff --git a/.github/workflows/label-approved.yml b/.github/workflows/label-approved.yml index 976d29f74..2113c468a 100644 --- a/.github/workflows/label-approved.yml +++ b/.github/workflows/label-approved.yml @@ -9,6 +9,10 @@ jobs: if: github.repository_owner == 'tiangolo' runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: docker://tiangolo/label-approved:0.0.2 with: token: ${{ secrets.FASTAPI_LABEL_APPROVED }} diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 0461f3dd3..e38870f46 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -20,6 +20,10 @@ jobs: latest-changes: runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 with: # To allow latest-changes to commit to the main branch diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml index cd7affbc3..44ee83ec0 100644 --- a/.github/workflows/notify-translations.yml +++ b/.github/workflows/notify-translations.yml @@ -19,6 +19,10 @@ jobs: notify-translations: runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 # Allow debugging with tmate - name: Setup tmate session diff --git a/.github/workflows/people.yml b/.github/workflows/people.yml index aa7f34464..4480a1427 100644 --- a/.github/workflows/people.yml +++ b/.github/workflows/people.yml @@ -15,6 +15,10 @@ jobs: if: github.repository_owner == 'tiangolo' runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 # Ref: https://github.com/actions/runner/issues/2033 - name: Fix git safe.directory in container diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index c6d894d9f..4e689d95c 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -14,6 +14,10 @@ jobs: runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/setup-python@v4 with: python-version: '3.9' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbaf759c3..6a512a019 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,10 @@ jobs: lint: runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 @@ -42,6 +46,10 @@ jobs: pydantic-version: ["pydantic-v1", "pydantic-v2"] fail-fast: false steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 @@ -80,6 +88,10 @@ jobs: needs: [test] runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: @@ -110,6 +122,10 @@ jobs: - coverage-combine runs-on: ubuntu-latest steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: From d943e02232081a795874b00e399dcb0a0b179daf Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 4 Aug 2023 18:19:22 +0000 Subject: [PATCH 060/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index dc61f76f2..e45373f7b 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 👷 Add GitHub Actions step dump context to debug external failures. PR [#10008](https://github.com/tiangolo/fastapi/pull/10008) by [@tiangolo](https://github.com/tiangolo). * 🔧 Restore MkDocs Material pin after the fix. PR [#10001](https://github.com/tiangolo/fastapi/pull/10001) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo). * ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). From 19a2c3bb54ecef5fab936b45e4c262b283e100f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 22:47:07 +0200 Subject: [PATCH 061/317] =?UTF-8?q?=E2=9C=A8=20Enable=20Pydantic's=20seria?= =?UTF-8?q?lization=20mode=20for=20responses,=20add=20support=20for=20Pyda?= =?UTF-8?q?ntic's=20`computed=5Ffield`,=20better=20OpenAPI=20for=20respons?= =?UTF-8?q?e=20models,=20proper=20required=20attributes,=20better=20genera?= =?UTF-8?q?ted=20clients=20(#10011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Enable Pydantic's serialization mode for responses * ✅ Update tests with new Pydantic v2 serialization mode * ✅ Add a test for Pydantic v2's computed_field --- fastapi/routing.py | 4 +- tests/test_computed_fields.py | 77 +++++++ tests/test_filter_pydantic_sub_model_pv2.py | 8 +- .../test_body_updates/test_tutorial001.py | 210 ++++++++++++++--- .../test_tutorial001_py310.py | 211 +++++++++++++++--- .../test_tutorial001_py39.py | 211 +++++++++++++++--- .../test_dataclasses/test_tutorial002.py | 12 +- .../test_dataclasses/test_tutorial003.py | 185 ++++++++++++--- .../test_extra_models/test_tutorial003.py | 13 +- .../test_tutorial003_py310.py | 13 +- .../test_tutorial004.py | 155 +++++++++++-- .../test_tutorial005.py | 155 +++++++++++-- .../test_tutorial005_py310.py | 156 +++++++++++-- .../test_tutorial005_py39.py | 156 +++++++++++-- .../test_response_model/test_tutorial003.py | 8 +- .../test_tutorial003_01.py | 8 +- .../test_tutorial003_01_py310.py | 8 +- .../test_tutorial003_py310.py | 8 +- .../test_response_model/test_tutorial004.py | 8 +- .../test_tutorial004_py310.py | 8 +- .../test_tutorial004_py39.py | 8 +- .../test_response_model/test_tutorial005.py | 8 +- .../test_tutorial005_py310.py | 8 +- .../test_response_model/test_tutorial006.py | 8 +- .../test_tutorial006_py310.py | 8 +- .../test_security/test_tutorial005.py | 8 +- .../test_security/test_tutorial005_an.py | 8 +- .../test_tutorial005_an_py310.py | 8 +- .../test_security/test_tutorial005_an_py39.py | 8 +- .../test_security/test_tutorial005_py310.py | 8 +- .../test_security/test_tutorial005_py39.py | 8 +- 31 files changed, 1446 insertions(+), 256 deletions(-) create mode 100644 tests/test_computed_fields.py diff --git a/fastapi/routing.py b/fastapi/routing.py index d8ff0579c..6efd40ff3 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -448,9 +448,7 @@ class APIRoute(routing.Route): self.response_field = create_response_field( name=response_name, type_=self.response_model, - # TODO: This should actually set mode='serialization', just, that changes the schemas - # mode="serialization", - mode="validation", + mode="serialization", ) # Create a clone of the field, so that a Pydantic submodel is not returned # as is just because it's an instance of a subclass of a more limited class diff --git a/tests/test_computed_fields.py b/tests/test_computed_fields.py new file mode 100644 index 000000000..5286507b2 --- /dev/null +++ b/tests/test_computed_fields.py @@ -0,0 +1,77 @@ +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from .utils import needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client(): + app = FastAPI() + + from pydantic import BaseModel, computed_field + + class Rectangle(BaseModel): + width: int + length: int + + @computed_field + @property + def area(self) -> int: + return self.width * self.length + + @app.get("/") + def read_root() -> Rectangle: + return Rectangle(width=3, length=4) + + client = TestClient(app) + return client + + +@needs_pydanticv2 +def test_get(client: TestClient): + response = client.get("/") + assert response.status_code == 200, response.text + assert response.json() == {"width": 3, "length": 4, "area": 12} + + +@needs_pydanticv2 +def test_openapi_schema(client: TestClient): + 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": { + "/": { + "get": { + "summary": "Read Root", + "operationId": "read_root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Rectangle"} + } + }, + } + }, + } + } + }, + "components": { + "schemas": { + "Rectangle": { + "properties": { + "width": {"type": "integer", "title": "Width"}, + "length": {"type": "integer", "title": "Length"}, + "area": {"type": "integer", "title": "Area", "readOnly": True}, + }, + "type": "object", + "required": ["width", "length", "area"], + "title": "Rectangle", + } + } + }, + } diff --git a/tests/test_filter_pydantic_sub_model_pv2.py b/tests/test_filter_pydantic_sub_model_pv2.py index ae12179bd..9f5e6b08f 100644 --- a/tests/test_filter_pydantic_sub_model_pv2.py +++ b/tests/test_filter_pydantic_sub_model_pv2.py @@ -1,7 +1,7 @@ from typing import Optional import pytest -from dirty_equals import HasRepr, IsDict +from dirty_equals import HasRepr, IsDict, IsOneOf from fastapi import Depends, FastAPI from fastapi.exceptions import ResponseValidationError from fastapi.testclient import TestClient @@ -139,7 +139,11 @@ def test_openapi_schema(client: TestClient): }, "ModelA": { "title": "ModelA", - "required": ["name", "foo"], + "required": IsOneOf( + ["name", "description", "foo"], + # TODO remove when deprecating Pydantic v1 + ["name", "foo"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001.py b/tests/test_tutorial/test_body_updates/test_tutorial001.py index b02f7c81c..f1a46210a 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001.py @@ -1,7 +1,8 @@ import pytest -from dirty_equals import IsDict from fastapi.testclient import TestClient +from ...utils import needs_pydanticv1, needs_pydanticv2 + @pytest.fixture(name="client") def get_client(): @@ -36,7 +37,181 @@ def test_put(client: TestClient): } +@needs_pydanticv2 def test_openapi_schema(client: TestClient): + 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": { + "/items/{item_id}": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Read Item", + "operationId": "read_item_items__item_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + }, + "put": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + }, + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "type": "object", + "properties": { + "name": { + "title": "Name", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": { + "title": "Price", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "type": "object", + "required": ["name", "description", "price", "tax", "tags"], + "properties": { + "name": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Name", + }, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": { + "anyOf": [{"type": "number"}, {"type": "null"}], + "title": "Price", + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_pydanticv1 +def test_openapi_schema_pv1(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -124,36 +299,9 @@ def test_openapi_schema(client: TestClient): "title": "Item", "type": "object", "properties": { - "name": IsDict( - { - "title": "Name", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Name", "type": "string"} - ), - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - "price": IsDict( - { - "title": "Price", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Price", "type": "number"} - ), + "name": {"title": "Name", "type": "string"}, + "description": {"title": "Description", "type": "string"}, + "price": {"title": "Price", "type": "number"}, "tax": {"title": "Tax", "type": "number", "default": 10.5}, "tags": { "title": "Tags", diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py index 4af2652a7..ab696e4c8 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py @@ -1,8 +1,7 @@ import pytest -from dirty_equals import IsDict from fastapi.testclient import TestClient -from ...utils import needs_py310 +from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2 @pytest.fixture(name="client") @@ -41,7 +40,182 @@ def test_put(client: TestClient): @needs_py310 +@needs_pydanticv2 def test_openapi_schema(client: TestClient): + 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": { + "/items/{item_id}": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Read Item", + "operationId": "read_item_items__item_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + }, + "put": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + }, + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "type": "object", + "properties": { + "name": { + "title": "Name", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": { + "title": "Price", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "type": "object", + "required": ["name", "description", "price", "tax", "tags"], + "properties": { + "name": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Name", + }, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": { + "anyOf": [{"type": "number"}, {"type": "null"}], + "title": "Price", + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_py310 +@needs_pydanticv1 +def test_openapi_schema_pv1(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -129,36 +303,9 @@ def test_openapi_schema(client: TestClient): "title": "Item", "type": "object", "properties": { - "name": IsDict( - { - "title": "Name", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Name", "type": "string"} - ), - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - "price": IsDict( - { - "title": "Price", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Price", "type": "number"} - ), + "name": {"title": "Name", "type": "string"}, + "description": {"title": "Description", "type": "string"}, + "price": {"title": "Price", "type": "number"}, "tax": {"title": "Tax", "type": "number", "default": 10.5}, "tags": { "title": "Tags", diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py index 832f45388..2ee6a5cb4 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py @@ -1,8 +1,7 @@ import pytest -from dirty_equals import IsDict from fastapi.testclient import TestClient -from ...utils import needs_py39 +from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2 @pytest.fixture(name="client") @@ -41,7 +40,182 @@ def test_put(client: TestClient): @needs_py39 +@needs_pydanticv2 def test_openapi_schema(client: TestClient): + 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": { + "/items/{item_id}": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Read Item", + "operationId": "read_item_items__item_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + }, + "put": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "string"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + }, + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "type": "object", + "properties": { + "name": { + "title": "Name", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": { + "title": "Price", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "type": "object", + "required": ["name", "description", "price", "tax", "tags"], + "properties": { + "name": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Name", + }, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": { + "anyOf": [{"type": "number"}, {"type": "null"}], + "title": "Price", + }, + "tax": {"title": "Tax", "type": "number", "default": 10.5}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_py39 +@needs_pydanticv1 +def test_openapi_schema_pv1(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -129,36 +303,9 @@ def test_openapi_schema(client: TestClient): "title": "Item", "type": "object", "properties": { - "name": IsDict( - { - "title": "Name", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Name", "type": "string"} - ), - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - "price": IsDict( - { - "title": "Price", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Price", "type": "number"} - ), + "name": {"title": "Name", "type": "string"}, + "description": {"title": "Description", "type": "string"}, + "price": {"title": "Price", "type": "number"}, "tax": {"title": "Tax", "type": "number", "default": 10.5}, "tags": { "title": "Tags", diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial002.py b/tests/test_tutorial/test_dataclasses/test_tutorial002.py index 7d88e2861..4146f4cd6 100644 --- a/tests/test_tutorial/test_dataclasses/test_tutorial002.py +++ b/tests/test_tutorial/test_dataclasses/test_tutorial002.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.dataclasses.tutorial002 import app @@ -21,8 +21,7 @@ def test_get_item(): def test_openapi_schema(): response = client.get("/openapi.json") assert response.status_code == 200 - data = response.json() - assert data == { + assert response.json() == { "openapi": "3.1.0", "info": {"title": "FastAPI", "version": "0.1.0"}, "paths": { @@ -47,7 +46,11 @@ def test_openapi_schema(): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "price", "tags", "description", "tax"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, @@ -57,7 +60,6 @@ def test_openapi_schema(): "title": "Tags", "type": "array", "items": {"type": "string"}, - "default": [], } ) | IsDict( diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial003.py b/tests/test_tutorial/test_dataclasses/test_tutorial003.py index 597757e09..2e5809914 100644 --- a/tests/test_tutorial/test_dataclasses/test_tutorial003.py +++ b/tests/test_tutorial/test_dataclasses/test_tutorial003.py @@ -1,8 +1,9 @@ -from dirty_equals import IsDict from fastapi.testclient import TestClient from docs_src.dataclasses.tutorial003 import app +from ...utils import needs_pydanticv1, needs_pydanticv2 + client = TestClient(app) @@ -52,6 +53,7 @@ def test_get_authors(): ] +@needs_pydanticv2 def test_openapi_schema(): response = client.get("/openapi.json") assert response.status_code == 200 @@ -77,7 +79,7 @@ def test_openapi_schema(): "schema": { "title": "Items", "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, + "items": {"$ref": "#/components/schemas/ItemInput"}, } } }, @@ -132,26 +134,164 @@ def test_openapi_schema(): "schemas": { "Author": { "title": "Author", + "required": ["name", "items"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "items": { + "title": "Items", + "type": "array", + "items": {"$ref": "#/components/schemas/ItemOutput"}, + }, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "ItemInput": { + "title": "Item", "required": ["name"], "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "items": IsDict( - { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "required": ["name", "description"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_pydanticv1 +def test_openapi_schema_pv1(): + response = client.get("/openapi.json") + assert response.status_code == 200 + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/authors/{author_id}/items/": { + "post": { + "summary": "Create Author Items", + "operationId": "create_author_items_authors__author_id__items__post", + "parameters": [ + { + "required": True, + "schema": {"title": "Author Id", "type": "string"}, + "name": "author_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "title": "Items", + "type": "array", + "items": {"$ref": "#/components/schemas/Item"}, + } } - ), + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Author"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + "/authors/": { + "get": { + "summary": "Get Authors", + "operationId": "get_authors_authors__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Get Authors Authors Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/Author" + }, + } + } + }, + } + }, + } + }, + }, + "components": { + "schemas": { + "Author": { + "title": "Author", + "required": ["name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "items": { + "title": "Items", + "type": "array", + "items": {"$ref": "#/components/schemas/Item"}, + }, }, }, "HTTPValidationError": { @@ -171,16 +311,7 @@ def test_openapi_schema(): "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), + "description": {"title": "Description", "type": "string"}, }, }, "ValidationError": { diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003.py b/tests/test_tutorial/test_extra_models/test_tutorial003.py index 21192b7db..0ccb99948 100644 --- a/tests/test_tutorial/test_extra_models/test_tutorial003.py +++ b/tests/test_tutorial/test_extra_models/test_tutorial003.py @@ -1,3 +1,4 @@ +from dirty_equals import IsOneOf from fastapi.testclient import TestClient from docs_src.extra_models.tutorial003 import app @@ -76,7 +77,11 @@ def test_openapi_schema(): "schemas": { "PlaneItem": { "title": "PlaneItem", - "required": ["description", "size"], + "required": IsOneOf( + ["description", "type", "size"], + # TODO: remove when deprecating Pydantic v1 + ["description", "size"], + ), "type": "object", "properties": { "description": {"title": "Description", "type": "string"}, @@ -86,7 +91,11 @@ def test_openapi_schema(): }, "CarItem": { "title": "CarItem", - "required": ["description"], + "required": IsOneOf( + ["description", "type"], + # TODO: remove when deprecating Pydantic v1 + ["description"], + ), "type": "object", "properties": { "description": {"title": "Description", "type": "string"}, diff --git a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py b/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py index c17ddbbe1..b2fe65fd9 100644 --- a/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py +++ b/tests/test_tutorial/test_extra_models/test_tutorial003_py310.py @@ -1,4 +1,5 @@ import pytest +from dirty_equals import IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -86,7 +87,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "PlaneItem": { "title": "PlaneItem", - "required": ["description", "size"], + "required": IsOneOf( + ["description", "type", "size"], + # TODO: remove when deprecating Pydantic v1 + ["description", "size"], + ), "type": "object", "properties": { "description": {"title": "Description", "type": "string"}, @@ -96,7 +101,11 @@ def test_openapi_schema(client: TestClient): }, "CarItem": { "title": "CarItem", - "required": ["description"], + "required": IsOneOf( + ["description", "type"], + # TODO: remove when deprecating Pydantic v1 + ["description"], + ), "type": "object", "properties": { "description": {"title": "Description", "type": "string"}, diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py index dd123f48d..3ffc0bca7 100644 --- a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py @@ -1,8 +1,9 @@ -from dirty_equals import IsDict from fastapi.testclient import TestClient from docs_src.path_operation_advanced_configuration.tutorial004 import app +from ...utils import needs_pydanticv1, needs_pydanticv2 + client = TestClient(app) @@ -18,7 +19,137 @@ def test_query_params_str_validations(): } +@needs_pydanticv2 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": { + "/items/": { + "post": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Create an item", + "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + } + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "required": ["name", "description", "price", "tax", "tags"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_pydanticv1 +def test_openapi_schema_pv1(): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -69,27 +200,9 @@ def test_openapi_schema(): "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), + "description": {"title": "Description", "type": "string"}, "price": {"title": "Price", "type": "number"}, - "tax": IsDict( - { - "title": "Tax", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Tax", "type": "number"} - ), + "tax": {"title": "Tax", "type": "number"}, "tags": { "title": "Tags", "uniqueItems": True, diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py index e7e9a982e..ff98295a6 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py @@ -1,8 +1,9 @@ -from dirty_equals import IsDict from fastapi.testclient import TestClient from docs_src.path_operation_configuration.tutorial005 import app +from ...utils import needs_pydanticv1, needs_pydanticv2 + client = TestClient(app) @@ -18,7 +19,137 @@ def test_query_params_str_validations(): } +@needs_pydanticv2 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": { + "/items/": { + "post": { + "responses": { + "200": { + "description": "The created item", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Create an item", + "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + } + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "required": ["name", "description", "price", "tax", "tags"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_pydanticv1 +def test_openapi_schema_pv1(): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -69,27 +200,9 @@ def test_openapi_schema(): "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), + "description": {"title": "Description", "type": "string"}, "price": {"title": "Price", "type": "number"}, - "tax": IsDict( - { - "title": "Tax", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Tax", "type": "number"} - ), + "tax": {"title": "Tax", "type": "number"}, "tags": { "title": "Tags", "uniqueItems": True, diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py index ebfeb809c..ad1c09eae 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py @@ -1,8 +1,7 @@ import pytest -from dirty_equals import IsDict from fastapi.testclient import TestClient -from ...utils import needs_py310 +from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2 @pytest.fixture(name="client") @@ -27,7 +26,138 @@ def test_query_params_str_validations(client: TestClient): @needs_py310 +@needs_pydanticv2 def test_openapi_schema(client: TestClient): + 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": { + "/items/": { + "post": { + "responses": { + "200": { + "description": "The created item", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Create an item", + "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + } + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "required": ["name", "description", "price", "tax", "tags"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_py310 +@needs_pydanticv1 +def test_openapi_schema_pv1(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -78,27 +208,9 @@ def test_openapi_schema(client: TestClient): "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), + "description": {"title": "Description", "type": "string"}, "price": {"title": "Price", "type": "number"}, - "tax": IsDict( - { - "title": "Tax", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Tax", "type": "number"} - ), + "tax": {"title": "Tax", "type": "number"}, "tags": { "title": "Tags", "uniqueItems": True, diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py index 8e79afe96..045d1d402 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py @@ -1,8 +1,7 @@ import pytest -from dirty_equals import IsDict from fastapi.testclient import TestClient -from ...utils import needs_py39 +from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2 @pytest.fixture(name="client") @@ -27,7 +26,138 @@ def test_query_params_str_validations(client: TestClient): @needs_py39 +@needs_pydanticv2 def test_openapi_schema(client: TestClient): + 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": { + "/items/": { + "post": { + "responses": { + "200": { + "description": "The created item", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemOutput" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Create an item", + "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ItemInput"} + } + }, + "required": True, + }, + } + } + }, + "components": { + "schemas": { + "ItemInput": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ItemOutput": { + "title": "Item", + "required": ["name", "description", "price", "tax", "tags"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "price": {"title": "Price", "type": "number"}, + "tax": { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + }, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, + } + + +# TODO: remove when deprecating Pydantic v1 +@needs_py39 +@needs_pydanticv1 +def test_openapi_schema_pv1(client: TestClient): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == { @@ -78,27 +208,9 @@ def test_openapi_schema(client: TestClient): "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), + "description": {"title": "Description", "type": "string"}, "price": {"title": "Price", "type": "number"}, - "tax": IsDict( - { - "title": "Tax", - "anyOf": [{"type": "number"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Tax", "type": "number"} - ), + "tax": {"title": "Tax", "type": "number"}, "tags": { "title": "Tags", "uniqueItems": True, diff --git a/tests/test_tutorial/test_response_model/test_tutorial003.py b/tests/test_tutorial/test_response_model/test_tutorial003.py index 20221399b..384c8e0f1 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.response_model.tutorial003 import app @@ -70,7 +70,11 @@ def test_openapi_schema(): "schemas": { "UserOut": { "title": "UserOut", - "required": ["username", "email"], + "required": IsOneOf( + ["username", "email", "full_name"], + # TODO: remove when deprecating Pydantic v1 + ["username", "email"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_01.py b/tests/test_tutorial/test_response_model/test_tutorial003_01.py index e8f0658f4..3a6a0b20d 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_01.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_01.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.response_model.tutorial003_01 import app @@ -70,7 +70,11 @@ def test_openapi_schema(): "schemas": { "BaseUser": { "title": "BaseUser", - "required": ["username", "email"], + "required": IsOneOf( + ["username", "email", "full_name"], + # TODO: remove when deprecating Pydantic v1 + ["username", "email"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py b/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py index a69f8cc8d..6985b9de6 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -79,7 +79,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "BaseUser": { "title": "BaseUser", - "required": ["username", "email"], + "required": IsOneOf( + ["username", "email", "full_name"], + # TODO: remove when deprecating Pydantic v1 + ["username", "email"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial003_py310.py b/tests/test_tutorial/test_response_model/test_tutorial003_py310.py index 64dcd6cbd..3a3aee38a 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial003_py310.py +++ b/tests/test_tutorial/test_response_model/test_tutorial003_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -79,7 +79,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "UserOut": { "title": "UserOut", - "required": ["username", "email"], + "required": IsOneOf( + ["username", "email", "full_name"], + # TODO: remove when deprecating Pydantic v1 + ["username", "email"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial004.py b/tests/test_tutorial/test_response_model/test_tutorial004.py index 8beb847d1..e9bde18dd 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial004.py +++ b/tests/test_tutorial/test_response_model/test_tutorial004.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.response_model.tutorial004 import app @@ -79,7 +79,11 @@ def test_openapi_schema(): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax", "tags"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial004_py310.py b/tests/test_tutorial/test_response_model/test_tutorial004_py310.py index 28eb88c34..6f8a3cbea 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial004_py310.py +++ b/tests/test_tutorial/test_response_model/test_tutorial004_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -87,7 +87,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax", "tags"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial004_py39.py b/tests/test_tutorial/test_response_model/test_tutorial004_py39.py index 9e1a21f8d..cfaa1eba2 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial004_py39.py +++ b/tests/test_tutorial/test_response_model/test_tutorial004_py39.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py39 @@ -87,7 +87,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax", "tags"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial005.py b/tests/test_tutorial/test_response_model/test_tutorial005.py index 06e5d0fd1..b20864c07 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial005.py +++ b/tests/test_tutorial/test_response_model/test_tutorial005.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.response_model.tutorial005 import app @@ -102,7 +102,11 @@ def test_openapi_schema(): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial005_py310.py b/tests/test_tutorial/test_response_model/test_tutorial005_py310.py index 0f1566243..de552c8f2 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial005_py310.py +++ b/tests/test_tutorial/test_response_model/test_tutorial005_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -112,7 +112,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial006.py b/tests/test_tutorial/test_response_model/test_tutorial006.py index 6e6152b9f..1e47e2ead 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial006.py +++ b/tests/test_tutorial/test_response_model/test_tutorial006.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.response_model.tutorial006 import app @@ -102,7 +102,11 @@ def test_openapi_schema(): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_response_model/test_tutorial006_py310.py b/tests/test_tutorial/test_response_model/test_tutorial006_py310.py index 9a980ab5b..40058b12d 100644 --- a/tests/test_tutorial/test_response_model/test_tutorial006_py310.py +++ b/tests/test_tutorial/test_response_model/test_tutorial006_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -112,7 +112,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "Item": { "title": "Item", - "required": ["name", "price"], + "required": IsOneOf( + ["name", "description", "price", "tax"], + # TODO: remove when deprecating Pydantic v1 + ["name", "price"], + ), "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005.py b/tests/test_tutorial/test_security/test_tutorial005.py index 22ae76f42..c669c306d 100644 --- a/tests/test_tutorial/test_security/test_tutorial005.py +++ b/tests/test_tutorial/test_security/test_tutorial005.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.security.tutorial005 import ( @@ -267,7 +267,11 @@ def test_openapi_schema(): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005_an.py b/tests/test_tutorial/test_security/test_tutorial005_an.py index 07239cc89..aaab04f78 100644 --- a/tests/test_tutorial/test_security/test_tutorial005_an.py +++ b/tests/test_tutorial/test_security/test_tutorial005_an.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.security.tutorial005_an import ( @@ -267,7 +267,11 @@ def test_openapi_schema(): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005_an_py310.py b/tests/test_tutorial/test_security/test_tutorial005_an_py310.py index 1ab836639..243d0773c 100644 --- a/tests/test_tutorial/test_security/test_tutorial005_an_py310.py +++ b/tests/test_tutorial/test_security/test_tutorial005_an_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005_an_py39.py b/tests/test_tutorial/test_security/test_tutorial005_an_py39.py index 6aabbe04a..17a3f9aa2 100644 --- a/tests/test_tutorial/test_security/test_tutorial005_an_py39.py +++ b/tests/test_tutorial/test_security/test_tutorial005_an_py39.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py39 @@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005_py310.py b/tests/test_tutorial/test_security/test_tutorial005_py310.py index c21884df8..06455cd63 100644 --- a/tests/test_tutorial/test_security/test_tutorial005_py310.py +++ b/tests/test_tutorial/test_security/test_tutorial005_py310.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py310 @@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, diff --git a/tests/test_tutorial/test_security/test_tutorial005_py39.py b/tests/test_tutorial/test_security/test_tutorial005_py39.py index 170c5d60b..9455bfb4e 100644 --- a/tests/test_tutorial/test_security/test_tutorial005_py39.py +++ b/tests/test_tutorial/test_security/test_tutorial005_py39.py @@ -1,5 +1,5 @@ import pytest -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from ...utils import needs_py39 @@ -295,7 +295,11 @@ def test_openapi_schema(client: TestClient): "schemas": { "User": { "title": "User", - "required": ["username"], + "required": IsOneOf( + ["username", "email", "full_name", "disabled"], + # TODO: remove when deprecating Pydantic v1 + ["username"], + ), "type": "object", "properties": { "username": {"title": "Username", "type": "string"}, From 1c2051473865fcc76284eac177104fc342b51aeb Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 4 Aug 2023 20:47:42 +0000 Subject: [PATCH 062/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index e45373f7b..32224ffc0 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo). * 👷 Add GitHub Actions step dump context to debug external failures. PR [#10008](https://github.com/tiangolo/fastapi/pull/10008) by [@tiangolo](https://github.com/tiangolo). * 🔧 Restore MkDocs Material pin after the fix. PR [#10001](https://github.com/tiangolo/fastapi/pull/10001) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo). From 944c59180354e81988a7ed67454f3fdb879f5b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 22:50:34 +0200 Subject: [PATCH 063/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 32224ffc0..c3756ebd3 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,23 +2,34 @@ ## Latest Changes +### Features + * ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo). + +### Refactors + +* ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). +* ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). + +### Translations + +* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). +* 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). + +### Internal + * 👷 Add GitHub Actions step dump context to debug external failures. PR [#10008](https://github.com/tiangolo/fastapi/pull/10008) by [@tiangolo](https://github.com/tiangolo). * 🔧 Restore MkDocs Material pin after the fix. PR [#10001](https://github.com/tiangolo/fastapi/pull/10001) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update the Question template to ask for the Pydantic version. PR [#10000](https://github.com/tiangolo/fastapi/pull/10000) by [@tiangolo](https://github.com/tiangolo). -* ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). * 📍 Update MkDocs Material dependencies. PR [#9986](https://github.com/tiangolo/fastapi/pull/9986) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People. PR [#9999](https://github.com/tiangolo/fastapi/pull/9999) by [@tiangolo](https://github.com/tiangolo). * 🐳 Update Dockerfile with compatibility versions, to upgrade later. PR [#9998](https://github.com/tiangolo/fastapi/pull/9998) by [@tiangolo](https://github.com/tiangolo). * ➕ Add pydantic-settings to FastAPI People dependencies. PR [#9988](https://github.com/tiangolo/fastapi/pull/9988) by [@tiangolo](https://github.com/tiangolo). * ♻️ Update FastAPI People logic with new Pydantic. PR [#9985](https://github.com/tiangolo/fastapi/pull/9985) by [@tiangolo](https://github.com/tiangolo). -* ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). -* 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). * 🍱 Update sponsors, Fern badge. PR [#9982](https://github.com/tiangolo/fastapi/pull/9982) by [@tiangolo](https://github.com/tiangolo). * 👷 Deploy docs to Cloudflare Pages. PR [#9978](https://github.com/tiangolo/fastapi/pull/9978) by [@tiangolo](https://github.com/tiangolo). * 🔧 Update sponsor Fern. PR [#9979](https://github.com/tiangolo/fastapi/pull/9979) by [@tiangolo](https://github.com/tiangolo). * 👷 Update CI debug mode with Tmate. PR [#9977](https://github.com/tiangolo/fastapi/pull/9977) by [@tiangolo](https://github.com/tiangolo). -* 🌐 Remove Vietnamese note about missing translation. PR [#9957](https://github.com/tiangolo/fastapi/pull/9957) by [@tiangolo](https://github.com/tiangolo). ## 0.100.1 From 77d1f69b1f2dced4fc7e2e0e934ffc259a4b4a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 22:57:30 +0200 Subject: [PATCH 064/317] =?UTF-8?q?=F0=9F=93=8C=20Do=20not=20allow=20Pydan?= =?UTF-8?q?tic=202.1.0=20that=20breaks=20(require=202.1.1)=20(#10012)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f0917578f..9b7cca9c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ classifiers = [ ] dependencies = [ "starlette>=0.27.0,<0.28.0", - "pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,<3.0.0", + "pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0", "typing-extensions>=4.5.0", ] dynamic = ["version"] From 89a7cea56157837db092c8047d4c028193c30429 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 4 Aug 2023 20:58:08 +0000 Subject: [PATCH 065/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c3756ebd3..383be69ff 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 📌 Do not allow Pydantic 2.1.0 that breaks (require 2.1.1). PR [#10012](https://github.com/tiangolo/fastapi/pull/10012) by [@tiangolo](https://github.com/tiangolo). ### Features * ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo). From 4b5277744ad57a46fa28ea287012c83ba0a7f4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 22:59:44 +0200 Subject: [PATCH 066/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 383be69ff..a7a5a424e 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,7 +2,6 @@ ## Latest Changes -* 📌 Do not allow Pydantic 2.1.0 that breaks (require 2.1.1). PR [#10012](https://github.com/tiangolo/fastapi/pull/10012) by [@tiangolo](https://github.com/tiangolo). ### Features * ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo). @@ -12,6 +11,10 @@ * ✅ Fix tests for compatibility with pydantic 2.1.1. PR [#9943](https://github.com/tiangolo/fastapi/pull/9943) by [@dmontagu](https://github.com/dmontagu). * ✅ Fix test error in Windows for `jsonable_encoder`. PR [#9840](https://github.com/tiangolo/fastapi/pull/9840) by [@iudeen](https://github.com/iudeen). +### Upgrades + +* 📌 Do not allow Pydantic 2.1.0 that breaks (require 2.1.1). PR [#10012](https://github.com/tiangolo/fastapi/pull/10012) by [@tiangolo](https://github.com/tiangolo). + ### Translations * 🌐 Add Russian translation for `docs/ru/docs/tutorial/security/index.md`. PR [#9963](https://github.com/tiangolo/fastapi/pull/9963) by [@eVery1337](https://github.com/eVery1337). From 8adbafc0760c9fd0d97da748545b3a5f92dbb0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 4 Aug 2023 23:00:17 +0200 Subject: [PATCH 067/317] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.10?= =?UTF-8?q?1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 3 +++ fastapi/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index a7a5a424e..5957a73c0 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,9 @@ ## Latest Changes + +## 0.101.0 + ### Features * ✨ Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo). diff --git a/fastapi/__init__.py b/fastapi/__init__.py index 7dfeca0d4..c113ac1fd 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.100.1" +__version__ = "0.101.0" from starlette import status as status From d48a184dd8bf7063265af2b0d44ba101884ed1a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Aug 2023 10:22:39 +0200 Subject: [PATCH 068/317] =?UTF-8?q?=E2=AC=86=20Bump=20mkdocs-material=20fr?= =?UTF-8?q?om=209.1.17=20to=209.1.21=20(#9960)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.1.17 to 9.1.21. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.1.17...9.1.21) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 7152ebf7b..220d1ec3a 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,5 +1,5 @@ -e . -mkdocs-material==9.1.17 +mkdocs-material==9.1.21 mdx-include >=1.4.1,<2.0.0 mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0 typer-cli >=0.0.13,<0.0.14 From 8f316be088d63b9557d85fe25e9c3b26937fa9b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Aug 2023 10:22:58 +0200 Subject: [PATCH 069/317] =?UTF-8?q?=E2=AC=86=20Bump=20mypy=20from=201.4.0?= =?UTF-8?q?=20to=201.4.1=20(#9756)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mypy](https://github.com/python/mypy) from 1.4.0 to 1.4.1. - [Commits](https://github.com/python/mypy/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index abefac685..0113b6f7a 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -2,7 +2,7 @@ pydantic-settings >=2.0.0 pytest >=7.1.3,<8.0.0 coverage[toml] >= 6.5.0,< 8.0 -mypy ==1.4.0 +mypy ==1.4.1 ruff ==0.0.275 black == 23.3.0 httpx >=0.23.0,<0.25.0 From 0148c9508c9c42988503a16c1b909d1d268760dd Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:23:14 +0000 Subject: [PATCH 070/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 5957a73c0..63e27fc98 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ⬆ Bump mkdocs-material from 9.1.17 to 9.1.21. PR [#9960](https://github.com/tiangolo/fastapi/pull/9960) by [@dependabot[bot]](https://github.com/apps/dependabot). ## 0.101.0 From abfcb59fd065843e669e4e3a19bf5540e789c1ab Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:23:39 +0000 Subject: [PATCH 071/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 63e27fc98..621f82d55 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ⬆ Bump mypy from 1.4.0 to 1.4.1. PR [#9756](https://github.com/tiangolo/fastapi/pull/9756) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.1.17 to 9.1.21. PR [#9960](https://github.com/tiangolo/fastapi/pull/9960) by [@dependabot[bot]](https://github.com/apps/dependabot). ## 0.101.0 From 5891be5ff1156e28a7a3128248ce3268ef97118e Mon Sep 17 00:00:00 2001 From: Ahsan Sheraz Date: Sat, 5 Aug 2023 13:24:21 +0500 Subject: [PATCH 072/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Urdu=20translation?= =?UTF-8?q?=20for=20`docs/ur/docs/benchmarks.md`=20(#9974)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ur/docs/benchmarks.md | 52 ++++++++++++++++++++++++++++++++++++++ docs/ur/mkdocs.yml | 1 + 2 files changed, 53 insertions(+) create mode 100644 docs/ur/docs/benchmarks.md create mode 100644 docs/ur/mkdocs.yml diff --git a/docs/ur/docs/benchmarks.md b/docs/ur/docs/benchmarks.md new file mode 100644 index 000000000..9fc793e6f --- /dev/null +++ b/docs/ur/docs/benchmarks.md @@ -0,0 +1,52 @@ +# بینچ مارکس + +انڈیپنڈنٹ ٹیک امپور بینچ مارک **FASTAPI** Uvicorn کے تحت چلنے والی ایپلی کیشنز کو ایک تیز رفتار Python فریم ورک میں سے ایک ، صرف Starlette اور Uvicorn کے نیچے ( FASTAPI کے ذریعہ اندرونی طور پر استعمال کیا جاتا ہے ) (*) + +لیکن جب بینچ مارک اور موازنہ کی جانچ پڑتال کرتے ہو تو آپ کو مندرجہ ذیل بات ذہن میں رکھنی چاہئے. + +## بینچ مارک اور رفتار + +جب آپ بینچ مارک کی جانچ کرتے ہیں تو ، مساوی کے مقابلے میں مختلف اقسام کے متعدد اوزار دیکھنا عام ہے. + +خاص طور پر ، Uvicorn, Starlette اور FastAPI کو دیکھنے کے لئے ( بہت سے دوسرے ٹولز ) کے ساتھ موازنہ کیا گیا. + +ٹول کے ذریعہ حل ہونے والا آسان مسئلہ ، اس کی بہتر کارکردگی ہوگی. اور زیادہ تر بینچ مارک ٹول کے ذریعہ فراہم کردہ اضافی خصوصیات کی جانچ نہیں کرتے ہیں. + +درجہ بندی کی طرح ہے: + +
    +
  • ASGI :Uvicorn سرور
  • +
      +
    • Starlette: (Uvicorn استعمال کرتا ہے) ایک ویب مائیکرو فریم ورک
    • +
        +
      • FastAPI: (Starlette کا استعمال کرتا ہے) ایک API مائکرو فریم ورک جس میں APIs بنانے کے لیے کئی اضافی خصوصیات ہیں، ڈیٹا کی توثیق وغیرہ کے ساتھ۔
      • +
      +
    +
+ +
    +
  • Uvicorn:
  • +
      +
    • بہترین کارکردگی ہوگی، کیونکہ اس میں سرور کے علاوہ زیادہ اضافی کوڈ نہیں ہے۔
    • +
    • آپ براہ راست Uvicorn میں درخواست نہیں لکھیں گے۔ اس کا مطلب یہ ہوگا کہ آپ کے کوڈ میں کم و بیش، کم از کم، Starlette (یا FastAPI) کی طرف سے فراہم کردہ تمام کوڈ شامل کرنا ہوں گے۔ اور اگر آپ نے ایسا کیا تو، آپ کی حتمی ایپلیکیشن کا وہی اوور ہیڈ ہوگا جیسا کہ ایک فریم ورک استعمال کرنے اور آپ کے ایپ کوڈ اور کیڑے کو کم سے کم کرنا۔
    • +
    • اگر آپ Uvicorn کا موازنہ کر رہے ہیں تو اس کا موازنہ Daphne، Hypercorn، uWSGI وغیرہ ایپلیکیشن سرورز سے کریں۔
    • +
    +
+
    +
  • Starlette:
  • +
      +
    • Uvicorn کے بعد اگلی بہترین کارکردگی ہوگی۔ درحقیقت، Starlette چلانے کے لیے Uvicorn کا استعمال کرتی ہے۔ لہذا، یہ شاید زیادہ کوڈ پر عمل درآمد کرکے Uvicorn سے "سست" ہوسکتا ہے۔
    • +
    • لیکن یہ آپ کو آسان ویب ایپلیکیشنز بنانے کے لیے ٹولز فراہم کرتا ہے، راستوں پر مبنی روٹنگ کے ساتھ، وغیرہ۔
    • +
    • اگر آپ سٹارلیٹ کا موازنہ کر رہے ہیں تو اس کا موازنہ Sanic، Flask، Django وغیرہ سے کریں۔ ویب فریم ورکس (یا مائیکرو فریم ورکس)
    • +
    +
+
    +
  • FastAPI:
  • +
      +
    • جس طرح سے Uvicorn Starlette کا استعمال کرتا ہے اور اس سے تیز نہیں ہو سکتا، Starlette FastAPI کا استعمال کرتا ہے، اس لیے یہ اس سے تیز نہیں ہو سکتا۔
    • +
    • Starlette FastAPI کے اوپری حصے میں مزید خصوصیات فراہم کرتا ہے۔ وہ خصوصیات جن کی آپ کو APIs بناتے وقت تقریباً ہمیشہ ضرورت ہوتی ہے، جیسے ڈیٹا کی توثیق اور سیریلائزیشن۔ اور اسے استعمال کرنے سے، آپ کو خودکار دستاویزات مفت میں مل جاتی ہیں (خودکار دستاویزات چلنے والی ایپلی کیشنز میں اوور ہیڈ کو بھی شامل نہیں کرتی ہیں، یہ اسٹارٹ اپ پر تیار ہوتی ہیں)۔
    • +
    • اگر آپ نے FastAPI کا استعمال نہیں کیا ہے اور Starlette کو براہ راست استعمال کیا ہے (یا کوئی دوسرا ٹول، جیسے Sanic، Flask، Responder، وغیرہ) آپ کو تمام ڈیٹا کی توثیق اور سیریلائزیشن کو خود نافذ کرنا ہوگا۔ لہذا، آپ کی حتمی ایپلیکیشن اب بھی وہی اوور ہیڈ ہوگی جیسا کہ اسے FastAPI کا استعمال کرتے ہوئے بنایا گیا تھا۔ اور بہت سے معاملات میں، یہ ڈیٹا کی توثیق اور سیریلائزیشن ایپلی کیشنز میں لکھے گئے کوڈ کی سب سے بڑی مقدار ہے۔
    • +
    • لہذا، FastAPI کا استعمال کرکے آپ ترقیاتی وقت، Bugs، کوڈ کی لائنوں کی بچت کر رہے ہیں، اور شاید آپ کو وہی کارکردگی (یا بہتر) ملے گی اگر آپ اسے استعمال نہیں کرتے (جیسا کہ آپ کو یہ سب اپنے کوڈ میں لاگو کرنا ہوگا۔ )
    • +
    • اگر آپ FastAPI کا موازنہ کر رہے ہیں، تو اس کا موازنہ ویب ایپلیکیشن فریم ورک (یا ٹولز کے سیٹ) سے کریں جو ڈیٹا کی توثیق، سیریلائزیشن اور دستاویزات فراہم کرتا ہے، جیسے Flask-apispec، NestJS، Molten، وغیرہ۔ مربوط خودکار ڈیٹا کی توثیق، سیریلائزیشن اور دستاویزات کے ساتھ فریم ورک۔
    • +
    +
diff --git a/docs/ur/mkdocs.yml b/docs/ur/mkdocs.yml new file mode 100644 index 000000000..de18856f4 --- /dev/null +++ b/docs/ur/mkdocs.yml @@ -0,0 +1 @@ +INHERIT: ../en/mkdocs.yml From 1c919dee3ce73a5b3d134dbb335c0a38af8438b5 Mon Sep 17 00:00:00 2001 From: Aleksandr Pavlov Date: Sat, 5 Aug 2023 12:26:03 +0400 Subject: [PATCH 073/317] =?UTF-8?q?=F0=9F=8C=90=20Add=20Russian=20translat?= =?UTF-8?q?ion=20for=20`docs/ru/docs/tutorial/dependencies/global-dependen?= =?UTF-8?q?cies.md`=20(#9970)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: dedkot Co-authored-by: Vladislav Kramorenko <85196001+Xewus@users.noreply.github.com> --- .../dependencies/global-dependencies.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/ru/docs/tutorial/dependencies/global-dependencies.md diff --git a/docs/ru/docs/tutorial/dependencies/global-dependencies.md b/docs/ru/docs/tutorial/dependencies/global-dependencies.md new file mode 100644 index 000000000..870d42cf5 --- /dev/null +++ b/docs/ru/docs/tutorial/dependencies/global-dependencies.md @@ -0,0 +1,34 @@ +# Глобальные зависимости + +Для некоторых типов приложений может потребоваться добавить зависимости ко всему приложению. + +Подобно тому, как вы можете [добавлять зависимости через параметр `dependencies` в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, вы можете добавлять зависимости сразу ко всему `FastAPI` приложению. + +В этом случае они будут применяться ко всем *операциям пути* в приложении: + +=== "Python 3.9+" + + ```Python hl_lines="16" + {!> ../../../docs_src/dependencies/tutorial012_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="16" + {!> ../../../docs_src/dependencies/tutorial012_an.py!} + ``` + +=== "Python 3.6 non-Annotated" + + !!! tip "Подсказка" + Рекомендуется использовать 'Annotated' версию, если это возможно. + + ```Python hl_lines="15" + {!> ../../../docs_src/dependencies/tutorial012.py!} + ``` + +Все способы [добавления зависимостей в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения. + +## Зависимости для групп *операций пути* + +Позднее, читая о том, как структурировать более крупные [приложения, содержащие много файлов](../../tutorial/bigger-applications.md){.internal-link target=_blank}, вы узнаете, как объявить один параметр dependencies для целой группы *операций пути*. From d86a695db931d623b4225992817d1f9ed8eb259b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:26:40 +0000 Subject: [PATCH 074/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 621f82d55..d656813cb 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Urdu translation for `docs/ur/docs/benchmarks.md`. PR [#9974](https://github.com/tiangolo/fastapi/pull/9974) by [@AhsanSheraz](https://github.com/AhsanSheraz). * ⬆ Bump mypy from 1.4.0 to 1.4.1. PR [#9756](https://github.com/tiangolo/fastapi/pull/9756) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.1.17 to 9.1.21. PR [#9960](https://github.com/tiangolo/fastapi/pull/9960) by [@dependabot[bot]](https://github.com/apps/dependabot). From f2e80fae093e8d219e101715a8f92a15edf0ff31 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:28:26 +0000 Subject: [PATCH 075/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d656813cb..82d256b73 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/global-dependencies.md`. PR [#9970](https://github.com/tiangolo/fastapi/pull/9970) by [@dudyaosuplayer](https://github.com/dudyaosuplayer). * 🌐 Add Urdu translation for `docs/ur/docs/benchmarks.md`. PR [#9974](https://github.com/tiangolo/fastapi/pull/9974) by [@AhsanSheraz](https://github.com/AhsanSheraz). * ⬆ Bump mypy from 1.4.0 to 1.4.1. PR [#9756](https://github.com/tiangolo/fastapi/pull/9756) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.1.17 to 9.1.21. PR [#9960](https://github.com/tiangolo/fastapi/pull/9960) by [@dependabot[bot]](https://github.com/apps/dependabot). From b76112f1a5230a8724994b9fa9fcf5bb417b1e09 Mon Sep 17 00:00:00 2001 From: Reza Rohani Date: Sat, 5 Aug 2023 12:03:08 +0330 Subject: [PATCH 076/317] =?UTF-8?q?=F0=9F=93=9D=20Fix=20code=20highlightin?= =?UTF-8?q?g=20in=20`docs/en/docs/tutorial/bigger-applications.md`=20(#980?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update bigger-applications.md --- docs/en/docs/tutorial/bigger-applications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs/tutorial/bigger-applications.md b/docs/en/docs/tutorial/bigger-applications.md index daa7353a2..26d26475f 100644 --- a/docs/en/docs/tutorial/bigger-applications.md +++ b/docs/en/docs/tutorial/bigger-applications.md @@ -377,7 +377,7 @@ The `router` from `users` would overwrite the one from `items` and we wouldn't b So, to be able to use both of them in the same file, we import the submodules directly: -```Python hl_lines="4" +```Python hl_lines="5" {!../../../docs_src/bigger_applications/app/main.py!} ``` From 0b496ea1f8b75943617c3b69421e2b6bfef46006 Mon Sep 17 00:00:00 2001 From: Vicente Merino <47841749+VicenteMerino@users.noreply.github.com> Date: Sat, 5 Aug 2023 04:34:07 -0400 Subject: [PATCH 077/317] =?UTF-8?q?=F0=9F=93=9D=20Fix=20typo=20in=20`docs/?= =?UTF-8?q?en/docs/contributing.md`=20(#9878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vicente Merino --- docs/en/docs/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md index f968489ae..cfdb607d7 100644 --- a/docs/en/docs/contributing.md +++ b/docs/en/docs/contributing.md @@ -126,7 +126,7 @@ And if you update that local FastAPI source code when you run that Python file a That way, you don't have to "install" your local version to be able to test every change. !!! note "Technical Details" - This only happens when you install using this included `requiements.txt` instead of installing `pip install fastapi` directly. + This only happens when you install using this included `requirements.txt` instead of installing `pip install fastapi` directly. That is because inside of the `requirements.txt` file, the local version of FastAPI is marked to be installed in "editable" mode, with the `-e` option. From 51f5497f3f91efcaf23ee2cf408b8fa61178ed69 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:35:55 +0000 Subject: [PATCH 078/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 82d256b73..92061066f 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 📝 Fix code highlighting in `docs/en/docs/tutorial/bigger-applications.md`. PR [#9806](https://github.com/tiangolo/fastapi/pull/9806) by [@theonlykingpin](https://github.com/theonlykingpin). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/global-dependencies.md`. PR [#9970](https://github.com/tiangolo/fastapi/pull/9970) by [@dudyaosuplayer](https://github.com/dudyaosuplayer). * 🌐 Add Urdu translation for `docs/ur/docs/benchmarks.md`. PR [#9974](https://github.com/tiangolo/fastapi/pull/9974) by [@AhsanSheraz](https://github.com/AhsanSheraz). * ⬆ Bump mypy from 1.4.0 to 1.4.1. PR [#9756](https://github.com/tiangolo/fastapi/pull/9756) by [@dependabot[bot]](https://github.com/apps/dependabot). From 33e77b6e257eb263cfc82bf0572b453cb1272eb6 Mon Sep 17 00:00:00 2001 From: Adejumo Ridwan Suleiman Date: Sat, 5 Aug 2023 09:36:05 +0100 Subject: [PATCH 079/317] =?UTF-8?q?=F0=9F=93=9D=20Add=20external=20article?= =?UTF-8?q?:=20Build=20an=20SMS=20Spam=20Classifier=20Serverless=20Databas?= =?UTF-8?q?e=20with=20FaunaDB=20and=20FastAPI=20(#9847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/external_links.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/data/external_links.yml b/docs/en/data/external_links.yml index ad738df35..a7f766d16 100644 --- a/docs/en/data/external_links.yml +++ b/docs/en/data/external_links.yml @@ -1,5 +1,9 @@ articles: english: + - author: Adejumo Ridwan Suleiman + author_link: https://www.linkedin.com/in/adejumoridwan/ + link: https://medium.com/python-in-plain-english/build-an-sms-spam-classifier-serverless-database-with-faunadb-and-fastapi-23dbb275bc5b + title: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI - author: Raf Rasenberg author_link: https://rafrasenberg.com/about/ link: https://rafrasenberg.com/fastapi-lambda/ From 87e126be2e3633a46163ae28f0b418903fdfd515 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:36:27 +0000 Subject: [PATCH 080/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 92061066f..3b52700ef 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 📝 Fix typo in `docs/en/docs/contributing.md`. PR [#9878](https://github.com/tiangolo/fastapi/pull/9878) by [@VicenteMerino](https://github.com/VicenteMerino). * 📝 Fix code highlighting in `docs/en/docs/tutorial/bigger-applications.md`. PR [#9806](https://github.com/tiangolo/fastapi/pull/9806) by [@theonlykingpin](https://github.com/theonlykingpin). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/global-dependencies.md`. PR [#9970](https://github.com/tiangolo/fastapi/pull/9970) by [@dudyaosuplayer](https://github.com/dudyaosuplayer). * 🌐 Add Urdu translation for `docs/ur/docs/benchmarks.md`. PR [#9974](https://github.com/tiangolo/fastapi/pull/9974) by [@AhsanSheraz](https://github.com/AhsanSheraz). From bb7bbafb5fa496166037b04a9fbb108aed3b84c9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:38:32 +0000 Subject: [PATCH 081/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 3b52700ef..f8138e2a8 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 📝 Add external article: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI. PR [#9847](https://github.com/tiangolo/fastapi/pull/9847) by [@adejumoridwan](https://github.com/adejumoridwan). * 📝 Fix typo in `docs/en/docs/contributing.md`. PR [#9878](https://github.com/tiangolo/fastapi/pull/9878) by [@VicenteMerino](https://github.com/VicenteMerino). * 📝 Fix code highlighting in `docs/en/docs/tutorial/bigger-applications.md`. PR [#9806](https://github.com/tiangolo/fastapi/pull/9806) by [@theonlykingpin](https://github.com/theonlykingpin). * 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/global-dependencies.md`. PR [#9970](https://github.com/tiangolo/fastapi/pull/9970) by [@dudyaosuplayer](https://github.com/dudyaosuplayer). From 5e59acd35bd00018462b6401bf78e52c91b48f70 Mon Sep 17 00:00:00 2001 From: ElliottLarsen <86161304+ElliottLarsen@users.noreply.github.com> Date: Sat, 5 Aug 2023 02:39:38 -0600 Subject: [PATCH 082/317] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typos=20in?= =?UTF-8?q?=20comments=20on=20internal=20code=20in=20`fastapi/concurrency.?= =?UTF-8?q?py`=20and=20`fastapi/routing.py`=20(#9590)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marcelo Trylesinski --- fastapi/concurrency.py | 2 +- fastapi/routing.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastapi/concurrency.py b/fastapi/concurrency.py index 31b878d5d..754061c86 100644 --- a/fastapi/concurrency.py +++ b/fastapi/concurrency.py @@ -19,7 +19,7 @@ async def contextmanager_in_threadpool( ) -> AsyncGenerator[_T, None]: # blocking __exit__ from running waiting on a free thread # can create race conditions/deadlocks if the context manager itself - # has it's own internal pool (e.g. a database connection pool) + # has its own internal pool (e.g. a database connection pool) # to avoid this we let __exit__ run without a capacity limit # since we're creating a new limiter for each call, any non-zero limit # works (1 is arbitrary) diff --git a/fastapi/routing.py b/fastapi/routing.py index 6efd40ff3..1e3dfb4d5 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -83,7 +83,7 @@ def _prepare_response_content( if read_with_orm_mode: # Let from_orm extract the data from this model instead of converting # it now to a dict. - # Otherwise there's no way to extract lazy data that requires attribute + # Otherwise, there's no way to extract lazy data that requires attribute # access instead of dict iteration, e.g. lazy relationships. return res return _model_dump( @@ -456,7 +456,7 @@ class APIRoute(routing.Route): # that doesn't have the hashed_password. But because it's a subclass, it # would pass the validation and be returned as is. # By being a new field, no inheritance will be passed as is. A new model - # will be always created. + # will always be created. # TODO: remove when deprecating Pydantic v1 self.secure_cloned_response_field: Optional[ ModelField From 69d5ebf34d088019b7dfa15729dabe3603e2d16e Mon Sep 17 00:00:00 2001 From: Francis Bergin Date: Sat, 5 Aug 2023 04:40:24 -0400 Subject: [PATCH 083/317] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20?= =?UTF-8?q?release=20notes=20(#9835)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index f8138e2a8..fa8fd9d28 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -107,7 +107,7 @@ A command line tool that will **process your code** and update most of the thing ### Pydantic v1 -**This version of FastAPI still supports Pydantic v1**. And although Pydantic v1 will be deprecated at some point, ti will still be supported for a while. +**This version of FastAPI still supports Pydantic v1**. And although Pydantic v1 will be deprecated at some point, it will still be supported for a while. This means that you can install the new Pydantic v2, and if something fails, you can install Pydantic v1 while you fix any problems you might have, but having the latest FastAPI. From bdd991244d4ff1393725996ac15425857e643c6f Mon Sep 17 00:00:00 2001 From: Russ Biggs Date: Sat, 5 Aug 2023 02:41:21 -0600 Subject: [PATCH 084/317] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20?= =?UTF-8?q?deprecation=20warnings=20in=20`fastapi/params.py`=20(#9854)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix typo for deprecation warnings depreacated -> deprecated --- fastapi/params.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastapi/params.py b/fastapi/params.py index 30af5713e..2d8100650 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -69,7 +69,7 @@ class Param(FieldInfo): self.deprecated = deprecated if example is not _Unset: warnings.warn( - "`example` has been depreacated, please use `examples` instead", + "`example` has been deprecated, please use `examples` instead", category=DeprecationWarning, stacklevel=4, ) @@ -98,7 +98,7 @@ class Param(FieldInfo): kwargs["examples"] = examples if regex is not None: warnings.warn( - "`regex` has been depreacated, please use `pattern` instead", + "`regex` has been deprecated, please use `pattern` instead", category=DeprecationWarning, stacklevel=4, ) @@ -512,7 +512,7 @@ class Body(FieldInfo): self.deprecated = deprecated if example is not _Unset: warnings.warn( - "`example` has been depreacated, please use `examples` instead", + "`example` has been deprecated, please use `examples` instead", category=DeprecationWarning, stacklevel=4, ) From 0f4a962c201023333b653ea1412011d02e87dd65 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:43:01 +0000 Subject: [PATCH 085/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fa8fd9d28..ac8a67633 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✏️ Fix typos in comments on internal code in `fastapi/concurrency.py` and `fastapi/routing.py`. PR [#9590](https://github.com/tiangolo/fastapi/pull/9590) by [@ElliottLarsen](https://github.com/ElliottLarsen). * 📝 Add external article: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI. PR [#9847](https://github.com/tiangolo/fastapi/pull/9847) by [@adejumoridwan](https://github.com/adejumoridwan). * 📝 Fix typo in `docs/en/docs/contributing.md`. PR [#9878](https://github.com/tiangolo/fastapi/pull/9878) by [@VicenteMerino](https://github.com/VicenteMerino). * 📝 Fix code highlighting in `docs/en/docs/tutorial/bigger-applications.md`. PR [#9806](https://github.com/tiangolo/fastapi/pull/9806) by [@theonlykingpin](https://github.com/theonlykingpin). From 6df10c9753fa4a0f2d82506da63adf8985caca2b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:44:36 +0000 Subject: [PATCH 086/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ac8a67633..b195a7bb1 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✏️ Fix typo in release notes. PR [#9835](https://github.com/tiangolo/fastapi/pull/9835) by [@francisbergin](https://github.com/francisbergin). * ✏️ Fix typos in comments on internal code in `fastapi/concurrency.py` and `fastapi/routing.py`. PR [#9590](https://github.com/tiangolo/fastapi/pull/9590) by [@ElliottLarsen](https://github.com/ElliottLarsen). * 📝 Add external article: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI. PR [#9847](https://github.com/tiangolo/fastapi/pull/9847) by [@adejumoridwan](https://github.com/adejumoridwan). * 📝 Fix typo in `docs/en/docs/contributing.md`. PR [#9878](https://github.com/tiangolo/fastapi/pull/9878) by [@VicenteMerino](https://github.com/VicenteMerino). From 942ee69d857710ee4f0dffce50b6d4d4db10b540 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 5 Aug 2023 08:46:58 +0000 Subject: [PATCH 087/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b195a7bb1..723f338a9 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* ✏️ Fix typo in deprecation warnings in `fastapi/params.py`. PR [#9854](https://github.com/tiangolo/fastapi/pull/9854) by [@russbiggs](https://github.com/russbiggs). * ✏️ Fix typo in release notes. PR [#9835](https://github.com/tiangolo/fastapi/pull/9835) by [@francisbergin](https://github.com/francisbergin). * ✏️ Fix typos in comments on internal code in `fastapi/concurrency.py` and `fastapi/routing.py`. PR [#9590](https://github.com/tiangolo/fastapi/pull/9590) by [@ElliottLarsen](https://github.com/ElliottLarsen). * 📝 Add external article: Build an SMS Spam Classifier Serverless Database with FaunaDB and FastAPI. PR [#9847](https://github.com/tiangolo/fastapi/pull/9847) by [@adejumoridwan](https://github.com/adejumoridwan). From 14c96ef31bc2069dbc7dad82a501de10d83c4cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 9 Aug 2023 15:26:33 +0200 Subject: [PATCH 088/317] =?UTF-8?q?=F0=9F=94=A7=20Update=20sponsors,=20add?= =?UTF-8?q?=20Jina=20back=20as=20bronze=20sponsor=20(#10050)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/sponsors.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 53cdb9bad..6cfd5b556 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -37,3 +37,6 @@ bronze: - url: https://www.flint.sh title: IT expertise, consulting and development by passionate people img: https://fastapi.tiangolo.com/img/sponsors/flint.png + - url: https://bit.ly/3JJ7y5C + title: Build cross-modal and multimodal applications on the cloud + img: https://fastapi.tiangolo.com/img/sponsors/jina2.svg From 01383a57cbdf57cf1ba9b39381e6ab37c8d30792 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 9 Aug 2023 13:27:14 +0000 Subject: [PATCH 089/317] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 723f338a9..486b57071 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,7 @@ ## Latest Changes +* 🔧 Update sponsors, add Jina back as bronze sponsor. PR [#10050](https://github.com/tiangolo/fastapi/pull/10050) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix typo in deprecation warnings in `fastapi/params.py`. PR [#9854](https://github.com/tiangolo/fastapi/pull/9854) by [@russbiggs](https://github.com/russbiggs). * ✏️ Fix typo in release notes. PR [#9835](https://github.com/tiangolo/fastapi/pull/9835) by [@francisbergin](https://github.com/francisbergin). * ✏️ Fix typos in comments on internal code in `fastapi/concurrency.py` and `fastapi/routing.py`. PR [#9590](https://github.com/tiangolo/fastapi/pull/9590) by [@ElliottLarsen](https://github.com/ElliottLarsen). From 87398723f91efb24834bdded970bc5065049d50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 9 Aug 2023 19:04:49 +0200 Subject: [PATCH 090/317] =?UTF-8?q?=F0=9F=94=A7=20Add=20sponsor=20Porter?= =?UTF-8?q?=20(#10051)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/data/sponsors.yml | 3 +++ docs/en/data/sponsors_badge.yml | 2 ++ docs/en/docs/img/sponsors/porter-banner.png | Bin 0 -> 17260 bytes docs/en/docs/img/sponsors/porter.png | Bin 0 -> 23992 bytes docs/en/overrides/main.html | 6 ++++++ 5 files changed, 11 insertions(+) create mode 100755 docs/en/docs/img/sponsors/porter-banner.png create mode 100755 docs/en/docs/img/sponsors/porter.png diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 6cfd5b556..6d9119520 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -8,6 +8,9 @@ gold: - url: https://www.buildwithfern.com/?utm_source=tiangolo&utm_medium=website&utm_campaign=main-badge title: Fern | SDKs and API docs img: https://fastapi.tiangolo.com/img/sponsors/fern.svg + - url: https://www.porter.run + title: Deploy FastAPI on AWS with a few clicks + img: https://fastapi.tiangolo.com/img/sponsors/porter.png silver: - url: https://www.deta.sh/?ref=fastapi title: The launchpad for all your (team's) ideas diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml index b3cb06327..7c3bb2f47 100644 --- a/docs/en/data/sponsors_badge.yml +++ b/docs/en/data/sponsors_badge.yml @@ -17,3 +17,5 @@ logins: - databento-bot - nanram22 - Flint-company + - porter-dev + - fern-api diff --git a/docs/en/docs/img/sponsors/porter-banner.png b/docs/en/docs/img/sponsors/porter-banner.png new file mode 100755 index 0000000000000000000000000000000000000000..fa2e741c0cbb326b271e3e3f545a4dbc55f72519 GIT binary patch literal 17260 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 LNkvXXu0mjfJEqlj literal 0 HcmV?d00001 diff --git a/docs/en/docs/img/sponsors/porter.png b/docs/en/docs/img/sponsors/porter.png new file mode 100755 index 0000000000000000000000000000000000000000..582a15156470bd8af7ebf8e9c05beddea1a7de11 GIT binary patch literal 23992 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�S;@abbD|Vr_Aj3d(~E-cl*Fup6_8=vF_W zAPbrVK;MjrJ-+s%RzcjarUc9|^u8iqE@{GleDV2Jc_#&k1-HeSkXau8Zlp?rRt#R} z|5kA+Wew1jZg2Hx&Ao&Qq09NCJgp+%PkAv{mwUF+_<%p`)|#g5BJ>j0Q~LZ z%VEy5FVw3w*~)ZZxG=l_V30!MBfB<=nT^o2Qmo$f#?f!Sy5vm(0gdCAn9p7OW$tgT zWRgJJTi3}Onu~W`!@|C&)D+N^oRm0u1-r4XXY2Lb@i-Q|X@T-x%9N z^p)U_fBvugAA)b4_9I7YrtOL=t^l7t_G9p+6OM&xk57kduf18-Qg2tqOwJ(CLd4zj z>!FpL^uNP0%YXOkI&NH9?Poqd@jL2r(Wfhh_!53) z8(hZ#w&{kqhrvn~;KPOwhsBE*!;3GztTd1x*yWdB4weiILdQ;}472G+FFz6%EEo<0 zgG*qUWk$O+BS(&e6_#Hf1_uXW(V|7LaN#0p3z_%yM{RlQt$8qa?psL@M~)ofhL!SB z`W07L9@_0Ty!7%bQbtC!GNrI8(=Gh#PYg*W*V1W2#3-oUM+Qt)V1968!q+H?>a7X& z^><`#yb^1j(2$%X2S?GZ!ODiE47_lB^ltqh|z>FEQV7>JwSTnliUdBD~ z9UH-?KYb+ZwBxq0^2#eY@LqH6jd0$17r>n7UT|fO8Zi>S_ua3-l~>*X4^Mp@jy&QJ zc;_zL!Yi-53d2SWhqg7zS6g)zIO3y+!2bL1kCgJOvMK-(hFDLV2v7^gQKUH+xZjf| zO@#B#Jricneipv)#gkyhQ_sLg8?6W5{QBoDfcAn99d;}{Gy6HHEZEnz;97Id)!~?< z4|AsZQ-WKsMI;+~55A@o?SsH^ZfuUF|?RZ{9pt@0U*e41D0={o(ZQoC$Z| zeZQ-Bw_SIFGkM*^84qf+-L!%|o z=JRwVH3yBwuj08~uTfc>nqYRzE*YaM4F5WQ4X<4Z`G$7oT3vtdu9ag6Wo_SFWA8*m z(#lFevZ?IWWmU>$sVdE$?%z!|-VoklHSS;kb_KModHVC8eFq+S>~Z+ir@smd=FfMu zd}j6>xb?QX;7=F)3cmgAufnmP{#tlD1HdOg@e#Q2PnW{^zx|6fwHG;acVJ)%tZBhF zZ~g*!YUVR=*OY(Rdjqb%-FM#wj{EFUaKurchnsJ{Lt1muUoW?scQKq}>%aNdJK@pC zo^WlPyya%j0@!`858AR9$G&D=?;$A*w@_);IPI1ShmZ~wD6tO61hgh9q*^0QE+A?} z)w1Zzq|D#-YV>i3XsUC%ij8&lzuW_3Qj?}&wh5k z^;H)pl`LAc1b+Inb77mUCc!q_Y#{^u;!7{XdB6FKGrc+TDzt+7^}hGM2OfCv5xDi% zyK2vFy6Fyh`Q=w(@4a>vLXtrE@tObWO#MJ4`AdBA!X-&-WQ=rbx)8Im?P=lhAaZ_$ z+JCjSMp{W@y9{*Bql$4LW2E|TY@l7Ae{F4!LoZ1>lrxalPAKn&%Ou;AGja9@y44l{mG{U_*^En^Uk~e~UNJelir<~$FY%tdNo zce3j|x=o^COk6`QLXR~xTYr$PQ$YPzOzrBax=q>`VehPwGJai`UZp&wtE%P0#vdS| zbV&K8SYzm8yU3<`wTb&0f>Gf=4sL-g>obrXf!Yw{(lsTq#gbXm(CECaX?OKaX*}df zvL}T)pj2wTcy*^qtnd?Okyuli>}ML(D%A7D^cir`MVCt+TF5bd`b>Dl&IwbLnbM86 zT6xKm0hm8;0f^h82AZ@iJzHkaXUK2U@m3l(00X;@VMZkNOaKf77zGq0|TE9kty@KbwM>smA2 zwIXQdyQbU+lP7QHrmXlL{|y^93@-e`FX5DvPmoncnubzWRn=BBNt(tLN?}!>tOTr> z6-67nytZBF(p1XQOUF|NTWH4z97N4t;(?r3YVi=n+$wbl>AU=1KoRK`cv@F8*YQszV#*8VEuJt=k2x0 zCL6+WpZ>Ugemgue^$AcXuEvcGYU$nFx%1$yx%2Iu@`edBU46}sc5Zn^IOgMrc~jqW zjce~-d%hF4w}G_FuedfH7io7sUJ)=QkU-A(wKPFstNZk?YZo&@;LIdfLtRiv^S|Q{ z3g7i-rPB;~xn-H}+bE_1fZqvXmkbfX;|M2!5^m2j*4V=I+4h1GGhv3*AWhC zxq&oLo*#wcJuD{62CJB_K}hr%_p##Y?T2b|#@BIwU`thl7 z{;$u1i!ZqnUa(U*6DF(;d)n!r*>j$QpPzHSi&}#c2x0*!CYo=jkgm0Jz@Iq!BXG(o zC&KJ!=D>5$y#SY8ehqy4J7>eGR!GnyjXUnR+f5(sywi5DtBs1EbIxzzAsZlL(8Ycf zz?l&Ov*%EtB?Df9PpG^i=|kAr1)b1MEYx6lwG==t!0-Rb1Gt*8Ut~dq5OBK{jM->VH2hSj4#_#9ZUX#1d@RX4dZ7} z%aM1gkfydQPZ1PjzW*zKVpb}%+x{LiW)*mB+H`n$>SLnqK!XDPc0kHQn)8Z(T&(kxHXyb7>SL|eo(WGs{j4*|X%SARGw6a{ zbWNT5xZ5N#dGe-iK?X&)AAa~Tm^ST6xb5~Su)}s+!Jd1*3wE**>4kQ>=)ChUfd9Ve zGQSSMwrRE1R&hbHTW`BNuC_x=H*D~*pjI8RUku3j8Kwr&C=jy%svuoSSAAJqA+ z$U_->G5>hwV<^AKoE>b8tsV>81v09_eq5J#Y;s=tzG zswb;)nwS^n8;+-*;YMg15vMBruW2 z^v9lh5LAIl{d{~!%B~6w+-e~#v2(E0H3D_p{?URDs%QAH5!S~Y>HJyxqs1Bv7cH>q z7du+h_;hUDv~bb_P3Q=`oM_b{W+|C`OFy8Z^3&nG%OQcj2*gbI4aO>!3@TbZ-JpBPk+l>7^MK z3-@$oUP6MmmRl5suKZ4>a4wPdr}Y96y4;B%^xXvDQ)>+LG=cP0lCVizv7QENdAkUJ z5m281y?OsU`sR~*g!z)QD zH!k#y06Z`-m9Uu@HWK?<3N0F6++?O@b zh)T#Y82wsb*3up9Kt)Q}y_o=g6gyBnJ@`uxFou^usAER}dP=z_!NOY6bV;LFGTy;% zdgPDSAKG;crI&-+9DGN-O zXNtHB!oaAOxxsAto7r+sEeR8%70u?HiPCf_Xf>ep{j`8GzhX`3^}+yS$bEHZ6IYIQ z3{Yv6?9)XNqDj99r=6IAff(JO^w0&`0mV~F9Ue2RYxW}0u-=7ccLmclMZutiWUv@K zo;tFAAoc^?I&o(z)2;ZA=n-CP@9!bx>xZkHzJhB7ABMz~BRiE1FTB z;k=Ztf~0`}PS9Kkk!8bPU*35X2F5k2B1Dpr=$G_$OspKC&?xjKw}0!LiJpw<^ALb; zpv3aC9Pmx`V0jcFXYmO7$gCzaxLp0b%+YOJ*K#t|i~=ZT00?H32|`pY)Zi-gMv@9K zra=alff(KG)auH+sZB$G6pTgFIR|c5wqC5F-(M8VMb%#|1of&blhkQU6NvRs)UAZ3 zWxu}S-r($4%jS6+LEN`kaRpHjt5SVJDX7N7%86!Q)};+n%|G^UGO=%E=I*mYD zndXb>EqNiz=p=4%_>GdJ?y6!S%m3GD>~v0;p7&~6NBeap=w|5=n<@t>26EQo!2t+r z7sLmqX&y>XQ#=^8l8aO;>jRZQ@CrASqo!OcS+wD&sw$qk>ZD*cS~E(&b2`P=Gki3i z^mCmj5ukucS-Sm|YJ)_h!|F6ycmm-RPJ@Ys7b^%+6NOvdm=yR%4T^Thx`4Gb)=R;I z<)Zrsajv;_4egw`0GR2D^l=jK5J`x&*0Qzin6mO(l|nUccL|x(G~d_Jol*1(TAug5 zyZ7Q6zs9B~=jvVB!{=9F^S`KPsT<=)(^OE^oqgy11JWujbpVZP)*1(P?YAUVVG!-b- zNlYBWQWRRo#;7LhwgQv`NaaRI{w5Qci!B{~S3hLHw?CGt_&H14ZXyhuHK0DF5Xlm6 z#-mRH<1*n{dN>SwbI#9yqBEmA!E}sK=d=1yc z4EEGSg$F$aUc%3+i*6mcsYEC-@EilX1u?2_$N0Rr$eWygiY~qfDVpFY+Lwb|?l%dH zjt>%gIY3(+JLNtm!YyxnnjRy!CHYK2NL0a6Ffw})({TIqDU={~^sT&M&>Ly#RT8jy z---dGVp*9<&gJ&eqy)r@S4$>yPXD$xfe@MoCkH|E^X3%k?%=QvJVYh4OnhxOsDxrf>!`gTDT$U zSwJHPxgf)6K4yUv6cCannVO&2_Z&6rw*Y%Yk*!5l$_%LC-6tpSeq5pa_${FQm z@Dti_Ks$~Q>v4com@u#@lkpLK3h`Be(L)+XB?d2IvUivPWYD`V&adv2=+P|TihaXo ze>$-MD$gg{7v(hmJyM=ZO_yL$)uT15QE?L84!w{gDkCFyUoysq1wwtS`>=@XZ7$?1 z-7C&cBdSDIv;t}XZwv-iiD{ae&{z{r&_(#A0tyv9V}I&&RCzH>W3lA~5!}9A z73fVIOgowSQXov?w3t}VF_e-AukwhmUQ?ZPxYomPHNdwZ9FnA3^XK{U2dN2vh%j?4FeB^1K%`Zq>Gs*)*eC81XlO-j;!_9TL(7U zXhJBrPTQAXehu!q_rat>+Taf@|GlHlXx>=Qn>+#txJ{0JZXDd(seF5ROJWsIy70e! ze%fxh^;Vm~@ZoK^?z&q6vV|Pn*M0ZdjqgZH={TR~Uw9d2JUJ@??Zk;2!s=sIgBxzT zqX=M62MmW8P`d|f&-7RrP2)^K_-U~4T(q^`SL;ilf5)z|DU2GmlI6GiV9Db7LbLJX zH+Ls|%$j+>yyg~uO%#Ff_`HVoEVZ++N& zv(4PmM)T*-gGV2I#GPx#JH40}^txy6Rk8vLu?9JDB`{iKY|XZ7=42uzAe9c#AX&L$ zrJ48MdsjIAxR1LH6e-Ga=(7~{ck$v<8!0w8Zp9;-;Fok0pI=pFDw(!clQtZzafMYXCs`wnFDhz$I_D40RgSU-jx0VU0C6hI4;)jP#A!e6>~Af}@T)*;X+CU;5H}-9Bo4t+m$L@R?74 z6)wH(Pw>POk6M~94`2Mkmtni@wu2X*p98PI{wl1x>Pm3J7mtIxr`!!Ef9ni*?X}lp z0qF`TfUepTgpT5dQ1?3#7wrPvi@%dWk!KOSnxXZ4`p)k<$bp#h44nT%F#4GyA6nU1OZyRJQCf`>KMylN@$0#8{angL_`QS}9L zK_wa@sW>G~A~Ah>>hF#_zdywVM1{eotF#Hdpz#dYVHOtvcEW;wWKBvC$8e~3$Iqc| zw%I1I>MAS2g@3+WfwyvT2E+jou)Do$dwB7sS76OG$HE5=-UrS)``5sSA>>1U_D|1) zKVEo=0>vMJkBNtwYUf9D_Lvp?O@dCm951$12)=Z533Qc@=0vc*|Q&UAYOij)nM+MukgsY ztQ%~&g`?ZZk;}k(6DGowGp2&pYU9UEfR$ES8Sc3AW_MQDH@|T@j2$}$zIoEutb({- zcyNnJo56R#^DX$r&wm6*e(czAS_)q#qjpAl(LvF2fGHglOB{QSp^2I^r32{ zvxY$OrG0|5%%BO=dSV&|vxpC#kOm5AS^}mPOYB1{Ei)W4r|X2|tgZ@jD=5A? zGYw?X@v(dDxiegSHN95lI@o*9U0|gZmrI0+2AlLZoUlTh`{@kolTQ8-tUZ2BcQOdd zTO~3+DbPEi3t|#I&pp3d-Z%tdjytBQRK4_HyEP0pdB^_N)V~=XnR>YuR+C`aWml8* zbLLEi1qe(;K7ICdp|f6HrjA~*lo96K*J-q=ro`B*@8fuRwXV`K(GRo&(OG# z18P;2ZYybPAYsUuGZKeKf&9GduD=!j_{YD&e|+R1`17AHa|eZ!rX|yE+ikXhU3S?P zR$X}|c-5L8H{UV^ZoKJES=caY^hh}3@DIQxf4>?QFJ1!gd+*+`sWlC$csefT%75J8 z&Uw<)0?ZUF`&O~qYG>;k@3_O(FnaVTc;=aB;r2W4g}dA#>BefR9bnwJHQ<5^E{Xj? z5RH8BLHod4)+fBy_9Ll7)fI1OP47QOj2LEprgy-ed%Y9Jj#9;h115@UH9K& zvQoN@H{RT8^M{;Uv&zb2oT8ce)Kki815vPd-+dpvXx+uhlPAGH|9NvpT!duGNobe~ zKs|Np^dS0wd~ySo1{P#Mi+_;(q1HwarDNlTbrC?O00EE&ee10^_rU-=*ZbuYKLua; z(y{L3#QW}l*qR<4Yo;6rCx7#JSYf$korGF;^k_Ktlm7vmZMqTs?CjHEhwUfB^Uu8q z3oQr^|H#4c>;L+J9f0-3uLdPV=yUAqW8g_UZ8HPaW>s zv4Rao9QT*I9c_s~y*XaciwEFKDL@5Tot4@2-~lro&TD&91r>$V$aD#Q>jMKji|8dwsO3UlD-ofe!=H~dd zce1|v8y2LG!Mu4d!PB$uvO%VuY_MsBS0MJkInPYB{_Mt{*3KG0SZmyd&H|h@>rqRm zd#t&W}>E@twlK6$u(_D^?j zaL}JEwbDu}z<>Sc_u;E2ei}Y|{K@WqY)j=B`A0warqyiA!AFn$f_ps(T{Hj(9r$hw zpyOc9voBgrcr%=Q((&+qt7(7w^Yc~z5VqTPOZWcV>ubsb7REue!l%ocm!%E0`v4wJCh@gwx%tLMcBD z@{Kp&1!w)_WNVq+Wqq$7J74T1E96$Sg5}%aImh~Mqiytcw0noJV+hM^q^G6Rq2tIS{;ciyq*4yuK>FI1QdaL4>zI+Be z`0!(XXPc$Z>8G9mAOF~)aG5pz->@1v;X$J~(b8wbjo%A1XIvX1&lNm5{bty7vwf|v zx)D4(XR1p%=h;VL`yCF36;>Q;HL~ZmO*YxyT6{0SE3ZCl>v$QCJn}2nOy1bud(l1L ze9|^{CS@+ndU}TY{lxTXRuDb`pE%|-u*Mo|z*SdY0kh}KiAz&$fBd_w0Gcdav0kcl zRh(N7(J`Pm59xVx{-{{`q`n~qiv)X!nVT5#B52Zq0c;3MwuK-kAd<|*wv zciGMgu9s~fu^`k|N{I8gzYxUramkJx zguxbY6E+*p#Hxvj=12MS9NBDU*NQV@^3YVpxN&Q_fhHd?E|v6)G^k@!?s>@hZ1fVY zS6_SGm2>N@_jnD9to6JkyZ62a-HW;?GJD(Y_eSE`za6X}HfPRrb_!)$v0s;6ah>(4 z_j1Rr(o3z6v0$FO`>UO{c*^$IzgOu` zHf~@7ZDg(&K)}WfL7$^Xjf8jZyp^?9ZgR9{Q}0zPfF80yIpBajt-t(F_r^g@lihaR z!9}b24gqM(Ehai&`zL3eXMyp8ue*wMdv_&TJG<<>jScWjgI8aDJrOc|GAGeuM;i=V zeT~&5g8S&S={~KA@!A)yrA9Adqq>=)`FU6qX`9i4b=KV-UVUX6yz$1&xbVk5pZd_H zu^(J^Sn**;vUK%NygYJ45RLHn8KrKOFJVL+zB?E%2T1{lvZJ zO0j}+SCp>{ykRHC5t2R4;$X+C-3^?c*s9jjbNvjp38|hV6wGXNKHExGqTg(TD67~{ z8q_mRJ3&q5vPotMIIOaue9Hx#i1F!~^W5|CEdtz6H?RmVz5JTf?8HQ&6qk0z6_$64 zDM)L_l}z!PY0W|c6Pbzk-~Wg+T@O9v02_VvQv`eNv9r_MbYB7vnmqQC-p70FXHQZz z3MNo6v0t~qnfQ(k;8VvO24lvoDrOS3Z@l$iXU>}K!0^HgFTts&|I`Nh4utQ2=POQF zk!vyak;mcI+wOtsPtNjFDt7}wR(vr-W>I;XH(uG&k%C@)a;YyXT4`ib z)A|5^}<@ zzG=sGgazSbJ9d6bX~v9uV4H0Yu9f8SrlVl`^nXd+)2BZOyYGIWoo-s$T4Bq= ziYu)O(;j=}y;~XP@U*O#y2V z^x^bYFf&LJFEe_COG~CL`FI__>LaYu@ytPQ+goBan!Jt!J%cT_Q(5$0pd7qm%Y_pv zbdkA9$FBa-nt5k@`^(Ncm~EMs%=}w!z1s!ZlAx99Ke<5j-dd2+!(UYI9((NI%+TNd z{$dwhC+$SwCMG`fC*KHQ_6M@xap!$Dc=ezS1dVgA_uSY9+jiY`dn;7-hf_{H+Xc2N ztYkoiW)eiQ(dS*Zc8qrP$3IvYM<3@mGeMiOf z*7UK%R`?`$WYQJ~SQq3P2b$S7khRAiAG3kPm91qr$qMEfcH9?9Tc3S)rq$%H!lX&t zTcNzrS^&f0@h2XYK5j8-E9(X>hKC=1P|?#1lb2q81^(;Y-@?B8>}6e)cfkc0{!Oe! z;RzE%Q+ka&^CJJQ=}triU_ihgS~#Ip(_>Wz&)e~LR2B0*im-mtnx~Ij4e-DNk680# zO}8jurInY5M<1ImzD!_cf>CsI?AX=d zX**R>xdjp|@zBokJ>}kkJ9?Q>cDiPvB9{M`d}j)*J@@=et_`=`HpOb}SKKrXy}f_} zMgO?!Ce_Cbzy%rrXy!yX@O|%r+pI5o(ciB0Qxv2vu?4cWJ22^b>(e`X=h*<$YgQXP zZMEerxaOK$oUl9VCnv#&4%yF2{D-|?2Qk{t03y0k+MRdY0?s|}A}e^T%h4I|{#9qt z(s9693r8RIAv;&O1N`h4zjI@t+noB>SQ}^2{z0Qu-j;RkCjm(9-f73l&aH4e&oCaQ zN9EH@0%>*<*Fi#*{TT}W$ZuW*51v6@+Nrr!Qc325MpfM*E;KN z3%l)coaK#;t>&L*<0a48Ak#d{dz;v2o56z*T%YvsjW=Jl5$S1GXl@C#V{6vT>6T~a zN?Z2d|6muqI_`5HFDf`<$5(A|u+^@d2~MlKSo?O8U3K;8r2N9d^RN^-T_jQZs|5@S ze=cnm&9|E0tNvSU)m7lMQ%{5q)}P>30?oHfGvleJ-68>+29i8Mt5Rq8*=JX)b@sD? zqc=o*Qh;Rq_%)$reIs7{L4Tk6)JNQ_Hm5!^-4Tu%t!=e6&7Ur3r*01akN3+a0uCyW z*-XKvhb&N^vhAUKq=~Qp=WVe6etWq2W}3I0@zgUiC8XcN7Y}L+nu7ns^v#15S?m-Dif_H9q#~4Q%H*!+rLGxTUxztg_8YT@P-v8w^+KrX6MXzcI7R$QR)|7 zcoMeV_5j<))og5Jx}%vsj_AoJABJ~K+zQrTe<69 zWu(82tuMU6hU>#U*0LG2LX)gTx0NyoY7=F8=vQt%@zhew^G+3mU95PkB1p5p6wLZ( znyv!&fTPIRbB|r%J@4Mt{T=u|J5BTT)T9=Hm-&WwSbuuYy>^B@tpMUc9+}7={?Pt* z3d09`Xt4kLqN}WL`VRQQ=Z3 z@%yr7+d$XfFS{N-`7$olemdwOls?Uz`8X`I%<@)1tZs$+w8ScT;DLLs`*)Y^+ZSxW>SM6(y6f5~{mQnF zYr=aE*blya`pNM8^Dn?9f4?$p`Bb~-qKzEQ-IonRs%H)4ub@fe&U=sls z0KH)0B5x&BRZ5k>ko3=A|9TaSwL*iYPsj|W$S?`33opDJw%&R(aOa5G{Etn05`OfP z^V~*-pPqS=)htWg)C>tCGNrG%_BNR&y5YvVZTlv`hY#HsKJfm%-RU0Zp8t2NS(br| z|Nc+W%1)cQ6>a3JqgWXfm^$XDL!DM6U!P3c_}Z0VCJSSGUzV3q&b9G?*Is`!Xb23! znef^iTi0)Ycd^s{q=k308kguzF4UB}AA~>H7{eQHyyZ0$nVb|4pz-isg=n?qi z1%I*r@T+mHcZfFoH3J~l3l=fQ_qeAjEbq)1D-0%XwtLs`R8Rjnd(P=c(i`P%kV!XY zp=Iv*^XE&t#J7Uj5%S&EU3V>5ee77bT=6kG<>DA3Xb_U%7ytEt*a+>#nng70N9a#ij^!auSQpM>7ZXCPtoi;kW<^t`+PI0`UV0vefCU+}!Vy#cx@9 z4M2V+3~tLBHF_mW+l96czsY9g$mMP1d!Y+p8JU}kuo*RK8P~6Q^WLm)tO;o;7&m@A zjnRrOu0RVqXpMs$RAFNd$%-nIt*&A&LcM(h50sXw(Q_yb;;Me-h2DTVN;(T~*NE zr5P~X$?fJ^j_?m=4Xn~-W3m}NB+{VPW}9?O4YD(TZCHSOsaD$qU~sVIK+Lbt)P(bE zPW_?j9Yt7qOQ0eSS4#7+GpwIT0W3C+#iRt?R%T{tDX<{YG-6=X>B+(XU0*sugSlgK z&;!FAj*k!oS{3}|^rdE1IPF9SRpJVu7Wjh(A*^VafY_Hzn8rHdsxWRhD*u;2 z>efsL0nGEADCbXCferXrVSn0Br&+Y%V5mtYUU) zn}7mRK!|pZWzaOz^y#ylIq4;&f8Q;(gEk_+aDa}Zv6^OpkFo7Yn;bJc4NHdyRDoE$ z<0I4)Mr-8A;WmJ@y!A2fbf$R(pY8+uh3X#n31WY_0oXt{RF;SnH0_pH49p-MoioFDhE50}A9zXdfjiCzUhI}n^4Z5)7{xvTXw8eLciaH*E+C#Fb1x64Q!IiD z;tno!Cc7Vt4!bU)ANq6&UXi2&Jv`m$re&TKBvDncgd1G(PQkB7XQ6>xoJk)sMz>!lbAY>-2$XFq1^7Ae+pg*=^E3!Q^w#!~2_c zs(f!OD#nKd#M<;>U@*C~pyGQ%9*mllgb@L)+5`|BM6VERrq~^4r%5scMYqi5XGEXH9vWiDPH|}15$3A__n~NUs z@mL-7Dk(59(@8D6VQht-V_`8w4th=N=vQOZctH#LvY65)QJ#8AkO3YK;CH9AMJx!5 zpcH^=+&MUvSQc6IRDfA1`NR`_ybWj& z^Qo3ht12!!alNe~fT=9Qrhe!@-IG_wumu5OdI>s;tW}~Bg`E+?n>ai)G@tlsB1Kp2 z59?u4jPJF2YJ!TOO)gBoE2W|r&X{1eY1(&8h47uqd zjpp8j5&*#~a|oQ!60@z+E2s3r3*;bH2#j%={K`=Vue3`GB9yh&ylql^+@S60{JWMg zx=-h{E0;%U-Lb-zV{TbG$|h~c#Fvag5TMw@WafLA50f^{{ipLpSz96q9k`qln6*`< z58v}zmmTIPh*A5ZntwqV<$+HPDt-w37N?mrB^RtP?hwa3vds)w1}`=|)y#+3cn_P( zR~?UeQ_UorI4~FgX|paVmx0&uejXhNM~l#z*_}^3FyH{e=TCDJT;$Z5tb^f@JoWN5 zOq#8ruLTfheSpNu(BO?}W51)>9hb_H0E(I)li(3AwIg>yPl>g786K6>Qp@sMl@Hpv z1I?rMHcA60@rFS2D$%}`2$vWf^9S;|SI8P8TG~g^Q-R=j6?kAWaNUu@?$ab7^=7n* zN;EN<)nLe_CJnPMY*l%o;?p{so2bomGrSJobgB!~IJ@+kPcq(*3MSMYrkaBK@U2m? zt_oxME{Ad@{B2LGHmaR0$(2cu^jQzEVkuuCEwah42mw*^B4)q^0L%!hn*)jT?zk7I z5n!uo&@njyxz*~x05KiS6%TfjiFqeo9mp%?Gn=4Apx7Jv7eQ%LOaNL7d=)-@u3)ZK zQ8TyN?clfjkrh8YFjW=SBm3BZ7dX5gIvxrbj%sm2jD27aZZV-V%`5#;8r(;ey|2rr zB+tQ99HGTg=)j3?IVz7CrSEvgGgT0dj!rHl=YfjKbz#}c46NKFJroHTK|NC{+Lcqq z0Ga1I2DJLl0750Wu!Gk*dIOqosqt=bhLgFZ-Axn-H{u%lwt&xl>V$dZjtQ^wG*IXVC;$ebleaJH6DYwHi%>MG;s%kbYKazhQ$KFi6^c*n zH*okRq8J1YgW3VM<%3;SE7g|NnGSYby2y}<#_UH!6bLLR*fe*iZ?xJmYHAGdI&0eX zfYcP-MIB}LG%;beLY{LV z&LsLZ@`VB}_mP3haVFrNYFKe4$un+=Uv;wH(fzc*SOo1(HN)J>6-fSk5I^^fQxb)n;TeoAP7G!QH zw3*wbO;BOtq@T*lkUr{iqCj%s!(pzC#&}KWt$M%kfsf&JuSEr~g)0@@a(*g-z8Z-0 zzyPA5ISQ?P-NeeN;=&NW?waq#9Z7@kTyM4zTAt53nWaSd?S%JQ$y_Ih-!AN9{F&Em zj%uwLnMVONJO@Qz&cmtL!VsTZxW2bQ3l?}_3e#X7a{Vc4`7uAcWh6rSJrsD+gW2V8EvIqGdc);Kn3+; z+@d~>q!E==qU){$KEMak3<{tsUGiPkiPwRMKfm==! zSFNClEz>!WyG01TT&JB#OAQSApqHA0O1VG{1S-L%z?*345TxR?TH}G%1|jn9rjs04 zd>PIUc3}1nqT*t!>^8W=4Vi^H6TOaQSA>qx+nmcl+pEX2A{Xr)toGw&MgA~sY20O zm#UK32bbvj5lmk+sd9lRG)!jldWD&yr;I2Q1w#V#Xr0uqP;OhBs@$RC#f+5|g0(yd zc@ecBPWC+-Ch8x=OqOWv%V4b}TF1=KgAfM>gqplsh67vlUS0}pk&n4#i3f5O%xSKg z0xXV!Iq2n95OuUrPuiZeQ3F`8iK!4{p(Fr`0-#cUFtrnKaR^eyvy9b4K zGN0&vr$$B)Xu;{vS7}MTf$Q2lEue}|YD*xikl7u$b?q4&pP7o8mS@JVJc94TV~%ZnxG)4j}VaEaf>*D<1r>1-v6Tz4vEPHTqKRLDT6eaeym)4pe< zAqI0UZoqF3_hvJj&KS<;C2dPv`P^VTAPxrqw;g=o0KQgRv?hb4YU#+aqElEJ8P{45 z3&+~3lQ_aEF>*DiBThJU_#I4?)<>J&`ryE^9PBc|9Mk}ffhnjlbe+s~?JHA)kZN^Z z9%^NvOlG=x{yz%R%2AZx8d*|c-fDA+bPpQVl^OW-&PnS2e=R+S`56ROXgi|;lqcG z5TWBD$!7Ms}-h`yx;ABYlgvKt0O@yE&N&%wQkFSf_m+> z%wu9a^2Ym&f#!x{wFHXFy^I_JV7(isO=hiu!WX~dA_1U~7ywf9*)%YJU1NK`U~}L1 zliJ{4Jcg;|)8Mm8oow!hv0^O@kec(geB4tL@3BDj!9B|3rGrs(~!5Da`E2Q?hI{zom=)Ap?ey@d9l!3r$<{o!0@#UTb7OSMgjJGjRcAP9Bxn zj?y0OI@LEPV6B}|P^H2LGN5yuf~`Wn+R5>%8Gz(F6Cr^)Ch0=!)~gG}0Za3D$jhw?GKb+K7dIhVGFb{JYlbM12Vva&PfD@<(Dgms& zI&SZ@Zf9Je5zO!)99RSME+`lP0XhfI;*mX3n7PIM*ik;OsRJ-v32-?*G+db$k6;Oz ziO=~gsUQ@&Zz6bEpm)4WVvN?D@l%%78m}Qm&nhfwODYNhO#0H%faNk3pcCuDK-%{t zn1w>5Hu*L=G&NZ3c049UfC6OA-xc72NTv8tgFU9x8aoOEEC?5@YcWWH!0q9w0l+FT z6SX8GAd(hoS#Y*T4ug>+N5KGTpCya@d~ncQRE}Q=(h?1=4+f|SKd}ZPGhvkrHh?%S z#_flo(fqU#-5WtK=jxcCH9V;3Rhpz(TjBUz+LzQ$08GKF!7yJ;X;a_5-R>my>N#YN z4@eZ6mu(vF8*&iHna(OdTU#9mipl|)89oKC=pG0+KwrirPt|>sl?RC?i^?K(3w*MU zJ3N4zRE0I!6)!NU@j9deC)1Wn8>xDAn^ZXv4zp9`Xj({-4B@QXX?$CapBy2|?J-JA z3W(!(Q8P6_K}~Zek}MR2NoD*s04=p$k~~bWVwo6)ihfB{0wAS{MblMEs1Q+U|FG3k z1Mcwmkru=w!(8!VcdsL7Jh3MBtGU=G*3va?7sln0YLNh93CJnac~E#D(zI8n6Q{!b zc1iD(w!@q(X_r>`P2dKnfd{-M9iZJoE#F4;nxy5Xqf!m*0342o^#BgOaJv=Gdmfaz zVFJXGPMDt#n%DUl+n^ zG<~*}k$eIp2~@_mCbeW3@!=~O`e^!^%w&dC$k!|f-74b&El;qP#0Rk=D0HeED5~-L zMghn%S0)S)YEn+5WnPwlD0!>oR<6Bc-IdxYBhR%K)F4*Cf?5}XB5cF@wwY=Sxo~_*N`Q%LThE;0Fyq68XHSs1jW=` zf_j4SdAyqm2Nnd%jL~K4g%XKGPE6={UT{3~*q7y3JrOXy4@e-U01^eZoYqw!C2$Xf zfEQ0Obvo(Eh3OjuQ*+us*Vab4y?n0K$o43qc`I-|ft7&m{BkqsHLwHiaL>SDZ#3PD z3oWX^038t&3{*bZg!21nbSs$Y!yH|_Y3{YJ?}ryIZvF(rVhzXq9We(>(<9-L`i=T- zOrx}vF{$P>2BJVj-xj@5BV+S$@9F`*NjD#lN4DXp77iDh&FE@ljyT!Cp-;Zi52=1o!;fG~Xe8v#ITTc`tr zPpn{I1z*z>&p1#LIEgt&SaX_=4y7Y#{Hm=%=S#K0N2fiA#H#3MCLpX3@w>-x~J1gwVDD?832%adhUk`fVq!KgaT>-t*jMl zpPMruWO`R2e{6qJXNM!>(%Mw5DBhBmnAphFTn@jTg6Smv!dhQV(wdU8WMyRv!jG;t z4{}<%FH|!XADWdWLtt2eZVG|`C?u1{#GWYQ0n>UT_RD}TYGfri7%*i3`*u}o(Aj?z zz=Yqt5rTqPWIhg~xnj%6WKPo>YI?h$AM9a{!Rc-7-kc6c+8UD#oYYnEj+aW&DhjN! zA6&V^`r6Kn^-Df!YRC;DnY&iFm_dJ-K^1~eZWj}Hzu1GiElk=80)$WZh{3D22XH$E z$5=qPWs|LV_LXa`_{`wmQN=r=z(jLbM^@`sDQKgDJPK5`{6QGYyNv?{osS+icO>f^ z{2-(jfc8;AZ5~j(?Jb@N+PG*$#XMNYX8mZqM2q@-xw146@us!276nv$nwJy_{wVmzEPNMkPqf=`hp2;klTmP}9uOSIU+S29uRdK^LL6j4@)w zNWbXAow2;w`;Z}WLA0d9!3V=~%#PAPB4C1f&%Us1kdIQ!eaJb(6{_Q7y(vq1$()x+ zv<262YwmVh0oYDsGJrjZ+7^7|2lGY2EY?iicP`)Un^pjJik!%ty2mm@( zoQQ5E95B&YCC%*H%UqN4Ge6@(oCfAf60n4nWsQpo=o4+LTL3A5BdC&;>bEwa((;qi z8#Od*R!)>MU93MnyQ-2io+5B7wHt`icK9R@YmSmn%;q#N_;4U0O-KQ+fyI96X|R5v zH~ZXdwsSoxjiVJ2&tR^?!VK#Nj~Fh3sU1$RB+G#4=QlG14b1TkTYYC3Mc4Mk2xPS9|3GMGl{n2;{ilDB|du!e52df