Browse Source

Merge branch 'master' into mypy-bump-pydantic

pull/12970/head
Sofie Van Landeghem 10 months ago
committed by GitHub
parent
commit
95adafa9c5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 44
      README.md
  2. 14
      docs/en/docs/release-notes.md
  3. 4
      docs_src/handling_errors/tutorial005.py
  4. 2
      fastapi/__init__.py
  5. 4
      fastapi/exception_handlers.py
  6. 3
      fastapi/openapi/utils.py
  7. 8
      pyproject.toml
  8. 2
      requirements-docs-tests.txt
  9. 2
      requirements-github-actions.txt
  10. 14
      scripts/docs.py
  11. 2
      tests/test_enforce_once_required_parameter.py

44
README.md

@ -42,7 +42,7 @@ The key features are:
<small>* estimation based on tests on an internal development team, building production applications.</small> <small>* estimation based on tests on an internal development team, building production applications.</small>
## Sponsors { #sponsors } ## Sponsors
<!-- sponsors --> <!-- sponsors -->
@ -67,7 +67,7 @@ The key features are:
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a> <a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions { #opinions } ## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._" "_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
@ -113,7 +113,7 @@ The key features are:
--- ---
## **Typer**, the FastAPI of CLIs { #typer-the-fastapi-of-clis } ## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a> <a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
@ -121,14 +121,14 @@ If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀 **Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements { #requirements } ## Requirements
FastAPI stands on the shoulders of giants: FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts. * <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> for the data parts. * <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation { #installation } ## Installation
Create and activate a <a href="https://fastapi.tiangolo.com/virtual-environments/" class="external-link" target="_blank">virtual environment</a> and then install FastAPI: Create and activate a <a href="https://fastapi.tiangolo.com/virtual-environments/" class="external-link" target="_blank">virtual environment</a> and then install FastAPI:
@ -144,9 +144,9 @@ $ pip install "fastapi[standard]"
**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals. **Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals.
## Example { #example } ## Example
### Create it { #create-it } ### Create it
Create a file `main.py` with: Create a file `main.py` with:
@ -197,7 +197,7 @@ If you don't know, check the _"In a hurry?"_ section about <a href="https://fast
</details> </details>
### Run it { #run-it } ### Run it
Run the server with: Run the server with:
@ -239,7 +239,7 @@ You can read more about it in the <a href="https://fastapi.tiangolo.com/fastapi-
</details> </details>
### Check it { #check-it } ### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>. Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
@ -256,7 +256,7 @@ You already created an API that:
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`. * The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`. * The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs { #interactive-api-docs } ### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
@ -264,7 +264,7 @@ You will see the automatic interactive API documentation (provided by <a href="h
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs { #alternative-api-docs } ### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>. And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
@ -272,7 +272,7 @@ You will see the alternative automatic documentation (provided by <a href="https
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade { #example-upgrade } ## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request. Now modify the file `main.py` to receive a body from a `PUT` request.
@ -310,7 +310,7 @@ def update_item(item_id: int, item: Item):
The `fastapi dev` server should reload automatically. The `fastapi dev` server should reload automatically.
### Interactive API docs upgrade { #interactive-api-docs-upgrade } ### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
@ -326,7 +326,7 @@ Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_bl
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png) ![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade { #alternative-api-docs-upgrade } ### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>. And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
@ -334,7 +334,7 @@ And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" targe
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) ![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap { #recap } ### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters. In summary, you declare **once** the types of parameters, body, etc. as function parameters.
@ -446,17 +446,17 @@ For a more complete example including more features, see the <a href="https://fa
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
## Performance { #performance } ## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*) Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>. To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Dependencies { #dependencies } ## Dependencies
FastAPI depends on Pydantic and Starlette. FastAPI depends on Pydantic and Starlette.
### `standard` Dependencies { #standard-dependencies } ### `standard` Dependencies
When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies: When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies:
@ -476,15 +476,15 @@ Used by FastAPI:
* `fastapi-cli[standard]` - to provide the `fastapi` command. * `fastapi-cli[standard]` - to provide the `fastapi` command.
* This includes `fastapi-cloud-cli`, which allows you to deploy your FastAPI application to <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>. * This includes `fastapi-cloud-cli`, which allows you to deploy your FastAPI application to <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
### Without `standard` Dependencies { #without-standard-dependencies } ### Without `standard` Dependencies
If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`. If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`.
### Without `fastapi-cloud-cli` { #without-fastapi-cloud-cli } ### Without `fastapi-cloud-cli`
If you want to install FastAPI with the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`. If you want to install FastAPI with the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Additional Optional Dependencies { #additional-optional-dependencies } ### Additional Optional Dependencies
There are some additional dependencies you might want to install. There are some additional dependencies you might want to install.
@ -498,6 +498,6 @@ Additional optional FastAPI dependencies:
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`. * <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`. * <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
## License { #license } ## License
This project is licensed under the terms of the MIT license. This project is licensed under the terms of the MIT license.

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

@ -7,6 +7,20 @@ hide:
## Latest Changes ## Latest Changes
### Refactors
* 📌 Pin `httpx` to `>=0.23.0,<1.0.0`. PR [#14086](https://github.com/fastapi/fastapi/pull/14086) by [@YuriiMotov](https://github.com/YuriiMotov).
### Internal
* 🛠️ Update `docs.py generate-readme` command to remove permalinks from headers. PR [#14055](https://github.com/fastapi/fastapi/pull/14055) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.116.2
### Upgrades
* ⬆️ Upgrade Starlette supported version range to >=0.40.0,<0.49.0. PR [#14077](https://github.com/fastapi/fastapi/pull/14077) by [@musicinmybrain](https://github.com/musicinmybrain).
### Docs ### Docs
* 📝 Add documentation for Behind a Proxy - Proxy Forwarded Headers, using `--forwarded-allow-ips="*"`. PR [#14028](https://github.com/fastapi/fastapi/pull/14028) by [@tiangolo](https://github.com/tiangolo). * 📝 Add documentation for Behind a Proxy - Proxy Forwarded Headers, using `--forwarded-allow-ips="*"`. PR [#14028](https://github.com/fastapi/fastapi/pull/14028) by [@tiangolo](https://github.com/tiangolo).

4
docs_src/handling_errors/tutorial005.py

@ -1,4 +1,4 @@
from fastapi import FastAPI, Request, status from fastapi import FastAPI, Request
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
@ -10,7 +10,7 @@ app = FastAPI()
@app.exception_handler(RequestValidationError) @app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError): async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse( return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, status_code=422,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
) )

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.116.1" __version__ = "0.116.2"
from starlette import status as status from starlette import status as status

4
fastapi/exception_handlers.py

@ -5,7 +5,7 @@ from fastapi.websockets import WebSocket
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import JSONResponse, Response from starlette.responses import JSONResponse, Response
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY, WS_1008_POLICY_VIOLATION from starlette.status import WS_1008_POLICY_VIOLATION
async def http_exception_handler(request: Request, exc: HTTPException) -> Response: async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
@ -21,7 +21,7 @@ async def request_validation_exception_handler(
request: Request, exc: RequestValidationError request: Request, exc: RequestValidationError
) -> JSONResponse: ) -> JSONResponse:
return JSONResponse( return JSONResponse(
status_code=HTTP_422_UNPROCESSABLE_ENTITY, status_code=422,
content={"detail": jsonable_encoder(exc.errors())}, content={"detail": jsonable_encoder(exc.errors())},
) )

3
fastapi/openapi/utils.py

@ -35,7 +35,6 @@ from fastapi.utils import (
from pydantic import BaseModel from pydantic import BaseModel
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
from starlette.routing import BaseRoute from starlette.routing import BaseRoute
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
from typing_extensions import Literal from typing_extensions import Literal
validation_error_definition = { validation_error_definition = {
@ -416,7 +415,7 @@ def get_openapi_path(
) )
deep_dict_update(openapi_response, process_response) deep_dict_update(openapi_response, process_response)
openapi_response["description"] = description openapi_response["description"] = description
http422 = str(HTTP_422_UNPROCESSABLE_ENTITY) http422 = "422"
all_route_params = get_flat_params(route.dependant) all_route_params = get_flat_params(route.dependant)
if (all_route_params or route.body_field) and not any( if (all_route_params or route.body_field) and not any(
status in operation["responses"] status in operation["responses"]

8
pyproject.toml

@ -43,7 +43,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
] ]
dependencies = [ dependencies = [
"starlette>=0.40.0,<0.48.0", "starlette>=0.40.0,<0.49.0",
"pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0", "pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0",
"typing-extensions>=4.8.0", "typing-extensions>=4.8.0",
] ]
@ -60,7 +60,7 @@ Changelog = "https://fastapi.tiangolo.com/release-notes/"
standard = [ standard = [
"fastapi-cli[standard] >=0.0.8", "fastapi-cli[standard] >=0.0.8",
# For the test client # For the test client
"httpx >=0.23.0", "httpx >=0.23.0,<1.0.0",
# For templates # For templates
"jinja2 >=3.1.5", "jinja2 >=3.1.5",
# For forms and file uploads # For forms and file uploads
@ -79,7 +79,7 @@ standard = [
standard-no-fastapi-cloud-cli = [ standard-no-fastapi-cloud-cli = [
"fastapi-cli[standard-no-fastapi-cloud-cli] >=0.0.8", "fastapi-cli[standard-no-fastapi-cloud-cli] >=0.0.8",
# For the test client # For the test client
"httpx >=0.23.0", "httpx >=0.23.0,<1.0.0",
# For templates # For templates
"jinja2 >=3.1.5", "jinja2 >=3.1.5",
# For forms and file uploads # For forms and file uploads
@ -98,7 +98,7 @@ standard-no-fastapi-cloud-cli = [
all = [ all = [
"fastapi-cli[standard] >=0.0.8", "fastapi-cli[standard] >=0.0.8",
# # For the test client # # For the test client
"httpx >=0.23.0", "httpx >=0.23.0,<1.0.0",
# For templates # For templates
"jinja2 >=3.1.5", "jinja2 >=3.1.5",
# For forms and file uploads # For forms and file uploads

2
requirements-docs-tests.txt

@ -1,4 +1,4 @@
# For mkdocstrings and tests # For mkdocstrings and tests
httpx >=0.23.0,<0.29.0 httpx >=0.23.0,<1.0.0
# For linting and generating docs versions # For linting and generating docs versions
ruff ==0.12.7 ruff ==0.12.7

2
requirements-github-actions.txt

@ -1,6 +1,6 @@
PyGithub>=2.3.0,<3.0.0 PyGithub>=2.3.0,<3.0.0
pydantic>=2.5.3,<3.0.0 pydantic>=2.5.3,<3.0.0
pydantic-settings>=2.1.0,<3.0.0 pydantic-settings>=2.1.0,<3.0.0
httpx>=0.27.0,<0.29.0 httpx>=0.27.0,<1.0.0
pyyaml >=5.3.1,<7.0.0 pyyaml >=5.3.1,<7.0.0
smokeshow smokeshow

14
scripts/docs.py

@ -44,6 +44,8 @@ en_config_path: Path = en_docs_path / mkdocs_name
site_path = Path("site").absolute() site_path = Path("site").absolute()
build_site_path = Path("site_build").absolute() build_site_path = Path("site_build").absolute()
header_with_permalink_pattern = re.compile(r"^(#{1,6}) (.+?)(\s*\{\s*#.*\s*\})\s*$")
@lru_cache @lru_cache
def is_mkdocs_insiders() -> bool: def is_mkdocs_insiders() -> bool:
@ -154,9 +156,21 @@ index_sponsors_template = """
""" """
def remove_header_permalinks(content: str):
lines: list[str] = []
for line in content.split("\n"):
match = header_with_permalink_pattern.match(line)
if match:
hashes, title, *_ = match.groups()
line = f"{hashes} {title}"
lines.append(line)
return "\n".join(lines)
def generate_readme_content() -> str: def generate_readme_content() -> str:
en_index = en_docs_path / "docs" / "index.md" en_index = en_docs_path / "docs" / "index.md"
content = en_index.read_text("utf-8") content = en_index.read_text("utf-8")
content = remove_header_permalinks(content) # remove permalinks from headers
match_pre = re.search(r"</style>\n\n", content) match_pre = re.search(r"</style>\n\n", content)
match_start = re.search(r"<!-- sponsors -->", content) match_start = re.search(r"<!-- sponsors -->", content)
match_end = re.search(r"<!-- /sponsors -->", content) match_end = re.search(r"<!-- /sponsors -->", content)

2
tests/test_enforce_once_required_parameter.py

@ -102,7 +102,7 @@ def test_schema():
def test_get_invalid(): def test_get_invalid():
response = client.get("/foo") response = client.get("/foo")
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY assert response.status_code == 422
def test_get_valid(): def test_get_valid():

Loading…
Cancel
Save