Compare commits

..

5 Commits

Author SHA1 Message Date
Sebastián Ramírez
a82e5f2fac 🔖 Release version 0.137.1 (#15766)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-15 11:26:48 +00:00
github-actions[bot]
edd1461589 📝 Update release notes
[skip ci]
2026-06-15 11:20:24 +00:00
Sebastián Ramírez
b78c82262f 🚨 Fix typing checks for APIRoute (#15765) 2026-06-15 13:19:51 +02:00
github-actions[bot]
e0f8cadf09 📝 Update release notes
[skip ci]
2026-06-15 10:55:32 +00:00
Sebastián Ramírez
d8aad201eb 🐛 Fix bug, allow empty path in path operation in prefixless router (#15763) 2026-06-15 12:55:06 +02:00
14 changed files with 128 additions and 24 deletions

View File

@@ -7,6 +7,13 @@ hide:
## Latest Changes
## 0.137.1 (2026-06-15)
### Fixes
* 🚨 Fix typing checks for APIRoute. PR [#15765](https://github.com/fastapi/fastapi/pull/15765) by [@tiangolo](https://github.com/tiangolo).
* 🐛 Fix bug, allow empty path in path operation in prefixless router. PR [#15763](https://github.com/fastapi/fastapi/pull/15763) by [@tiangolo](https://github.com/tiangolo).
## 0.137.0 (2026-06-14)
### Breaking Changes

View File

@@ -469,7 +469,7 @@ Experimente mudar a seguinte linha:
... "item_price": item.price ...
```
...e veja como seu editor irá autocompletar os atributos e saberá os tipos:
...e veja como seu editor irá auto-completar os atributos e saberá os tipos:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
@@ -492,7 +492,9 @@ Para um exemplo mais completo incluindo mais recursos, veja o <a href="https://f
### Implemente sua aplicação (opcional) { #deploy-your-app-optional }
Você pode opcionalmente implantar sua aplicação FastAPI na [FastAPI Cloud](https://fastapicloud.com) com um único comando. 🚀
Você pode opcionalmente implantar sua aplicação FastAPI na [FastAPI Cloud](https://fastapicloud.com), vá e entre na lista de espera se ainda não o fez. 🚀
Se você já tem uma conta na **FastAPI Cloud** (nós convidamos você da lista de espera 😉), pode implantar sua aplicação com um único comando.
<div class="termy">
@@ -508,8 +510,6 @@ Deploying to FastAPI Cloud...
</div>
A CLI detectará automaticamente sua aplicação FastAPI e a implantará na nuvem. Se você não estiver autenticado, o navegador será aberto para concluir o processo de autenticação.
É isso! Agora você pode acessar sua aplicação nesse URL. ✨
#### Sobre a FastAPI Cloud { #about-fastapi-cloud }

View File

@@ -108,7 +108,7 @@ Por exemplo:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// note | Nota
/// info | Informação
`Body` também possui todas as validações adicionais e metadados de parâmetros como em `Query`,`Path` e outras que você verá depois.
@@ -123,7 +123,7 @@ Por padrão, o **FastAPI** esperará que seu conteúdo venha no corpo diretament
Mas se você quiser que ele espere por um JSON com uma chave `item` e dentro dele os conteúdos do modelo, como ocorre ao declarar vários parâmetros de corpo, você pode usar o parâmetro especial de `Body` chamado `embed`:
```Python
item: Annotated[Item, Body(embed=True)]
item: Item = Body(embed=True)
```
como em:

View File

@@ -8,7 +8,7 @@ Sua API quase sempre precisa enviar um corpo na **resposta**. Mas os clientes n
Para declarar um corpo da **requisição**, você utiliza os modelos do [Pydantic](https://docs.pydantic.dev/) com todos os seus poderes e benefícios.
/// note | Nota
/// info | Informação
Para enviar dados, você deveria usar um dos: `POST` (o mais comum), `PUT`, `DELETE` ou `PATCH`.

View File

@@ -24,13 +24,13 @@ Mas lembre-se que quando você importa `Query`, `Path`, `Cookie` e outras de `fa
///
/// note | Nota
/// info | Informação
Para declarar cookies, você precisa usar `Cookie`, pois caso contrário, os parâmetros seriam interpretados como parâmetros de consulta.
///
/// note | Nota
/// info | Informação
Tenha em mente que, como os **navegadores lidam com cookies** de maneiras especiais e nos bastidores, eles **não** permitem facilmente que o **JavaScript** os acesse.

View File

@@ -72,13 +72,13 @@ Você pode especificar a descrição da resposta com o parâmetro `response_desc
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// note | Nota
/// info | Informação
Observe que `response_description` se refere especificamente à resposta, a `description` se refere à *operação de rota* em geral.
Note que `response_description` se refere especificamente à resposta, a `description` se refere à *operação de rota* em geral.
///
/// tip | Dica
/// check | Verifique
OpenAPI especifica que cada *operação de rota* requer uma descrição de resposta.

View File

@@ -8,7 +8,7 @@ Primeiro, importe `Path` de `fastapi`, e importe `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// note | Nota
/// info | Informação
O FastAPI adicionou suporte a `Annotated` (e passou a recomendá-lo) na versão 0.95.0.
@@ -131,7 +131,7 @@ E você também pode declarar validações numéricas:
* `lt`: menor que (`l`ess `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
/// note | Nota
/// info | Informação
`Query`, `Path` e outras classes que você verá depois são subclasses de uma classe comum `Param`.

View File

@@ -2,7 +2,7 @@
Você pode definir arquivos para serem enviados pelo cliente usando `File`.
/// note | Nota
/// info | Informação
Para receber arquivos enviados, primeiro instale [`python-multipart`](https://github.com/Kludex/python-multipart).
@@ -28,7 +28,7 @@ Crie parâmetros de arquivo da mesma forma que você faria para `Body` ou `Form`
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// note | Nota
/// info | Informação
`File` é uma classe que herda diretamente de `Form`.

View File

@@ -2,7 +2,7 @@
Você pode definir arquivos e campos de formulário ao mesmo tempo usando `File` e `Form`.
/// note | Nota
/// info | Informação
Para receber arquivos carregados e/ou dados de formulário, primeiro instale [`python-multipart`](https://github.com/Kludex/python-multipart).

View File

@@ -18,7 +18,7 @@ Observe que `status_code` é um parâmetro do método "decorador" (`get`, `post`
O parâmetro `status_code` recebe um número com o código de status HTTP.
/// note | Nota
/// info | Informação
`status_code` também pode receber um `IntEnum`, como [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) do Python.

View File

@@ -8,7 +8,7 @@ Com ele, você pode usar o [pytest](https://docs.pytest.org/) diretamente com **
## Usando `TestClient` { #using-testclient }
/// note | Nota
/// info | Informação
Para usar o `TestClient`, primeiro instale [`httpx`](https://www.python-httpx.org).
@@ -142,7 +142,7 @@ Por exemplo:
Para mais informações sobre como passar dados para o backend (usando `httpx` ou `TestClient`), consulte a [documentação do HTTPX](https://www.python-httpx.org).
/// note | Nota
/// info | Informação
Observe que o `TestClient` recebe dados que podem ser convertidos para JSON, não para modelos Pydantic.

View File

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

View File

@@ -1062,6 +1062,41 @@ def _populate_api_route_state(
class APIRoute(routing.Route):
stream_item_type: Any | None
response_model: Any
summary: str | None
response_description: str
deprecated: bool | None
operation_id: str | None
response_model_include: IncEx | None
response_model_exclude: IncEx | None
response_model_by_alias: bool
response_model_exclude_unset: bool
response_model_exclude_defaults: bool
response_model_exclude_none: bool
include_in_schema: bool
response_class: type[Response] | DefaultPlaceholder
dependency_overrides_provider: Any | None
callbacks: list[BaseRoute] | None
openapi_extra: dict[str, Any] | None
generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder
strict_content_type: bool | DefaultPlaceholder
tags: list[str | Enum]
responses: dict[int | str, dict[str, Any]]
unique_id: str
status_code: int | None
response_field: ModelField | None
stream_item_field: ModelField | None
dependencies: list[params.Depends]
description: str
response_fields: dict[int | str, ModelField]
dependant: Dependant
_flat_dependant: Dependant
_embed_body_fields: bool
body_field: ModelField | None
is_sse_stream: bool
is_json_stream: bool
def __init__(
self,
path: str,
@@ -2435,9 +2470,16 @@ class APIRouter(routing.Router):
"A path prefix must not end with '/', as the routes will start with '/'"
)
else:
for r in _iter_included_route_candidates(router.routes):
path = getattr(r, "path", None)
name = getattr(r, "name", "unknown")
for route, route_context in _iter_routes_with_context(router.routes):
if route_context is None:
path = getattr(route, "path", None)
name = getattr(route, "name", "unknown")
elif route_context.starlette_route is not None:
path = getattr(route_context.starlette_route, "path", None)
name = getattr(route_context.starlette_route, "name", "unknown")
else:
path = route_context.path
name = route_context.name
if path is not None and not path:
raise FastAPIError(
f"Prefix and path cannot be both empty (path operation: {name})"

View File

@@ -2,6 +2,7 @@ from typing import Annotated, cast
import pytest
from fastapi import APIRouter, Body, Depends, FastAPI, Request
from fastapi.exceptions import FastAPIError
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
from fastapi.routing import (
APIRoute,
@@ -807,6 +808,60 @@ def test_no_prefix_include_validation_sees_effective_starlette_route_candidates(
assert cast(Route, candidates[0]).path == "/child/items"
def test_no_prefix_include_validation_sees_effective_api_route_path():
leaf_router = APIRouter()
@leaf_router.get("")
def read_items():
return []
parent_router = APIRouter()
parent_router.include_router(leaf_router, prefix="/items")
# for coverage
candidates = list(_iter_included_route_candidates(parent_router.routes))
assert cast(APIRoute, candidates[0]).path == ""
app = FastAPI()
app.include_router(parent_router)
client = TestClient(app)
response = client.get("/items")
assert response.status_code == 200, response.text
assert response.json() == []
def test_no_prefix_include_validation_sees_effective_starlette_route_path():
def endpoint(request):
return PlainTextResponse("ok")
child_router = APIRouter(routes=[Route("/items", endpoint, name="read_items")])
parent_router = APIRouter()
parent_router.include_router(child_router, prefix="/child")
app = FastAPI()
app.include_router(parent_router)
client = TestClient(app)
response = client.get("/child/items")
assert response.status_code == 200, response.text
assert response.text == "ok"
def test_no_prefix_include_validation_rejects_empty_effective_api_route_path():
router = APIRouter()
@router.get("")
def read_items(): # pragma: no cover
return []
app = FastAPI()
with pytest.raises(FastAPIError):
app.include_router(router)
def test_apirouter_matches_fallback_without_include_context():
router = APIRouter()