mirror of https://github.com/tiangolo/fastapi.git
✨ Add reference (code API) docs with PEP 727, add subclass with custom docstrings for `BackgroundTasks`, refactor docs structure (#10392)
* ➕ Add mkdocstrings and griffe-typingdoc to dependencies * 🔧 Add mkdocstrings configs to MkDocs * 📝 Add first WIP reference page * ⬆️ Upgrade typing-extensions to the minimum version including Doc() * 📝 Add docs to FastAPI parameters * 📝 Add docstrings for OpenAPI docs utils * 📝 Add docstrings for security utils * 📝 Add docstrings for UploadFile * 📝 Update docstrings in FastAPI class * 📝 Add docstrings for path operation methods * 📝 Add docstring for jsonable_encoder * 📝 Add docstrings for exceptions * 📝 Add docstsrings for parameter functions * 📝 Add docstrings for responses * 📝 Add docstrings for APIRouter * ♻️ Sub-class BackgroundTasks to document it with docstrings * 📝 Update usage of background tasks in dependencies * ✅ Update tests with new deprecation warnings * 📝 Add new reference docs * 🔧 Update MkDocs with new reference docs * ✅ Update pytest fixture, deprecation is raised only once * 🎨 Update format for types in exceptions.py * ♻️ Update annotations in BackgroundTask, `Annotated` can't take ParamSpec's P.args or P.kwargs * ✏️ Fix typos caught by @pawamoy * 🔧 Update and fix MkDocstrings configs from @pawamoy tips * 📝 Update reference docs * ✏️ Fix typos found by @pawamoy * ➕ Add HTTPX as a dependency for docs, for the TestClient * 🔧 Update MkDocs config, rename websockets reference * 🔇 Add type-ignores for Doc as the stubs haven't been released for mypy * 🔥 Remove duplicated deprecated notice * 🔇 Remove typing error for unreleased stub in openapi/docs.py * ✅ Add tests for UploadFile for coverage * ⬆️ Upgrade griffe-typingdoc==0.2.2 * 📝 Refactor docs structure * 🔨 Update README generation with new index frontmatter and style * 🔨 Update generation of languages, remove from top menu, keep in lang menu * 📝 Add OpenAPI Pydantic models * 🔨 Update docs script to not translate Reference and Release Notes * 🔧 Add reference for OpenAPI models * 🔧 Update MkDocs config for mkdocstrings insiders * 👷 Install mkdocstring insiders in CI for docs * 🐛 Fix MkDocstrings insiders install URL * ➕ Move dependencies shared by docs and tests to its own requirements file * 👷 Update cache keys for test and docs dependencies * 📝 Remove no longer needed __init__ placeholder docstrings * 📝 Move docstring for APIRouter to the class level (not __init__ level) * 🔥 Remove no longer needed dummy placeholder __init__ docstring
This commit is contained in:
parent
3fa44aabe3
commit
05ca41cfd1
|
|
@ -44,14 +44,17 @@ jobs:
|
|||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06
|
||||
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt', 'requirements-docs-tests.txt') }}-v06
|
||||
- name: Install docs extras
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install -r requirements-docs.txt
|
||||
# Install MkDocs Material Insiders here just to put it in the cache for the rest of the steps
|
||||
- name: Install Material for MkDocs Insiders
|
||||
if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||
run: |
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/griffe-typing-deprecated.git
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git
|
||||
- name: Export Language Codes
|
||||
id: show-langs
|
||||
run: |
|
||||
|
|
@ -80,13 +83,16 @@ jobs:
|
|||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v06
|
||||
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt', 'requirements-docs-tests.txt') }}-v06
|
||||
- name: Install docs extras
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install -r requirements-docs.txt
|
||||
- name: Install Material for MkDocs Insiders
|
||||
if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||
run: |
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/griffe-typing-deprecated.git
|
||||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git
|
||||
- name: Update Languages
|
||||
run: python ./scripts/docs.py update-languages
|
||||
- uses: actions/cache@v3
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v06
|
||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v06
|
||||
- name: Install Dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install -r requirements-tests.txt
|
||||
|
|
@ -62,7 +62,7 @@ jobs:
|
|||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v06
|
||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt', 'requirements-docs-tests.txt') }}-test-v06
|
||||
- name: Install Dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: pip install -r requirements-tests.txt
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# About
|
||||
|
||||
About FastAPI, its design, inspiration and more. 🤓
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
---
|
||||
hide:
|
||||
- navigation
|
||||
---
|
||||
|
||||
# FastAPI People
|
||||
|
||||
FastAPI has an amazing community that welcomes people from all backgrounds.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
---
|
||||
hide:
|
||||
- navigation
|
||||
---
|
||||
|
||||
# Features
|
||||
|
||||
## FastAPI features
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# Help
|
||||
|
||||
Help and get help, contribute, get involved. 🤝
|
||||
|
|
@ -1,3 +1,12 @@
|
|||
---
|
||||
hide:
|
||||
- navigation
|
||||
---
|
||||
|
||||
<style>
|
||||
.md-content .md-typeset h1 { display: none; }
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# Learn
|
||||
|
||||
Here are the introductory sections and the tutorials to learn **FastAPI**.
|
||||
|
||||
You could consider this a **book**, a **course**, the **official** and recommended way to learn FastAPI. 😎
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# `APIRouter` class
|
||||
|
||||
Here's the reference information for the `APIRouter` class, with all its parameters,
|
||||
attributes and methods.
|
||||
|
||||
You can import the `APIRouter` class directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter
|
||||
```
|
||||
|
||||
::: fastapi.APIRouter
|
||||
options:
|
||||
members:
|
||||
- websocket
|
||||
- include_router
|
||||
- get
|
||||
- put
|
||||
- post
|
||||
- delete
|
||||
- options
|
||||
- head
|
||||
- patch
|
||||
- trace
|
||||
- on_event
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Background Tasks - `BackgroundTasks`
|
||||
|
||||
You can declare a parameter in a *path operation function* or dependency function
|
||||
with the type `BackgroundTasks`, and then you can use it to schedule the execution
|
||||
of background tasks after the response is sent.
|
||||
|
||||
You can import it directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import BackgroundTasks
|
||||
```
|
||||
|
||||
::: fastapi.BackgroundTasks
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Dependencies - `Depends()` and `Security()`
|
||||
|
||||
## `Depends()`
|
||||
|
||||
Dependencies are handled mainly with the special function `Depends()` that takes a
|
||||
callable.
|
||||
|
||||
Here is the reference for it and its parameters.
|
||||
|
||||
You can import it directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import Depends
|
||||
```
|
||||
|
||||
::: fastapi.Depends
|
||||
|
||||
## `Security()`
|
||||
|
||||
For many scenarios, you can handle security (authorization, authentication, etc.) with
|
||||
dependendencies, using `Depends()`.
|
||||
|
||||
But when you want to also declare OAuth2 scopes, you can use `Security()` instead of
|
||||
`Depends()`.
|
||||
|
||||
You can import `Security()` directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import Security
|
||||
```
|
||||
|
||||
::: fastapi.Security
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Encoders - `jsonable_encoder`
|
||||
|
||||
::: fastapi.encoders.jsonable_encoder
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Exceptions - `HTTPException` and `WebSocketException`
|
||||
|
||||
These are the exceptions that you can raise to show errors to the client.
|
||||
|
||||
When you raise an exception, as would happen with normal Python, the rest of the
|
||||
excecution is aborted. This way you can raise these exceptions from anywhere in the
|
||||
code to abort a request and show the error to the client.
|
||||
|
||||
You can use:
|
||||
|
||||
* `HTTPException`
|
||||
* `WebSocketException`
|
||||
|
||||
These exceptions can be imported directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, WebSocketException
|
||||
```
|
||||
|
||||
::: fastapi.HTTPException
|
||||
|
||||
::: fastapi.WebSocketException
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# `FastAPI` class
|
||||
|
||||
Here's the reference information for the `FastAPI` class, with all its parameters,
|
||||
attributes and methods.
|
||||
|
||||
You can import the `FastAPI` class directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
```
|
||||
|
||||
::: fastapi.FastAPI
|
||||
options:
|
||||
members:
|
||||
- openapi_version
|
||||
- webhooks
|
||||
- state
|
||||
- dependency_overrides
|
||||
- openapi
|
||||
- websocket
|
||||
- include_router
|
||||
- get
|
||||
- put
|
||||
- post
|
||||
- delete
|
||||
- options
|
||||
- head
|
||||
- patch
|
||||
- trace
|
||||
- on_event
|
||||
- middleware
|
||||
- exception_handler
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# `HTTPConnection` class
|
||||
|
||||
When you want to define dependencies that should be compatible with both HTTP and
|
||||
WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a
|
||||
`Request` or a `WebSocket`.
|
||||
|
||||
You can import it from `fastapi.requests`:
|
||||
|
||||
```python
|
||||
from fastapi.requests import HTTPConnection
|
||||
```
|
||||
|
||||
::: fastapi.requests.HTTPConnection
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Reference - Code API
|
||||
|
||||
Here's the reference or code API, the classes, functions, parameters, attributes, and
|
||||
all the FastAPI parts you can use in you applications.
|
||||
|
||||
If you want to **learn FastAPI** you are much better off reading the
|
||||
[FastAPI Tutorial](https://fastapi.tiangolo.com/tutorial/).
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# Middleware
|
||||
|
||||
There are several middlewares available provided by Starlette directly.
|
||||
|
||||
Read more about them in the
|
||||
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/).
|
||||
|
||||
::: fastapi.middleware.cors.CORSMiddleware
|
||||
|
||||
It can be imported from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
```
|
||||
|
||||
::: fastapi.middleware.gzip.GZipMiddleware
|
||||
|
||||
It can be imported from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
```
|
||||
|
||||
::: fastapi.middleware.httpsredirect.HTTPSRedirectMiddleware
|
||||
|
||||
It can be imported from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
||||
```
|
||||
|
||||
::: fastapi.middleware.trustedhost.TrustedHostMiddleware
|
||||
|
||||
It can be imported from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
```
|
||||
|
||||
::: fastapi.middleware.wsgi.WSGIMiddleware
|
||||
|
||||
It can be imported from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||
```
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# OpenAPI `docs`
|
||||
|
||||
Utilities to handle OpenAPI automatic UI documentation, including Swagger UI (by default at `/docs`) and ReDoc (by default at `/redoc`).
|
||||
|
||||
::: fastapi.openapi.docs.get_swagger_ui_html
|
||||
|
||||
::: fastapi.openapi.docs.get_redoc_html
|
||||
|
||||
::: fastapi.openapi.docs.get_swagger_ui_oauth2_redirect_html
|
||||
|
||||
::: fastapi.openapi.docs.swagger_ui_default_parameters
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# OpenAPI
|
||||
|
||||
There are several utilities to handle OpenAPI.
|
||||
|
||||
You normally don't need to use them unless you have a specific advanced use case that requires it.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# OpenAPI `models`
|
||||
|
||||
OpenAPI Pydantic models used to generate and validate the generated OpenAPI.
|
||||
|
||||
::: fastapi.openapi.models
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# Request Parameters
|
||||
|
||||
Here's the reference information for the request parameters.
|
||||
|
||||
These are the special functions that you can put in *path operation function*
|
||||
parameters or dependency functions with `Annotated` to get data from the request.
|
||||
|
||||
It includes:
|
||||
|
||||
* `Query()`
|
||||
* `Path()`
|
||||
* `Body()`
|
||||
* `Cookie()`
|
||||
* `Header()`
|
||||
* `Form()`
|
||||
* `File()`
|
||||
|
||||
You can import them all directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import Body, Cookie, File, Form, Header, Path, Query
|
||||
```
|
||||
|
||||
::: fastapi.Query
|
||||
|
||||
::: fastapi.Path
|
||||
|
||||
::: fastapi.Body
|
||||
|
||||
::: fastapi.Cookie
|
||||
|
||||
::: fastapi.Header
|
||||
|
||||
::: fastapi.Form
|
||||
|
||||
::: fastapi.File
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# `Request` class
|
||||
|
||||
You can declare a parameter in a *path operation function* or dependency to be of type
|
||||
`Request` and then you can access the raw request object directly, without any
|
||||
validation, etc.
|
||||
|
||||
You can import it directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import Request
|
||||
```
|
||||
|
||||
!!! tip
|
||||
When you want to define dependencies that should be compatible with both HTTP and
|
||||
WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a
|
||||
`Request` or a `WebSocket`.
|
||||
|
||||
::: fastapi.Request
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# `Response` class
|
||||
|
||||
You can declare a parameter in a *path operation function* or dependency to be of type
|
||||
`Response` and then you can set data for the response like headers or cookies.
|
||||
|
||||
You can also use it directly to create an instance of it and return it from your *path
|
||||
operations*.
|
||||
|
||||
You can import it directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import Response
|
||||
```
|
||||
|
||||
::: fastapi.Response
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
# Custom Response Classes - File, HTML, Redirect, Streaming, etc.
|
||||
|
||||
There are several custom response classes you can use to create an instance and return
|
||||
them directly from your *path operations*.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
|
||||
|
||||
You can import them directly from `fastapi.responses`:
|
||||
|
||||
```python
|
||||
from fastapi.responses import (
|
||||
FileResponse,
|
||||
HTMLResponse,
|
||||
JSONResponse,
|
||||
ORJSONResponse,
|
||||
PlainTextResponse,
|
||||
RedirectResponse,
|
||||
Response,
|
||||
StreamingResponse,
|
||||
UJSONResponse,
|
||||
)
|
||||
```
|
||||
|
||||
## FastAPI Responses
|
||||
|
||||
There are a couple of custom FastAPI response classes, you can use them to optimize JSON performance.
|
||||
|
||||
::: fastapi.responses.UJSONResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.ORJSONResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
## Starlette Responses
|
||||
|
||||
::: fastapi.responses.FileResponse
|
||||
options:
|
||||
members:
|
||||
- chunk_size
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.HTMLResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.JSONResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.PlainTextResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.RedirectResponse
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.Response
|
||||
options:
|
||||
members:
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
||||
::: fastapi.responses.StreamingResponse
|
||||
options:
|
||||
members:
|
||||
- body_iterator
|
||||
- charset
|
||||
- status_code
|
||||
- media_type
|
||||
- body
|
||||
- background
|
||||
- raw_headers
|
||||
- render
|
||||
- init_headers
|
||||
- headers
|
||||
- set_cookie
|
||||
- delete_cookie
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
# Security Tools
|
||||
|
||||
When you need to declare dependencies with OAuth2 scopes you use `Security()`.
|
||||
|
||||
But you still need to define what is the dependable, the callable that you pass as
|
||||
a parameter to `Depends()` or `Security()`.
|
||||
|
||||
There are multiple tools that you can use to create those dependables, and they get
|
||||
integrated into OpenAPI so they are shown in the automatic docs UI, they can be used
|
||||
by automatically generated clients and SDKs, etc.
|
||||
|
||||
You can import them from `fastapi.security`:
|
||||
|
||||
```python
|
||||
from fastapi.security import (
|
||||
APIKeyCookie,
|
||||
APIKeyHeader,
|
||||
APIKeyQuery,
|
||||
HTTPAuthorizationCredentials,
|
||||
HTTPBasic,
|
||||
HTTPBasicCredentials,
|
||||
HTTPBearer,
|
||||
HTTPDigest,
|
||||
OAuth2,
|
||||
OAuth2AuthorizationCodeBearer,
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
OAuth2PasswordRequestFormStrict,
|
||||
OpenIdConnect,
|
||||
SecurityScopes,
|
||||
)
|
||||
```
|
||||
|
||||
## API Key Security Schemes
|
||||
|
||||
::: fastapi.security.APIKeyCookie
|
||||
|
||||
::: fastapi.security.APIKeyHeader
|
||||
|
||||
::: fastapi.security.APIKeyQuery
|
||||
|
||||
## HTTP Authentication Schemes
|
||||
|
||||
::: fastapi.security.HTTPBasic
|
||||
|
||||
::: fastapi.security.HTTPBearer
|
||||
|
||||
::: fastapi.security.HTTPDigest
|
||||
|
||||
## HTTP Credentials
|
||||
|
||||
::: fastapi.security.HTTPAuthorizationCredentials
|
||||
|
||||
::: fastapi.security.HTTPBasicCredentials
|
||||
|
||||
## OAuth2 Authentication
|
||||
|
||||
::: fastapi.security.OAuth2
|
||||
|
||||
::: fastapi.security.OAuth2AuthorizationCodeBearer
|
||||
|
||||
::: fastapi.security.OAuth2PasswordBearer
|
||||
|
||||
## OAuth2 Password Form
|
||||
|
||||
::: fastapi.security.OAuth2PasswordRequestForm
|
||||
|
||||
::: fastapi.security.OAuth2PasswordRequestFormStrict
|
||||
|
||||
## OAuth2 Security Scopes in Dependencies
|
||||
|
||||
::: fastapi.security.SecurityScopes
|
||||
|
||||
## OpenID Connect
|
||||
|
||||
::: fastapi.security.OpenIdConnect
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Static Files - `StaticFiles`
|
||||
|
||||
You can use the `StaticFiles` class to serve static files, like JavaScript, CSS, images, etc.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Static Files](https://fastapi.tiangolo.com/tutorial/static-files/).
|
||||
|
||||
You can import it directly from `fastapi.staticfiles`:
|
||||
|
||||
```python
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
```
|
||||
|
||||
::: fastapi.staticfiles.StaticFiles
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Status Codes
|
||||
|
||||
You can import the `status` module from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import status
|
||||
```
|
||||
|
||||
`status` is provided directly by Starlette.
|
||||
|
||||
It containes a group of named constants (variables) with integer status codes.
|
||||
|
||||
For example:
|
||||
|
||||
* 200: `status.HTTP_200_OK`
|
||||
* 403: `status.HTTP_403_FORBIDDEN`
|
||||
* etc.
|
||||
|
||||
It can be convenient to quickly access HTTP (and WebSocket) status codes in your app,
|
||||
using autocompletion for the name without having to remember the integer status codes
|
||||
by memory.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs about Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, status
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", status_code=status.HTTP_418_IM_A_TEAPOT)
|
||||
def read_items():
|
||||
return [{"name": "Plumbus"}, {"name": "Portal Gun"}]
|
||||
```
|
||||
|
||||
::: fastapi.status
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Templating - `Jinja2Templates`
|
||||
|
||||
You can use the `Jinja2Templates` class to render Jinja templates.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Templates](https://fastapi.tiangolo.com/advanced/templates/).
|
||||
|
||||
You can import it directly from `fastapi.templating`:
|
||||
|
||||
```python
|
||||
from fastapi.templating import Jinja2Templates
|
||||
```
|
||||
|
||||
::: fastapi.templating.Jinja2Templates
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Test Client - `TestClient`
|
||||
|
||||
You can use the `TestClient` class to test FastAPI applications without creating an actual HTTP and socket connection, just communicating directly with the FastAPI code.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Testing](https://fastapi.tiangolo.com/tutorial/testing/).
|
||||
|
||||
You can import it directly from `fastapi.testclient`:
|
||||
|
||||
```python
|
||||
from fastapi.testclient import TestClient
|
||||
```
|
||||
|
||||
::: fastapi.testclient.TestClient
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# `UploadFile` class
|
||||
|
||||
You can define *path operation function* parameters to be of the type `UploadFile`
|
||||
to receive files from the request.
|
||||
|
||||
You can import it directly from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import UploadFile
|
||||
```
|
||||
|
||||
::: fastapi.UploadFile
|
||||
options:
|
||||
members:
|
||||
- file
|
||||
- filename
|
||||
- size
|
||||
- headers
|
||||
- content_type
|
||||
- read
|
||||
- write
|
||||
- seek
|
||||
- close
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# WebSockets
|
||||
|
||||
When defining WebSockets, you normally declare a parameter of type `WebSocket` and
|
||||
with it you can read data from the client and send data to it.
|
||||
|
||||
It is provided directly by Starlette, but you can import it from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import WebSocket
|
||||
```
|
||||
|
||||
!!! tip
|
||||
When you want to define dependencies that should be compatible with both HTTP and
|
||||
WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a
|
||||
`Request` or a `WebSocket`.
|
||||
|
||||
::: fastapi.WebSocket
|
||||
options:
|
||||
members:
|
||||
- scope
|
||||
- app
|
||||
- url
|
||||
- base_url
|
||||
- headers
|
||||
- query_params
|
||||
- path_params
|
||||
- cookies
|
||||
- client
|
||||
- state
|
||||
- url_for
|
||||
- client_state
|
||||
- application_state
|
||||
- receive
|
||||
- send
|
||||
- accept
|
||||
- receive_text
|
||||
- receive_bytes
|
||||
- receive_json
|
||||
- iter_text
|
||||
- iter_bytes
|
||||
- iter_json
|
||||
- send_text
|
||||
- send_bytes
|
||||
- send_json
|
||||
- close
|
||||
|
||||
When a client disconnects, a `WebSocketDisconnect` exception is raised, you can catch
|
||||
it.
|
||||
|
||||
You can import it directly form `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi import WebSocketDisconnect
|
||||
```
|
||||
|
||||
::: fastapi.WebSocketDisconnect
|
||||
|
||||
## WebSockets - additional classes
|
||||
|
||||
Additional classes for handling WebSockets.
|
||||
|
||||
Provided directly by Starlette, but you can import it from `fastapi`:
|
||||
|
||||
```python
|
||||
from fastapi.websockets import WebSocketDisconnect, WebSocketState
|
||||
```
|
||||
|
||||
::: fastapi.websockets.WebSocketDisconnect
|
||||
|
||||
::: fastapi.websockets.WebSocketState
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
---
|
||||
hide:
|
||||
- navigation
|
||||
---
|
||||
|
||||
# Release Notes
|
||||
|
||||
## Latest Changes
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# Resources
|
||||
|
||||
Additional resources, external links, articles and more. ✈️
|
||||
|
|
@ -30,6 +30,7 @@ theme:
|
|||
- content.code.annotate
|
||||
- content.code.copy
|
||||
- content.code.select
|
||||
- navigation.tabs
|
||||
icon:
|
||||
repo: fontawesome/brands/github-alt
|
||||
logo: img/icon-white.svg
|
||||
|
|
@ -52,142 +53,174 @@ plugins:
|
|||
advanced/custom-request-and-route.md: how-to/custom-request-and-route.md
|
||||
advanced/conditional-openapi.md: how-to/conditional-openapi.md
|
||||
advanced/extending-openapi.md: how-to/extending-openapi.md
|
||||
mkdocstrings:
|
||||
handlers:
|
||||
python:
|
||||
options:
|
||||
extensions:
|
||||
- griffe_typingdoc
|
||||
show_root_heading: true
|
||||
show_if_no_docstring: true
|
||||
preload_modules: [httpx, starlette]
|
||||
inherited_members: true
|
||||
members_order: source
|
||||
separate_signature: true
|
||||
unwrap_annotated: true
|
||||
filters: ["!^_"]
|
||||
merge_init_into_class: true
|
||||
docstring_section_style: spacy
|
||||
signature_crossrefs: true
|
||||
show_symbol_type_heading: true
|
||||
show_symbol_type_toc: true
|
||||
nav:
|
||||
- FastAPI: index.md
|
||||
- Languages:
|
||||
- en: /
|
||||
- de: /de/
|
||||
- em: /em/
|
||||
- es: /es/
|
||||
- fa: /fa/
|
||||
- fr: /fr/
|
||||
- he: /he/
|
||||
- id: /id/
|
||||
- ja: /ja/
|
||||
- ko: /ko/
|
||||
- pl: /pl/
|
||||
- pt: /pt/
|
||||
- ru: /ru/
|
||||
- tr: /tr/
|
||||
- uk: /uk/
|
||||
- ur: /ur/
|
||||
- vi: /vi/
|
||||
- yo: /yo/
|
||||
- zh: /zh/
|
||||
- features.md
|
||||
- Learn:
|
||||
- learn/index.md
|
||||
- python-types.md
|
||||
- async.md
|
||||
- Tutorial - User Guide:
|
||||
- tutorial/index.md
|
||||
- tutorial/first-steps.md
|
||||
- tutorial/path-params.md
|
||||
- tutorial/query-params.md
|
||||
- tutorial/body.md
|
||||
- tutorial/query-params-str-validations.md
|
||||
- tutorial/path-params-numeric-validations.md
|
||||
- tutorial/body-multiple-params.md
|
||||
- tutorial/body-fields.md
|
||||
- tutorial/body-nested-models.md
|
||||
- tutorial/schema-extra-example.md
|
||||
- tutorial/extra-data-types.md
|
||||
- tutorial/cookie-params.md
|
||||
- tutorial/header-params.md
|
||||
- tutorial/response-model.md
|
||||
- tutorial/extra-models.md
|
||||
- tutorial/response-status-code.md
|
||||
- tutorial/request-forms.md
|
||||
- tutorial/request-files.md
|
||||
- tutorial/request-forms-and-files.md
|
||||
- tutorial/handling-errors.md
|
||||
- tutorial/path-operation-configuration.md
|
||||
- tutorial/encoder.md
|
||||
- tutorial/body-updates.md
|
||||
- Dependencies:
|
||||
- tutorial/dependencies/index.md
|
||||
- tutorial/dependencies/classes-as-dependencies.md
|
||||
- tutorial/dependencies/sub-dependencies.md
|
||||
- tutorial/dependencies/dependencies-in-path-operation-decorators.md
|
||||
- tutorial/dependencies/global-dependencies.md
|
||||
- tutorial/dependencies/dependencies-with-yield.md
|
||||
- Security:
|
||||
- tutorial/security/index.md
|
||||
- tutorial/security/first-steps.md
|
||||
- tutorial/security/get-current-user.md
|
||||
- tutorial/security/simple-oauth2.md
|
||||
- tutorial/security/oauth2-jwt.md
|
||||
- tutorial/middleware.md
|
||||
- tutorial/cors.md
|
||||
- tutorial/sql-databases.md
|
||||
- tutorial/bigger-applications.md
|
||||
- tutorial/background-tasks.md
|
||||
- tutorial/metadata.md
|
||||
- tutorial/static-files.md
|
||||
- tutorial/testing.md
|
||||
- tutorial/debugging.md
|
||||
- Advanced User Guide:
|
||||
- advanced/index.md
|
||||
- advanced/path-operation-advanced-configuration.md
|
||||
- advanced/additional-status-codes.md
|
||||
- advanced/response-directly.md
|
||||
- advanced/custom-response.md
|
||||
- advanced/additional-responses.md
|
||||
- advanced/response-cookies.md
|
||||
- advanced/response-headers.md
|
||||
- advanced/response-change-status-code.md
|
||||
- advanced/advanced-dependencies.md
|
||||
- Advanced Security:
|
||||
- advanced/security/index.md
|
||||
- advanced/security/oauth2-scopes.md
|
||||
- advanced/security/http-basic-auth.md
|
||||
- advanced/using-request-directly.md
|
||||
- advanced/dataclasses.md
|
||||
- advanced/middleware.md
|
||||
- advanced/sub-applications.md
|
||||
- advanced/behind-a-proxy.md
|
||||
- advanced/templates.md
|
||||
- advanced/websockets.md
|
||||
- advanced/events.md
|
||||
- advanced/testing-websockets.md
|
||||
- advanced/testing-events.md
|
||||
- advanced/testing-dependencies.md
|
||||
- advanced/testing-database.md
|
||||
- advanced/async-tests.md
|
||||
- advanced/settings.md
|
||||
- advanced/openapi-callbacks.md
|
||||
- advanced/openapi-webhooks.md
|
||||
- advanced/wsgi.md
|
||||
- advanced/generate-clients.md
|
||||
- Deployment:
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
- deployment/https.md
|
||||
- deployment/manually.md
|
||||
- deployment/concepts.md
|
||||
- deployment/cloud.md
|
||||
- deployment/server-workers.md
|
||||
- deployment/docker.md
|
||||
- How To - Recipes:
|
||||
- how-to/index.md
|
||||
- how-to/general.md
|
||||
- how-to/sql-databases-peewee.md
|
||||
- how-to/async-sql-encode-databases.md
|
||||
- how-to/nosql-databases-couchbase.md
|
||||
- how-to/graphql.md
|
||||
- how-to/custom-request-and-route.md
|
||||
- how-to/conditional-openapi.md
|
||||
- how-to/extending-openapi.md
|
||||
- how-to/separate-openapi-schemas.md
|
||||
- how-to/custom-docs-ui-assets.md
|
||||
- how-to/configure-swagger-ui.md
|
||||
- Reference (Code API):
|
||||
- reference/index.md
|
||||
- reference/fastapi.md
|
||||
- reference/parameters.md
|
||||
- reference/status.md
|
||||
- reference/uploadfile.md
|
||||
- reference/exceptions.md
|
||||
- reference/dependencies.md
|
||||
- reference/apirouter.md
|
||||
- reference/background.md
|
||||
- reference/request.md
|
||||
- reference/websockets.md
|
||||
- reference/httpconnection.md
|
||||
- reference/response.md
|
||||
- reference/responses.md
|
||||
- reference/middleware.md
|
||||
- OpenAPI:
|
||||
- reference/openapi/index.md
|
||||
- reference/openapi/docs.md
|
||||
- reference/openapi/models.md
|
||||
- reference/security/index.md
|
||||
- reference/encoders.md
|
||||
- reference/staticfiles.md
|
||||
- reference/templating.md
|
||||
- reference/testclient.md
|
||||
- fastapi-people.md
|
||||
- python-types.md
|
||||
- Tutorial - User Guide:
|
||||
- tutorial/index.md
|
||||
- tutorial/first-steps.md
|
||||
- tutorial/path-params.md
|
||||
- tutorial/query-params.md
|
||||
- tutorial/body.md
|
||||
- tutorial/query-params-str-validations.md
|
||||
- tutorial/path-params-numeric-validations.md
|
||||
- tutorial/body-multiple-params.md
|
||||
- tutorial/body-fields.md
|
||||
- tutorial/body-nested-models.md
|
||||
- tutorial/schema-extra-example.md
|
||||
- tutorial/extra-data-types.md
|
||||
- tutorial/cookie-params.md
|
||||
- tutorial/header-params.md
|
||||
- tutorial/response-model.md
|
||||
- tutorial/extra-models.md
|
||||
- tutorial/response-status-code.md
|
||||
- tutorial/request-forms.md
|
||||
- tutorial/request-files.md
|
||||
- tutorial/request-forms-and-files.md
|
||||
- tutorial/handling-errors.md
|
||||
- tutorial/path-operation-configuration.md
|
||||
- tutorial/encoder.md
|
||||
- tutorial/body-updates.md
|
||||
- Dependencies:
|
||||
- tutorial/dependencies/index.md
|
||||
- tutorial/dependencies/classes-as-dependencies.md
|
||||
- tutorial/dependencies/sub-dependencies.md
|
||||
- tutorial/dependencies/dependencies-in-path-operation-decorators.md
|
||||
- tutorial/dependencies/global-dependencies.md
|
||||
- tutorial/dependencies/dependencies-with-yield.md
|
||||
- Security:
|
||||
- tutorial/security/index.md
|
||||
- tutorial/security/first-steps.md
|
||||
- tutorial/security/get-current-user.md
|
||||
- tutorial/security/simple-oauth2.md
|
||||
- tutorial/security/oauth2-jwt.md
|
||||
- tutorial/middleware.md
|
||||
- tutorial/cors.md
|
||||
- tutorial/sql-databases.md
|
||||
- tutorial/bigger-applications.md
|
||||
- tutorial/background-tasks.md
|
||||
- tutorial/metadata.md
|
||||
- tutorial/static-files.md
|
||||
- tutorial/testing.md
|
||||
- tutorial/debugging.md
|
||||
- Advanced User Guide:
|
||||
- advanced/index.md
|
||||
- advanced/path-operation-advanced-configuration.md
|
||||
- advanced/additional-status-codes.md
|
||||
- advanced/response-directly.md
|
||||
- advanced/custom-response.md
|
||||
- advanced/additional-responses.md
|
||||
- advanced/response-cookies.md
|
||||
- advanced/response-headers.md
|
||||
- advanced/response-change-status-code.md
|
||||
- advanced/advanced-dependencies.md
|
||||
- Advanced Security:
|
||||
- advanced/security/index.md
|
||||
- advanced/security/oauth2-scopes.md
|
||||
- advanced/security/http-basic-auth.md
|
||||
- advanced/using-request-directly.md
|
||||
- advanced/dataclasses.md
|
||||
- advanced/middleware.md
|
||||
- advanced/sub-applications.md
|
||||
- advanced/behind-a-proxy.md
|
||||
- advanced/templates.md
|
||||
- advanced/websockets.md
|
||||
- advanced/events.md
|
||||
- advanced/testing-websockets.md
|
||||
- advanced/testing-events.md
|
||||
- advanced/testing-dependencies.md
|
||||
- advanced/testing-database.md
|
||||
- advanced/async-tests.md
|
||||
- advanced/settings.md
|
||||
- advanced/openapi-callbacks.md
|
||||
- advanced/openapi-webhooks.md
|
||||
- advanced/wsgi.md
|
||||
- advanced/generate-clients.md
|
||||
- async.md
|
||||
- Deployment:
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
- deployment/https.md
|
||||
- deployment/manually.md
|
||||
- deployment/concepts.md
|
||||
- deployment/cloud.md
|
||||
- deployment/server-workers.md
|
||||
- deployment/docker.md
|
||||
- How To - Recipes:
|
||||
- how-to/index.md
|
||||
- how-to/general.md
|
||||
- how-to/sql-databases-peewee.md
|
||||
- how-to/async-sql-encode-databases.md
|
||||
- how-to/nosql-databases-couchbase.md
|
||||
- how-to/graphql.md
|
||||
- how-to/custom-request-and-route.md
|
||||
- how-to/conditional-openapi.md
|
||||
- how-to/extending-openapi.md
|
||||
- how-to/separate-openapi-schemas.md
|
||||
- how-to/custom-docs-ui-assets.md
|
||||
- how-to/configure-swagger-ui.md
|
||||
- project-generation.md
|
||||
- alternatives.md
|
||||
- history-design-future.md
|
||||
- external-links.md
|
||||
- benchmarks.md
|
||||
- help-fastapi.md
|
||||
- newsletter.md
|
||||
- contributing.md
|
||||
- Resources:
|
||||
- resources/index.md
|
||||
- project-generation.md
|
||||
- external-links.md
|
||||
- newsletter.md
|
||||
- About:
|
||||
- about/index.md
|
||||
- alternatives.md
|
||||
- history-design-future.md
|
||||
- benchmarks.md
|
||||
- Help:
|
||||
- help/index.md
|
||||
- help-fastapi.md
|
||||
- contributing.md
|
||||
- release-notes.md
|
||||
markdown_extensions:
|
||||
toc:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1,59 @@
|
|||
from starlette.background import BackgroundTasks as BackgroundTasks # noqa
|
||||
from typing import Any, Callable
|
||||
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
from typing_extensions import Annotated, Doc, ParamSpec # type: ignore [attr-defined]
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
||||
class BackgroundTasks(StarletteBackgroundTasks):
|
||||
"""
|
||||
A collection of background tasks that will be called after a response has been
|
||||
sent to the client.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import BackgroundTasks, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def write_notification(email: str, message=""):
|
||||
with open("log.txt", mode="w") as email_file:
|
||||
content = f"notification for {email}: {message}"
|
||||
email_file.write(content)
|
||||
|
||||
|
||||
@app.post("/send-notification/{email}")
|
||||
async def send_notification(email: str, background_tasks: BackgroundTasks):
|
||||
background_tasks.add_task(write_notification, email, message="some notification")
|
||||
return {"message": "Notification sent in the background"}
|
||||
```
|
||||
"""
|
||||
|
||||
def add_task(
|
||||
self,
|
||||
func: Annotated[
|
||||
Callable[P, Any],
|
||||
Doc(
|
||||
"""
|
||||
The function to call after the response is sent.
|
||||
|
||||
It can be a regular `def` function or an `async def` function.
|
||||
"""
|
||||
),
|
||||
],
|
||||
*args: P.args,
|
||||
**kwargs: P.kwargs,
|
||||
) -> None:
|
||||
"""
|
||||
Add a function to be called in the background after the response is sent.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
|
||||
"""
|
||||
return super().add_task(func, *args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,14 @@
|
|||
from typing import Any, Callable, Dict, Iterable, Type, TypeVar, cast
|
||||
from typing import (
|
||||
Any,
|
||||
BinaryIO,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Optional,
|
||||
Type,
|
||||
TypeVar,
|
||||
cast,
|
||||
)
|
||||
|
||||
from fastapi._compat import (
|
||||
PYDANTIC_V2,
|
||||
|
|
@ -14,9 +24,120 @@ from starlette.datastructures import Headers as Headers # noqa: F401
|
|||
from starlette.datastructures import QueryParams as QueryParams # noqa: F401
|
||||
from starlette.datastructures import State as State # noqa: F401
|
||||
from starlette.datastructures import UploadFile as StarletteUploadFile
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class UploadFile(StarletteUploadFile):
|
||||
"""
|
||||
A file uploaded in a request.
|
||||
|
||||
Define it as a *path operation function* (or dependency) parameter.
|
||||
|
||||
If you are using a regular `def` function, you can use the `upload_file.file`
|
||||
attribute to access the raw standard Python file (blocking, not async), useful and
|
||||
needed for non-async code.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Request Files](https://fastapi.tiangolo.com/tutorial/request-files/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import FastAPI, File, UploadFile
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/files/")
|
||||
async def create_file(file: Annotated[bytes, File()]):
|
||||
return {"file_size": len(file)}
|
||||
|
||||
|
||||
@app.post("/uploadfile/")
|
||||
async def create_upload_file(file: UploadFile):
|
||||
return {"filename": file.filename}
|
||||
```
|
||||
"""
|
||||
|
||||
file: Annotated[
|
||||
BinaryIO,
|
||||
Doc("The standard Python file object (non-async)."),
|
||||
]
|
||||
filename: Annotated[Optional[str], Doc("The original file name.")]
|
||||
size: Annotated[Optional[int], Doc("The size of the file in bytes.")]
|
||||
headers: Annotated[Headers, Doc("The headers of the request.")]
|
||||
content_type: Annotated[
|
||||
Optional[str], Doc("The content type of the request, from the headers.")
|
||||
]
|
||||
|
||||
async def write(
|
||||
self,
|
||||
data: Annotated[
|
||||
bytes,
|
||||
Doc(
|
||||
"""
|
||||
The bytes to write to the file.
|
||||
"""
|
||||
),
|
||||
],
|
||||
) -> None:
|
||||
"""
|
||||
Write some bytes to the file.
|
||||
|
||||
You normally wouldn't use this from a file you read in a request.
|
||||
|
||||
To be awaitable, compatible with async, this is run in threadpool.
|
||||
"""
|
||||
return await super().write(data)
|
||||
|
||||
async def read(
|
||||
self,
|
||||
size: Annotated[
|
||||
int,
|
||||
Doc(
|
||||
"""
|
||||
The number of bytes to read from the file.
|
||||
"""
|
||||
),
|
||||
] = -1,
|
||||
) -> bytes:
|
||||
"""
|
||||
Read some bytes from the file.
|
||||
|
||||
To be awaitable, compatible with async, this is run in threadpool.
|
||||
"""
|
||||
return await super().read(size)
|
||||
|
||||
async def seek(
|
||||
self,
|
||||
offset: Annotated[
|
||||
int,
|
||||
Doc(
|
||||
"""
|
||||
The position in bytes to seek to in the file.
|
||||
"""
|
||||
),
|
||||
],
|
||||
) -> None:
|
||||
"""
|
||||
Move to a position in the file.
|
||||
|
||||
Any next read or write will be done from that position.
|
||||
|
||||
To be awaitable, compatible with async, this is run in threadpool.
|
||||
"""
|
||||
return await super().seek(offset)
|
||||
|
||||
async def close(self) -> None:
|
||||
"""
|
||||
Close the file.
|
||||
|
||||
To be awaitable, compatible with async, this is run in threadpool.
|
||||
"""
|
||||
return await super().close()
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls: Type["UploadFile"]) -> Iterable[Callable[..., Any]]:
|
||||
yield cls.validate
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ from fastapi._compat import (
|
|||
serialize_sequence_value,
|
||||
value_is_sequence,
|
||||
)
|
||||
from fastapi.background import BackgroundTasks
|
||||
from fastapi.concurrency import (
|
||||
AsyncExitStack,
|
||||
asynccontextmanager,
|
||||
|
|
@ -56,7 +57,7 @@ from fastapi.security.oauth2 import OAuth2, SecurityScopes
|
|||
from fastapi.security.open_id_connect_url import OpenIdConnect
|
||||
from fastapi.utils import create_response_field, get_path_param_names
|
||||
from pydantic.fields import FieldInfo
|
||||
from starlette.background import BackgroundTasks
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
from starlette.datastructures import FormData, Headers, QueryParams, UploadFile
|
||||
from starlette.requests import HTTPConnection, Request
|
||||
|
|
@ -305,7 +306,7 @@ def add_non_field_param_to_dependency(
|
|||
elif lenient_issubclass(type_annotation, Response):
|
||||
dependant.response_param_name = param_name
|
||||
return True
|
||||
elif lenient_issubclass(type_annotation, BackgroundTasks):
|
||||
elif lenient_issubclass(type_annotation, StarletteBackgroundTasks):
|
||||
dependant.background_tasks_param_name = param_name
|
||||
return True
|
||||
elif lenient_issubclass(type_annotation, SecurityScopes):
|
||||
|
|
@ -382,7 +383,14 @@ def analyze_param(
|
|||
|
||||
if lenient_issubclass(
|
||||
type_annotation,
|
||||
(Request, WebSocket, HTTPConnection, Response, BackgroundTasks, SecurityScopes),
|
||||
(
|
||||
Request,
|
||||
WebSocket,
|
||||
HTTPConnection,
|
||||
Response,
|
||||
StarletteBackgroundTasks,
|
||||
SecurityScopes,
|
||||
),
|
||||
):
|
||||
assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
|
||||
assert (
|
||||
|
|
@ -510,14 +518,14 @@ async def solve_dependencies(
|
|||
request: Union[Request, WebSocket],
|
||||
dependant: Dependant,
|
||||
body: Optional[Union[Dict[str, Any], FormData]] = None,
|
||||
background_tasks: Optional[BackgroundTasks] = None,
|
||||
background_tasks: Optional[StarletteBackgroundTasks] = None,
|
||||
response: Optional[Response] = None,
|
||||
dependency_overrides_provider: Optional[Any] = None,
|
||||
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
|
||||
) -> Tuple[
|
||||
Dict[str, Any],
|
||||
List[Any],
|
||||
Optional[BackgroundTasks],
|
||||
Optional[StarletteBackgroundTasks],
|
||||
Response,
|
||||
Dict[Tuple[Callable[..., Any], Tuple[str]], Any],
|
||||
]:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from pydantic import BaseModel
|
|||
from pydantic.color import Color
|
||||
from pydantic.networks import AnyUrl, NameEmail
|
||||
from pydantic.types import SecretBytes, SecretStr
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
from ._compat import PYDANTIC_V2, Url, _model_dump
|
||||
|
||||
|
|
@ -99,16 +100,107 @@ encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)
|
|||
|
||||
|
||||
def jsonable_encoder(
|
||||
obj: Any,
|
||||
include: Optional[IncEx] = None,
|
||||
exclude: Optional[IncEx] = None,
|
||||
by_alias: bool = True,
|
||||
exclude_unset: bool = False,
|
||||
exclude_defaults: bool = False,
|
||||
exclude_none: bool = False,
|
||||
custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None,
|
||||
sqlalchemy_safe: bool = True,
|
||||
obj: Annotated[
|
||||
Any,
|
||||
Doc(
|
||||
"""
|
||||
The input object to convert to JSON.
|
||||
"""
|
||||
),
|
||||
],
|
||||
include: Annotated[
|
||||
Optional[IncEx],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `include` parameter, passed to Pydantic models to set the
|
||||
fields to include.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
exclude: Annotated[
|
||||
Optional[IncEx],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `exclude` parameter, passed to Pydantic models to set the
|
||||
fields to exclude.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
by_alias: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `by_alias` parameter, passed to Pydantic models to define if
|
||||
the output should use the alias names (when provided) or the Python
|
||||
attribute names. In an API, if you set an alias, it's probably because you
|
||||
want to use it in the result, so you probably want to leave this set to
|
||||
`True`.
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
exclude_unset: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `exclude_unset` parameter, passed to Pydantic models to define
|
||||
if it should exclude from the output the fields that were not explicitly
|
||||
set (and that only had their default values).
|
||||
"""
|
||||
),
|
||||
] = False,
|
||||
exclude_defaults: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `exclude_defaults` parameter, passed to Pydantic models to define
|
||||
if it should exclude from the output the fields that had the same default
|
||||
value, even when they were explicitly set.
|
||||
"""
|
||||
),
|
||||
] = False,
|
||||
exclude_none: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `exclude_none` parameter, passed to Pydantic models to define
|
||||
if it should exclude from the output any fields that have a `None` value.
|
||||
"""
|
||||
),
|
||||
] = False,
|
||||
custom_encoder: Annotated[
|
||||
Optional[Dict[Any, Callable[[Any], Any]]],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `custom_encoder` parameter, passed to Pydantic models to define
|
||||
a custom encoder.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
sqlalchemy_safe: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Exclude from the output any fields that start with the name `_sa`.
|
||||
|
||||
This is mainly a hack for compatibility with SQLAlchemy objects, they
|
||||
store internal SQLAlchemy-specific state in attributes named with `_sa`,
|
||||
and those objects can't (and shouldn't be) serialized to JSON.
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
) -> Any:
|
||||
"""
|
||||
Convert any object to something that can be encoded in JSON.
|
||||
|
||||
This is used internally by FastAPI to make sure anything you return can be
|
||||
encoded as JSON before it is sent to the client.
|
||||
|
||||
You can also use it yourself, for example to convert objects before saving them
|
||||
in a database that supports only JSON.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for JSON Compatible Encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
|
||||
"""
|
||||
custom_encoder = custom_encoder or {}
|
||||
if custom_encoder:
|
||||
if type(obj) in custom_encoder:
|
||||
|
|
|
|||
|
|
@ -1,20 +1,141 @@
|
|||
from typing import Any, Dict, Optional, Sequence, Type
|
||||
from typing import Any, Dict, Optional, Sequence, Type, Union
|
||||
|
||||
from pydantic import BaseModel, create_model
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.exceptions import WebSocketException as WebSocketException # noqa: F401
|
||||
from starlette.exceptions import WebSocketException as StarletteWebSocketException
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class HTTPException(StarletteHTTPException):
|
||||
"""
|
||||
An HTTP exception you can raise in your own code to show errors to the client.
|
||||
|
||||
This is for client errors, invalid authentication, invalid data, etc. Not for server
|
||||
errors in your code.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {"foo": "The Foo Wrestlers"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
if item_id not in items:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return {"item": items[item_id]}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
status_code: int,
|
||||
detail: Any = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
status_code: Annotated[
|
||||
int,
|
||||
Doc(
|
||||
"""
|
||||
HTTP status code to send to the client.
|
||||
"""
|
||||
),
|
||||
],
|
||||
detail: Annotated[
|
||||
Any,
|
||||
Doc(
|
||||
"""
|
||||
Any data to be sent to the client in the `detail` key of the JSON
|
||||
response.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
headers: Annotated[
|
||||
Optional[Dict[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
Any headers to send to the client in the response.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
super().__init__(status_code=status_code, detail=detail, headers=headers)
|
||||
|
||||
|
||||
class WebSocketException(StarletteWebSocketException):
|
||||
"""
|
||||
A WebSocket exception you can raise in your own code to show errors to the client.
|
||||
|
||||
This is for client errors, invalid authentication, invalid data, etc. Not for server
|
||||
errors in your code.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import (
|
||||
Cookie,
|
||||
FastAPI,
|
||||
WebSocket,
|
||||
WebSocketException,
|
||||
status,
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.websocket("/items/{item_id}/ws")
|
||||
async def websocket_endpoint(
|
||||
*,
|
||||
websocket: WebSocket,
|
||||
session: Annotated[str | None, Cookie()] = None,
|
||||
item_id: str,
|
||||
):
|
||||
if session is None:
|
||||
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
|
||||
await websocket.accept()
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(f"Session cookie is: {session}")
|
||||
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
code: Annotated[
|
||||
int,
|
||||
Doc(
|
||||
"""
|
||||
A closing code from the
|
||||
[valid codes defined in the specification](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1).
|
||||
"""
|
||||
),
|
||||
],
|
||||
reason: Annotated[
|
||||
Union[str, None],
|
||||
Doc(
|
||||
"""
|
||||
The reason to close the WebSocket connection.
|
||||
|
||||
It is UTF-8-encoded data. The interpretation of the reason is up to the
|
||||
application, it is not specified by the WebSocket specification.
|
||||
|
||||
It could contain text that could be human-readable or interpretable
|
||||
by the client code, etc.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
super().__init__(code=code, reason=reason)
|
||||
|
||||
|
||||
RequestErrorModel: Type[BaseModel] = create_model("Request")
|
||||
WebSocketErrorModel: Type[BaseModel] = create_model("WebSocket")
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,18 @@ from typing import Any, Dict, Optional
|
|||
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from starlette.responses import HTMLResponse
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
swagger_ui_default_parameters = {
|
||||
swagger_ui_default_parameters: Annotated[
|
||||
Dict[str, Any],
|
||||
Doc(
|
||||
"""
|
||||
Default configurations for Swagger UI.
|
||||
|
||||
You can use it as a template to add any other configurations needed.
|
||||
"""
|
||||
),
|
||||
] = {
|
||||
"dom_id": "#swagger-ui",
|
||||
"layout": "BaseLayout",
|
||||
"deepLinking": True,
|
||||
|
|
@ -15,15 +25,91 @@ swagger_ui_default_parameters = {
|
|||
|
||||
def get_swagger_ui_html(
|
||||
*,
|
||||
openapi_url: str,
|
||||
title: str,
|
||||
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
|
||||
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
|
||||
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
oauth2_redirect_url: Optional[str] = None,
|
||||
init_oauth: Optional[Dict[str, Any]] = None,
|
||||
swagger_ui_parameters: Optional[Dict[str, Any]] = None,
|
||||
openapi_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The OpenAPI URL that Swagger UI should load and use.
|
||||
|
||||
This is normally done automatically by FastAPI using the default URL
|
||||
`/openapi.json`.
|
||||
"""
|
||||
),
|
||||
],
|
||||
title: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The HTML `<title>` content, normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
],
|
||||
swagger_js_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to use to load the Swagger UI JavaScript.
|
||||
|
||||
It is normally set to a CDN URL.
|
||||
"""
|
||||
),
|
||||
] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
|
||||
swagger_css_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to use to load the Swagger UI CSS.
|
||||
|
||||
It is normally set to a CDN URL.
|
||||
"""
|
||||
),
|
||||
] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
|
||||
swagger_favicon_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL of the favicon to use. It is normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
] = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
oauth2_redirect_url: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
init_oauth: Annotated[
|
||||
Optional[Dict[str, Any]],
|
||||
Doc(
|
||||
"""
|
||||
A dictionary with Swagger UI OAuth2 initialization configurations.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
swagger_ui_parameters: Annotated[
|
||||
Optional[Dict[str, Any]],
|
||||
Doc(
|
||||
"""
|
||||
Configuration parameters for Swagger UI.
|
||||
|
||||
It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
) -> HTMLResponse:
|
||||
"""
|
||||
Generate and return the HTML that loads Swagger UI for the interactive
|
||||
API docs (normally served at `/docs`).
|
||||
|
||||
You would only call this function yourself if you needed to override some parts,
|
||||
for example the URLs to use to load Swagger UI's JavaScript and CSS.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
|
||||
and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
|
||||
"""
|
||||
current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
|
||||
if swagger_ui_parameters:
|
||||
current_swagger_ui_parameters.update(swagger_ui_parameters)
|
||||
|
|
@ -74,12 +160,62 @@ def get_swagger_ui_html(
|
|||
|
||||
def get_redoc_html(
|
||||
*,
|
||||
openapi_url: str,
|
||||
title: str,
|
||||
redoc_js_url: str = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
|
||||
redoc_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
with_google_fonts: bool = True,
|
||||
openapi_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The OpenAPI URL that ReDoc should load and use.
|
||||
|
||||
This is normally done automatically by FastAPI using the default URL
|
||||
`/openapi.json`.
|
||||
"""
|
||||
),
|
||||
],
|
||||
title: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The HTML `<title>` content, normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
],
|
||||
redoc_js_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to use to load the ReDoc JavaScript.
|
||||
|
||||
It is normally set to a CDN URL.
|
||||
"""
|
||||
),
|
||||
] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
|
||||
redoc_favicon_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL of the favicon to use. It is normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
] = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
with_google_fonts: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
Load and use Google Fonts.
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
) -> HTMLResponse:
|
||||
"""
|
||||
Generate and return the HTML response that loads ReDoc for the alternative
|
||||
API docs (normally served at `/redoc`).
|
||||
|
||||
You would only call this function yourself if you needed to override some parts,
|
||||
for example the URLs to use to load ReDoc's JavaScript and CSS.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
|
||||
"""
|
||||
html = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
|
@ -118,6 +254,11 @@ def get_redoc_html(
|
|||
|
||||
|
||||
def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse:
|
||||
"""
|
||||
Generate the HTML response with the OAuth2 redirection for Swagger UI.
|
||||
|
||||
You normally don't need to use or change this.
|
||||
"""
|
||||
# copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
|
||||
html = """
|
||||
<!doctype html>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -21,12 +21,26 @@ except ImportError: # pragma: nocover
|
|||
|
||||
|
||||
class UJSONResponse(JSONResponse):
|
||||
"""
|
||||
JSON response using the high-performance 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/).
|
||||
"""
|
||||
|
||||
def render(self, content: Any) -> bytes:
|
||||
assert ujson is not None, "ujson must be installed to use UJSONResponse"
|
||||
return ujson.dumps(content, ensure_ascii=False).encode("utf-8")
|
||||
|
||||
|
||||
class ORJSONResponse(JSONResponse):
|
||||
"""
|
||||
JSON response using the high-performance 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/).
|
||||
"""
|
||||
|
||||
def render(self, content: Any) -> bytes:
|
||||
assert orjson is not None, "orjson must be installed to use ORJSONResponse"
|
||||
return orjson.dumps(
|
||||
|
|
|
|||
3483
fastapi/routing.py
3483
fastapi/routing.py
File diff suppressed because it is too large
Load Diff
|
|
@ -5,6 +5,7 @@ from fastapi.security.base import SecurityBase
|
|||
from starlette.exceptions import HTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class APIKeyBase(SecurityBase):
|
||||
|
|
@ -12,13 +13,83 @@ class APIKeyBase(SecurityBase):
|
|||
|
||||
|
||||
class APIKeyQuery(APIKeyBase):
|
||||
"""
|
||||
API key authentication using a query parameter.
|
||||
|
||||
This defines the name of the query parameter that should be provided in the request
|
||||
with the API key and integrates that into the OpenAPI documentation. It extracts
|
||||
the key value sent in the query parameter automatically and provides it as the
|
||||
dependency result. But it doesn't define how to send that API key to the client.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be a string containing the key value.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import APIKeyQuery
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
query_scheme = APIKeyQuery(name="api_key")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(api_key: str = Depends(query_scheme)):
|
||||
return {"api_key": api_key}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
name: Annotated[
|
||||
str,
|
||||
Doc("Query parameter name."),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the query parameter is not provided, `APIKeyQuery` will
|
||||
automatically cancel the request and sebd the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the query parameter is not
|
||||
available, instead of erroring out, the dependency result will be
|
||||
`None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in a query
|
||||
parameter or in an HTTP Bearer token).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model: APIKey = APIKey(
|
||||
**{"in": APIKeyIn.query}, # type: ignore[arg-type]
|
||||
|
|
@ -41,13 +112,79 @@ class APIKeyQuery(APIKeyBase):
|
|||
|
||||
|
||||
class APIKeyHeader(APIKeyBase):
|
||||
"""
|
||||
API key authentication using a header.
|
||||
|
||||
This defines the name of the header that should be provided in the request with
|
||||
the API key and integrates that into the OpenAPI documentation. It extracts
|
||||
the key value sent in the header automatically and provides it as the dependency
|
||||
result. But it doesn't define how to send that key to the client.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be a string containing the key value.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import APIKeyHeader
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
header_scheme = APIKeyHeader(name="x-key")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(key: str = Depends(header_scheme)):
|
||||
return {"key": key}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
name: Annotated[str, Doc("Header name.")],
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the header is not provided, `APIKeyHeader` will
|
||||
automatically cancel the request and send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the header is not available,
|
||||
instead of erroring out, the dependency result will be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in a header or
|
||||
in an HTTP Bearer token).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model: APIKey = APIKey(
|
||||
**{"in": APIKeyIn.header}, # type: ignore[arg-type]
|
||||
|
|
@ -70,13 +207,79 @@ class APIKeyHeader(APIKeyBase):
|
|||
|
||||
|
||||
class APIKeyCookie(APIKeyBase):
|
||||
"""
|
||||
API key authentication using a cookie.
|
||||
|
||||
This defines the name of the cookie that should be provided in the request with
|
||||
the API key and integrates that into the OpenAPI documentation. It extracts
|
||||
the key value sent in the cookie automatically and provides it as the dependency
|
||||
result. But it doesn't define how to set that cookie.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be a string containing the key value.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import APIKeyCookie
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
cookie_scheme = APIKeyCookie(name="session")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(session: str = Depends(cookie_scheme)):
|
||||
return {"session": session}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
name: Annotated[str, Doc("Cookie name.")],
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the cookie is not provided, `APIKeyCookie` will
|
||||
automatically cancel the request and send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the cookie is not available,
|
||||
instead of erroring out, the dependency result will be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in a cookie or
|
||||
in an HTTP Bearer token).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model: APIKey = APIKey(
|
||||
**{"in": APIKeyIn.cookie}, # type: ignore[arg-type]
|
||||
|
|
|
|||
|
|
@ -10,16 +10,60 @@ from fastapi.security.utils import get_authorization_scheme_param
|
|||
from pydantic import BaseModel
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class HTTPBasicCredentials(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
"""
|
||||
The HTTP Basic credendials given as the result of using `HTTPBasic` in a
|
||||
dependency.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
|
||||
"""
|
||||
|
||||
username: Annotated[str, Doc("The HTTP Basic username.")]
|
||||
password: Annotated[str, Doc("The HTTP Basic password.")]
|
||||
|
||||
|
||||
class HTTPAuthorizationCredentials(BaseModel):
|
||||
scheme: str
|
||||
credentials: str
|
||||
"""
|
||||
The HTTP authorization credentials in the result of using `HTTPBearer` or
|
||||
`HTTPDigest` in a dependency.
|
||||
|
||||
The HTTP authorization header value is split by the first space.
|
||||
|
||||
The first part is the `scheme`, the second part is the `credentials`.
|
||||
|
||||
For example, in an HTTP Bearer token scheme, the client will send a header
|
||||
like:
|
||||
|
||||
```
|
||||
Authorization: Bearer deadbeef12346
|
||||
```
|
||||
|
||||
In this case:
|
||||
|
||||
* `scheme` will have the value `"Bearer"`
|
||||
* `credentials` will have the value `"deadbeef12346"`
|
||||
"""
|
||||
|
||||
scheme: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The HTTP authorization scheme extracted from the header value.
|
||||
"""
|
||||
),
|
||||
]
|
||||
credentials: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The HTTP authorization credentials extracted from the header value.
|
||||
"""
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class HTTPBase(SecurityBase):
|
||||
|
|
@ -51,13 +95,89 @@ class HTTPBase(SecurityBase):
|
|||
|
||||
|
||||
class HTTPBasic(HTTPBase):
|
||||
"""
|
||||
HTTP Basic authentication.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be an `HTTPBasicCredentials` object containing the
|
||||
`username` and the `password`.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
|
||||
return {"username": credentials.username, "password": credentials.password}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
scheme_name: Optional[str] = None,
|
||||
realm: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
realm: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
HTTP Basic authentication realm.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the HTTP Basic authentication is not provided (a
|
||||
header), `HTTPBasic` will automatically cancel the request and send the
|
||||
client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Basic authentication
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in HTTP Basic
|
||||
authentication or in an HTTP Bearer token).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model = HTTPBaseModel(scheme="basic", description=description)
|
||||
self.scheme_name = scheme_name or self.__class__.__name__
|
||||
|
|
@ -98,13 +218,81 @@ class HTTPBasic(HTTPBase):
|
|||
|
||||
|
||||
class HTTPBearer(HTTPBase):
|
||||
"""
|
||||
HTTP Bearer token authentication.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be an `HTTPAuthorizationCredentials` object containing
|
||||
the `scheme` and the `credentials`.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBearer()
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(
|
||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
|
||||
):
|
||||
return {"scheme": credentials.scheme, "credentials": credentials.credentials}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
bearerFormat: Optional[str] = None,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the HTTP Bearer token not provided (in an
|
||||
`Authorization` header), `HTTPBearer` will automatically cancel the
|
||||
request and send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Bearer token
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in an HTTP
|
||||
Bearer token or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description)
|
||||
self.scheme_name = scheme_name or self.__class__.__name__
|
||||
|
|
@ -134,12 +322,79 @@ class HTTPBearer(HTTPBase):
|
|||
|
||||
|
||||
class HTTPDigest(HTTPBase):
|
||||
"""
|
||||
HTTP Digest authentication.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an instance object and use that object as the dependency in `Depends()`.
|
||||
|
||||
The dependency result will be an `HTTPAuthorizationCredentials` object containing
|
||||
the `scheme` and the `credentials`.
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPDigest()
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(
|
||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
|
||||
):
|
||||
return {"scheme": credentials.scheme, "credentials": credentials.credentials}
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if the HTTP Digest not provided, `HTTPDigest` will
|
||||
automatically cancel the request and send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Digest is not
|
||||
available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, in HTTP
|
||||
Digest or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model = HTTPBaseModel(scheme="digest", description=description)
|
||||
self.scheme_name = scheme_name or self.__class__.__name__
|
||||
|
|
|
|||
|
|
@ -10,51 +10,136 @@ from starlette.requests import Request
|
|||
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
||||
|
||||
# TODO: import from typing when deprecating Python 3.9
|
||||
from typing_extensions import Annotated
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class OAuth2PasswordRequestForm:
|
||||
"""
|
||||
This is a dependency class, use it like:
|
||||
This is a dependency class to collect the `username` and `password` as form data
|
||||
for an OAuth2 password flow.
|
||||
|
||||
@app.post("/login")
|
||||
def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
data = form_data.parse()
|
||||
print(data.username)
|
||||
print(data.password)
|
||||
for scope in data.scopes:
|
||||
print(scope)
|
||||
if data.client_id:
|
||||
print(data.client_id)
|
||||
if data.client_secret:
|
||||
print(data.client_secret)
|
||||
return data
|
||||
The OAuth2 specification dictates that for a password flow the data should be
|
||||
collected using form data (instead of JSON) and that it should have the specific
|
||||
fields `username` and `password`.
|
||||
|
||||
All the initialization parameters are extracted from the request.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
It creates the following Form request parameters in your endpoint:
|
||||
@app.post("/login")
|
||||
def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
|
||||
data = {}
|
||||
data["scopes"] = []
|
||||
for scope in form_data.scopes:
|
||||
data["scopes"].append(scope)
|
||||
if form_data.client_id:
|
||||
data["client_id"] = form_data.client_id
|
||||
if form_data.client_secret:
|
||||
data["client_secret"] = form_data.client_secret
|
||||
return data
|
||||
```
|
||||
|
||||
grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
|
||||
Nevertheless, this dependency class is permissive and allows not passing it. If you want to enforce it,
|
||||
use instead the OAuth2PasswordRequestFormStrict dependency.
|
||||
username: username string. The OAuth2 spec requires the exact field name "username".
|
||||
password: password string. The OAuth2 spec requires the exact field name "password".
|
||||
scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
|
||||
"items:read items:write users:read profile openid"
|
||||
client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
||||
using HTTP Basic auth, as: client_id:client_secret
|
||||
client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
||||
using HTTP Basic auth, as: client_id:client_secret
|
||||
Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
|
||||
You could have custom internal logic to separate it by colon caracters (`:`) or
|
||||
similar, and get the two parts `items` and `read`. Many applications do that to
|
||||
group and organize permisions, you could do it as well in your application, just
|
||||
know that that it is application specific, it's not part of the specification.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
grant_type: Annotated[Union[str, None], Form(pattern="password")] = None,
|
||||
username: Annotated[str, Form()],
|
||||
password: Annotated[str, Form()],
|
||||
scope: Annotated[str, Form()] = "",
|
||||
client_id: Annotated[Union[str, None], Form()] = None,
|
||||
client_secret: Annotated[Union[str, None], Form()] = None,
|
||||
grant_type: Annotated[
|
||||
Union[str, None],
|
||||
Form(pattern="password"),
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 spec says it is required and MUST be the fixed string
|
||||
"password". Nevertheless, this dependency class is permissive and
|
||||
allows not passing it. If you want to enforce it, use instead the
|
||||
`OAuth2PasswordRequestFormStrict` dependency.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
username: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
`username` string. The OAuth2 spec requires the exact field name
|
||||
`username`.
|
||||
"""
|
||||
),
|
||||
],
|
||||
password: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
`password` string. The OAuth2 spec requires the exact field name
|
||||
`password".
|
||||
"""
|
||||
),
|
||||
],
|
||||
scope: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
A single string with actually several scopes separated by spaces. Each
|
||||
scope is also a string.
|
||||
|
||||
For example, a single string with:
|
||||
|
||||
```python
|
||||
"items:read items:write users:read profile openid"
|
||||
````
|
||||
|
||||
would represent the scopes:
|
||||
|
||||
* `items:read`
|
||||
* `items:write`
|
||||
* `users:read`
|
||||
* `profile`
|
||||
* `openid`
|
||||
"""
|
||||
),
|
||||
] = "",
|
||||
client_id: Annotated[
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
If there's a `client_id`, it can be sent as part of the form fields.
|
||||
But the OAuth2 specification recommends sending the `client_id` and
|
||||
`client_secret` (if any) using HTTP Basic auth.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
client_secret: Annotated[
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
If there's a `client_password` (and a `client_id`), they can be sent
|
||||
as part of the form fields. But the OAuth2 specification recommends
|
||||
sending the `client_id` and `client_secret` (if any) using HTTP Basic
|
||||
auth.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
):
|
||||
self.grant_type = grant_type
|
||||
self.username = username
|
||||
|
|
@ -66,23 +151,54 @@ class OAuth2PasswordRequestForm:
|
|||
|
||||
class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
||||
"""
|
||||
This is a dependency class, use it like:
|
||||
This is a dependency class to collect the `username` and `password` as form data
|
||||
for an OAuth2 password flow.
|
||||
|
||||
@app.post("/login")
|
||||
def login(form_data: OAuth2PasswordRequestFormStrict = Depends()):
|
||||
data = form_data.parse()
|
||||
print(data.username)
|
||||
print(data.password)
|
||||
for scope in data.scopes:
|
||||
print(scope)
|
||||
if data.client_id:
|
||||
print(data.client_id)
|
||||
if data.client_secret:
|
||||
print(data.client_secret)
|
||||
return data
|
||||
The OAuth2 specification dictates that for a password flow the data should be
|
||||
collected using form data (instead of JSON) and that it should have the specific
|
||||
fields `username` and `password`.
|
||||
|
||||
All the initialization parameters are extracted from the request.
|
||||
|
||||
The only difference between `OAuth2PasswordRequestFormStrict` and
|
||||
`OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the
|
||||
client to send the form field `grant_type` with the value `"password"`, which
|
||||
is required in the OAuth2 specification (it seems that for no particular reason),
|
||||
while for `OAuth2PasswordRequestForm` `grant_type` is optional.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
It creates the following Form request parameters in your endpoint:
|
||||
@app.post("/login")
|
||||
def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]):
|
||||
data = {}
|
||||
data["scopes"] = []
|
||||
for scope in form_data.scopes:
|
||||
data["scopes"].append(scope)
|
||||
if form_data.client_id:
|
||||
data["client_id"] = form_data.client_id
|
||||
if form_data.client_secret:
|
||||
data["client_secret"] = form_data.client_secret
|
||||
return data
|
||||
```
|
||||
|
||||
Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
|
||||
You could have custom internal logic to separate it by colon caracters (`:`) or
|
||||
similar, and get the two parts `items` and `read`. Many applications do that to
|
||||
group and organize permisions, you could do it as well in your application, just
|
||||
know that that it is application specific, it's not part of the specification.
|
||||
|
||||
|
||||
grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
|
||||
This dependency is strict about it. If you want to be permissive, use instead the
|
||||
|
|
@ -99,12 +215,85 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
grant_type: Annotated[str, Form(pattern="password")],
|
||||
username: Annotated[str, Form()],
|
||||
password: Annotated[str, Form()],
|
||||
scope: Annotated[str, Form()] = "",
|
||||
client_id: Annotated[Union[str, None], Form()] = None,
|
||||
client_secret: Annotated[Union[str, None], Form()] = None,
|
||||
grant_type: Annotated[
|
||||
str,
|
||||
Form(pattern="password"),
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 spec says it is required and MUST be the fixed string
|
||||
"password". This dependency is strict about it. If you want to be
|
||||
permissive, use instead the `OAuth2PasswordRequestForm` dependency
|
||||
class.
|
||||
"""
|
||||
),
|
||||
],
|
||||
username: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
`username` string. The OAuth2 spec requires the exact field name
|
||||
`username`.
|
||||
"""
|
||||
),
|
||||
],
|
||||
password: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
`password` string. The OAuth2 spec requires the exact field name
|
||||
`password".
|
||||
"""
|
||||
),
|
||||
],
|
||||
scope: Annotated[
|
||||
str,
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
A single string with actually several scopes separated by spaces. Each
|
||||
scope is also a string.
|
||||
|
||||
For example, a single string with:
|
||||
|
||||
```python
|
||||
"items:read items:write users:read profile openid"
|
||||
````
|
||||
|
||||
would represent the scopes:
|
||||
|
||||
* `items:read`
|
||||
* `items:write`
|
||||
* `users:read`
|
||||
* `profile`
|
||||
* `openid`
|
||||
"""
|
||||
),
|
||||
] = "",
|
||||
client_id: Annotated[
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
If there's a `client_id`, it can be sent as part of the form fields.
|
||||
But the OAuth2 specification recommends sending the `client_id` and
|
||||
`client_secret` (if any) using HTTP Basic auth.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
client_secret: Annotated[
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
If there's a `client_password` (and a `client_id`), they can be sent
|
||||
as part of the form fields. But the OAuth2 specification recommends
|
||||
sending the `client_id` and `client_secret` (if any) using HTTP Basic
|
||||
auth.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
):
|
||||
super().__init__(
|
||||
grant_type=grant_type,
|
||||
|
|
@ -117,13 +306,69 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
|||
|
||||
|
||||
class OAuth2(SecurityBase):
|
||||
"""
|
||||
This is the base class for OAuth2 authentication, an instance of it would be used
|
||||
as a dependency. All other OAuth2 classes inherit from it and customize it for
|
||||
each OAuth2 flow.
|
||||
|
||||
You normally would not create a new class inheriting from it but use one of the
|
||||
existing subclasses, and maybe compose them if you want to support multiple flows.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
flows: Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]] = OAuthFlowsModel(),
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
flows: Annotated[
|
||||
Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]],
|
||||
Doc(
|
||||
"""
|
||||
The dictionary of OAuth2 flows.
|
||||
"""
|
||||
),
|
||||
] = OAuthFlowsModel(),
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if no HTTP Auhtorization header is provided, required for
|
||||
OAuth2 authentication, it will automatically cancel the request and
|
||||
send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Authorization header
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, with OAuth2
|
||||
or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model = OAuth2Model(
|
||||
flows=cast(OAuthFlowsModel, flows), description=description
|
||||
|
|
@ -144,13 +389,74 @@ class OAuth2(SecurityBase):
|
|||
|
||||
|
||||
class OAuth2PasswordBearer(OAuth2):
|
||||
"""
|
||||
OAuth2 flow for authentication using a bearer token obtained with a password.
|
||||
An instance of it would be used as a dependency.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tokenUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
tokenUrl: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to obtain the OAuth2 token. This would be the *path operation*
|
||||
that has `OAuth2PasswordRequestForm` as a dependency.
|
||||
"""
|
||||
),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
scopes: Annotated[
|
||||
Optional[Dict[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 scopes that would be required by the *path operations* that
|
||||
use this dependency.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if no HTTP Auhtorization header is provided, required for
|
||||
OAuth2 authentication, it will automatically cancel the request and
|
||||
send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Authorization header
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, with OAuth2
|
||||
or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
if not scopes:
|
||||
scopes = {}
|
||||
|
|
@ -180,15 +486,79 @@ class OAuth2PasswordBearer(OAuth2):
|
|||
|
||||
|
||||
class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
"""
|
||||
OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code
|
||||
flow. An instance of it would be used as a dependency.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
authorizationUrl: str,
|
||||
tokenUrl: str,
|
||||
refreshUrl: Optional[str] = None,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
tokenUrl: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to obtain the OAuth2 token.
|
||||
"""
|
||||
),
|
||||
],
|
||||
refreshUrl: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The URL to refresh the token and obtain a new one.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
scopes: Annotated[
|
||||
Optional[Dict[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 scopes that would be required by the *path operations* that
|
||||
use this dependency.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if no HTTP Auhtorization header is provided, required for
|
||||
OAuth2 authentication, it will automatically cancel the request and
|
||||
send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Authorization header
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, with OAuth2
|
||||
or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
if not scopes:
|
||||
scopes = {}
|
||||
|
|
@ -226,6 +596,45 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
|||
|
||||
|
||||
class SecurityScopes:
|
||||
def __init__(self, scopes: Optional[List[str]] = None):
|
||||
self.scopes = scopes or []
|
||||
self.scope_str = " ".join(self.scopes)
|
||||
"""
|
||||
This is a special class that you can define in a parameter in a dependency to
|
||||
obtain the OAuth2 scopes required by all the dependencies in the same chain.
|
||||
|
||||
This way, multiple dependencies can have different scopes, even when used in the
|
||||
same *path operation*. And with this, you can access all the scopes required in
|
||||
all those dependencies in a single place.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scopes: Annotated[
|
||||
Optional[List[str]],
|
||||
Doc(
|
||||
"""
|
||||
This will be filled by FastAPI.
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
):
|
||||
self.scopes: Annotated[
|
||||
List[str],
|
||||
Doc(
|
||||
"""
|
||||
The list of all the scopes required by dependencies.
|
||||
"""
|
||||
),
|
||||
] = (
|
||||
scopes or []
|
||||
)
|
||||
self.scope_str: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
All the scopes required by all the dependencies in a single string
|
||||
separated by spaces, as defined in the OAuth2 specification.
|
||||
"""
|
||||
),
|
||||
] = " ".join(self.scopes)
|
||||
|
|
|
|||
|
|
@ -5,16 +5,66 @@ from fastapi.security.base import SecurityBase
|
|||
from starlette.exceptions import HTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
|
||||
|
||||
class OpenIdConnect(SecurityBase):
|
||||
"""
|
||||
OpenID Connect authentication class. An instance of it would be used as a
|
||||
dependency.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
openIdConnectUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
openIdConnectUrl: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The OpenID Connect URL.
|
||||
"""
|
||||
),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
|
||||
It will be included in the generated OpenAPI (e.g. visible at `/docs`).
|
||||
"""
|
||||
),
|
||||
] = None,
|
||||
auto_error: Annotated[
|
||||
bool,
|
||||
Doc(
|
||||
"""
|
||||
By default, if no HTTP Auhtorization header is provided, required for
|
||||
OpenID Connect authentication, it will automatically cancel the request
|
||||
and send the client an error.
|
||||
|
||||
If `auto_error` is set to `False`, when the HTTP Authorization header
|
||||
is not available, instead of erroring out, the dependency result will
|
||||
be `None`.
|
||||
|
||||
This is useful when you want to have optional authentication.
|
||||
|
||||
It is also useful when you want to have authentication that can be
|
||||
provided in one of multiple optional ways (for example, with OpenID
|
||||
Connect or in a cookie).
|
||||
"""
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
self.model = OpenIdConnectModel(
|
||||
openIdConnectUrl=openIdConnectUrl, description=description
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ classifiers = [
|
|||
dependencies = [
|
||||
"starlette>=0.27.0,<0.28.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.5.0",
|
||||
"typing-extensions>=4.8.0",
|
||||
# TODO: remove this pin after upgrading Starlette 0.31.1
|
||||
"anyio>=3.7.1,<4.0.0",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
# For mkdocstrings and tests
|
||||
httpx >=0.23.0,<0.25.0
|
||||
black == 23.3.0
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
-e .
|
||||
-r requirements-docs-tests.txt
|
||||
mkdocs-material==9.1.21
|
||||
mdx-include >=1.4.1,<2.0.0
|
||||
mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0
|
||||
|
|
@ -12,3 +13,5 @@ jieba==0.42.1
|
|||
pillow==9.5.0
|
||||
# For image processing by Material for MkDocs
|
||||
cairosvg==2.7.0
|
||||
mkdocstrings[python]==0.23.0
|
||||
griffe-typingdoc==0.2.2
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
-e .
|
||||
-r requirements-docs-tests.txt
|
||||
pydantic-settings >=2.0.0
|
||||
pytest >=7.1.3,<8.0.0
|
||||
coverage[toml] >= 6.5.0,< 8.0
|
||||
mypy ==1.4.1
|
||||
ruff ==0.0.275
|
||||
black == 23.3.0
|
||||
httpx >=0.23.0,<0.25.0
|
||||
email_validator >=1.1.1,<3.0.0
|
||||
dirty-equals ==0.6.0
|
||||
# TODO: once removing databases from tutorial, upgrade SQLAlchemy
|
||||
|
|
|
|||
|
|
@ -153,17 +153,21 @@ index_sponsors_template = """
|
|||
def generate_readme_content() -> str:
|
||||
en_index = en_docs_path / "docs" / "index.md"
|
||||
content = en_index.read_text("utf-8")
|
||||
match_pre = re.search(r"</style>\n\n", content)
|
||||
match_start = re.search(r"<!-- sponsors -->", content)
|
||||
match_end = re.search(r"<!-- /sponsors -->", content)
|
||||
sponsors_data_path = en_docs_path / "data" / "sponsors.yml"
|
||||
sponsors = mkdocs.utils.yaml_load(sponsors_data_path.read_text(encoding="utf-8"))
|
||||
if not (match_start and match_end):
|
||||
raise RuntimeError("Couldn't auto-generate sponsors section")
|
||||
if not match_pre:
|
||||
raise RuntimeError("Couldn't find pre section (<style>) in index.md")
|
||||
frontmatter_end = match_pre.end()
|
||||
pre_end = match_start.end()
|
||||
post_start = match_end.start()
|
||||
template = Template(index_sponsors_template)
|
||||
message = template.render(sponsors=sponsors)
|
||||
pre_content = content[:pre_end]
|
||||
pre_content = content[frontmatter_end:pre_end]
|
||||
post_content = content[post_start:]
|
||||
new_content = pre_content + message + post_content
|
||||
return new_content
|
||||
|
|
@ -286,7 +290,6 @@ def update_config() -> None:
|
|||
else:
|
||||
use_name = alternate_dict[url]
|
||||
new_alternate.append({"link": url, "name": use_name})
|
||||
config["nav"][1] = {"Languages": languages}
|
||||
config["extra"]["alternate"] = new_alternate
|
||||
en_config_path.write_text(
|
||||
yaml.dump(config, sort_keys=False, width=200, allow_unicode=True),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ from mkdocs.structure.files import File, Files
|
|||
from mkdocs.structure.nav import Link, Navigation, Section
|
||||
from mkdocs.structure.pages import Page
|
||||
|
||||
non_traslated_sections = [
|
||||
"reference/",
|
||||
"release-notes.md",
|
||||
]
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_missing_translation_content(docs_dir: str) -> str:
|
||||
|
|
@ -123,6 +128,9 @@ def on_page_markdown(
|
|||
markdown: str, *, page: Page, config: MkDocsConfig, files: Files
|
||||
) -> str:
|
||||
if isinstance(page.file, EnFile):
|
||||
for excluded_section in non_traslated_sections:
|
||||
if page.file.src_path.startswith(excluded_section):
|
||||
return markdown
|
||||
missing_translation_content = get_missing_translation_content(config.docs_dir)
|
||||
header = ""
|
||||
body = markdown
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import io
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
|
@ -52,3 +53,20 @@ def test_upload_file_is_closed(tmp_path: Path):
|
|||
|
||||
assert testing_file_store
|
||||
assert testing_file_store[0].file.closed
|
||||
|
||||
|
||||
# For UploadFile coverage, segments copied from Starlette tests
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_upload_file():
|
||||
stream = io.BytesIO(b"data")
|
||||
file = UploadFile(filename="file", file=stream, size=4)
|
||||
assert await file.read() == b"data"
|
||||
assert file.size == 4
|
||||
await file.write(b" and more data!")
|
||||
assert await file.read() == b""
|
||||
assert file.size == 19
|
||||
await file.seek(0)
|
||||
assert await file.read() == b"data and more data!"
|
||||
await file.close()
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ def state() -> State:
|
|||
return State()
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings(
|
||||
r"ignore:\s*on_event is deprecated, use lifespan event handlers instead.*:DeprecationWarning"
|
||||
)
|
||||
def test_router_events(state: State) -> None:
|
||||
app = FastAPI()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.async_sql_databases.tutorial001 import app
|
||||
|
||||
from ...utils import needs_pydanticv1
|
||||
|
||||
|
||||
@pytest.fixture(name="app", scope="module")
|
||||
def get_app():
|
||||
with pytest.warns(DeprecationWarning):
|
||||
from docs_src.async_sql_databases.tutorial001 import app
|
||||
yield app
|
||||
|
||||
|
||||
# TODO: pv2 add version with Pydantic v2
|
||||
@needs_pydanticv1
|
||||
def test_create_read():
|
||||
def test_create_read(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
note = {"text": "Foo bar", "completed": False}
|
||||
response = client.post("/notes/", json=note)
|
||||
|
|
@ -21,7 +28,7 @@ def test_create_read():
|
|||
assert data in response.json()
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
def test_openapi_schema(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.events.tutorial001 import app
|
||||
|
||||
@pytest.fixture(name="app", scope="module")
|
||||
def get_app():
|
||||
with pytest.warns(DeprecationWarning):
|
||||
from docs_src.events.tutorial001 import app
|
||||
yield app
|
||||
|
||||
|
||||
def test_events():
|
||||
def test_events(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
def test_openapi_schema(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.events.tutorial002 import app
|
||||
|
||||
@pytest.fixture(name="app", scope="module")
|
||||
def get_app():
|
||||
with pytest.warns(DeprecationWarning):
|
||||
from docs_src.events.tutorial002 import app
|
||||
yield app
|
||||
|
||||
|
||||
def test_events():
|
||||
def test_events(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
|
|
@ -12,7 +19,7 @@ def test_events():
|
|||
assert "Application shutdown" in log.read()
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
def test_openapi_schema(app: FastAPI):
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from docs_src.app_testing.tutorial003 import test_read_items
|
||||
import pytest
|
||||
|
||||
|
||||
def test_main():
|
||||
with pytest.warns(DeprecationWarning):
|
||||
from docs_src.app_testing.tutorial003 import test_read_items
|
||||
test_read_items()
|
||||
|
|
|
|||
Loading…
Reference in New Issue