Compare commits

...

16 Commits

Author SHA1 Message Date
Sebastián Ramírez
b423b73c35 🔖 Release version 0.131.0 2026-02-22 17:36:21 +01:00
github-actions[bot]
70e8558352 📝 Update release notes
[skip ci]
2026-02-22 16:35:25 +00:00
Sebastián Ramírez
48e9835732 🗑️ Deprecate ORJSONResponse and UJSONResponse (#14964) 2026-02-22 17:34:59 +01:00
Sebastián Ramírez
2e62fb1513 📝 Update release notes 2026-02-22 17:18:26 +01:00
Sebastián Ramírez
eb544e704c 🔖 Release version 0.130.0 2026-02-22 17:14:53 +01:00
github-actions[bot]
bc06e4296d 📝 Update release notes
[skip ci]
2026-02-22 16:07:56 +00:00
Sebastián Ramírez
590a5e5355 Serialize JSON response with Pydantic (in Rust), when there's a Pydantic return type or response model (#14962) 2026-02-22 17:07:19 +01:00
Sebastián Ramírez
1e78a36b73 🔖 Release version 0.129.2 2026-02-21 18:24:59 +01:00
github-actions[bot]
f921de6495 📝 Update release notes
[skip ci]
2026-02-21 17:23:22 +00:00
Sebastián Ramírez
4ab8138554 ⬆️ Upgrade pytest (#14959) 2026-02-21 18:22:54 +01:00
github-actions[bot]
468d5173ed 📝 Update release notes
[skip ci]
2026-02-21 16:45:38 +00:00
Sebastián Ramírez
c9455d5400 👷 Fix CI, do not attempt to publish fastapi-slim (#14958) 2026-02-21 16:45:15 +00:00
github-actions[bot]
69ae1d0f28 📝 Update release notes
[skip ci]
2026-02-21 16:27:01 +00:00
Sebastián Ramírez
083b6ebe9e Drop support for fastapi-slim, no more versions will be released, use only "fastapi[standard]" or fastapi (#14957) 2026-02-21 16:26:38 +00:00
github-actions[bot]
1b9a351ee8 📝 Update release notes
[skip ci]
2026-02-21 16:16:27 +00:00
Sebastián Ramírez
f55ab7e020 🔧 Update pyproject.toml, remove unneeded lines (#14956) 2026-02-21 16:16:03 +00:00
23 changed files with 394 additions and 392 deletions

2
.github/labeler.yml vendored
View File

@@ -29,8 +29,6 @@ internal:
- scripts/**
- .gitignore
- .pre-commit-config.yaml
- pdm_build.py
- requirements*.txt
- uv.lock
- docs/en/data/sponsors.yml
- docs/en/overrides/main.html

View File

@@ -8,11 +8,6 @@ on:
jobs:
publish:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- fastapi
- fastapi-slim
permissions:
id-token: write
contents: read
@@ -26,14 +21,9 @@ jobs:
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"
# Issue ref: https://github.com/actions/setup-python/issues/436
# cache: "pip"
# cache-dependency-path: pyproject.toml
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Build distribution
run: uv build
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
- name: Publish
run: uv publish

View File

@@ -1,6 +1,6 @@
# 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}.
@@ -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`.
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
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`.
By default FastAPI returns JSON responses.
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
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.
For large responses, returning a `Response` directly is much faster than returning a dictionary.
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`.
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_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.
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.
### JSON Performance { #json-performance }
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
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*.
/// 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.
///
/// tip
The `ORJSONResponse` is only available in FastAPI, not in Starlette.
///
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## 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.
### `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 }
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.
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`.
@@ -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. 😉
### `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 }
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
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] *}

View File

@@ -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.
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 }
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`.
@@ -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] *}
## 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 }
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.

View File

@@ -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}.
## 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 }
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}.

View File

@@ -22,7 +22,13 @@ from fastapi.responses import (
## 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
options:

View File

@@ -7,6 +7,29 @@ hide:
## Latest Changes
## 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
### Internal
* ⬆️ Upgrade pytest. PR [#14959](https://github.com/fastapi/fastapi/pull/14959) by [@tiangolo](https://github.com/tiangolo).
* 👷 Fix CI, do not attempt to publish `fastapi-slim`. PR [#14958](https://github.com/fastapi/fastapi/pull/14958) by [@tiangolo](https://github.com/tiangolo).
* Drop support for `fastapi-slim`, no more versions will be released, use only `"fastapi[standard]"` or `fastapi`. PR [#14957](https://github.com/fastapi/fastapi/pull/14957) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update pyproject.toml, remove unneeded lines. PR [#14956](https://github.com/fastapi/fastapi/pull/14956) by [@tiangolo](https://github.com/tiangolo).
## 0.129.1
### Fixes

View File

@@ -13,6 +13,7 @@ FastAPI will use this return type to:
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
* This will be used by the **automatic docs**.
* 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:

View File

@@ -1,9 +1,9 @@
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/")
async def read_items():
return [{"item_id": "Foo"}]
return "<h1>Items</h1><p>This is a list of items.</p>"

View File

@@ -1,2 +0,0 @@
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_e

View File

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

View File

@@ -199,6 +199,32 @@ class ModelField:
exclude_none=exclude_none,
)
def serialize_json(
self,
value: Any,
*,
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = True,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> bytes:
# What calls this code passes a value that already called
# self._type_adapter.validate_python(value)
# This uses Pydantic's dump_json() which serializes directly to JSON
# bytes in one pass (via Rust), avoiding the intermediate Python dict
# step of dump_python(mode="json") + json.dumps().
return self._type_adapter.dump_json(
value,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from
# ModelField to its JSON Schema.

View File

@@ -1,5 +1,6 @@
from typing import Any
from fastapi.exceptions import FastAPIDeprecationWarning
from starlette.responses import FileResponse as FileResponse # noqa
from starlette.responses import HTMLResponse as HTMLResponse # 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 Response as Response # noqa
from starlette.responses import StreamingResponse as StreamingResponse # noqa
from typing_extensions import deprecated
try:
import ujson
@@ -20,12 +22,29 @@ except ImportError: # pragma: nocover
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):
"""
JSON response using the high-performance ujson library to serialize data to JSON.
"""JSON response using the ujson library to serialize data to JSON.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
**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/).
**Note**: `ujson` is not included with FastAPI and must be installed
separately, e.g. `pip install ujson`.
"""
def render(self, content: Any) -> bytes:
@@ -33,12 +52,29 @@ class UJSONResponse(JSONResponse):
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):
"""
JSON response using the high-performance orjson library to serialize data to JSON.
"""JSON response using the orjson library to serialize data to JSON.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
**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/).
**Note**: `orjson` is not included with FastAPI and must be installed
separately, e.g. `pip install orjson`.
"""
def render(self, content: Any) -> bytes:

View File

@@ -271,6 +271,7 @@ async def serialize_response(
exclude_none: bool = False,
is_coroutine: bool = True,
endpoint_ctx: EndpointContext | None = None,
dump_json: bool = False,
) -> Any:
if field:
if is_coroutine:
@@ -286,8 +287,8 @@ async def serialize_response(
body=response_content,
endpoint_ctx=ctx,
)
return field.serialize(
serializer = field.serialize_json if dump_json else field.serialize
return serializer(
value,
include=include,
exclude=exclude,
@@ -443,6 +444,14 @@ def get_request_handler(
response_args["status_code"] = current_status_code
if solved_result.response.status_code:
response_args["status_code"] = solved_result.response.status_code
# Use the fast path (dump_json) when no custom response
# class was set and a response field with a TypeAdapter
# exists. Serializes directly to JSON bytes via Pydantic's
# Rust core, skipping the intermediate Python dict +
# json.dumps() step.
use_dump_json = response_field is not None and isinstance(
response_class, DefaultPlaceholder
)
content = await serialize_response(
field=response_field,
response_content=raw_response,
@@ -454,8 +463,16 @@ def get_request_handler(
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
endpoint_ctx=endpoint_ctx,
dump_json=use_dump_json,
)
response = actual_response_class(content, **response_args)
if use_dump_json:
response = Response(
content=content,
media_type="application/json",
**response_args,
)
else:
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(solved_result.response.headers.raw)

View File

@@ -1,40 +0,0 @@
import os
from typing import Any
from pdm.backend.hooks import Context
TIANGOLO_BUILD_PACKAGE = os.getenv("TIANGOLO_BUILD_PACKAGE")
def pdm_build_initialize(context: Context) -> None:
metadata = context.config.metadata
# Get main version
version = metadata["version"]
# Get custom config for the current package, from the env var
all_configs_config: dict[str, Any] = context.config.data["tool"]["tiangolo"][
"_internal-slim-build"
]["packages"]
if TIANGOLO_BUILD_PACKAGE not in all_configs_config:
return
config = all_configs_config[TIANGOLO_BUILD_PACKAGE]
project_config: dict[str, Any] = config["project"]
# Override main [project] configs with custom configs for this package
for key, value in project_config.items():
metadata[key] = value
# Get custom build config for the current package
build_config: dict[str, Any] = (
config.get("tool", {}).get("pdm", {}).get("build", {})
)
# Override PDM build config with custom build config for this package
for key, value in build_config.items():
context.config.build_config[key] = value
# Get main dependencies
dependencies: list[str] = metadata.get("dependencies", [])
# Sync versions in dependencies
new_dependencies = []
for dep in dependencies:
new_dep = f"{dep}>={version}"
new_dependencies.append(new_dep)
metadata["dependencies"] = new_dependencies

View File

@@ -57,7 +57,6 @@ Issues = "https://github.com/fastapi/fastapi/issues"
Changelog = "https://fastapi.tiangolo.com/release-notes/"
[project.optional-dependencies]
standard = [
"fastapi-cli[standard] >=0.0.8",
# For the test client
@@ -106,10 +105,6 @@ all = [
"itsdangerous >=1.1.0",
# For Starlette's schema generation, would not be used with FastAPI
"pyyaml >=5.3.1",
# For UJSONResponse
"ujson >=5.8.0",
# For ORJSONResponse
"orjson >=3.9.3",
# To validate email fields
"email-validator >=2.0.0",
# Uvicorn with uvloop
@@ -152,6 +147,10 @@ docs = [
docs-tests = [
"httpx >=0.23.0,<1.0.0",
"ruff >=0.14.14",
# For UJSONResponse
"ujson >=5.8.0",
# For ORJSONResponse
"orjson >=3.9.3",
]
github-actions = [
"httpx >=0.27.0,<1.0.0",
@@ -171,7 +170,7 @@ tests = [
"mypy >=1.14.1",
"pwdlib[argon2] >=0.2.1",
"pyjwt >=2.9.0",
"pytest >=7.1.3,<9.0.0",
"pytest >=9.0.0",
"pytest-codspeed >=4.2.0",
"pyyaml >=5.3.1,<7.0.0",
"sqlmodel >=0.0.31",
@@ -199,32 +198,6 @@ source-includes = [
"docs/en/docs/img/favicon.png",
]
[tool.tiangolo._internal-slim-build.packages.fastapi-slim.project]
name = "fastapi-slim"
readme = "fastapi-slim/README.md"
dependencies = [
"fastapi",
]
optional-dependencies = {}
scripts = {}
[tool.tiangolo._internal-slim-build.packages.fastapi-slim.tool.pdm.build]
# excludes needs to explicitly exclude the top level python packages,
# otherwise PDM includes them by default
# A "*" glob pattern can't be used here because in PDM internals, the patterns are put
# in a set (unordered, order varies) and each excluded file is assigned one of the
# glob patterns that matches, as the set is unordered, the matched pattern could be "*"
# independent of the order here. And then the internal code would give it a lower score
# than the one for a default included file.
# By not using "*" and explicitly excluding the top level packages, they get a higher
# score than the default inclusion
excludes = ["fastapi", "tests", "pdm_build.py"]
# source-includes needs to explicitly define some value because PDM will check the
# truthy value of the list, and if empty, will include some defaults, including "tests",
# an empty string doesn't match anything, but makes the list truthy, so that PDM
# doesn't override it during the build.
source-includes = [""]
[tool.mypy]
plugins = ["pydantic.mypy"]
strict = true
@@ -245,25 +218,16 @@ disallow_incomplete_defs = false
disallow_untyped_defs = false
disallow_untyped_calls = false
[tool.pytest.ini_options]
[tool.pytest]
minversion = "9.0"
addopts = [
"--strict-config",
"--strict-markers",
"--ignore=docs_src",
]
xfail_strict = true
junit_family = "xunit2"
strict_xfail = true
filterwarnings = [
"error",
# see https://trio.readthedocs.io/en/stable/history.html#trio-0-22-0-2022-09-28
"ignore:You seem to already have a custom.*:RuntimeWarning:trio",
# TODO: remove after upgrading SQLAlchemy to a version that includes the following changes
# https://github.com/sqlalchemy/sqlalchemy/commit/59521abcc0676e936b31a523bd968fc157fef0c2
'ignore:datetime\.datetime\.utcfromtimestamp\(\) is deprecated and scheduled for removal in a future version\..*:DeprecationWarning:sqlalchemy',
# Trio 24.1.0 raises a warning from attrs
# Ref: https://github.com/python-trio/trio/pull/3054
# Remove once there's a new version of Trio
'ignore:The `hash` argument is deprecated*:DeprecationWarning:trio',
]
[tool.coverage.run]
@@ -280,7 +244,6 @@ dynamic_context = "test_function"
omit = [
"docs_src/response_model/tutorial003_04_py39.py",
"docs_src/response_model/tutorial003_04_py310.py",
"docs_src/dependencies/tutorial008_an_py39.py", # difficult to mock
"docs_src/dependencies/tutorial013_an_py310.py", # temporary code example?
"docs_src/dependencies/tutorial014_an_py310.py", # temporary code example?
# Pydantic v1 migration, no longer tested
@@ -288,202 +251,6 @@ omit = [
"docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py",
"docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py",
"docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py",
# TODO: remove all the ignores below when all translations use the new Python 3.10 files
"docs_src/additional_responses/tutorial001_py39.py",
"docs_src/additional_responses/tutorial003_py39.py",
"docs_src/advanced_middleware/tutorial001_py39.py",
"docs_src/advanced_middleware/tutorial002_py39.py",
"docs_src/advanced_middleware/tutorial003_py39.py",
"docs_src/app_testing/app_a_py39/main.py",
"docs_src/app_testing/app_a_py39/test_main.py",
"docs_src/app_testing/tutorial001_py39.py",
"docs_src/app_testing/tutorial002_py39.py",
"docs_src/app_testing/tutorial003_py39.py",
"docs_src/app_testing/tutorial004_py39.py",
"docs_src/async_tests/app_a_py39/main.py",
"docs_src/async_tests/app_a_py39/test_main.py",
"docs_src/authentication_error_status_code/tutorial001_an_py39.py",
"docs_src/background_tasks/tutorial001_py39.py",
"docs_src/behind_a_proxy/tutorial001_01_py39.py",
"docs_src/behind_a_proxy/tutorial001_py39.py",
"docs_src/behind_a_proxy/tutorial002_py39.py",
"docs_src/behind_a_proxy/tutorial003_py39.py",
"docs_src/behind_a_proxy/tutorial004_py39.py",
"docs_src/bigger_applications/app_an_py39/dependencies.py",
"docs_src/bigger_applications/app_an_py39/internal/admin.py",
"docs_src/bigger_applications/app_an_py39/main.py",
"docs_src/bigger_applications/app_an_py39/routers/items.py",
"docs_src/bigger_applications/app_an_py39/routers/users.py",
"docs_src/bigger_applications/app_py39/dependencies.py",
"docs_src/bigger_applications/app_py39/main.py",
"docs_src/body_nested_models/tutorial008_py39.py",
"docs_src/body_nested_models/tutorial009_py39.py",
"docs_src/conditional_openapi/tutorial001_py39.py",
"docs_src/configure_swagger_ui/tutorial001_py39.py",
"docs_src/configure_swagger_ui/tutorial002_py39.py",
"docs_src/configure_swagger_ui/tutorial003_py39.py",
"docs_src/cors/tutorial001_py39.py",
"docs_src/custom_docs_ui/tutorial001_py39.py",
"docs_src/custom_docs_ui/tutorial002_py39.py",
"docs_src/custom_response/tutorial001_py39.py",
"docs_src/custom_response/tutorial001b_py39.py",
"docs_src/custom_response/tutorial002_py39.py",
"docs_src/custom_response/tutorial003_py39.py",
"docs_src/custom_response/tutorial004_py39.py",
"docs_src/custom_response/tutorial005_py39.py",
"docs_src/custom_response/tutorial006_py39.py",
"docs_src/custom_response/tutorial006b_py39.py",
"docs_src/custom_response/tutorial006c_py39.py",
"docs_src/custom_response/tutorial007_py39.py",
"docs_src/custom_response/tutorial008_py39.py",
"docs_src/custom_response/tutorial009_py39.py",
"docs_src/custom_response/tutorial009b_py39.py",
"docs_src/custom_response/tutorial009c_py39.py",
"docs_src/custom_response/tutorial010_py39.py",
"docs_src/debugging/tutorial001_py39.py",
"docs_src/dependencies/tutorial006_an_py39.py",
"docs_src/dependencies/tutorial006_py39.py",
"docs_src/dependencies/tutorial007_py39.py",
"docs_src/dependencies/tutorial008_py39.py",
"docs_src/dependencies/tutorial008b_an_py39.py",
"docs_src/dependencies/tutorial008b_py39.py",
"docs_src/dependencies/tutorial008c_an_py39.py",
"docs_src/dependencies/tutorial008c_py39.py",
"docs_src/dependencies/tutorial008d_an_py39.py",
"docs_src/dependencies/tutorial008d_py39.py",
"docs_src/dependencies/tutorial008e_an_py39.py",
"docs_src/dependencies/tutorial008e_py39.py",
"docs_src/dependencies/tutorial010_py39.py",
"docs_src/dependencies/tutorial011_an_py39.py",
"docs_src/dependencies/tutorial011_py39.py",
"docs_src/dependencies/tutorial012_an_py39.py",
"docs_src/dependencies/tutorial012_py39.py",
"docs_src/events/tutorial001_py39.py",
"docs_src/events/tutorial002_py39.py",
"docs_src/events/tutorial003_py39.py",
"docs_src/extending_openapi/tutorial001_py39.py",
"docs_src/extra_models/tutorial004_py39.py",
"docs_src/extra_models/tutorial005_py39.py",
"docs_src/first_steps/tutorial001_py39.py",
"docs_src/first_steps/tutorial003_py39.py",
"docs_src/generate_clients/tutorial001_py39.py",
"docs_src/generate_clients/tutorial002_py39.py",
"docs_src/generate_clients/tutorial003_py39.py",
"docs_src/generate_clients/tutorial004_py39.py",
"docs_src/graphql_/tutorial001_py39.py",
"docs_src/handling_errors/tutorial001_py39.py",
"docs_src/handling_errors/tutorial002_py39.py",
"docs_src/handling_errors/tutorial003_py39.py",
"docs_src/handling_errors/tutorial004_py39.py",
"docs_src/handling_errors/tutorial005_py39.py",
"docs_src/handling_errors/tutorial006_py39.py",
"docs_src/metadata/tutorial001_1_py39.py",
"docs_src/metadata/tutorial001_py39.py",
"docs_src/metadata/tutorial002_py39.py",
"docs_src/metadata/tutorial003_py39.py",
"docs_src/metadata/tutorial004_py39.py",
"docs_src/middleware/tutorial001_py39.py",
"docs_src/openapi_webhooks/tutorial001_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial001_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial002_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial003_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial005_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial006_py39.py",
"docs_src/path_operation_advanced_configuration/tutorial007_py39.py",
"docs_src/path_operation_configuration/tutorial002b_py39.py",
"docs_src/path_operation_configuration/tutorial006_py39.py",
"docs_src/path_params/tutorial001_py39.py",
"docs_src/path_params/tutorial002_py39.py",
"docs_src/path_params/tutorial003_py39.py",
"docs_src/path_params/tutorial003b_py39.py",
"docs_src/path_params/tutorial004_py39.py",
"docs_src/path_params/tutorial005_py39.py",
"docs_src/path_params_numeric_validations/tutorial002_an_py39.py",
"docs_src/path_params_numeric_validations/tutorial002_py39.py",
"docs_src/path_params_numeric_validations/tutorial003_an_py39.py",
"docs_src/path_params_numeric_validations/tutorial003_py39.py",
"docs_src/path_params_numeric_validations/tutorial004_an_py39.py",
"docs_src/path_params_numeric_validations/tutorial004_py39.py",
"docs_src/path_params_numeric_validations/tutorial005_an_py39.py",
"docs_src/path_params_numeric_validations/tutorial005_py39.py",
"docs_src/path_params_numeric_validations/tutorial006_an_py39.py",
"docs_src/path_params_numeric_validations/tutorial006_py39.py",
"docs_src/python_types/tutorial001_py39.py",
"docs_src/python_types/tutorial002_py39.py",
"docs_src/python_types/tutorial003_py39.py",
"docs_src/python_types/tutorial004_py39.py",
"docs_src/python_types/tutorial005_py39.py",
"docs_src/python_types/tutorial006_py39.py",
"docs_src/python_types/tutorial007_py39.py",
"docs_src/python_types/tutorial008_py39.py",
"docs_src/python_types/tutorial008b_py39.py",
"docs_src/python_types/tutorial009_py39.py",
"docs_src/python_types/tutorial009b_py39.py",
"docs_src/python_types/tutorial009c_py39.py",
"docs_src/python_types/tutorial010_py39.py",
"docs_src/python_types/tutorial013_py39.py",
"docs_src/query_params/tutorial001_py39.py",
"docs_src/query_params/tutorial005_py39.py",
"docs_src/query_params_str_validations/tutorial005_an_py39.py",
"docs_src/query_params_str_validations/tutorial005_py39.py",
"docs_src/query_params_str_validations/tutorial006_an_py39.py",
"docs_src/query_params_str_validations/tutorial006_py39.py",
"docs_src/query_params_str_validations/tutorial012_an_py39.py",
"docs_src/query_params_str_validations/tutorial012_py39.py",
"docs_src/query_params_str_validations/tutorial013_an_py39.py",
"docs_src/query_params_str_validations/tutorial013_py39.py",
"docs_src/request_files/tutorial001_03_an_py39.py",
"docs_src/request_files/tutorial001_03_py39.py",
"docs_src/request_files/tutorial001_an_py39.py",
"docs_src/request_files/tutorial001_py39.py",
"docs_src/request_files/tutorial002_an_py39.py",
"docs_src/request_files/tutorial002_py39.py",
"docs_src/request_files/tutorial003_an_py39.py",
"docs_src/request_files/tutorial003_py39.py",
"docs_src/request_form_models/tutorial001_an_py39.py",
"docs_src/request_form_models/tutorial001_py39.py",
"docs_src/request_form_models/tutorial002_an_py39.py",
"docs_src/request_form_models/tutorial002_py39.py",
"docs_src/request_forms/tutorial001_an_py39.py",
"docs_src/request_forms/tutorial001_py39.py",
"docs_src/request_forms_and_files/tutorial001_an_py39.py",
"docs_src/request_forms_and_files/tutorial001_py39.py",
"docs_src/response_change_status_code/tutorial001_py39.py",
"docs_src/response_cookies/tutorial001_py39.py",
"docs_src/response_cookies/tutorial002_py39.py",
"docs_src/response_directly/tutorial002_py39.py",
"docs_src/response_headers/tutorial001_py39.py",
"docs_src/response_headers/tutorial002_py39.py",
"docs_src/response_model/tutorial003_02_py39.py",
"docs_src/response_model/tutorial003_03_py39.py",
"docs_src/response_status_code/tutorial001_py39.py",
"docs_src/response_status_code/tutorial002_py39.py",
"docs_src/security/tutorial001_an_py39.py",
"docs_src/security/tutorial001_py39.py",
"docs_src/security/tutorial006_an_py39.py",
"docs_src/security/tutorial006_py39.py",
"docs_src/security/tutorial007_an_py39.py",
"docs_src/security/tutorial007_py39.py",
"docs_src/settings/app01_py39/config.py",
"docs_src/settings/app01_py39/main.py",
"docs_src/settings/app02_an_py39/config.py",
"docs_src/settings/app02_an_py39/main.py",
"docs_src/settings/app02_an_py39/test_main.py",
"docs_src/settings/app02_py39/config.py",
"docs_src/settings/app02_py39/main.py",
"docs_src/settings/app02_py39/test_main.py",
"docs_src/settings/app03_an_py39/config.py",
"docs_src/settings/app03_an_py39/main.py",
"docs_src/settings/app03_py39/config.py",
"docs_src/settings/app03_py39/main.py",
"docs_src/settings/tutorial001_py39.py",
"docs_src/static_files/tutorial001_py39.py",
"docs_src/sub_applications/tutorial001_py39.py",
"docs_src/templates/tutorial001_py39.py",
"docs_src/using_request_directly/tutorial001_py39.py",
"docs_src/websockets/tutorial001_py39.py",
"docs_src/websockets/tutorial003_py39.py",
"docs_src/wsgi/tutorial001_py39.py",
]
[tool.coverage.report]

View File

@@ -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"})

View File

@@ -0,0 +1,51 @@
from unittest.mock import patch
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
app = FastAPI()
@app.get("/default")
def get_default() -> Item:
return Item(name="widget", price=9.99)
@app.get("/explicit", response_class=JSONResponse)
def get_explicit() -> Item:
return Item(name="widget", price=9.99)
client = TestClient(app)
def test_default_response_class_skips_json_dumps():
"""When no response_class is set, the fast path serializes directly to
JSON bytes via Pydantic's dump_json and never calls json.dumps."""
with patch(
"starlette.responses.json.dumps", wraps=__import__("json").dumps
) as mock_dumps:
response = client.get("/default")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
mock_dumps.assert_not_called()
def test_explicit_response_class_uses_json_dumps():
"""When response_class is explicitly set to JSONResponse, the normal path
is used and json.dumps is called via JSONResponse.render()."""
with patch(
"starlette.responses.json.dumps", wraps=__import__("json").dumps
) as mock_dumps:
response = client.get("/explicit")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
mock_dumps.assert_called_once()

View File

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

View File

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

View File

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

View File

@@ -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",
}
}
},
}
)

30
uv.lock generated
View File

@@ -1083,12 +1083,10 @@ all = [
{ name = "httpx" },
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "orjson" },
{ name = "pydantic-extra-types" },
{ name = "pydantic-settings" },
{ name = "python-multipart" },
{ name = "pyyaml" },
{ name = "ujson" },
{ name = "uvicorn", extra = ["standard"] },
]
standard = [
@@ -1134,6 +1132,7 @@ dev = [
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
{ name = "mypy" },
{ name = "orjson" },
{ name = "pillow" },
{ name = "playwright" },
{ name = "prek" },
@@ -1151,6 +1150,7 @@ dev = [
{ name = "typer" },
{ name = "types-orjson" },
{ name = "types-ujson" },
{ name = "ujson" },
]
docs = [
{ name = "black" },
@@ -1165,15 +1165,19 @@ docs = [
{ name = "mkdocs-material" },
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
{ name = "orjson" },
{ name = "pillow" },
{ name = "python-slugify" },
{ name = "pyyaml" },
{ name = "ruff" },
{ name = "typer" },
{ name = "ujson" },
]
docs-tests = [
{ name = "httpx" },
{ name = "orjson" },
{ name = "ruff" },
{ name = "ujson" },
]
github-actions = [
{ name = "httpx" },
@@ -1192,6 +1196,7 @@ tests = [
{ name = "httpx" },
{ name = "inline-snapshot" },
{ name = "mypy" },
{ name = "orjson" },
{ name = "pwdlib", extra = ["argon2"] },
{ name = "pyjwt" },
{ name = "pytest" },
@@ -1202,6 +1207,7 @@ tests = [
{ name = "strawberry-graphql" },
{ name = "types-orjson" },
{ name = "types-ujson" },
{ name = "ujson" },
]
translations = [
{ name = "gitpython" },
@@ -1225,7 +1231,6 @@ requires-dist = [
{ name = "jinja2", marker = "extra == 'all'", 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 = "orjson", marker = "extra == 'all'", specifier = ">=3.9.3" },
{ name = "pydantic", specifier = ">=2.7.0" },
{ name = "pydantic-extra-types", marker = "extra == 'all'", 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 = "typing-extensions", specifier = ">=4.8.0" },
{ 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 == 'standard'", 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 = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
{ name = "mypy", specifier = ">=1.14.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "playwright", specifier = ">=1.57.0" },
{ name = "prek", specifier = ">=0.2.22" },
@@ -1276,7 +1281,7 @@ dev = [
{ name = "pydantic-ai", specifier = ">=0.4.10" },
{ name = "pygithub", specifier = ">=2.8.1" },
{ name = "pyjwt", specifier = ">=2.9.0" },
{ name = "pytest", specifier = ">=7.1.3,<9.0.0" },
{ name = "pytest", specifier = ">=9.0.0" },
{ name = "pytest-codspeed", specifier = ">=4.2.0" },
{ name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
@@ -1286,6 +1291,7 @@ dev = [
{ name = "typer", specifier = ">=0.21.1" },
{ name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
{ name = "ujson", specifier = ">=5.8.0" },
]
docs = [
{ name = "black", specifier = ">=25.1.0" },
@@ -1300,15 +1306,19 @@ docs = [
{ name = "mkdocs-material", specifier = ">=9.7.0" },
{ name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" },
{ name = "typer", specifier = ">=0.21.1" },
{ name = "ujson", specifier = ">=5.8.0" },
]
docs-tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "ruff", specifier = ">=0.14.14" },
{ name = "ujson", specifier = ">=5.8.0" },
]
github-actions = [
{ name = "httpx", specifier = ">=0.27.0,<1.0.0" },
@@ -1327,9 +1337,10 @@ tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "inline-snapshot", specifier = ">=0.21.1" },
{ name = "mypy", specifier = ">=1.14.1" },
{ name = "orjson", specifier = ">=3.9.3" },
{ name = "pwdlib", extras = ["argon2"], specifier = ">=0.2.1" },
{ name = "pyjwt", specifier = ">=2.9.0" },
{ name = "pytest", specifier = ">=7.1.3,<9.0.0" },
{ name = "pytest", specifier = ">=9.0.0" },
{ name = "pytest-codspeed", specifier = ">=4.2.0" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" },
@@ -1337,6 +1348,7 @@ tests = [
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
{ name = "types-orjson", specifier = ">=3.6.2" },
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
{ name = "ujson", specifier = ">=5.8.0" },
]
translations = [
{ name = "gitpython", specifier = ">=3.1.46" },
@@ -4330,7 +4342,7 @@ wheels = [
[[package]]
name = "pytest"
version = "8.4.2"
version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -4341,9 +4353,9 @@ dependencies = [
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]
[[package]]