Browse Source

♻️ Restore master files

pull/14971/head
Sebastián Ramírez 5 months ago
parent
commit
f5d8c9cf85
  1. 80
      docs/en/docs/advanced/custom-response.md
  2. 28
      docs/en/docs/advanced/response-directly.md
  3. 4
      docs/en/docs/how-to/general.md
  4. 8
      docs/en/docs/reference/responses.md
  5. 19
      docs/en/docs/release-notes.md
  6. 1
      docs/en/docs/tutorial/response-model.md
  7. 6
      docs_src/custom_response/tutorial010_py310.py
  8. 2
      fastapi/__init__.py
  9. 52
      fastapi/responses.py
  10. 9
      pyproject.toml
  11. 73
      tests/test_deprecated_responses.py
  12. 7
      tests/test_orjson_response_class.py
  13. 3
      tests/test_tutorial/test_custom_response/test_tutorial001.py
  14. 8
      tests/test_tutorial/test_custom_response/test_tutorial001b.py
  15. 50
      tests/test_tutorial/test_custom_response/test_tutorial010.py
  16. 20
      uv.lock

80
docs/en/docs/advanced/custom-response.md

@ -1,6 +1,6 @@
# Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others } # Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others }
By default, **FastAPI** will return the responses using `JSONResponse`. By default, **FastAPI** will return JSON responses.
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}. You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
@ -10,43 +10,27 @@ But you can also declare the `Response` that you want to be used (e.g. any `Resp
The contents that you return from your *path operation function* will be put inside of that `Response`. The contents that you return from your *path operation function* will be put inside of that `Response`.
And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
/// note /// note
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs. If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
/// ///
## Use `ORJSONResponse` { #use-orjsonresponse } ## JSON Responses { #json-responses }
For example, if you are squeezing performance, you can install and use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> and set the response to be `ORJSONResponse`.
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
For large responses, returning a `Response` directly is much faster than returning a dictionary. By default FastAPI returns JSON responses.
This is because by default, FastAPI will inspect every item inside and make sure it is serializable as JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models. If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class. If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
/// info
The parameter `response_class` will also be used to define the "media type" of the response.
In this case, the HTTP header `Content-Type` will be set to `application/json`.
And it will be documented as such in OpenAPI.
/// If you declare a `response_class` with a JSON media type (`application/json`), like is the case with the `JSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. But the data won't be serialized to JSON bytes with Pydantic, instead it will be converted with the `jsonable_encoder` and then passed to the `JSONResponse` class, which will serialize it to bytes using the standard JSON library in Python.
/// tip ### JSON Performance { #json-performance }
The `ORJSONResponse` is only available in FastAPI, not in Starlette. In short, if you want the maximum performance, use a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} and don't declare a `response_class` in the *path operation decorator*.
/// {* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML Response { #html-response } ## HTML Response { #html-response }
@ -154,40 +138,6 @@ Takes some data and returns an `application/json` encoded response.
This is the default response used in **FastAPI**, as you read above. This is the default response used in **FastAPI**, as you read above.
### `ORJSONResponse` { #orjsonresponse }
A fast alternative JSON response using <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, as you read above.
/// info
This requires installing `orjson` for example with `pip install orjson`.
///
### `UJSONResponse` { #ujsonresponse }
An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
/// info
This requires installing `ujson` for example with `pip install ujson`.
///
/// warning
`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
///
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
/// tip
It's possible that `ORJSONResponse` might be a faster alternative.
///
### `RedirectResponse` { #redirectresponse } ### `RedirectResponse` { #redirectresponse }
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default. Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
@ -268,7 +218,7 @@ In this case, you can return the file path directly from your *path operation* f
You can create your own custom response class, inheriting from `Response` and using it. You can create your own custom response class, inheriting from `Response` and using it.
For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, but with some custom settings not used in the included `ORJSONResponse` class. For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> with some settings.
Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`. Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`.
@ -292,13 +242,21 @@ Now instead of returning:
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉 Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉
### `orjson` or Response Model { #orjson-or-response-model }
If what you are looking for is performance, you are probably better off using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than an `orjson` response.
With a response model, FastAPI will use Pydantic to serialize the data to JSON, without using intermediate steps, like converting it with `jsonable_encoder`, which would happen in any other case.
And under the hood, Pydantic uses the same underlying Rust mechanisms as `orjson` to serialize to JSON, so you will already get the best performance with a response model.
## Default response class { #default-response-class } ## Default response class { #default-response-class }
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default. When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
The parameter that defines this is `default_response_class`. The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`. In the example below, **FastAPI** will use `HTMLResponse` by default, in all *path operations*, instead of JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *} {* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}

28
docs/en/docs/advanced/response-directly.md

@ -2,19 +2,23 @@
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc. When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}. If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client. If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
But you can return a `JSONResponse` directly from your *path operations*. You could also create a `JSONResponse` directly and return it.
It might be useful, for example, to return custom headers or cookies. /// tip
You will normally have much better performance using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than returning a `JSONResponse` directly, as that way it serializes the data using Pydantic, in Rust.
///
## Return a `Response` { #return-a-response } ## Return a `Response` { #return-a-response }
In fact, you can return any `Response` or any sub-class of it. You can return any `Response` or any sub-class of it.
/// tip /// info
`JSONResponse` itself is a sub-class of `Response`. `JSONResponse` itself is a sub-class of `Response`.
@ -56,6 +60,18 @@ You could put your XML content in a string, put that in a `Response`, and return
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *} {* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## How a Response Model Works { #how-a-response-model-works }
When you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} in a path operation, **FastAPI** will use it to serialize the data to JSON, using Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
As that will happen on the Rust side, the performance will be much better than if it was done with regular Python and the `JSONResponse` class.
When using a response model FastAPI won't use the `jsonable_encoder` to convert the data (which would be slower) nor the `JSONResponse` class.
Instead it takes the JSON bytes generated with Pydantic using the response model and returns a `Response` with the right media type for JSON directly (`application/json`).
## Notes { #notes } ## Notes { #notes }
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically. When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.

4
docs/en/docs/how-to/general.md

@ -6,6 +6,10 @@ Here are several pointers to other places in the docs, for general or frequent q
To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}. To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
## Optimize Response Performance - Response Model - Return Type { #optimize-response-performance-response-model-return-type }
To optimize performance when returning JSON data, use a return type or response model, that way Pydantic will handle the serialization to JSON on the Rust side, without going through Python. Read more in the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
## Documentation Tags - OpenAPI { #documentation-tags-openapi } ## Documentation Tags - OpenAPI { #documentation-tags-openapi }
To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}. To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.

8
docs/en/docs/reference/responses.md

@ -22,7 +22,13 @@ from fastapi.responses import (
## FastAPI Responses ## FastAPI Responses
There are a couple of custom FastAPI response classes, you can use them to optimize JSON performance. There were a couple of custom FastAPI response classes that were intended to optimize JSON performance.
However, they are now deprecated as you will now get better performance by using a [Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/).
That way, Pydantic will serialize the data into JSON bytes on the Rust side, which will achieve better performance than these custom JSON responses.
Read more about it in [Custom Response - HTML, Stream, File, others - `orjson` or Response Model](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model).
::: fastapi.responses.UJSONResponse ::: fastapi.responses.UJSONResponse
options: options:

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

@ -7,6 +7,25 @@ hide:
## Latest Changes ## Latest Changes
### Internal
* 👷 Do not run codspeed with coverage as it's not tracked. PR [#14966](https://github.com/fastapi/fastapi/pull/14966) by [@tiangolo](https://github.com/tiangolo).
* 👷 Do not include benchmark tests in coverage to speed up coverage processing. PR [#14965](https://github.com/fastapi/fastapi/pull/14965) by [@tiangolo](https://github.com/tiangolo).
## 0.131.0
### Breaking Changes
* 🗑️ Deprecate `ORJSONResponse` and `UJSONResponse`. PR [#14964](https://github.com/fastapi/fastapi/pull/14964) by [@tiangolo](https://github.com/tiangolo).
## 0.130.0
### Features
* ✨ Serialize JSON response with Pydantic (in Rust), when there's a Pydantic return type or response model. PR [#14962](https://github.com/fastapi/fastapi/pull/14962) by [@tiangolo](https://github.com/tiangolo).
* This results in 2x (or more) performance increase for JSON responses.
* New docs: [Custom Response - JSON Performance](https://fastapi.tiangolo.com/advanced/custom-response/#json-performance).
## 0.129.2 ## 0.129.2
### Internal ### Internal

1
docs/en/docs/tutorial/response-model.md

@ -13,6 +13,7 @@ FastAPI will use this return type to:
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*. * Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
* This will be used by the **automatic docs**. * This will be used by the **automatic docs**.
* It will also be used by automatic client code generation tools. * It will also be used by automatic client code generation tools.
* **Serialize** the returned data to JSON using Pydantic, which is written in **Rust**, so it will be **much faster**.
But most importantly: But most importantly:

6
docs_src/custom_response/tutorial010_py310.py

@ -1,9 +1,9 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.responses import ORJSONResponse from fastapi.responses import HTMLResponse
app = FastAPI(default_response_class=ORJSONResponse) app = FastAPI(default_response_class=HTMLResponse)
@app.get("/items/") @app.get("/items/")
async def read_items(): async def read_items():
return [{"item_id": "Foo"}] return "<h1>Items</h1><p>This is a list of items.</p>"

2
fastapi/__init__.py

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

52
fastapi/responses.py

@ -1,5 +1,6 @@
from typing import Any from typing import Any
from fastapi.exceptions import FastAPIDeprecationWarning
from starlette.responses import FileResponse as FileResponse # noqa from starlette.responses import FileResponse as FileResponse # noqa
from starlette.responses import HTMLResponse as HTMLResponse # noqa from starlette.responses import HTMLResponse as HTMLResponse # noqa
from starlette.responses import JSONResponse as JSONResponse # noqa from starlette.responses import JSONResponse as JSONResponse # noqa
@ -7,6 +8,7 @@ from starlette.responses import PlainTextResponse as PlainTextResponse # noqa
from starlette.responses import RedirectResponse as RedirectResponse # noqa from starlette.responses import RedirectResponse as RedirectResponse # noqa
from starlette.responses import Response as Response # noqa from starlette.responses import Response as Response # noqa
from starlette.responses import StreamingResponse as StreamingResponse # noqa from starlette.responses import StreamingResponse as StreamingResponse # noqa
from typing_extensions import deprecated
try: try:
import ujson import ujson
@ -20,12 +22,29 @@ except ImportError: # pragma: nocover
orjson = None # type: ignore orjson = None # type: ignore
@deprecated(
"UJSONResponse is deprecated, FastAPI now serializes data directly to JSON "
"bytes via Pydantic when a return type or response model is set, which is "
"faster and doesn't need a custom response class. Read more in the FastAPI "
"docs: https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model "
"and https://fastapi.tiangolo.com/tutorial/response-model/",
category=FastAPIDeprecationWarning,
stacklevel=2,
)
class UJSONResponse(JSONResponse): class UJSONResponse(JSONResponse):
""" """JSON response using the ujson library to serialize data to JSON.
JSON response using the high-performance ujson library to serialize data to JSON.
**Deprecated**: `UJSONResponse` is deprecated. FastAPI now serializes data
directly to JSON bytes via Pydantic when a return type or response model is
set, which is faster and doesn't need a custom response class.
Read more in the
[FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model)
and the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
Read more about it in the **Note**: `ujson` is not included with FastAPI and must be installed
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/). separately, e.g. `pip install ujson`.
""" """
def render(self, content: Any) -> bytes: def render(self, content: Any) -> bytes:
@ -33,12 +52,29 @@ class UJSONResponse(JSONResponse):
return ujson.dumps(content, ensure_ascii=False).encode("utf-8") return ujson.dumps(content, ensure_ascii=False).encode("utf-8")
@deprecated(
"ORJSONResponse is deprecated, FastAPI now serializes data directly to JSON "
"bytes via Pydantic when a return type or response model is set, which is "
"faster and doesn't need a custom response class. Read more in the FastAPI "
"docs: https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model "
"and https://fastapi.tiangolo.com/tutorial/response-model/",
category=FastAPIDeprecationWarning,
stacklevel=2,
)
class ORJSONResponse(JSONResponse): class ORJSONResponse(JSONResponse):
""" """JSON response using the orjson library to serialize data to JSON.
JSON response using the high-performance orjson library to serialize data to JSON.
**Deprecated**: `ORJSONResponse` is deprecated. FastAPI now serializes data
directly to JSON bytes via Pydantic when a return type or response model is
set, which is faster and doesn't need a custom response class.
Read more in the
[FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model)
and the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
Read more about it in the **Note**: `orjson` is not included with FastAPI and must be installed
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/). separately, e.g. `pip install orjson`.
""" """
def render(self, content: Any) -> bytes: def render(self, content: Any) -> bytes:

9
pyproject.toml

@ -105,10 +105,6 @@ all = [
"itsdangerous >=1.1.0", "itsdangerous >=1.1.0",
# For Starlette's schema generation, would not be used with FastAPI # For Starlette's schema generation, would not be used with FastAPI
"pyyaml >=5.3.1", "pyyaml >=5.3.1",
# For UJSONResponse
"ujson >=5.8.0",
# For ORJSONResponse
"orjson >=3.9.3",
# To validate email fields # To validate email fields
"email-validator >=2.0.0", "email-validator >=2.0.0",
# Uvicorn with uvloop # Uvicorn with uvloop
@ -151,6 +147,10 @@ docs = [
docs-tests = [ docs-tests = [
"httpx >=0.23.0,<1.0.0", "httpx >=0.23.0,<1.0.0",
"ruff >=0.14.14", "ruff >=0.14.14",
# For UJSONResponse
"ujson >=5.8.0",
# For ORJSONResponse
"orjson >=3.9.3",
] ]
github-actions = [ github-actions = [
"httpx >=0.27.0,<1.0.0", "httpx >=0.27.0,<1.0.0",
@ -242,6 +242,7 @@ relative_files = true
context = '${CONTEXT}' context = '${CONTEXT}'
dynamic_context = "test_function" dynamic_context = "test_function"
omit = [ omit = [
"tests/benchmarks/*",
"docs_src/response_model/tutorial003_04_py39.py", "docs_src/response_model/tutorial003_04_py39.py",
"docs_src/response_model/tutorial003_04_py310.py", "docs_src/response_model/tutorial003_04_py310.py",
"docs_src/dependencies/tutorial013_an_py310.py", # temporary code example? "docs_src/dependencies/tutorial013_an_py310.py", # temporary code example?

73
tests/test_deprecated_responses.py

@ -0,0 +1,73 @@
import warnings
import pytest
from fastapi import FastAPI
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.responses import ORJSONResponse, UJSONResponse
from fastapi.testclient import TestClient
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
# ORJSON
def _make_orjson_app() -> FastAPI:
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/items")
def get_items() -> Item:
return Item(name="widget", price=9.99)
return app
def test_orjson_response_returns_correct_data():
app = _make_orjson_app()
client = TestClient(app)
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
response = client.get("/items")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
def test_orjson_response_emits_deprecation_warning():
with pytest.warns(FastAPIDeprecationWarning, match="ORJSONResponse is deprecated"):
ORJSONResponse(content={"hello": "world"})
# UJSON
def _make_ujson_app() -> FastAPI:
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
app = FastAPI(default_response_class=UJSONResponse)
@app.get("/items")
def get_items() -> Item:
return Item(name="widget", price=9.99)
return app
def test_ujson_response_returns_correct_data():
app = _make_ujson_app()
client = TestClient(app)
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
response = client.get("/items")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
def test_ujson_response_emits_deprecation_warning():
with pytest.warns(FastAPIDeprecationWarning, match="UJSONResponse is deprecated"):
UJSONResponse(content={"hello": "world"})

7
tests/test_orjson_response_class.py

@ -1,8 +1,13 @@
import warnings
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.responses import ORJSONResponse from fastapi.responses import ORJSONResponse
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from sqlalchemy.sql.elements import quoted_name from sqlalchemy.sql.elements import quoted_name
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
app = FastAPI(default_response_class=ORJSONResponse) app = FastAPI(default_response_class=ORJSONResponse)
@ -16,6 +21,8 @@ client = TestClient(app)
def test_orjson_non_str_keys(): def test_orjson_non_str_keys():
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
with client: with client:
response = client.get("/orjson_non_str_keys") response = client.get("/orjson_non_str_keys")
assert response.json() == {"msg": "Hello World", "1": 1} assert response.json() == {"msg": "Hello World", "1": 1}

3
tests/test_tutorial/test_custom_response/test_tutorial001.py

@ -9,7 +9,6 @@ from inline_snapshot import snapshot
name="client", name="client",
params=[ params=[
pytest.param("tutorial001_py310"), pytest.param("tutorial001_py310"),
pytest.param("tutorial010_py310"),
], ],
) )
def get_client(request: pytest.FixtureRequest): def get_client(request: pytest.FixtureRequest):
@ -18,12 +17,14 @@ def get_client(request: pytest.FixtureRequest):
return client return client
@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_get_custom_response(client: TestClient): def test_get_custom_response(client: TestClient):
response = client.get("/items/") response = client.get("/items/")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}] assert response.json() == [{"item_id": "Foo"}]
@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_openapi_schema(client: TestClient): def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text

8
tests/test_tutorial/test_custom_response/test_tutorial001b.py

@ -1,17 +1,25 @@
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from inline_snapshot import snapshot from inline_snapshot import snapshot
with warnings.catch_warnings():
warnings.simplefilter("ignore", FastAPIDeprecationWarning)
from docs_src.custom_response.tutorial001b_py310 import app from docs_src.custom_response.tutorial001b_py310 import app
client = TestClient(app) client = TestClient(app)
@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_get_custom_response(): def test_get_custom_response():
response = client.get("/items/") response = client.get("/items/")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}] assert response.json() == [{"item_id": "Foo"}]
@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_openapi_schema(): def test_openapi_schema():
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text

50
tests/test_tutorial/test_custom_response/test_tutorial010.py

@ -0,0 +1,50 @@
import importlib
import pytest
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@pytest.fixture(
name="client",
params=[
pytest.param("tutorial010_py310"),
],
)
def get_client(request: pytest.FixtureRequest):
mod = importlib.import_module(f"docs_src.custom_response.{request.param}")
client = TestClient(mod.app)
return client
def test_get_custom_response(client: TestClient):
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.text == snapshot("<h1>Items</h1><p>This is a list of items.</p>")
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == snapshot(
{
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"text/html": {"schema": {"type": "string"}}
},
}
},
"summary": "Read Items",
"operationId": "read_items_items__get",
}
}
},
}
)

20
uv.lock

@ -1083,12 +1083,10 @@ all = [
{ name = "httpx" }, { name = "httpx" },
{ name = "itsdangerous" }, { name = "itsdangerous" },
{ name = "jinja2" }, { name = "jinja2" },
{ name = "orjson" },
{ name = "pydantic-extra-types" }, { name = "pydantic-extra-types" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "python-multipart" }, { name = "python-multipart" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "ujson" },
{ name = "uvicorn", extra = ["standard"] }, { name = "uvicorn", extra = ["standard"] },
] ]
standard = [ standard = [
@ -1134,6 +1132,7 @@ dev = [
{ name = "mkdocs-redirects" }, { name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] }, { name = "mkdocstrings", extra = ["python"] },
{ name = "mypy" }, { name = "mypy" },
{ name = "orjson" },
{ name = "pillow" }, { name = "pillow" },
{ name = "playwright" }, { name = "playwright" },
{ name = "prek" }, { name = "prek" },
@ -1151,6 +1150,7 @@ dev = [
{ name = "typer" }, { name = "typer" },
{ name = "types-orjson" }, { name = "types-orjson" },
{ name = "types-ujson" }, { name = "types-ujson" },
{ name = "ujson" },
] ]
docs = [ docs = [
{ name = "black" }, { name = "black" },
@ -1165,15 +1165,19 @@ docs = [
{ name = "mkdocs-material" }, { name = "mkdocs-material" },
{ name = "mkdocs-redirects" }, { name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] }, { name = "mkdocstrings", extra = ["python"] },
{ name = "orjson" },
{ name = "pillow" }, { name = "pillow" },
{ name = "python-slugify" }, { name = "python-slugify" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "ruff" }, { name = "ruff" },
{ name = "typer" }, { name = "typer" },
{ name = "ujson" },
] ]
docs-tests = [ docs-tests = [
{ name = "httpx" }, { name = "httpx" },
{ name = "orjson" },
{ name = "ruff" }, { name = "ruff" },
{ name = "ujson" },
] ]
github-actions = [ github-actions = [
{ name = "httpx" }, { name = "httpx" },
@ -1192,6 +1196,7 @@ tests = [
{ name = "httpx" }, { name = "httpx" },
{ name = "inline-snapshot" }, { name = "inline-snapshot" },
{ name = "mypy" }, { name = "mypy" },
{ name = "orjson" },
{ name = "pwdlib", extra = ["argon2"] }, { name = "pwdlib", extra = ["argon2"] },
{ name = "pyjwt" }, { name = "pyjwt" },
{ name = "pytest" }, { name = "pytest" },
@ -1202,6 +1207,7 @@ tests = [
{ name = "strawberry-graphql" }, { name = "strawberry-graphql" },
{ name = "types-orjson" }, { name = "types-orjson" },
{ name = "types-ujson" }, { name = "types-ujson" },
{ name = "ujson" },
] ]
translations = [ translations = [
{ name = "gitpython" }, { name = "gitpython" },
@ -1225,7 +1231,6 @@ requires-dist = [
{ name = "jinja2", marker = "extra == 'all'", specifier = ">=3.1.5" }, { name = "jinja2", marker = "extra == 'all'", specifier = ">=3.1.5" },
{ name = "jinja2", marker = "extra == 'standard'", specifier = ">=3.1.5" }, { name = "jinja2", marker = "extra == 'standard'", specifier = ">=3.1.5" },
{ name = "jinja2", marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=3.1.5" }, { name = "jinja2", marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=3.1.5" },
{ name = "orjson", marker = "extra == 'all'", specifier = ">=3.9.3" },
{ name = "pydantic", specifier = ">=2.7.0" }, { name = "pydantic", specifier = ">=2.7.0" },
{ name = "pydantic-extra-types", marker = "extra == 'all'", specifier = ">=2.0.0" }, { name = "pydantic-extra-types", marker = "extra == 'all'", specifier = ">=2.0.0" },
{ name = "pydantic-extra-types", marker = "extra == 'standard'", specifier = ">=2.0.0" }, { name = "pydantic-extra-types", marker = "extra == 'standard'", specifier = ">=2.0.0" },
@ -1240,7 +1245,6 @@ requires-dist = [
{ name = "starlette", specifier = ">=0.40.0,<1.0.0" }, { name = "starlette", specifier = ">=0.40.0,<1.0.0" },
{ name = "typing-extensions", specifier = ">=4.8.0" }, { name = "typing-extensions", specifier = ">=4.8.0" },
{ name = "typing-inspection", specifier = ">=0.4.2" }, { name = "typing-inspection", specifier = ">=0.4.2" },
{ name = "ujson", marker = "extra == 'all'", specifier = ">=5.8.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.12.0" }, { name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.12.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.12.0" }, { name = "uvicorn", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.12.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=0.12.0" }, { name = "uvicorn", extras = ["standard"], marker = "extra == 'standard-no-fastapi-cloud-cli'", specifier = ">=0.12.0" },
@ -1269,6 +1273,7 @@ dev = [
{ name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" }, { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
{ name = "mypy", specifier = ">=1.14.1" }, { name = "mypy", specifier = ">=1.14.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" }, { name = "pillow", specifier = ">=11.3.0" },
{ name = "playwright", specifier = ">=1.57.0" }, { name = "playwright", specifier = ">=1.57.0" },
{ name = "prek", specifier = ">=0.2.22" }, { name = "prek", specifier = ">=0.2.22" },
@ -1286,6 +1291,7 @@ dev = [
{ name = "typer", specifier = ">=0.21.1" }, { name = "typer", specifier = ">=0.21.1" },
{ name = "types-orjson", specifier = ">=3.6.2" }, { name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" }, { name = "types-ujson", specifier = ">=5.10.0.20240515" },
{ name = "ujson", specifier = ">=5.8.0" },
] ]
docs = [ docs = [
{ name = "black", specifier = ">=25.1.0" }, { name = "black", specifier = ">=25.1.0" },
@ -1300,15 +1306,19 @@ docs = [
{ name = "mkdocs-material", specifier = ">=9.7.0" }, { name = "mkdocs-material", specifier = ">=9.7.0" },
{ name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" }, { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" }, { name = "pillow", specifier = ">=11.3.0" },
{ name = "python-slugify", specifier = ">=8.0.4" }, { name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" }, { name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" }, { name = "ruff", specifier = ">=0.14.14" },
{ name = "typer", specifier = ">=0.21.1" }, { name = "typer", specifier = ">=0.21.1" },
{ name = "ujson", specifier = ">=5.8.0" },
] ]
docs-tests = [ docs-tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" }, { name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "ruff", specifier = ">=0.14.14" }, { name = "ruff", specifier = ">=0.14.14" },
{ name = "ujson", specifier = ">=5.8.0" },
] ]
github-actions = [ github-actions = [
{ name = "httpx", specifier = ">=0.27.0,<1.0.0" }, { name = "httpx", specifier = ">=0.27.0,<1.0.0" },
@ -1327,6 +1337,7 @@ tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" }, { name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "inline-snapshot", specifier = ">=0.21.1" }, { name = "inline-snapshot", specifier = ">=0.21.1" },
{ name = "mypy", specifier = ">=1.14.1" }, { name = "mypy", specifier = ">=1.14.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pwdlib", extras = ["argon2"], specifier = ">=0.2.1" }, { name = "pwdlib", extras = ["argon2"], specifier = ">=0.2.1" },
{ name = "pyjwt", specifier = ">=2.9.0" }, { name = "pyjwt", specifier = ">=2.9.0" },
{ name = "pytest", specifier = ">=9.0.0" }, { name = "pytest", specifier = ">=9.0.0" },
@ -1337,6 +1348,7 @@ tests = [
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" }, { name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
{ name = "types-orjson", specifier = ">=3.6.2" }, { name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" }, { name = "types-ujson", specifier = ">=5.10.0.20240515" },
{ name = "ujson", specifier = ">=5.8.0" },
] ]
translations = [ translations = [
{ name = "gitpython", specifier = ">=3.1.46" }, { name = "gitpython", specifier = ">=3.1.46" },

Loading…
Cancel
Save