diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21413fc66..7af88b5aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.12 + rev: v0.11.13 hooks: - id: ruff args: diff --git a/README.md b/README.md index da9446e2b..ab5143084 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,6 @@ The key features are: - @@ -57,7 +56,7 @@ The key features are: - + diff --git a/docs/de/docs/advanced/generate-clients.md b/docs/de/docs/advanced/generate-clients.md index 38a69031c..f491d29af 100644 --- a/docs/de/docs/advanced/generate-clients.md +++ b/docs/de/docs/advanced/generate-clients.md @@ -20,7 +20,7 @@ Einige von diesen ✨ [**sponsern FastAPI**](../help-fastapi.md#den-autor-sponse Und es zeigt deren wahres Engagement für FastAPI und seine **Community** (Sie), da diese Ihnen nicht nur einen **guten Service** bieten möchten, sondern auch sicherstellen möchten, dass Sie über ein **gutes und gesundes Framework** verfügen, FastAPI. 🙇 -Beispielsweise könnten Sie Speakeasy ausprobieren. +Beispielsweise könnten Sie Speakeasy ausprobieren. Es gibt auch mehrere andere Unternehmen, welche ähnliche Dienste anbieten und die Sie online suchen und finden können. 🤓 diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 897ca7b8d..f712b6179 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -5,9 +5,6 @@ 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.porter.run - title: Deploy FastAPI on AWS with a few clicks - img: https://fastapi.tiangolo.com/img/sponsors/porter.png - url: https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge title: "Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files" img: https://fastapi.tiangolo.com/img/sponsors/scalar.svg @@ -33,7 +30,7 @@ silver: - url: https://databento.com/ title: Pay as you go for market data img: https://fastapi.tiangolo.com/img/sponsors/databento.svg - - url: https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship + - url: https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship title: SDKs for your API | Speakeasy img: https://fastapi.tiangolo.com/img/sponsors/speakeasy.png - url: https://www.svix.com/ diff --git a/docs/en/docs/advanced/generate-clients.md b/docs/en/docs/advanced/generate-clients.md index b2aef5037..55e6a08b1 100644 --- a/docs/en/docs/advanced/generate-clients.md +++ b/docs/en/docs/advanced/generate-clients.md @@ -22,7 +22,7 @@ And it shows their true commitment to FastAPI and its **community** (you), as th For example, you might want to try: -* Speakeasy +* Speakeasy * Stainless * liblab diff --git a/docs/en/docs/advanced/response-directly.md b/docs/en/docs/advanced/response-directly.md index 691b1e7cd..759b762b5 100644 --- a/docs/en/docs/advanced/response-directly.md +++ b/docs/en/docs/advanced/response-directly.md @@ -58,7 +58,7 @@ You could put your XML content in a string, put that in a `Response`, and return ## Notes -When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically. +When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically. But you can still document it as described in [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}. diff --git a/docs/en/docs/async.md b/docs/en/docs/async.md index 63bd8ca68..8207ec480 100644 --- a/docs/en/docs/async.md +++ b/docs/en/docs/async.md @@ -40,7 +40,7 @@ def results(): --- -If your application (somehow) doesn't have to communicate with anything else and wait for it to respond, use `async def`. +If your application (somehow) doesn't have to communicate with anything else and wait for it to respond, use `async def`, even if you don't need to use `await` inside. --- diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index b5d7f8d3a..68eeaae7b 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,12 +7,40 @@ hide: ## Latest Changes +### Docs + +* Misprint. PR [#13800](https://github.com/fastapi/fastapi/pull/13800) by [@NavesSapnis](https://github.com/NavesSapnis). +* 📝 Update Speakeasy URL to Speakeasy Sandbox. PR [#13697](https://github.com/fastapi/fastapi/pull/13697) by [@ndimares](https://github.com/ndimares). + +### Translations + +* 🌐 Add Russian translation for `docs/ru/docs/advanced/index.md`. PR [#13797](https://github.com/fastapi/fastapi/pull/13797) by [@NavesSapnis](https://github.com/NavesSapnis). + +## 0.115.13 + +### Fixes + +* 🐛 Fix truncating the model's description with form feed (`\f`) character for Pydantic V2. PR [#13698](https://github.com/fastapi/fastapi/pull/13698) by [@YuriiMotov](https://github.com/YuriiMotov). + +### Refactors + +* ✨ Add `refreshUrl` parameter in `OAuth2PasswordBearer`. PR [#11460](https://github.com/fastapi/fastapi/pull/11460) by [@snosratiershad](https://github.com/snosratiershad). +* 🚸 Set format to password for fields `password` and `client_secret` in `OAuth2PasswordRequestForm`, make docs show password fields for passwords. PR [#11032](https://github.com/fastapi/fastapi/pull/11032) by [@Thodoris1999](https://github.com/Thodoris1999). +* ✅ Simplify tests for `settings`. PR [#13505](https://github.com/fastapi/fastapi/pull/13505) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). +* ✅ Simplify tests for `validate_response_recursive`. PR [#13507](https://github.com/fastapi/fastapi/pull/13507) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). + ### Upgrades * ⬆️ Update ReDoc to version 2.x. PR [#9700](https://github.com/fastapi/fastapi/pull/9700) by [@joakimnordling](https://github.com/joakimnordling). ### Docs +* 📝 Add annotations to HTTP middleware example. PR [#11530](https://github.com/fastapi/fastapi/pull/11530) by [@Kilo59](https://github.com/Kilo59). +* 📝 Clarify in CORS docs that wildcards and credentials are mutually exclusive. PR [#9829](https://github.com/fastapi/fastapi/pull/9829) by [@dfioravanti](https://github.com/dfioravanti). +* ✏️ Fix typo in docstring. PR [#13532](https://github.com/fastapi/fastapi/pull/13532) by [@comp64](https://github.com/comp64). +* 📝 Clarify guidance on using `async def` without `await`. PR [#13642](https://github.com/fastapi/fastapi/pull/13642) by [@swastikpradhan1999](https://github.com/swastikpradhan1999). +* 📝 Update exclude-parameters-from-openapi documentation links. PR [#13600](https://github.com/fastapi/fastapi/pull/13600) by [@timonrieger](https://github.com/timonrieger). +* 📝 Clarify the middleware execution order in docs. PR [#13699](https://github.com/fastapi/fastapi/pull/13699) by [@YuriiMotov](https://github.com/YuriiMotov). * 🍱 Update Drawio diagrams SVGs, single file per diagram, sans-serif font. PR [#13706](https://github.com/fastapi/fastapi/pull/13706) by [@tiangolo](https://github.com/tiangolo). * 📝 Update docs for "Help FastAPI", simplify and reduce "sponsor" section. PR [#13670](https://github.com/fastapi/fastapi/pull/13670) by [@tiangolo](https://github.com/tiangolo). * 📝 Remove unnecessary bullet from docs. PR [#13641](https://github.com/fastapi/fastapi/pull/13641) by [@Adamowoc](https://github.com/Adamowoc). @@ -24,6 +52,10 @@ hide: ### Translations +* 🌐 Add Russian Translation for `docs/ru/docs/advanced/response-change-status-code.md`. PR [#13791](https://github.com/fastapi/fastapi/pull/13791) by [@NavesSapnis](https://github.com/NavesSapnis). +* 🌐 Add Persian translation for `docs/fa/docs/learn/index.md`. PR [#13518](https://github.com/fastapi/fastapi/pull/13518) by [@Mohammad222PR](https://github.com/Mohammad222PR). +* 🌐 Add Korean translation for `docs/ko/docs/advanced/sub-applications.md`. PR [#4543](https://github.com/fastapi/fastapi/pull/4543) by [@NinaHwang](https://github.com/NinaHwang). +* 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/schema-extra-example.md`. PR [#13769](https://github.com/fastapi/fastapi/pull/13769) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * ✏️ Remove redundant words in docs/zh/docs/python-types.md. PR [#13774](https://github.com/fastapi/fastapi/pull/13774) by [@CharleeWa](https://github.com/CharleeWa). * 🌐 Add Ukrainian translation for `docs/uk/docs/tutorial/query-param-models.md`. PR [#13748](https://github.com/fastapi/fastapi/pull/13748) by [@valentinDruzhinin](https://github.com/valentinDruzhinin). * 🌐 Add Bengali translation for `docs/bn/docs/environment-variables.md`. PR [#13629](https://github.com/fastapi/fastapi/pull/13629) by [@SakibSibly](https://github.com/SakibSibly). @@ -47,6 +79,9 @@ hide: ### Internal +* 🔨 Resolve Pydantic deprecation warnings in internal script. PR [#13696](https://github.com/fastapi/fastapi/pull/13696) by [@emmanuel-ferdman](https://github.com/emmanuel-ferdman). +* 🔧 Update sponsors: remove Porter. PR [#13783](https://github.com/fastapi/fastapi/pull/13783) by [@tiangolo](https://github.com/tiangolo). +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13781](https://github.com/fastapi/fastapi/pull/13781) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13757](https://github.com/fastapi/fastapi/pull/13757) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump griffe-typingdoc from 0.2.7 to 0.2.8. PR [#13751](https://github.com/fastapi/fastapi/pull/13751) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🍱 Update sponsors: Dribia badge size. PR [#13773](https://github.com/fastapi/fastapi/pull/13773) by [@tiangolo](https://github.com/tiangolo). diff --git a/docs/en/docs/tutorial/cors.md b/docs/en/docs/tutorial/cors.md index cf31cfcf5..5ca4437b3 100644 --- a/docs/en/docs/tutorial/cors.md +++ b/docs/en/docs/tutorial/cors.md @@ -57,7 +57,10 @@ The following arguments are supported: * `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`. * `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods. * `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for simple CORS requests. -* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins` cannot be set to `['*']` for credentials to be allowed, origins must be specified. +* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. + + None of `allow_origins`, `allow_methods` and `allow_headers` can be set to `['*']` if `allow_credentials` is set to `True`. All of them must be explicitly specified. + * `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`. * `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`. diff --git a/docs/en/docs/tutorial/middleware.md b/docs/en/docs/tutorial/middleware.md index 4f7980165..b7c03a319 100644 --- a/docs/en/docs/tutorial/middleware.md +++ b/docs/en/docs/tutorial/middleware.md @@ -65,6 +65,29 @@ Here we use -
- - - - -
diff --git a/docs/es/docs/advanced/generate-clients.md b/docs/es/docs/advanced/generate-clients.md index bf2e5cb4f..b664bceac 100644 --- a/docs/es/docs/advanced/generate-clients.md +++ b/docs/es/docs/advanced/generate-clients.md @@ -22,7 +22,7 @@ Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que n Por ejemplo, podrías querer probar: -* Speakeasy +* Speakeasy * Stainless * liblab diff --git a/docs/fa/docs/learn/index.md b/docs/fa/docs/learn/index.md new file mode 100644 index 000000000..06aa7f00e --- /dev/null +++ b/docs/fa/docs/learn/index.md @@ -0,0 +1,5 @@ +# یادگیری + +اینجا بخش‌های مقدماتی و آموزش‌هایی هستن که برای یادگیری **FastAPI** بهت کمک می‌کنن. + +می‌تونی اینو یه **کتاب**، یه **دوره آموزشی**، یا راه **رسمی** و پیشنهادی برای یادگیری FastAPI در نظر بگیری. 😎 diff --git a/docs/ko/docs/advanced/sub-applications.md b/docs/ko/docs/advanced/sub-applications.md new file mode 100644 index 000000000..c5835de15 --- /dev/null +++ b/docs/ko/docs/advanced/sub-applications.md @@ -0,0 +1,67 @@ +# 하위 응용프로그램 - 마운트 + +만약 각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 응용프로그램이 필요하다면, 메인 어플리케이션에 하나 (또는 그 이상의) 하위-응용프로그램(들)을 “마운트"해서 사용할 수 있습니다. + +## **FastAPI** 응용프로그램 마운트 + +“마운트"이란 완전히 “독립적인" 응용프로그램을 특정 경로에 추가하여 해당 하위 응용프로그램에서 선언된 *경로 동작*을 통해 해당 경로 아래에 있는 모든 작업들을 처리할 수 있도록 하는 것을 의미합니다. + +### 최상단 응용프로그램 + +먼저, 메인, 최상단의 **FastAPI** 응용프로그램과 이것의 *경로 동작*을 생성합니다: + +{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *} + +### 하위 응용프로그램 + +다음으로, 하위 응용프로그램과 이것의 *경로 동작*을 생성합니다: + +이 하위 응용프로그램은 또 다른 표준 FastAPI 응용프로그램입니다. 다만 이것은 “마운트”될 것입니다: + +{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *} + +### 하위 응용프로그램 마운트 + +최상단 응용프로그램, `app`에 하위 응용프로그램, `subapi`를 마운트합니다. + +이 예시에서, 하위 응용프로그램션은 `/subapi` 경로에 마운트 될 것입니다: + +{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *} + +### 자동으로 생성된 API 문서 확인 + +이제, `uvicorn`으로 메인 응용프로그램을 실행하십시오. 당신의 파일이 `main.py`라면, 이렇게 실행합니다: + +
+ +```console +$ uvicorn main:app --reload + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +그리고 http://127.0.0.1:8000/docs에서 문서를 여십시오. + +메인 응용프로그램의 *경로 동작*만을 포함하는, 메인 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다: + + + +다음으로, http://127.0.0.1:8000/subapi/docs에서 하위 응용프로그램의 문서를 여십시오. + +하위 경로 접두사 `/subapi` 아래에 선언된 *경로 동작* 을 포함하는, 하위 응용프로그램에 대한 자동 API 문서를 확인할 수 있습니다: + + + +두 사용자 인터페이스 중 어느 하나를 사용해야하는 경우, 브라우저는 특정 응용프로그램 또는 하위 응용프로그램과 각각 통신할 수 있기 때문에 올바르게 동작할 것입니다. + +### 기술적 세부사항: `root_path` + +위에 설명된 것과 같이 하위 응용프로그램을 마운트하는 경우, FastAPI는 `root_path`라고 하는 ASGI 명세의 매커니즘을 사용하여 하위 응용프로그램에 대한 마운트 경로 통신을 처리합니다. + +이를 통해, 하위 응용프로그램은 문서 UI를 위해 경로 접두사를 사용해야 한다는 사실을 인지합니다. + +하위 응용프로그램에도 역시 다른 하위 응용프로그램을 마운트하는 것이 가능하며 FastAPI가 모든 `root_path` 들을 자동적으로 처리하기 때문에 모든 것은 올바르게 동작할 것입니다. + +`root_path`와 이것을 사용하는 방법에 대해서는 [프록시의 뒷단](./behind-a-proxy.md){.internal-link target=_blank} 섹션에서 배울 수 있습니다. diff --git a/docs/pt/docs/advanced/generate-clients.md b/docs/pt/docs/advanced/generate-clients.md index 04d7c0071..dc6b29511 100644 --- a/docs/pt/docs/advanced/generate-clients.md +++ b/docs/pt/docs/advanced/generate-clients.md @@ -22,7 +22,7 @@ E isso mostra o verdadeiro compromisso deles com o FastAPI e sua **comunidade** Por exemplo, você pode querer experimentar: -* Speakeasy +* Speakeasy * Stainless * liblab diff --git a/docs/ru/docs/advanced/index.md b/docs/ru/docs/advanced/index.md new file mode 100644 index 000000000..b5cb733e7 --- /dev/null +++ b/docs/ru/docs/advanced/index.md @@ -0,0 +1,21 @@ +# Расширенное руководство пользователя + +## Дополнительные возможности + +Основное [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank} должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**. + +В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности. + +/// tip + +Следующие разделы **не обязательно являются "продвинутыми"**. + +И вполне возможно, что для вашего случая использования решение находится в одном из них. + +/// + +## Сначала прочитайте Учебник - Руководство пользователя + +Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md){.internal-link target=_blank}. + +И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи. diff --git a/docs/ru/docs/advanced/response-change-status-code.md b/docs/ru/docs/advanced/response-change-status-code.md new file mode 100644 index 000000000..37dade99f --- /dev/null +++ b/docs/ru/docs/advanced/response-change-status-code.md @@ -0,0 +1,31 @@ +# Response - Изменение cтатус кода + +Вы, вероятно, уже читали о том, что можно установить [Состояние ответа по умолчанию](../tutorial/response-status-code.md){.internal-link target=_blank}. + +Но в некоторых случаях вам нужно вернуть код состояния, отличный от установленного по умолчанию. + +## Пример использования + +Например, представьте, что вы хотите возвращать HTTP код состояния "OK" `200` по умолчанию. + +Но если данные не существовали, вы хотите создать их и вернуть HTTP код состояния "CREATED" `201`. + +При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`. + +Для таких случаев вы можете использовать параметр `Response`. + +## Использование параметра `Response` + +Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (так же как для cookies и headers). + +И затем вы можете установить `status_code` в этом *временном* объекте ответа. + +{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *} + +После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.). + +И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта. + +**FastAPI** будет использовать этот *временный* ответ для извлечения кода состояния (а также cookies и headers) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`. + +Вы также можете объявить параметр `Response` в зависимостях и установить код состояния в них. Но помните, что последнее установленное значение будет иметь приоритет. diff --git a/docs/uk/docs/tutorial/schema-extra-example.md b/docs/uk/docs/tutorial/schema-extra-example.md new file mode 100644 index 000000000..853fd5e65 --- /dev/null +++ b/docs/uk/docs/tutorial/schema-extra-example.md @@ -0,0 +1,222 @@ +# Декларування прикладів вхідних даних + +Ви можете задати приклади даних, які Ваш застосунок може отримувати. + +Ось кілька способів, як це зробити. + +## Додаткові дані JSON-схеми в моделях Pydantic + +Ви можете задати `examples` для моделі Pydantic, які буде додано до згенерованої JSON-схеми. + +//// tab | Pydantic v2 + +{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} + +//// + +//// tab | Pydantic v1 + +{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *} + +//// + +Ця додаткова інформація буде додана як є до **JSON-схеми**, і вона буде використовуватися в документації до API. + +//// tab | Pydantic v2 + +У версії Pydantic 2 використовується атрибут `model_config`, який приймає `dict`, як описано в документації Pydantic: Конфігурація. + +Ви можете встановити `"json_schema_extra"` як `dict`, що містить будь-які додаткові дані, які Ви хочете відобразити у згенерованій JSON-схемі, включаючи `examples`. + +//// + +//// tab | Pydantic v1 + +У версії Pydantic 1 використовується внутрішній клас `Config` і параметр `schema_extra`, як описано в документації Pydantic: Налаштування схеми. + +Ви можете задати `schema_extra` як `dict`, що містить будь-які додаткові дані, які Ви хочете бачити у згенерованій JSON-схемі, включаючи `examples`. + +//// + +/// tip | Підказка + +Ви можете використати ту ж техніку, щоб розширити JSON-схему і додати власну додаткову інформацію. + +Наприклад, Ви можете використати її для додавання метаданих для інтерфейсу користувача на фронтенді тощо. + +/// + +/// info | Інформація + +OpenAPI 3.1.0 (який використовується починаючи з FastAPI 0.99.0) додав підтримку `examples`, що є частиною стандарту **JSON-схеми**. + +До цього підтримувався лише ключ `example` з одним прикладом. Він все ще підтримується в OpenAPI 3.1.0, але є застарілим і не входить до стандарту JSON Schema. Тому рекомендується перейти з `example` на `examples`. 🤓 + +Більше про це можна прочитати в кінці цієї сторінки. + +/// + +## Додаткові аргументи `Field` + +Коли ви використовуєте `Field()` у моделях Pydantic, Ви також можете вказати додаткові `examples`: + +{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *} + +## `examples` у JSON-схемі — OpenAPI + +При використанні будь-кого з наступного: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +Ви також можете задати набір `examples` з додатковою інформацією, яка буде додана до їхніх **JSON-схем** у **OpenAPI**. + +### `Body` з `examples` + +Тут ми передаємо `examples`, які містять один приклад очікуваних даних у `Body()`: + +{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *} + +### Приклад у UI документації + +За допомогою будь-якого з наведених вище методів це виглядатиме так у документації за `/docs`: + + + +### `Body` з кількома `examples` + +Звичайно, Ви також можете передати кілька `examples`: + +{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *} + +Коли Ви це робите, приклади будуть частиною внутрішньої **JSON-схеми** для цих даних. + +Втім, на момент написання цього (26 серпня 2023), Swagger UI — інструмент, який відповідає за відображення UI документації — не підтримує показ кількох прикладів у **JSON-схеми**. Але нижче можна прочитати про обхідний шлях. + +### Специфічні для OpenAPI `examples` + +Ще до того, як **JSON-схема** почала підтримувати `examples`, OpenAPI вже мала підтримку поля з такою ж назвою — `examples`. + +Це **специфічне для OpenAPI** поле `examples` розміщується в іншій частині специфікації OpenAPI — у **деталях кожної *операції шляху***, а не всередині самої JSON-схеми. + +Swagger UI вже давно підтримує це поле `examples`. Тому Ви можете використовувати його, щоб **відображати** кілька **прикладів у документації**. + +Це поле `examples` у специфікації OpenAPI — це `dict` (словник) з **кількома прикладами** (а не список `list`), кожен із яких може містити додаткову інформацію, що буде додана до **OpenAPI**. + +Воно не включається до JSON Schema кожного параметра, а розміщується зовні, безпосередньо в *операції шляху*. + +### Використання параметра `openapi_examples` + +Ви можете оголосити специфічні для OpenAPI `examples` у FastAPI за допомогою параметра `openapi_examples` для: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +Ключі словника (`dict`) ідентифікують кожен приклад, а кожне значення `dict` — кожен специфічний словник `dict` в `examples` може містити: + +* `summary`: короткий опис прикладу. +* `description`: розгорнутий опис (може містити Markdown). +* `value`: сам приклад, наприклад, словник (`dict`). +* `externalValue`: альтернатива `value`, URL-адреса, що вказує на приклад. Проте ця опція може не підтримуватися більшістю інструментів, на відміну від `value`. + +Використання виглядає так: + +{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *} + +### Приклади OpenAPI у UI документації + +З параметром `openapi_examples`, доданим до `Body()`, документація `/docs` виглядатиме так: + + + +## Технічні деталі + +/// tip | Підказка + +Якщо Ви вже використовуєте **FastAPI** версії **0.99.0 або вище**, Ви можете **пропустити** цей розділ. + +Він більш актуальний для старих версій, до появи OpenAPI 3.1.0. + +Можна вважати це коротким **історичним екскурсом** у OpenAPI та JSON Schema. 🤓 + +/// + +/// warning | Попередження + +Це дуже технічна інформація про стандарти **JSON Schema** і **OpenAPI**. + +Якщо вищезгадані ідеї вже працюють у Вас — можете не заглиблюватися в ці деталі. + +/// + +До OpenAPI 3.1.0 специфікація використовувала стару та модифіковану версію **JSON Schema**. + +Оскільки JSON Schema раніше не підтримувала `examples`, OpenAPI додала власне поле `examples`. + +OpenAPI також додала `example` і `examples` до інших частин специфікації: + +* `Parameter Object` (в специфікації) використовується FastAPI для: + * `Path()` + * `Query()` + * `Header()` + * `Cookie()` +* `Request Body Object`, в полі `content`, в `Media Type Object` (в специфікації) використовується FastAPI для: + * `Body()` + * `File()` + * `Form()` + +/// info | Інформація + +Цей старий параметр `examples`, специфічний для OpenAPI, тепер називається `openapi_examples`, починаючи з FastAPI версії `0.103.0`. + +/// + +### Поле `examples` у JSON Schema + +Пізніше JSON Schema додала поле `examples` у нову версію специфікації. + +І вже OpenAPI 3.1.0 базується на цій новій версії (JSON Schema 2020-12), яка включає поле `examples`. + +Тепер це поле `examples` є пріоритетним і замінює старе (і кастомне) поле `example`, яке стало застарілим. + +Нове поле `examples` у JSON Schema — це **просто список (`list`)** прикладів, без додаткових метаданих (на відміну від OpenAPI). + +/// info | Інформація + +Навіть після того, як з'явився OpenAPI 3.1.0, який підтримував examples у JSON Schema, інструмент Swagger UI ще деякий час не підтримував цю версію (підтримка з’явилась з версії 5.0.0 🎉). + +Через це версії FastAPI до 0.99.0 все ще використовували версії OpenAPI нижчі за 3.1.0. + +/// + +### `Examples` в Pydantic і FastAPI + +Коли Ви додаєте `examples` у модель Pydantic через `schema_extra` або `Field(examples=["something"])`, ці приклади додаються до **JSON Schema** цієї моделі. + +І ця **JSON Schema** Pydantic-моделі включається до **OpenAPI** Вашого API, а потім використовується в UI документації (docs UI). + +У версіях FastAPI до 0.99.0 (починаючи з 0.99.0 використовується новіший OpenAPI 3.1.0), коли Ви використовували `example` або `examples` з іншими утилітами (`Query()`, `Body()` тощо), ці приклади не додавалися до JSON Schema, який описує ці дані (навіть не до власної версії JSON Schema у OpenAPI). Натомість вони додавалися безпосередньо до опису *обробника шляху* *(path operation)* в OpenAPI (тобто поза межами частин, які використовують JSON Schema). + +Але тепер, коли FastAPI 0.99.0 і вище використовують OpenAPI 3.1.0, а той — JSON Schema 2020-12, разом із Swagger UI 5.0.0 і вище — все стало більш узгодженим, і examples тепер включаються до JSON Schema. + +### Swagger UI та специфічні для OpenAPI `examples` + +Раніше (станом на 26 серпня 2023 року) Swagger UI не підтримував кілька прикладів у JSON Schema, тому користувачі не мали можливості показати декілька прикладів у документації. + +Щоб вирішити це, FastAPI починаючи з версії 0.103.0 **додав підтримку** старого **OpenAPI-специфічного** поля `examples` через новий параметр `openapi_examples`. 🤓 + +### Підсумок + +Раніше я казав, що не люблю історію... а тепер ось я — розповідаю "технічні історичні" лекції. 😅 + +Коротко: **оновіться до FastAPI 0.99.0 або вище** — і все стане значно **простішим, узгодженим та інтуїтивно зрозумілим**, і Вам не доведеться знати всі ці історичні деталі. 😎 diff --git a/fastapi/__init__.py b/fastapi/__init__.py index 80eb783da..1768d0204 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.115.12" +__version__ = "0.115.13" from starlette import status as status diff --git a/fastapi/_compat.py b/fastapi/_compat.py index c07e4a3b0..227ad837d 100644 --- a/fastapi/_compat.py +++ b/fastapi/_compat.py @@ -16,6 +16,7 @@ from typing import ( Tuple, Type, Union, + cast, ) from fastapi.exceptions import RequestErrorModel @@ -231,6 +232,10 @@ if PYDANTIC_V2: field_mapping, definitions = schema_generator.generate_definitions( inputs=inputs ) + for item_def in cast(Dict[str, Dict[str, Any]], definitions).values(): + if "description" in item_def: + item_description = cast(str, item_def["description"]).split("\f")[0] + item_def["description"] = item_description return field_mapping, definitions # type: ignore[return-value] def is_scalar_field(field: ModelField) -> bool: diff --git a/fastapi/applications.py b/fastapi/applications.py index 6d427cdc2..05c7bd2be 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -748,7 +748,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -1720,7 +1720,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2093,7 +2093,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2471,7 +2471,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2849,7 +2849,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3222,7 +3222,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3595,7 +3595,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3968,7 +3968,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -4346,7 +4346,7 @@ class FastAPI(Starlette): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -4425,7 +4425,7 @@ class FastAPI(Starlette): app = FastAPI() - @app.put("/items/{item_id}") + @app.trace("/items/{item_id}") def trace_item(item_id: str): return None ``` @@ -4515,14 +4515,17 @@ class FastAPI(Starlette): ```python import time + from typing import Awaitable, Callable - from fastapi import FastAPI, Request + from fastapi import FastAPI, Request, Response app = FastAPI() @app.middleware("http") - async def add_process_time_header(request: Request, call_next): + async def add_process_time_header( + request: Request, call_next: Callable[[Request], Awaitable[Response]] + ) -> Response: start_time = time.time() response = await call_next(request) process_time = time.time() - start_time diff --git a/fastapi/routing.py b/fastapi/routing.py index 457481e32..bf61a65c1 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -814,7 +814,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -1626,7 +1626,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2003,7 +2003,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2385,7 +2385,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -2767,7 +2767,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3144,7 +3144,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3521,7 +3521,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -3903,7 +3903,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, @@ -4285,7 +4285,7 @@ class APIRouter(routing.Router): This affects the generated OpenAPI (e.g. visible at `/docs`). Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). + [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi). """ ), ] = True, diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index 9efa65a4c..51065e4e2 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -85,7 +85,7 @@ class OAuth2PasswordRequestForm: ], password: Annotated[ str, - Form(), + Form(json_schema_extra={"format": "password"}), Doc( """ `password` string. The OAuth2 spec requires the exact field name @@ -130,7 +130,7 @@ class OAuth2PasswordRequestForm: ] = None, client_secret: Annotated[ Union[str, None], - Form(), + Form(json_schema_extra={"format": "password"}), Doc( """ If there's a `client_password` (and a `client_id`), they can be sent @@ -459,11 +459,26 @@ class OAuth2PasswordBearer(OAuth2): """ ), ] = True, + refreshUrl: Annotated[ + Optional[str], + Doc( + """ + The URL to refresh the token and obtain a new one. + """ + ), + ] = None, ): if not scopes: scopes = {} flows = OAuthFlowsModel( - password=cast(Any, {"tokenUrl": tokenUrl, "scopes": scopes}) + password=cast( + Any, + { + "tokenUrl": tokenUrl, + "refreshUrl": refreshUrl, + "scopes": scopes, + }, + ) ) super().__init__( flows=flows, diff --git a/scripts/label_approved.py b/scripts/label_approved.py index 271444504..81de92efb 100644 --- a/scripts/label_approved.py +++ b/scripts/label_approved.py @@ -27,7 +27,7 @@ if settings.debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) -logging.debug(f"Using config: {settings.json()}") +logging.debug(f"Using config: {settings.model_dump_json()}") g = Github(settings.token.get_secret_value()) repo = g.get_repo(settings.github_repository) for pr in repo.get_pulls(state="open"): @@ -48,7 +48,7 @@ for pr in repo.get_pulls(state="open"): ] config = settings.config or default_config for approved_label, conf in config.items(): - logging.debug(f"Processing config: {conf.json()}") + logging.debug(f"Processing config: {conf.model_dump_json()}") if conf.await_label is None or (conf.await_label in pr_label_by_name): logging.debug(f"Processable PR: {pr.number}") if len(approved_reviews) >= conf.number: diff --git a/tests/test_openapi_model_description_trim_on_formfeed.py b/tests/test_openapi_model_description_trim_on_formfeed.py new file mode 100644 index 000000000..e18d4f6b2 --- /dev/null +++ b/tests/test_openapi_model_description_trim_on_formfeed.py @@ -0,0 +1,31 @@ +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import BaseModel + +app = FastAPI() + + +class MyModel(BaseModel): + """ + A model with a form feed character in the title. + \f + Text after form feed character. + """ + + +@app.get("/foo") +def foo(v: MyModel): # pragma: no cover + pass + + +client = TestClient(app) + + +def test_openapi(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + openapi_schema = response.json() + + assert openapi_schema["components"]["schemas"]["MyModel"]["description"] == ( + "A model with a form feed character in the title.\n" + ) diff --git a/tests/test_tutorial/test_security/test_tutorial003.py b/tests/test_tutorial/test_security/test_tutorial003.py index 37fc2618f..2bbb2e851 100644 --- a/tests/test_tutorial/test_security/test_tutorial003.py +++ b/tests/test_tutorial/test_security/test_tutorial003.py @@ -163,7 +163,11 @@ def test_openapi_schema(client: TestClient): } ), "username": {"title": "Username", "type": "string"}, - "password": {"title": "Password", "type": "string"}, + "password": { + "title": "Password", + "type": "string", + "format": "password", + }, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": IsDict( { @@ -179,11 +183,16 @@ def test_openapi_schema(client: TestClient): { "title": "Client Secret", "anyOf": [{"type": "string"}, {"type": "null"}], + "format": "password", } ) | IsDict( # TODO: remove when deprecating Pydantic v1 - {"title": "Client Secret", "type": "string"} + { + "title": "Client Secret", + "type": "string", + "format": "password", + } ), }, }, diff --git a/tests/test_tutorial/test_security/test_tutorial005.py b/tests/test_tutorial/test_security/test_tutorial005.py index 88c3d7815..ad644d61b 100644 --- a/tests/test_tutorial/test_security/test_tutorial005.py +++ b/tests/test_tutorial/test_security/test_tutorial005.py @@ -377,7 +377,11 @@ def test_openapi_schema(mod: ModuleType): } ), "username": {"title": "Username", "type": "string"}, - "password": {"title": "Password", "type": "string"}, + "password": { + "title": "Password", + "type": "string", + "format": "password", + }, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": IsDict( { @@ -393,11 +397,16 @@ def test_openapi_schema(mod: ModuleType): { "title": "Client Secret", "anyOf": [{"type": "string"}, {"type": "null"}], + "format": "password", } ) | IsDict( # TODO: remove when deprecating Pydantic v1 - {"title": "Client Secret", "type": "string"} + { + "title": "Client Secret", + "type": "string", + "format": "password", + } ), }, }, diff --git a/tests/test_tutorial/test_settings/test_tutorial001.py b/tests/test_tutorial/test_settings/test_tutorial001.py index eb30dbcee..92a5782d4 100644 --- a/tests/test_tutorial/test_settings/test_tutorial001.py +++ b/tests/test_tutorial/test_settings/test_tutorial001.py @@ -1,14 +1,26 @@ +import importlib + +import pytest from fastapi.testclient import TestClient from pytest import MonkeyPatch -from ...utils import needs_pydanticv2 +from ...utils import needs_pydanticv1, needs_pydanticv2 -@needs_pydanticv2 -def test_settings(monkeypatch: MonkeyPatch): +@pytest.fixture( + name="app", + params=[ + pytest.param("tutorial001", marks=needs_pydanticv2), + pytest.param("tutorial001_pv1", marks=needs_pydanticv1), + ], +) +def get_app(request: pytest.FixtureRequest, monkeypatch: MonkeyPatch): monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com") - from docs_src.settings.tutorial001 import app + mod = importlib.import_module(f"docs_src.settings.{request.param}") + return mod.app + +def test_settings(app): client = TestClient(app) response = client.get("/info") assert response.status_code == 200, response.text diff --git a/tests/test_tutorial/test_settings/test_tutorial001_pv1.py b/tests/test_tutorial/test_settings/test_tutorial001_pv1.py deleted file mode 100644 index e4659de66..000000000 --- a/tests/test_tutorial/test_settings/test_tutorial001_pv1.py +++ /dev/null @@ -1,19 +0,0 @@ -from fastapi.testclient import TestClient -from pytest import MonkeyPatch - -from ...utils import needs_pydanticv1 - - -@needs_pydanticv1 -def test_settings(monkeypatch: MonkeyPatch): - monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com") - from docs_src.settings.tutorial001_pv1 import app - - client = TestClient(app) - response = client.get("/info") - assert response.status_code == 200, response.text - assert response.json() == { - "app_name": "Awesome API", - "admin_email": "admin@example.com", - "items_per_user": 50, - } diff --git a/tests/test_validate_response_recursive/app_pv1.py b/tests/test_validate_response_recursive/app.py similarity index 79% rename from tests/test_validate_response_recursive/app_pv1.py rename to tests/test_validate_response_recursive/app.py index 4cfc4b3ee..d23d27980 100644 --- a/tests/test_validate_response_recursive/app_pv1.py +++ b/tests/test_validate_response_recursive/app.py @@ -1,6 +1,7 @@ from typing import List from fastapi import FastAPI +from fastapi._compat import PYDANTIC_V2 from pydantic import BaseModel app = FastAPI() @@ -11,9 +12,6 @@ class RecursiveItem(BaseModel): name: str -RecursiveItem.update_forward_refs() - - class RecursiveSubitemInSubmodel(BaseModel): sub_items2: List["RecursiveItemViaSubmodel"] = [] name: str @@ -24,7 +22,13 @@ class RecursiveItemViaSubmodel(BaseModel): name: str -RecursiveSubitemInSubmodel.update_forward_refs() +if PYDANTIC_V2: + RecursiveItem.model_rebuild() + RecursiveSubitemInSubmodel.model_rebuild() + RecursiveItemViaSubmodel.model_rebuild() +else: + RecursiveItem.update_forward_refs() + RecursiveSubitemInSubmodel.update_forward_refs() @app.get("/items/recursive", response_model=RecursiveItem) diff --git a/tests/test_validate_response_recursive/app_pv2.py b/tests/test_validate_response_recursive/app_pv2.py deleted file mode 100644 index 8c93a8349..000000000 --- a/tests/test_validate_response_recursive/app_pv2.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import List - -from fastapi import FastAPI -from pydantic import BaseModel - -app = FastAPI() - - -class RecursiveItem(BaseModel): - sub_items: List["RecursiveItem"] = [] - name: str - - -RecursiveItem.model_rebuild() - - -class RecursiveSubitemInSubmodel(BaseModel): - sub_items2: List["RecursiveItemViaSubmodel"] = [] - name: str - - -class RecursiveItemViaSubmodel(BaseModel): - sub_items1: List[RecursiveSubitemInSubmodel] = [] - name: str - - -RecursiveSubitemInSubmodel.model_rebuild() -RecursiveItemViaSubmodel.model_rebuild() - - -@app.get("/items/recursive", response_model=RecursiveItem) -def get_recursive(): - return {"name": "item", "sub_items": [{"name": "subitem", "sub_items": []}]} - - -@app.get("/items/recursive-submodel", response_model=RecursiveItemViaSubmodel) -def get_recursive_submodel(): - return { - "name": "item", - "sub_items1": [ - { - "name": "subitem", - "sub_items2": [ - { - "name": "subsubitem", - "sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], - } - ], - } - ], - } diff --git a/tests/test_validate_response_recursive/test_validate_response_recursive_pv2.py b/tests/test_validate_response_recursive/test_validate_response_recursive.py similarity index 90% rename from tests/test_validate_response_recursive/test_validate_response_recursive_pv2.py rename to tests/test_validate_response_recursive/test_validate_response_recursive.py index 7d45e7fe4..21a299ab8 100644 --- a/tests/test_validate_response_recursive/test_validate_response_recursive_pv2.py +++ b/tests/test_validate_response_recursive/test_validate_response_recursive.py @@ -1,12 +1,9 @@ from fastapi.testclient import TestClient -from ..utils import needs_pydanticv2 +from .app import app -@needs_pydanticv2 def test_recursive(): - from .app_pv2 import app - client = TestClient(app) response = client.get("/items/recursive") assert response.status_code == 200, response.text diff --git a/tests/test_validate_response_recursive/test_validate_response_recursive_pv1.py b/tests/test_validate_response_recursive/test_validate_response_recursive_pv1.py deleted file mode 100644 index de578ae03..000000000 --- a/tests/test_validate_response_recursive/test_validate_response_recursive_pv1.py +++ /dev/null @@ -1,33 +0,0 @@ -from fastapi.testclient import TestClient - -from ..utils import needs_pydanticv1 - - -@needs_pydanticv1 -def test_recursive(): - from .app_pv1 import app - - client = TestClient(app) - response = client.get("/items/recursive") - assert response.status_code == 200, response.text - assert response.json() == { - "sub_items": [{"name": "subitem", "sub_items": []}], - "name": "item", - } - - response = client.get("/items/recursive-submodel") - assert response.status_code == 200, response.text - assert response.json() == { - "name": "item", - "sub_items1": [ - { - "name": "subitem", - "sub_items2": [ - { - "name": "subsubitem", - "sub_items1": [{"name": "subsubsubitem", "sub_items2": []}], - } - ], - } - ], - }