Showing preview only (856K chars total). Download the full file or copy to clipboard to get everything.
Repository: Kludex/starlette
Branch: main
Commit: 9ee951980bae
Files: 120
Total size: 817.8 KB
Directory structure:
gitextract_gn9w6fxg/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-issue.md
│ │ └── config.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── CITATION.cff
├── LICENSE.md
├── README.md
├── docs/
│ ├── CNAME
│ ├── applications.md
│ ├── authentication.md
│ ├── background.md
│ ├── config.md
│ ├── contributing.md
│ ├── css/
│ │ └── custom.css
│ ├── database.md
│ ├── endpoints.md
│ ├── exceptions.md
│ ├── graphql.md
│ ├── index.md
│ ├── js/
│ │ └── custom.js
│ ├── lifespan.md
│ ├── middleware.md
│ ├── overrides/
│ │ ├── main.html
│ │ └── partials/
│ │ └── toc-item.html
│ ├── release-notes.md
│ ├── requests.md
│ ├── responses.md
│ ├── routing.md
│ ├── schemas.md
│ ├── server-push.md
│ ├── staticfiles.md
│ ├── templates.md
│ ├── testclient.md
│ ├── third-party-packages.md
│ ├── threadpool.md
│ └── websockets.md
├── mkdocs.yml
├── pyproject.toml
├── scripts/
│ ├── README.md
│ ├── build
│ ├── check
│ ├── coverage
│ ├── docs
│ ├── install
│ ├── lint
│ ├── sync-version
│ └── test
├── starlette/
│ ├── __init__.py
│ ├── _exception_handler.py
│ ├── _utils.py
│ ├── applications.py
│ ├── authentication.py
│ ├── background.py
│ ├── concurrency.py
│ ├── config.py
│ ├── convertors.py
│ ├── datastructures.py
│ ├── endpoints.py
│ ├── exceptions.py
│ ├── formparsers.py
│ ├── middleware/
│ │ ├── __init__.py
│ │ ├── authentication.py
│ │ ├── base.py
│ │ ├── cors.py
│ │ ├── errors.py
│ │ ├── exceptions.py
│ │ ├── gzip.py
│ │ ├── httpsredirect.py
│ │ ├── sessions.py
│ │ ├── trustedhost.py
│ │ └── wsgi.py
│ ├── py.typed
│ ├── requests.py
│ ├── responses.py
│ ├── routing.py
│ ├── schemas.py
│ ├── staticfiles.py
│ ├── status.py
│ ├── templating.py
│ ├── testclient.py
│ ├── types.py
│ └── websockets.py
└── tests/
├── __init__.py
├── conftest.py
├── middleware/
│ ├── __init__.py
│ ├── test_base.py
│ ├── test_cors.py
│ ├── test_errors.py
│ ├── test_gzip.py
│ ├── test_https_redirect.py
│ ├── test_middleware.py
│ ├── test_session.py
│ ├── test_trusted_host.py
│ └── test_wsgi.py
├── statics/
│ └── example.txt
├── test__utils.py
├── test_applications.py
├── test_authentication.py
├── test_background.py
├── test_concurrency.py
├── test_config.py
├── test_convertors.py
├── test_datastructures.py
├── test_endpoints.py
├── test_exceptions.py
├── test_formparsers.py
├── test_requests.py
├── test_responses.py
├── test_routing.py
├── test_schemas.py
├── test_staticfiles.py
├── test_status.py
├── test_templates.py
├── test_testclient.py
├── test_websockets.py
└── types.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: Kludex
================================================
FILE: .github/ISSUE_TEMPLATE/1-issue.md
================================================
---
name: Issue
about: Please only raise an issue if you've been advised to do so after discussion. Thanks! 🙏
---
The starting point for issues should usually be a discussion...
https://github.com/Kludex/starlette/discussions
Possible bugs may be raised as a "Potential Issue" discussion, feature requests may be raised as an "Ideas" discussion. We can then determine if the discussion needs to be escalated into an "Issue" or not.
This will help us ensure that the "Issues" list properly reflects ongoing or needed work on the project.
---
- [ ] Initially raised as discussion #...
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/Kludex/starlette/discussions
about: >
The "Discussions" forum is where you want to start. 💖
- name: Chat
url: https://discord.gg/SWU73HffbV
about: >
Our community chat forum.
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
groups:
python-packages:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: monthly
groups:
github-actions:
patterns:
- "*"
================================================
FILE: .github/pull_request_template.md
================================================
<!-- Thanks for contributing to Starlette! 💚
Given this is a project maintained by volunteers, please read this template to not waste your time, or ours! 😁 -->
# Summary
<!-- Write a small summary about what is happening here. -->
# Checklist
- [ ] I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
- [ ] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
- [ ] I've updated the documentation accordingly.
================================================
FILE: .github/workflows/main.yml
================================================
---
name: Test Suite
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
tests:
name: "Python ${{ matrix.python-version }}"
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Install dependencies
run: scripts/install
- name: Run linting checks
run: scripts/check
if: ${{ matrix.python-version != '3.14' }}
- name: "Build package & docs"
run: scripts/build
- name: "Run tests"
run: scripts/test
- name: "Enforce coverage"
run: scripts/coverage
# https://github.com/marketplace/actions/alls-green#why
check:
if: always()
needs: [tests]
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}
================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish
on:
push:
tags:
- "*"
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
with:
python-version: "3.11"
enable-cache: true
- name: Install dependencies
run: scripts/install
- name: Build package & docs
run: scripts/build
- name: Upload package distributions
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: package-distributions
path: dist/
- name: Upload documentation
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: documentation
path: site/
pypi-publish:
runs-on: ubuntu-latest
needs: build
if: success() && startsWith(github.ref, 'refs/tags/')
permissions:
id-token: write
environment:
name: pypi
url: https://pypi.org/project/starlette
steps:
- name: Download artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
docs-publish:
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Configure GitHub Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
- name: Download artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: documentation
path: site/
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: site
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
id: deployment
docs-cloudflare:
runs-on: ubuntu-latest
needs: build
environment:
name: cloudflare
url: https://starlette.dev
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: documentation
path: site/
- uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: >
pages deploy ./site
--project-name starlette
--commit-hash ${{ github.sha }}
--branch main
================================================
FILE: .gitignore
================================================
*.pyc
test.db
.coverage
.pytest_cache/
.mypy_cache/
__pycache__/
htmlcov/
site/
*.egg-info/
venv*/
.venv/
.python-version
build/
dist/
================================================
FILE: CITATION.cff
================================================
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: Starlette
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Marcelo
family-names: Trylesinski
email: marcelotryle@gmail.com
- given-names: Tom
family-names: Christie
email: tom@tomchristie.com
repository-code: "https://github.com/Kludex/starlette"
url: "https://starlette.dev/"
abstract: Starlette is an ASGI web framework for Python.
keywords:
- asgi
- starlette
license: BSD-3-Clause
================================================
FILE: LICENSE.md
================================================
Copyright © 2018, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/starlette_dark.svg" width="420px">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/starlette.svg" width="420px">
<img alt="starlette-logo" src="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/starlette.svg">
</picture>
</p>
<p align="center">
<em>✨ The little ASGI framework that shines. ✨</em>
</p>
---
[](https://github.com/Kludex/starlette/actions)
[](https://pypi.python.org/pypi/starlette)
[](https://pypi.org/project/starlette)
[](https://discord.gg/RxKUF5JuHs)
---
**Documentation**: <a href="https://starlette.dev/" target="_blank">https://starlette.dev</a>
**Source Code**: <a href="https://github.com/Kludex/starlette" target="_blank">https://github.com/Kludex/starlette</a>
---
# Starlette
Starlette is a lightweight [ASGI][asgi] framework/toolkit,
which is ideal for building async web services in Python.
It is production-ready, and gives you the following:
* A lightweight, low-complexity HTTP web framework.
* WebSocket support.
* In-process background tasks.
* Startup and shutdown events.
* Test client built on `httpx`.
* CORS, GZip, Static Files, Streaming responses.
* Session and Cookie support.
* 100% test coverage.
* 100% type annotated codebase.
* Few hard dependencies.
* Compatible with `asyncio` and `trio` backends.
* Great overall performance [against independent benchmarks][techempower].
## Installation
```shell
$ pip install starlette
```
You'll also want to install an ASGI server, such as [uvicorn](https://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://hypercorn.readthedocs.io/en/latest/).
```shell
$ pip install uvicorn
```
## Example
```python title="main.py"
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request):
return JSONResponse({'hello': 'world'})
routes = [
Route("/", endpoint=homepage)
]
app = Starlette(debug=True, routes=routes)
```
Then run the application using Uvicorn:
```shell
$ uvicorn main:app
```
## Dependencies
Starlette only requires `anyio`, and the following are optional:
* [`httpx`][httpx] - Required if you want to use the `TestClient`.
* [`jinja2`][jinja2] - Required if you want to use `Jinja2Templates`.
* [`python-multipart`][python-multipart] - Required if you want to support form parsing, with `request.form()`.
* [`itsdangerous`][itsdangerous] - Required for `SessionMiddleware` support.
* [`pyyaml`][pyyaml] - Required for `SchemaGenerator` support.
You can install all of these with `pip install starlette[full]`.
## Framework or Toolkit
Starlette is designed to be used either as a complete framework, or as
an ASGI toolkit. You can use any of its components independently.
```python
from starlette.responses import PlainTextResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = PlainTextResponse('Hello, world!')
await response(scope, receive, send)
```
Run the `app` application in `example.py`:
```shell
$ uvicorn example:app
INFO: Started server process [11509]
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Run uvicorn with `--reload` to enable auto-reloading on code changes.
## Modularity
The modularity that Starlette is designed on promotes building re-usable
components that can be shared between any ASGI framework. This should enable
an ecosystem of shared middleware and mountable applications.
The clean API separation also means it's easier to understand each component
in isolation.
---
<p align="center"><i>Starlette is <a href="https://github.com/Kludex/starlette/blob/main/LICENSE.md">BSD licensed</a> code.<br/>Designed & crafted with care.</i></br>— ⭐️ —</p>
[asgi]: https://asgi.readthedocs.io/en/latest/
[httpx]: https://www.python-httpx.org/
[jinja2]: https://jinja.palletsprojects.com/
[python-multipart]: https://multipart.fastapiexpert.com/
[itsdangerous]: https://itsdangerous.palletsprojects.com/
[sqlalchemy]: https://www.sqlalchemy.org
[pyyaml]: https://pyyaml.org/wiki/PyYAMLDocumentation
[techempower]: https://www.techempower.com/benchmarks/#hw=ph&test=fortune&l=zijzen-sf
================================================
FILE: docs/CNAME
================================================
www.starlette.io
================================================
FILE: docs/applications.md
================================================
??? abstract "API Reference"
::: starlette.applications.Starlette
options:
parameter_headings: false
show_root_heading: true
heading_level: 3
filters:
- "__init__"
Starlette includes an application class `Starlette` that nicely ties together all of
its other functionality.
```python
from contextlib import asynccontextmanager
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route, Mount, WebSocketRoute
from starlette.staticfiles import StaticFiles
def homepage(request):
return PlainTextResponse('Hello, world!')
def user_me(request):
username = "John Doe"
return PlainTextResponse('Hello, %s!' % username)
def user(request):
username = request.path_params['username']
return PlainTextResponse('Hello, %s!' % username)
async def websocket_endpoint(websocket):
await websocket.accept()
await websocket.send_text('Hello, websocket!')
await websocket.close()
@asynccontextmanager
async def lifespan(app):
print('Startup')
yield
print('Shutdown')
routes = [
Route('/', homepage),
Route('/user/me', user_me),
Route('/user/{username}', user),
WebSocketRoute('/ws', websocket_endpoint),
Mount('/static', StaticFiles(directory="static")),
]
app = Starlette(debug=True, routes=routes, lifespan=lifespan)
```
### Storing state on the app instance
You can store arbitrary extra state on the application instance, using the
generic `app.state` attribute.
For example:
```python
app.state.ADMIN_EMAIL = 'admin@example.org'
```
### Accessing the app instance
Where a `request` is available (i.e. endpoints and middleware), the app is available on `request.app`.
================================================
FILE: docs/authentication.md
================================================
Starlette offers a simple but powerful interface for handling authentication
and permissions. Once you've installed `AuthenticationMiddleware` with an
appropriate authentication backend the `request.user` and `request.auth`
interfaces will be available in your endpoints.
```python
from starlette.applications import Starlette
from starlette.authentication import (
AuthCredentials, AuthenticationBackend, AuthenticationError, SimpleUser
)
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.responses import PlainTextResponse
from starlette.routing import Route
import base64
import binascii
class BasicAuthBackend(AuthenticationBackend):
async def authenticate(self, conn):
if "Authorization" not in conn.headers:
return
auth = conn.headers["Authorization"]
try:
scheme, credentials = auth.split()
if scheme.lower() != 'basic':
return
decoded = base64.b64decode(credentials).decode("ascii")
except (ValueError, UnicodeDecodeError, binascii.Error) as exc:
raise AuthenticationError('Invalid basic auth credentials')
username, _, password = decoded.partition(":")
# TODO: You'd want to verify the username and password here.
return AuthCredentials(["authenticated"]), SimpleUser(username)
async def homepage(request):
if request.user.is_authenticated:
return PlainTextResponse('Hello, ' + request.user.display_name)
return PlainTextResponse('Hello, you')
routes = [
Route("/", endpoint=homepage)
]
middleware = [
Middleware(AuthenticationMiddleware, backend=BasicAuthBackend())
]
app = Starlette(routes=routes, middleware=middleware)
```
## Users
Once `AuthenticationMiddleware` is installed the `request.user` interface
will be available to endpoints or other middleware.
This interface should subclass `BaseUser`, which provides two properties,
as well as whatever other information your user model includes.
* `.is_authenticated`
* `.display_name`
Starlette provides two built-in user implementations: `UnauthenticatedUser()`,
and `SimpleUser(username)`.
## AuthCredentials
It is important that authentication credentials are treated as separate concept
from users. An authentication scheme should be able to restrict or grant
particular privileges independently of the user identity.
The `AuthCredentials` class provides the basic interface that `request.auth`
exposes:
* `.scopes`
## Permissions
Permissions are implemented as an endpoint decorator, that enforces that the
incoming request includes the required authentication scopes.
```python
from starlette.authentication import requires
@requires('authenticated')
async def dashboard(request):
...
```
You can include either one or multiple required scopes:
```python
from starlette.authentication import requires
@requires(['authenticated', 'admin'])
async def dashboard(request):
...
```
By default 403 responses will be returned when permissions are not granted.
In some cases you might want to customize this, for example to hide information
about the URL layout from unauthenticated users.
```python
from starlette.authentication import requires
@requires(['authenticated', 'admin'], status_code=404)
async def dashboard(request):
...
```
!!! note
The `status_code` parameter is not supported with WebSockets. The 403 (Forbidden)
status code will always be used for those.
Alternatively you might want to redirect unauthenticated users to a different
page.
```python
from starlette.authentication import requires
async def homepage(request):
...
@requires('authenticated', redirect='homepage')
async def dashboard(request):
...
```
When redirecting users, the page you redirect them to will include URL they originally requested at the `next` query param:
```python
from starlette.authentication import requires
from starlette.responses import RedirectResponse
@requires('authenticated', redirect='login')
async def admin(request):
...
async def login(request):
if request.method == "POST":
# Now that the user is authenticated,
# we can send them to their original request destination
if request.user.is_authenticated:
next_url = request.query_params.get("next")
if next_url:
return RedirectResponse(next_url)
return RedirectResponse("/")
```
For class-based endpoints, you should wrap the decorator
around a method on the class.
```python
from starlette.authentication import requires
from starlette.endpoints import HTTPEndpoint
class Dashboard(HTTPEndpoint):
@requires("authenticated")
async def get(self, request):
...
```
## Custom authentication error responses
You can customise the error response sent when a `AuthenticationError` is
raised by an auth backend:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
def on_auth_error(request: Request, exc: Exception):
return JSONResponse({"error": str(exc)}, status_code=401)
app = Starlette(
middleware=[
Middleware(AuthenticationMiddleware, backend=BasicAuthBackend(), on_error=on_auth_error),
],
)
```
================================================
FILE: docs/background.md
================================================
Starlette includes a `BackgroundTask` class for in-process background tasks.
A background task should be attached to a response, and will run only once
the response has been sent.
### Background Task
Used to add a single background task to a response.
Signature: `BackgroundTask(func, *args, **kwargs)`
```python
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.background import BackgroundTask
...
async def signup(request):
data = await request.json()
username = data['username']
email = data['email']
task = BackgroundTask(send_welcome_email, to_address=email)
message = {'status': 'Signup successful'}
return JSONResponse(message, background=task)
async def send_welcome_email(to_address):
...
routes = [
...
Route('/user/signup', endpoint=signup, methods=['POST'])
]
app = Starlette(routes=routes)
```
### BackgroundTasks
Used to add multiple background tasks to a response.
Signature: `BackgroundTasks(tasks=[])`
```python
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.background import BackgroundTasks
async def signup(request):
data = await request.json()
username = data['username']
email = data['email']
tasks = BackgroundTasks()
tasks.add_task(send_welcome_email, to_address=email)
tasks.add_task(send_admin_notification, username=username)
message = {'status': 'Signup successful'}
return JSONResponse(message, background=tasks)
async def send_welcome_email(to_address):
...
async def send_admin_notification(username):
...
routes = [
Route('/user/signup', endpoint=signup, methods=['POST'])
]
app = Starlette(routes=routes)
```
!!! important
The tasks are executed in order. In case one of the tasks raises
an exception, the following tasks will not get the opportunity to be executed.
================================================
FILE: docs/config.md
================================================
Starlette encourages a strict separation of configuration from code,
following [the twelve-factor pattern][twelve-factor].
Configuration should be stored in environment variables, or in a `.env` file
that is not committed to source control.
```python title="main.py"
from sqlalchemy import create_engine
from starlette.applications import Starlette
from starlette.config import Config
from starlette.datastructures import CommaSeparatedStrings, Secret
# Config will be read from environment variables and/or ".env" files.
config = Config(".env")
DEBUG = config('DEBUG', cast=bool, default=False)
DATABASE_URL = config('DATABASE_URL')
SECRET_KEY = config('SECRET_KEY', cast=Secret)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=CommaSeparatedStrings)
app = Starlette(debug=DEBUG)
engine = create_engine(DATABASE_URL)
...
```
```shell title=".env"
# Don't commit this to source control.
# Eg. Include ".env" in your `.gitignore` file.
DEBUG=True
DATABASE_URL=postgresql://user:password@localhost:5432/database
SECRET_KEY=43n080musdfjt54t-09sdgr
ALLOWED_HOSTS=127.0.0.1, localhost
```
## Configuration precedence
The order in which configuration values are read is:
* From an environment variable.
* From the `.env` file.
* The default value given in `config`.
If none of those match, then `config(...)` will raise an error.
## Secrets
For sensitive keys, the `Secret` class is useful, since it helps minimize
occasions where the value it holds could leak out into tracebacks or
other code introspection.
To get the value of a `Secret` instance, you must explicitly cast it to a string.
You should only do this at the point at which the value is used.
```python
>>> from myproject import settings
>>> settings.SECRET_KEY
Secret('**********')
>>> str(settings.SECRET_KEY)
'98n349$%8b8-7yjn0n8y93T$23r'
```
!!! tip
You can use `DatabaseURL` from `databases`
package [here](https://github.com/encode/databases/blob/ab5eb718a78a27afe18775754e9c0fa2ad9cd211/databases/core.py#L420)
to store database URLs and avoid leaking them in the logs.
## CommaSeparatedStrings
For holding multiple inside a single config key, the `CommaSeparatedStrings`
type is useful.
```python
>>> from myproject import settings
>>> print(settings.ALLOWED_HOSTS)
CommaSeparatedStrings(['127.0.0.1', 'localhost'])
>>> print(list(settings.ALLOWED_HOSTS))
['127.0.0.1', 'localhost']
>>> print(len(settings.ALLOWED_HOSTS))
2
>>> print(settings.ALLOWED_HOSTS[0])
'127.0.0.1'
```
## Reading or modifying the environment
In some cases you might want to read or modify the environment variables programmatically.
This is particularly useful in testing, where you may want to override particular
keys in the environment.
Rather than reading or writing from `os.environ`, you should use Starlette's
`environ` instance. This instance is a mapping onto the standard `os.environ`
that additionally protects you by raising an error if any environment variable
is set *after* the point that it has already been read by the configuration.
If you're using `pytest`, then you can setup any initial environment in
`tests/conftest.py`.
```python title="tests/conftest.py"
from starlette.config import environ
environ['DEBUG'] = 'TRUE'
```
## Reading prefixed environment variables
You can namespace the environment variables by setting `env_prefix` argument.
```python title="myproject/settings.py"
import os
from starlette.config import Config
os.environ['APP_DEBUG'] = 'yes'
os.environ['ENVIRONMENT'] = 'dev'
config = Config(env_prefix='APP_')
DEBUG = config('DEBUG') # lookups APP_DEBUG, returns "yes"
ENVIRONMENT = config('ENVIRONMENT') # lookups APP_ENVIRONMENT, raises KeyError as variable is not defined
```
## Custom encoding for environment files
By default, Starlette reads environment files using UTF-8 encoding.
You can specify a different encoding by setting `encoding` argument.
```python title="myproject/settings.py"
from starlette.config import Config
# Using custom encoding for .env file
config = Config(".env", encoding="latin-1")
```
## A full example
Structuring large applications can be complex. You need proper separation of
configuration and code, database isolation during tests, separate test and
production databases, etc...
Here we'll take a look at a complete example, that demonstrates how
we can start to structure an application.
First, let's keep our settings, our database table definitions, and our
application logic separated:
```python title="myproject/settings.py"
from starlette.config import Config
from starlette.datastructures import Secret
config = Config(".env")
DEBUG = config('DEBUG', cast=bool, default=False)
SECRET_KEY = config('SECRET_KEY', cast=Secret)
DATABASE_URL = config('DATABASE_URL')
```
```python title="myproject/tables.py"
import sqlalchemy
# Database table definitions.
metadata = sqlalchemy.MetaData()
organisations = sqlalchemy.Table(
...
)
```
```python title="myproject/app.py"
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.routing import Route
from myproject import settings
async def homepage(request):
...
routes = [
Route("/", endpoint=homepage)
]
middleware = [
Middleware(
SessionMiddleware,
secret_key=settings.SECRET_KEY,
)
]
app = Starlette(debug=settings.DEBUG, routes=routes, middleware=middleware)
```
Now let's deal with our test configuration.
We'd like to create a new test database every time the test suite runs,
and drop it once the tests complete. We'd also like to ensure
```python title="tests/conftest.py"
from starlette.config import environ
from starlette.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy_utils import create_database, database_exists, drop_database
# This line would raise an error if we use it after 'settings' has been imported.
environ['DEBUG'] = 'TRUE'
from myproject import settings
from myproject.app import app
from myproject.tables import metadata
@pytest.fixture(autouse=True, scope="session")
def setup_test_database():
"""
Create a clean test database every time the tests are run.
"""
url = settings.DATABASE_URL
engine = create_engine(url)
assert not database_exists(url), 'Test database already exists. Aborting tests.'
create_database(url) # Create the test database.
metadata.create_all(engine) # Create the tables.
yield # Run the tests.
drop_database(url) # Drop the test database.
@pytest.fixture()
def client():
"""
Make a 'client' fixture available to test cases.
"""
# Our fixture is created within a context manager. This ensures that
# application lifespan runs for every test case.
with TestClient(app) as test_client:
yield test_client
```
[twelve-factor]: https://12factor.net/config
================================================
FILE: docs/contributing.md
================================================
# Contributing
Thank you for being interested in contributing to Starlette.
There are many ways you can contribute to the project:
- Try Starlette and [report bugs/issues you find](https://github.com/Kludex/starlette/issues/new)
- [Implement new features](https://github.com/Kludex/starlette/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
- [Review Pull Requests of others](https://github.com/Kludex/starlette/pulls)
- Write documentation
- Participate in discussions
## Reporting Bugs or Other Issues
Found something that Starlette should support?
Stumbled upon some unexpected behaviour?
Contributions should generally start out with [a discussion](https://github.com/Kludex/starlette/discussions).
Possible bugs may be raised as a "Potential Issue" discussion, feature requests may
be raised as an "Ideas" discussion. We can then determine if the discussion needs
to be escalated into an "Issue" or not, or if we'd consider a pull request.
Try to be more descriptive as you can and in case of a bug report,
provide as much information as possible like:
- OS platform
- Python version
- Installed dependencies and versions (`python -m pip freeze`)
- Code snippet
- Error traceback
You should always try to reduce any examples to the *simplest possible case*
that demonstrates the issue.
## Development
To start developing Starlette, create a **fork** of the
[Starlette repository](https://github.com/Kludex/starlette) on GitHub.
Then clone your fork with the following command replacing `YOUR-USERNAME` with
your GitHub username:
```shell
$ git clone https://github.com/YOUR-USERNAME/starlette
```
You can now install the project and its dependencies using:
```shell
$ cd starlette
$ scripts/install
```
## Testing and Linting
We use custom shell scripts to automate testing, linting,
and documentation building workflow.
To run the tests, use:
```shell
$ scripts/test
```
Any additional arguments will be passed to `pytest`. See the [pytest documentation](https://docs.pytest.org/en/latest/how-to/usage.html) for more information.
For example, to run a single test script:
```shell
$ scripts/test tests/test_application.py
```
To run the code auto-formatting:
```shell
$ scripts/lint
```
Lastly, to run code checks separately (they are also run as part of `scripts/test`), run:
```shell
$ scripts/check
```
## Documenting
Documentation pages are located under the `docs/` folder.
To run the documentation site locally (useful for previewing changes), use:
```shell
$ scripts/docs
```
## Resolving Build / CI Failures
Once you've submitted your pull request, the test suite will automatically run, and the results will show up in GitHub.
If the test suite fails, you'll want to click through to the "Details" link, and try to identify why the test suite failed.
<p align="center" style="margin: 0 0 10px">
<img src="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/gh-actions-fail.png" alt='Failing PR commit status'>
</p>
Here are some common ways the test suite can fail:
### Check Job Failed
<p align="center" style="margin: 0 0 10px">
<img src="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/gh-actions-fail-check.png" alt='Failing GitHub action lint job'>
</p>
This job failing means there is either a code formatting issue or type-annotation issue.
You can look at the job output to figure out why it's failed or within a shell run:
```shell
$ scripts/check
```
It may be worth it to run `$ scripts/lint` to attempt auto-formatting the code
and if that job succeeds commit the changes.
### Docs Job Failed
This job failing means the documentation failed to build. This can happen for
a variety of reasons like invalid markdown or missing configuration within `mkdocs.yml`.
### Python 3.X Job Failed
<p align="center" style="margin: 0 0 10px">
<img src="https://raw.githubusercontent.com/Kludex/starlette/main/docs/img/gh-actions-fail-test.png" alt='Failing GitHub action test job'>
</p>
This job failing means the unit tests failed or not all code paths are covered by unit tests.
If tests are failing you will see this message under the coverage report:
`=== 1 failed, 435 passed, 1 skipped, 1 xfailed in 11.09s ===`
If tests succeed but coverage doesn't reach our current threshold, you will see this
message under the coverage report:
`FAIL Required test coverage of 100% not reached. Total coverage: 99.00%`
## Releasing
*This section is targeted at Starlette maintainers.*
Before releasing a new version, create a pull request that includes:
- **An update to the changelog**:
- We follow the format from [keepachangelog](https://keepachangelog.com/en/1.0.0/).
- [Compare](https://github.com/Kludex/starlette/compare/) `main` with the tag of the latest release, and list all entries that are of interest to our users:
- Things that **must** go in the changelog: added, changed, deprecated or removed features, and bug fixes.
- Things that **should not** go in the changelog: changes to documentation, tests or tooling.
- Try sorting entries in descending order of impact / importance.
- Keep it concise and to-the-point. 🎯
- **A version bump**: see `__version__.py`.
For an example, see [#1600](https://github.com/Kludex/starlette/pull/1600).
Once the release PR is merged, create a
[new release](https://github.com/Kludex/starlette/releases/new) including:
- Tag version like `0.13.3`.
- Release title `Version 0.13.3`
- Description copied from the changelog.
Once created this release will be automatically uploaded to PyPI.
================================================
FILE: docs/css/custom.css
================================================
/* Lighter dark mode colors */
[data-md-color-scheme="slate"] {
--md-default-bg-color: #263238;
--md-default-fg-color: #e0e0e0;
--md-code-bg-color: #2e3c43;
}
/* Announcement bar styling */
.announce-wrapper {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
min-height: 40px;
background-color: var(--md-primary-fg-color);
}
.announce-wrapper #announce-msg {
display: flex;
align-items: center;
justify-content: center;
}
.announce-wrapper #announce-msg div.item {
display: none;
}
.announce-wrapper #announce-msg div.item:first-child {
display: block;
}
a.announce-link:link,
a.announce-link:visited {
color: var(--md-primary-bg-color);
text-decoration: none;
font-weight: 500;
}
a.announce-link:hover {
color: var(--md-accent-fg-color);
text-decoration: underline;
}
================================================
FILE: docs/database.md
================================================
Starlette is not strictly tied to any particular database implementation.
You are free to use any async database library that you prefer. Some popular options include:
- [SQLAlchemy](https://www.sqlalchemy.org/) - The Python SQL toolkit with native async support (2.0+).
- [SQLModel](https://sqlmodel.tiangolo.com/) - SQL databases in Python, designed for simplicity, built on top of SQLAlchemy and Pydantic.
- [Tortoise ORM](https://tortoise.github.io/) - An easy-to-use asyncio ORM inspired by Django.
- [Piccolo](https://piccolo-orm.com/) - A fast, user-friendly ORM and query builder.
Refer to your chosen database library's documentation for specific connection and query patterns.
================================================
FILE: docs/endpoints.md
================================================
Starlette includes the classes `HTTPEndpoint` and `WebSocketEndpoint` that provide a class-based view pattern for
handling HTTP method dispatching and WebSocket sessions.
### HTTPEndpoint
The `HTTPEndpoint` class can be used as an ASGI application:
```python
from starlette.responses import PlainTextResponse
from starlette.endpoints import HTTPEndpoint
class App(HTTPEndpoint):
async def get(self, request):
return PlainTextResponse(f"Hello, world!")
```
If you're using a Starlette application instance to handle routing, you can
dispatch to an `HTTPEndpoint` class. Make sure to dispatch to the class itself,
rather than to an instance of the class:
```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.endpoints import HTTPEndpoint
from starlette.routing import Route
class Homepage(HTTPEndpoint):
async def get(self, request):
return PlainTextResponse(f"Hello, world!")
class User(HTTPEndpoint):
async def get(self, request):
username = request.path_params['username']
return PlainTextResponse(f"Hello, {username}")
routes = [
Route("/", Homepage),
Route("/{username}", User)
]
app = Starlette(routes=routes)
```
HTTP endpoint classes will respond with "405 Method not allowed" responses for any
request methods which do not map to a corresponding handler.
### WebSocketEndpoint
The `WebSocketEndpoint` class is an ASGI application that presents a wrapper around
the functionality of a `WebSocket` instance.
The ASGI connection scope is accessible on the endpoint instance via `.scope` and
has an attribute `encoding` which may optionally be set, in order to validate the expected websocket data in the `on_receive` method.
The encoding types are:
* `'json'`
* `'bytes'`
* `'text'`
There are three overridable methods for handling specific ASGI websocket message types:
* `async def on_connect(websocket, **kwargs)`
* `async def on_receive(websocket, data)`
* `async def on_disconnect(websocket, close_code)`
```python
from starlette.endpoints import WebSocketEndpoint
class App(WebSocketEndpoint):
encoding = 'bytes'
async def on_connect(self, websocket):
await websocket.accept()
async def on_receive(self, websocket, data):
await websocket.send_bytes(b"Message: " + data)
async def on_disconnect(self, websocket, close_code):
pass
```
The `WebSocketEndpoint` can also be used with the `Starlette` application class:
```python
import uvicorn
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint, HTTPEndpoint
from starlette.responses import HTMLResponse
from starlette.routing import Route, WebSocketRoute
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
class Homepage(HTTPEndpoint):
async def get(self, request):
return HTMLResponse(html)
class Echo(WebSocketEndpoint):
encoding = "text"
async def on_receive(self, websocket, data):
await websocket.send_text(f"Message text was: {data}")
routes = [
Route("/", Homepage),
WebSocketRoute("/ws", Echo)
]
app = Starlette(routes=routes)
```
================================================
FILE: docs/exceptions.md
================================================
Starlette allows you to install custom exception handlers to deal with
how you return responses when errors or handled exceptions occur.
```python
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import HTMLResponse
HTML_404_PAGE = ...
HTML_500_PAGE = ...
async def not_found(request: Request, exc: HTTPException):
return HTMLResponse(content=HTML_404_PAGE, status_code=exc.status_code)
async def server_error(request: Request, exc: HTTPException):
return HTMLResponse(content=HTML_500_PAGE, status_code=exc.status_code)
exception_handlers = {
404: not_found,
500: server_error
}
app = Starlette(routes=routes, exception_handlers=exception_handlers)
```
If `debug` is enabled and an error occurs, then instead of using the installed
500 handler, Starlette will respond with a traceback response.
```python
app = Starlette(debug=True, routes=routes, exception_handlers=exception_handlers)
```
As well as registering handlers for specific status codes, you can also
register handlers for classes of exceptions.
In particular you might want to override how the built-in `HTTPException` class
is handled. For example, to use JSON style responses:
```python
async def http_exception(request: Request, exc: HTTPException):
return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)
exception_handlers = {
HTTPException: http_exception
}
```
The `HTTPException` is also equipped with the `headers` argument. Which allows the propagation
of the headers to the response class:
```python
async def http_exception(request: Request, exc: HTTPException):
return JSONResponse(
{"detail": exc.detail},
status_code=exc.status_code,
headers=exc.headers
)
```
You might also want to override how `WebSocketException` is handled:
```python
async def websocket_exception(websocket: WebSocket, exc: WebSocketException):
await websocket.close(code=1008)
exception_handlers = {
WebSocketException: websocket_exception
}
```
## Errors and handled exceptions
It is important to differentiate between handled exceptions and errors.
Handled exceptions do not represent error cases. They are coerced into appropriate
HTTP responses, which are then sent through the standard middleware stack. By default
the `HTTPException` class is used to manage any handled exceptions.
Errors are any other exception that occurs within the application. These cases
should bubble through the entire middleware stack as exceptions. Any error
logging middleware should ensure that it re-raises the exception all the
way up to the server.
In practical terms, the error handled used is `exception_handler[500]` or `exception_handler[Exception]`.
Both keys `500` and `Exception` can be used. See below:
```python
async def handle_error(request: Request, exc: HTTPException):
# Perform some logic
return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)
exception_handlers = {
Exception: handle_error # or "500: handle_error"
}
```
It's important to notice that in case a [`BackgroundTask`](background.md) raises an exception,
it will be handled by the `handle_error` function, but at that point, the response was already sent. In other words,
the response created by `handle_error` will be discarded. In case the error happens before the response was sent, then
it will use the response object - in the above example, the returned `JSONResponse`.
In order to deal with this behaviour correctly, the middleware stack of a
`Starlette` application is configured like this:
* `ServerErrorMiddleware` - Returns 500 responses when server errors occur.
* Installed middleware
* `ExceptionMiddleware` - Deals with handled exceptions, and returns responses.
* Router
* Endpoints
## HTTPException
The `HTTPException` class provides a base class that you can use for any handled exceptions.
The `ExceptionMiddleware` implementation defaults to returning plain-text HTTP responses for any `HTTPException`.
* `HTTPException(status_code, detail=None, headers=None)`
You should only raise `HTTPException` inside routing or endpoints.
Middleware classes should instead just return appropriate responses directly.
You can use an `HTTPException` on a WebSocket endpoint. In case it's raised before `websocket.accept()`
the connection is not upgraded to a WebSocket connection, and the proper HTTP response is returned.
```python
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.routing import WebSocketRoute
from starlette.websockets import WebSocket
async def websocket_endpoint(websocket: WebSocket):
raise HTTPException(status_code=400, detail="Bad request")
app = Starlette(routes=[WebSocketRoute("/ws", websocket_endpoint)])
```
## WebSocketException
You can use the `WebSocketException` class to raise errors inside of WebSocket endpoints.
* `WebSocketException(code=1008, reason=None)`
You can set any code valid as defined [in the specification](https://tools.ietf.org/html/rfc6455#section-7.4.1).
================================================
FILE: docs/graphql.md
================================================
GraphQL support in Starlette was deprecated in version 0.15.0, and removed in version 0.17.0.
Although GraphQL support is no longer built in to Starlette, you can still use GraphQL with Starlette via 3rd party libraries. These libraries all have Starlette-specific guides to help you do just that:
- [Ariadne](https://ariadnegraphql.org/docs/starlette-integration.html)
- [`starlette-graphene3`](https://github.com/ciscorn/starlette-graphene3#example)
- [Strawberry](https://strawberry.rocks/docs/integrations/starlette)
- [`tartiflette-asgi`](https://tartiflette.github.io/tartiflette-asgi/usage/#starlette)
================================================
FILE: docs/index.md
================================================
<p align="center">
<img width="400px" src="/img/starlette.svg#only-light" alt="starlette"/>
<img width="400px" src="/img/starlette_dark.svg#only-dark" alt="starlette"/>
</p>
<p align="center">
<em>✨ The little ASGI framework that shines. ✨</em>
</p>
<p align="center">
<a href="https://github.com/Kludex/starlette/actions">
<img src="https://github.com/Kludex/starlette/workflows/Test%20Suite/badge.svg" alt="Build Status">
</a>
<a href="https://pypi.org/project/starlette/">
<img src="https://badge.fury.io/py/starlette.svg" alt="Package version">
</a>
<a href="https://pypi.org/project/starlette" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/starlette.svg?color=%2334D058" alt="Supported Python versions">
</a>
<a href="https://discord.gg/RxKUF5JuHs">
<img src="https://img.shields.io/discord/1051468649518616576?logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2" alt="Discord">
</a>
</p>
---
**Documentation**: <a href="https://starlette.dev/" target="_blank">https://starlette.dev</a>
**Source Code**: <a href="https://github.com/Kludex/starlette" target="_blank">https://github.com/Kludex/starlette</a>
---
# Introduction
Starlette is a lightweight [ASGI][asgi] framework/toolkit,
which is ideal for building async web services in Python.
It is production-ready, and gives you the following:
* A lightweight, low-complexity HTTP web framework.
* WebSocket support.
* In-process background tasks.
* Startup and shutdown events.
* Test client built on `httpx`.
* CORS, GZip, Static Files, Streaming responses.
* Session and Cookie support.
* 100% test coverage.
* 100% type annotated codebase.
* Few hard dependencies.
* Compatible with `asyncio` and `trio` backends.
* Great overall performance [against independent benchmarks][techempower].
## Sponsorship
Help us keep Starlette maintained and sustainable by [becoming a sponsor](https://github.com/sponsors/Kludex).
**Current sponsors:**
<div style="display: flex; flex-wrap: wrap; gap: 2rem; align-items: center; margin: 1rem 0;">
<a href="https://fastapi.tiangolo.com">
<img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI" style="height: 80px;">
</a>
<a href="https://huggingface.co">
<img src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo-with-title.svg" alt="Hugging Face" style="height: 80px;">
</a>
</div>
## Installation
```shell
pip install starlette
```
You'll also want to install an ASGI server, such as [uvicorn](https://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://hypercorn.readthedocs.io/en/latest/).
```shell
pip install uvicorn
```
## Example
```python title="main.py"
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request):
return JSONResponse({'hello': 'world'})
app = Starlette(debug=True, routes=[
Route('/', homepage),
])
```
Then run the application...
```shell
uvicorn main:app
```
## Dependencies
Starlette only requires `anyio`, and the following dependencies are optional:
* [`httpx`][httpx] - Required if you want to use the `TestClient`.
* [`jinja2`][jinja2] - Required if you want to use `Jinja2Templates`.
* [`python-multipart`][python-multipart] - Required if you want to support form parsing, with `request.form()`.
* [`itsdangerous`][itsdangerous] - Required for `SessionMiddleware` support.
* [`pyyaml`][pyyaml] - Required for `SchemaGenerator` support.
You can install all of these with `pip install starlette[full]`.
## Framework or Toolkit
Starlette is designed to be used either as a complete framework, or as
an ASGI toolkit. You can use any of its components independently.
```python title="main.py"
from starlette.responses import PlainTextResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = PlainTextResponse('Hello, world!')
await response(scope, receive, send)
```
Run the `app` application in `main.py`:
```shell
$ uvicorn main:app
INFO: Started server process [11509]
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Run uvicorn with `--reload` to enable auto-reloading on code changes.
## Modularity
The modularity that Starlette is designed on promotes building re-usable
components that can be shared between any ASGI framework. This should enable
an ecosystem of shared middleware and mountable applications.
The clean API separation also means it's easier to understand each component
in isolation.
---
<p align="center"><i>Starlette is <a href="https://github.com/Kludex/starlette/blob/main/LICENSE.md">BSD licensed</a> code.<br/>Designed & crafted with care.</i></br>— ⭐️ —</p>
[asgi]: https://asgi.readthedocs.io/en/latest/
[httpx]: https://www.python-httpx.org/
[jinja2]: https://jinja.palletsprojects.com/
[python-multipart]: https://multipart.fastapiexpert.com/
[itsdangerous]: https://itsdangerous.palletsprojects.com/
[sqlalchemy]: https://www.sqlalchemy.org
[pyyaml]: https://pyyaml.org/wiki/PyYAMLDocumentation
[techempower]: https://www.techempower.com/benchmarks/#hw=ph&test=fortune&l=zijzen-sf
================================================
FILE: docs/js/custom.js
================================================
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
async function showRandomAnnouncement(groupId, timeInterval) {
const announceGroup = document.getElementById(groupId);
if (announceGroup) {
let children = [].slice.call(announceGroup.children);
children = shuffle(children)
let index = 0
const announceRandom = () => {
children.forEach((el, i) => { el.style.display = "none" });
children[index].style.display = "block"
index = (index + 1) % children.length
}
announceRandom()
setInterval(announceRandom, timeInterval)
}
}
async function main() {
showRandomAnnouncement('announce-msg', 5000)
}
document$.subscribe(() => {
main()
})
================================================
FILE: docs/lifespan.md
================================================
Starlette applications can register a lifespan handler for dealing with
code that needs to run before the application starts up, or when the application
is shutting down.
```python
import contextlib
from starlette.applications import Starlette
@contextlib.asynccontextmanager
async def lifespan(app):
async with some_async_resource():
print("Run at startup!")
yield
print("Run on shutdown!")
routes = [
...
]
app = Starlette(routes=routes, lifespan=lifespan)
```
Starlette will not start serving any incoming requests until the lifespan has been run.
The lifespan teardown will run once all connections have been closed, and
any in-process background tasks have completed.
Consider using [`anyio.create_task_group()`](https://anyio.readthedocs.io/en/stable/tasks.html)
for managing asynchronous tasks.
## Lifespan State
The lifespan has the concept of `state`, which is a dictionary that
can be used to share the objects between the lifespan, and the requests.
```python
import contextlib
from typing import AsyncIterator, TypedDict
import httpx
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse
from starlette.routing import Route
class State(TypedDict):
http_client: httpx.AsyncClient
@contextlib.asynccontextmanager
async def lifespan(app: Starlette) -> AsyncIterator[State]:
async with httpx.AsyncClient() as client:
yield {"http_client": client}
async def homepage(request: Request) -> PlainTextResponse:
client = request.state.http_client
response = await client.get("https://www.example.com")
return PlainTextResponse(response.text)
app = Starlette(
lifespan=lifespan,
routes=[Route("/", homepage)]
)
```
The `state` received on the requests is a **shallow** copy of the state received on the
lifespan handler.
## Accessing State
The state can be accessed using either attribute-style or dictionary-style syntax.
The dictionary-style syntax was introduced in Starlette 0.52.0 (January 2026), with the idea of
improving type safety when using the lifespan state, given that `Request` became a generic over
the state type.
```python
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import TypedDict
import httpx
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import PlainTextResponse
from starlette.routing import Route
class State(TypedDict):
http_client: httpx.AsyncClient
@asynccontextmanager
async def lifespan(app: Starlette) -> AsyncIterator[State]:
async with httpx.AsyncClient() as client:
yield {"http_client": client}
async def homepage(request: Request[State]) -> PlainTextResponse:
client = request.state["http_client"]
reveal_type(client) # Revealed type is 'httpx.AsyncClient'
response = await client.get("https://www.example.com")
return PlainTextResponse(response.text)
app = Starlette(lifespan=lifespan, routes=[Route("/", homepage)])
```
This also works with WebSockets:
```python
async def websocket_endpoint(websocket: WebSocket[State]) -> None:
await websocket.accept()
client = websocket.state["http_client"]
response = await client.get("https://www.example.com")
await websocket.send_text(response.text)
await websocket.close()
app = Starlette(lifespan=lifespan, routes=[WebSocketRoute("/ws", websocket_endpoint)])
```
!!! note
There were many attempts to make this work with attribute-style access instead of
dictionary-style access, but none were satisfactory, given they would have been
breaking changes, or there were typing limitations.
For more details, see:
- [@Kludex/starlette#issues/3005](https://github.com/Kludex/starlette/issues/3005)
- [@python/typing#discussions/1457](https://github.com/python/typing/discussions/1457)
- [@Kludex/starlette#pull/3036](https://github.com/Kludex/starlette/pull/3036)
## Running lifespan in tests
You should use `TestClient` as a context manager, to ensure that the lifespan is called.
```python
from example import app
from starlette.testclient import TestClient
def test_homepage():
with TestClient(app) as client:
# Application's lifespan is called on entering the block.
response = client.get("/")
assert response.status_code == 200
# And the lifespan's teardown is run when exiting the block.
```
================================================
FILE: docs/middleware.md
================================================
Starlette includes several middleware classes for adding behavior that is applied across
your entire application. These are all implemented as standard ASGI
middleware classes, and can be applied either to Starlette or to any other ASGI application.
## Using middleware
The Starlette application class allows you to include the ASGI middleware
in a way that ensures that it remains wrapped by the exception handler.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
routes = ...
# Ensure that all requests include an 'example.com' or
# '*.example.com' host header, and strictly enforce https-only access.
middleware = [
Middleware(
TrustedHostMiddleware,
allowed_hosts=['example.com', '*.example.com'],
),
Middleware(HTTPSRedirectMiddleware)
]
app = Starlette(routes=routes, middleware=middleware)
```
Every Starlette application automatically includes two pieces of middleware by default:
* `ServerErrorMiddleware` - Ensures that application exceptions may return a custom 500 page, or display an application traceback in DEBUG mode. This is *always* the outermost middleware layer.
* `ExceptionMiddleware` - Adds exception handlers, so that particular types of expected exception cases can be associated with handler functions. For example raising `HTTPException(status_code=404)` within an endpoint will end up rendering a custom 404 page.
Middleware is evaluated from top-to-bottom, so the flow of execution in our example
application would look like this:
* Middleware
* `ServerErrorMiddleware`
* `TrustedHostMiddleware`
* `HTTPSRedirectMiddleware`
* `ExceptionMiddleware`
* Routing
* Endpoint
The following middleware implementations are available in the Starlette package:
## CORSMiddleware
Adds appropriate [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to outgoing responses in order to allow cross-origin requests from browsers.
The default parameters used by the CORSMiddleware implementation are restrictive by default,
so you'll need to explicitly enable particular origins, methods, or headers, in order
for browsers to be permitted to use them in a Cross-Domain context.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
routes = ...
middleware = [
Middleware(CORSMiddleware, allow_origins=['*'])
]
app = Starlette(routes=routes, middleware=middleware)
```
The following arguments are supported:
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. eg. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. eg. `'https://[a-zA-Z0-9-]+\.example\.org'`. Avoid using `.*` or `.+` as they match URL special characters (`/`, `@`, `#`, `?`) and may result in overly permissive origin matching. Use specific character classes like `[a-zA-Z0-9-]+` instead.
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for CORS requests.
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins`, `allow_methods` and `allow_headers` cannot be set to `['*']` for credentials to be allowed, all of them must be explicitly specified.
* `allow_private_network` - Indicates whether to accept cross-origin requests over a private network. Defaults to `False`.
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`.
The middleware responds to two particular types of HTTP request...
#### CORS preflight requests
These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers.
In this case the middleware will intercept the incoming request and respond with
appropriate CORS headers, and either a 200 or 400 response for informational purposes.
#### Simple requests
Any request with an `Origin` header. In this case the middleware will pass the
request through as normal, but will include appropriate CORS headers on the response.
#### Private Network Access (PNA)
Private Network Access is a browser security feature that restricts websites from public networks from accessing servers on private networks.
When a website attempts to make such a cross-network request, the browser will send a `Access-Control-Request-Private-Network: true` header in the
pre-flight request. If the `allow_private_network` flag is set to `True`, the middleware will include the `Access-Control-Allow-Private-Network: true`
header in the response, allowing the request. If set to `False`, the middleware will return a 400 response, blocking the request.
### CORSMiddleware Global Enforcement
When using CORSMiddleware with your Starlette application, it's important to ensure that CORS headers are applied even to error responses generated by unhandled exceptions. The recommended solution is to wrap the entire Starlette application with CORSMiddleware. This approach guarantees that even if an exception is caught by ServerErrorMiddleware (or other outer error-handling middleware), the response will still include the proper `Access-Control-Allow-Origin` header.
For example, instead of adding CORSMiddleware as an inner `middleware` via the Starlette middleware parameter, you can wrap your application as follows:
```python
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
import uvicorn
app = Starlette()
app = CORSMiddleware(app=app, allow_origins=["*"])
# ... your routes and middleware configuration ...
if __name__ == '__main__':
uvicorn.run(
app,
host='0.0.0.0',
port=8000
)
```
## SessionMiddleware
Adds signed cookie-based HTTP sessions. Session information is readable but not modifiable.
The session cookie is always set with the `"HttpOnly"` flag, preventing client-side JavaScript from accessing it.
Access or modify the session data using the `request.session` dictionary interface.
The following arguments are supported:
* `secret_key` - Should be a random string.
* `session_cookie` - Defaults to "session".
* `max_age` - Session expiry time in seconds. Defaults to 2 weeks. If set to `None` then the cookie will last as long as the browser session.
* `same_site` - SameSite flag prevents the browser from sending session cookie along with cross-site requests. Defaults to `'lax'`.
* `path` - The path set for the session cookie. Defaults to `'/'`.
* `https_only` - Indicate that the `"Secure"` flag should be set (can be used with HTTPS only). Defaults to `False`. Set this to `True` in production to ensure the session cookie is only sent over HTTPS.
* `domain` - Domain of the cookie used to share cookie between subdomains or cross-domains. The browser defaults the domain to the same host that set the cookie, excluding subdomains ([reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#domain_attribute)).
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
routes = ...
middleware = [
Middleware(SessionMiddleware, secret_key=..., https_only=True)
]
app = Starlette(routes=routes, middleware=middleware)
```
## HTTPSRedirectMiddleware
Enforces that all incoming requests must either be `https` or `wss`. Any incoming
requests to `http` or `ws` will be redirected to the secure scheme instead.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
routes = ...
middleware = [
Middleware(HTTPSRedirectMiddleware)
]
app = Starlette(routes=routes, middleware=middleware)
```
There are no configuration options for this middleware class.
## TrustedHostMiddleware
Enforces that all incoming requests have a correctly set `Host` header, in order
to guard against HTTP Host Header attacks.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
routes = ...
middleware = [
Middleware(TrustedHostMiddleware, allowed_hosts=['example.com', '*.example.com'])
]
app = Starlette(routes=routes, middleware=middleware)
```
The following arguments are supported:
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard
domains such as `*.example.com` are supported for matching subdomains. To allow any
hostname either use `allowed_hosts=["*"]` or omit the middleware.
* `www_redirect` - If set to True, requests to non-www versions of the allowed hosts will be redirected to their www counterparts. Defaults to `True`.
If an incoming request does not validate correctly then a 400 response will be sent.
## GZipMiddleware
Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header.
The middleware will handle both standard and streaming responses.
??? info "Buffer on streaming responses"
On streaming responses, the middleware will buffer the response before compressing it.
The idea is that we don't want to compress every small chunk of data, as it would be inefficient.
Instead, we buffer the response until it reaches a certain size, and then compress it.
This may cause a delay in the response, as the middleware waits for the buffer to fill up before compressing it.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.gzip import GZipMiddleware
routes = ...
middleware = [
Middleware(GZipMiddleware, minimum_size=1000, compresslevel=9)
]
app = Starlette(routes=routes, middleware=middleware)
```
The following arguments are supported:
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
* `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes.
The middleware won't GZip responses that already have either a `Content-Encoding` set, to prevent them from
being encoded twice, or a `Content-Type` set to `text/event-stream`, to avoid compressing server-sent events.
## BaseHTTPMiddleware
An abstract class that allows you to write ASGI middleware against a request/response
interface.
### Usage
To implement a middleware class using `BaseHTTPMiddleware`, you must override the
`async def dispatch(request, call_next)` method.
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
class CustomHeaderMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
response.headers['Custom'] = 'Example'
return response
routes = ...
middleware = [
Middleware(CustomHeaderMiddleware)
]
app = Starlette(routes=routes, middleware=middleware)
```
If you want to provide configuration options to the middleware class you should
override the `__init__` method, ensuring that the first argument is `app`, and
any remaining arguments are optional keyword arguments. Make sure to set the `app`
attribute on the instance if you do this.
```python
class CustomHeaderMiddleware(BaseHTTPMiddleware):
def __init__(self, app, header_value='Example'):
super().__init__(app)
self.header_value = header_value
async def dispatch(self, request, call_next):
response = await call_next(request)
response.headers['Custom'] = self.header_value
return response
middleware = [
Middleware(CustomHeaderMiddleware, header_value='Customized')
]
app = Starlette(routes=routes, middleware=middleware)
```
Middleware classes should not modify their state outside of the `__init__` method.
Instead you should keep any state local to the `dispatch` method, or pass it
around explicitly, rather than mutating the middleware instance.
### Limitations
Currently, the `BaseHTTPMiddleware` has some known limitations:
- Using `BaseHTTPMiddleware` will prevent changes to [`contextvars.ContextVar`](https://docs.python.org/3/library/contextvars.html#contextvars.ContextVar)s from propagating upwards. That is, if you set a value for a `ContextVar` in your endpoint and try to read it from a middleware you will find that the value is not the same value you set in your endpoint (see [this test](https://github.com/Kludex/starlette/blob/621abc747a6604825190b93467918a0ec6456a24/tests/middleware/test_base.py#L192-L223) for an example of this behavior). Importantly, this also means that if a `BaseHTTPMiddleware` is positioned earlier in the middleware stack, it will disrupt `contextvars` propagation for any subsequent Pure ASGI Middleware that relies on them.
To overcome these limitations, use [pure ASGI middleware](#pure-asgi-middleware), as shown below.
## Pure ASGI Middleware
The [ASGI spec](https://asgi.readthedocs.io/en/latest/) makes it possible to implement ASGI middleware using the ASGI interface directly, as a chain of ASGI applications that call into the next one. In fact, this is how middleware classes shipped with Starlette are implemented.
This lower-level approach provides greater control over behavior and enhanced interoperability across frameworks and servers. It also overcomes the [limitations of `BaseHTTPMiddleware`](#limitations).
### Writing pure ASGI middleware
The most common way to create an ASGI middleware is with a class.
```python
class ASGIMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
await self.app(scope, receive, send)
```
The middleware above is the most basic ASGI middleware. It receives a parent ASGI application as an argument for its constructor, and implements an `async __call__` method which calls into that parent application.
Some implementations such as [`asgi-cors`](https://github.com/simonw/asgi-cors/blob/10ef64bfcc6cd8d16f3014077f20a0fb8544ec39/asgi_cors.py) use an alternative style, using functions:
```python
import functools
def asgi_middleware():
def asgi_decorator(app):
@functools.wraps(app)
async def wrapped_app(scope, receive, send):
await app(scope, receive, send)
return wrapped_app
return asgi_decorator
```
In any case, ASGI middleware must be callables that accept three arguments: `scope`, `receive`, and `send`.
* `scope` is a dict holding information about the connection, where `scope["type"]` may be:
* [`"http"`](https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope): for HTTP requests.
* [`"websocket"`](https://asgi.readthedocs.io/en/latest/specs/www.html#websocket-connection-scope): for WebSocket connections.
* [`"lifespan"`](https://asgi.readthedocs.io/en/latest/specs/lifespan.html#scope): for ASGI lifespan messages.
* `receive` and `send` can be used to exchange ASGI event messages with the ASGI server — more on this below. The type and contents of these messages depend on the scope type. Learn more in the [ASGI specification](https://asgi.readthedocs.io/en/latest/specs/index.html).
### Using pure ASGI middleware
Pure ASGI middleware can be used like any other middleware:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from .middleware import ASGIMiddleware
routes = ...
middleware = [
Middleware(ASGIMiddleware),
]
app = Starlette(..., middleware=middleware)
```
See also [Using middleware](#using-middleware).
### Type annotations
There are two ways of annotating a middleware: using Starlette itself or [`asgiref`](https://github.com/django/asgiref).
* Using Starlette: for most common use cases.
```python
from starlette.types import ASGIApp, Message, Scope, Receive, Send
class ASGIMiddleware:
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "http":
return await self.app(scope, receive, send)
async def send_wrapper(message: Message) -> None:
# ... Do something
await send(message)
await self.app(scope, receive, send_wrapper)
```
* Using [`asgiref`](https://github.com/django/asgiref): for more rigorous type hinting.
```python
from asgiref.typing import ASGI3Application, ASGIReceiveCallable, ASGISendCallable, Scope
from asgiref.typing import ASGIReceiveEvent, ASGISendEvent
class ASGIMiddleware:
def __init__(self, app: ASGI3Application) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async def send_wrapper(message: ASGISendEvent) -> None:
# ... Do something
await send(message)
return await self.app(scope, receive, send_wrapper)
```
### Common patterns
#### Processing certain requests only
ASGI middleware can apply specific behavior according to the contents of `scope`.
For example, to only process HTTP requests, write this...
```python
class ASGIMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
... # Do something here!
await self.app(scope, receive, send)
```
Likewise, WebSocket-only middleware would guard on `scope["type"] != "websocket"`.
The middleware may also act differently based on the request method, URL, headers, etc.
#### Reusing Starlette components
Starlette provides several data structures that accept the ASGI `scope`, `receive` and/or `send` arguments, allowing you to work at a higher level of abstraction. Such data structures include [`Request`](requests.md#request), [`Headers`](requests.md#headers), [`QueryParams`](requests.md#query-parameters), [`URL`](requests.md#url), etc.
For example, you can instantiate a `Request` to more easily inspect an HTTP request:
```python
from starlette.requests import Request
class ASGIMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] == "http":
request = Request(scope)
... # Use `request.method`, `request.url`, `request.headers`, etc.
await self.app(scope, receive, send)
```
You can also reuse [responses](responses.md), which are ASGI applications as well.
#### Sending eager responses
Inspecting the connection `scope` allows you to conditionally call into a different ASGI app. One use case might be sending a response without calling into the app.
As an example, this middleware uses a dictionary to perform permanent redirects based on the requested path. This could be used to implement ongoing support of legacy URLs in case you need to refactor route URL patterns.
```python
from starlette.datastructures import URL
from starlette.responses import RedirectResponse
class RedirectsMiddleware:
def __init__(self, app, path_mapping: dict):
self.app = app
self.path_mapping = path_mapping
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
url = URL(scope=scope)
if url.path in self.path_mapping:
url = url.replace(path=self.path_mapping[url.path])
response = RedirectResponse(url, status_code=301)
await response(scope, receive, send)
return
await self.app(scope, receive, send)
```
Example usage would look like this:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
routes = ...
redirections = {
"/v1/resource/": "/v2/resource/",
# ...
}
middleware = [
Middleware(RedirectsMiddleware, path_mapping=redirections),
]
app = Starlette(routes=routes, middleware=middleware)
```
#### Inspecting or modifying the request
Request information can be accessed or changed by manipulating the `scope`. For a full example of this pattern, see Uvicorn's [`ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/fd4386fefb8fe8a4568831a7d8b2930d5fb61455/uvicorn/middleware/proxy_headers.py) which inspects and tweaks the `scope` when serving behind a frontend proxy.
Besides, wrapping the `receive` ASGI callable allows you to access or modify the HTTP request body by manipulating [`http.request`](https://asgi.readthedocs.io/en/latest/specs/www.html#request-receive-event) ASGI event messages.
As an example, this middleware computes and logs the size of the incoming request body...
```python
class LoggedRequestBodySizeMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
body_size = 0
async def receive_logging_request_body_size():
nonlocal body_size
message = await receive()
assert message["type"] == "http.request"
body_size += len(message.get("body", b""))
if not message.get("more_body", False):
print(f"Size of request body was: {body_size} bytes")
return message
await self.app(scope, receive_logging_request_body_size, send)
```
Likewise, WebSocket middleware may manipulate [`websocket.receive`](https://asgi.readthedocs.io/en/latest/specs/www.html#receive-receive-event) ASGI event messages to inspect or alter incoming WebSocket data.
For an example that changes the HTTP request body, see [`msgpack-asgi`](https://github.com/florimondmanca/msgpack-asgi).
#### Inspecting or modifying the response
Wrapping the `send` ASGI callable allows you to inspect or modify the HTTP response sent by the underlying application. To do so, react to [`http.response.start`](https://asgi.readthedocs.io/en/latest/specs/www.html#response-start-send-event) or [`http.response.body`](https://asgi.readthedocs.io/en/latest/specs/www.html#response-body-send-event) ASGI event messages.
As an example, this middleware adds some fixed extra response headers:
```python
from starlette.datastructures import MutableHeaders
class ExtraResponseHeadersMiddleware:
def __init__(self, app, headers):
self.app = app
self.headers = headers
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
return await self.app(scope, receive, send)
async def send_with_extra_headers(message):
if message["type"] == "http.response.start":
headers = MutableHeaders(scope=message)
for key, value in self.headers:
headers.append(key, value)
await send(message)
await self.app(scope, receive, send_with_extra_headers)
```
See also [`asgi-logger`](https://github.com/Kludex/asgi-logger/blob/main/asgi_logger/middleware.py) for an example that inspects the HTTP response and logs a configurable HTTP access log line.
Likewise, WebSocket middleware may manipulate [`websocket.send`](https://asgi.readthedocs.io/en/latest/specs/www.html#send-send-event) ASGI event messages to inspect or alter outgoing WebSocket data.
Note that if you change the response body, you will need to update the response `Content-Length` header to match the new response body length. See [`brotli-asgi`](https://github.com/fullonic/brotli-asgi) for a complete example.
#### Passing information to endpoints
If you need to share information with the underlying app or endpoints, you may store it into the `scope` dictionary. Note that this is a convention -- for example, Starlette uses this to share routing information with endpoints -- but it is not part of the ASGI specification. If you do so, be sure to avoid conflicts by using keys that have low chances of being used by other middleware or applications.
For example, when including the middleware below, endpoints would be able to access `request.scope["asgi_transaction_id"]`.
```python
import uuid
class TransactionIDMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
scope["asgi_transaction_id"] = uuid.uuid4()
await self.app(scope, receive, send)
```
#### Cleanup and error handling
You can wrap the application in a `try/except/finally` block or a context manager to perform cleanup operations or do error handling.
For example, the following middleware might collect metrics and process application exceptions...
```python
import time
class MonitoringMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
start = time.time()
try:
await self.app(scope, receive, send)
except Exception as exc:
... # Process the exception
raise
finally:
end = time.time()
elapsed = end - start
... # Submit `elapsed` as a metric to a monitoring backend
```
See also [`timing-asgi`](https://github.com/steinnes/timing-asgi) for a full example of this pattern.
### Gotchas
#### ASGI middleware should be stateless
Because ASGI is designed to handle concurrent requests, any connection-specific state should be scoped to the `__call__` implementation. Not doing so would typically lead to conflicting variable reads/writes across requests, and most likely bugs.
As an example, this would conditionally replace the response body, if an `X-Mock` header is present in the response...
=== "✅ Do"
```python
from starlette.datastructures import Headers
class MockResponseBodyMiddleware:
def __init__(self, app, content):
self.app = app
self.content = content
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
# A flag that we will turn `True` if the HTTP response
# has the 'X-Mock' header.
# ✅: Scoped to this function.
should_mock = False
async def maybe_send_with_mock_content(message):
nonlocal should_mock
if message["type"] == "http.response.start":
headers = Headers(raw=message["headers"])
should_mock = headers.get("X-Mock") == "1"
await send(message)
elif message["type"] == "http.response.body":
if should_mock:
message = {"type": "http.response.body", "body": self.content}
await send(message)
await self.app(scope, receive, maybe_send_with_mock_content)
```
=== "❌ Don't"
```python hl_lines="7-8"
from starlette.datastructures import Headers
class MockResponseBodyMiddleware:
def __init__(self, app, content):
self.app = app
self.content = content
# ❌: This variable would be read and written across requests!
self.should_mock = False
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async def maybe_send_with_mock_content(message):
if message["type"] == "http.response.start":
headers = Headers(raw=message["headers"])
self.should_mock = headers.get("X-Mock") == "1"
await send(message)
elif message["type"] == "http.response.body":
if self.should_mock:
message = {"type": "http.response.body", "body": self.content}
await send(message)
await self.app(scope, receive, maybe_send_with_mock_content)
```
See also [`GZipMiddleware`](https://github.com/Kludex/starlette/blob/9ef1b91c9c043197da6c3f38aa153fd874b95527/starlette/middleware/gzip.py) for a full example implementation that navigates this potential gotcha.
### Further reading
This documentation should be enough to have a good basis on how to create an ASGI middleware.
Nonetheless, there are great articles about the subject:
- [Introduction to ASGI: Emergence of an Async Python Web Ecosystem](https://florimond.dev/en/posts/2019/08/introduction-to-asgi-async-python-web/)
- [How to write ASGI middleware](https://pgjones.dev/blog/how-to-write-asgi-middleware-2021/)
## Using middleware in other frameworks
To wrap ASGI middleware around other ASGI applications, you should use the
more general pattern of wrapping the application instance:
```python
app = TrustedHostMiddleware(app, allowed_hosts=['example.com'])
```
You can do this with a Starlette application instance too, but it is preferable
to use the `middleware=<List of Middleware instances>` style, as it will:
* Ensure that everything remains wrapped in a single outermost `ServerErrorMiddleware`.
* Preserves the top-level `app` instance.
## Applying middleware to groups of routes
Middleware can also be added to `Mount` instances, which allows you to apply middleware to a group of routes or a sub-application:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.routing import Mount, Route
routes = [
Mount(
"/",
routes=[
Route(
"/example",
endpoint=...,
)
],
middleware=[Middleware(GZipMiddleware)]
)
]
app = Starlette(routes=routes)
```
Note that middleware used in this way is *not* wrapped in exception handling middleware like the middleware applied to the `Starlette` application is.
This is often not a problem because it only applies to middleware that inspect or modify the `Response`, and even then you probably don't want to apply this logic to error responses.
If you do want to apply the middleware logic to error responses only on some routes you have a couple of options:
* Add an `ExceptionMiddleware` onto the `Mount`
* Add a `try/except` block to your middleware and return an error response from there
* Split up marking and processing into two middlewares, one that gets put on `Mount` which marks the response as needing processing (for example by setting `scope["log-response"] = True`) and another applied to the `Starlette` application that does the heavy lifting.
The `Route`/`WebSocket` class also accepts a `middleware` argument, which allows you to apply middleware to a single route:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.routing import Route
routes = [
Route(
"/example",
endpoint=...,
middleware=[Middleware(GZipMiddleware)]
)
]
app = Starlette(routes=routes)
```
You can also apply middleware to the `Router` class, which allows you to apply middleware to a group of routes:
```python
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.routing import Route, Router
routes = [
Route("/example", endpoint=...),
Route("/another", endpoint=...),
]
router = Router(routes=routes, middleware=[Middleware(GZipMiddleware)])
```
## Third party middleware
#### [asgi-auth-github](https://github.com/simonw/asgi-auth-github)
This middleware adds authentication to any ASGI application, requiring users to sign in
using their GitHub account (via [OAuth](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/)).
Access can be restricted to specific users or to members of specific GitHub organizations or teams.
#### [asgi-csrf](https://github.com/simonw/asgi-csrf)
Middleware for protecting against CSRF attacks. This middleware implements the Double Submit Cookie pattern, where a cookie is set, then it is compared to a csrftoken hidden form field or an `x-csrftoken` HTTP header.
#### [AuthlibMiddleware](https://github.com/aogier/starlette-authlib)
A drop-in replacement for Starlette session middleware, using [authlib's jwt](https://docs.authlib.org/en/latest/jose/jwt.html)
module.
#### [BugsnagMiddleware](https://github.com/ashinabraham/starlette-bugsnag)
A middleware class for logging exceptions to [Bugsnag](https://www.bugsnag.com/).
#### [CSRFMiddleware](https://github.com/frankie567/starlette-csrf)
Middleware for protecting against CSRF attacks. This middleware implements the Double Submit Cookie pattern, where a cookie is set, then it is compared to an `x-csrftoken` HTTP header.
#### [EarlyDataMiddleware](https://github.com/HarrySky/starlette-early-data)
Middleware and decorator for detecting and denying [TLSv1.3 early data](https://tools.ietf.org/html/rfc8470) requests.
#### [PrometheusMiddleware](https://github.com/perdy/starlette-prometheus)
A middleware class for capturing Prometheus metrics related to requests and responses, including in progress requests, timing...
#### [ProxyHeadersMiddleware](https://github.com/encode/uvicorn/blob/main/uvicorn/middleware/proxy_headers.py)
Uvicorn includes a middleware class for determining the client IP address,
when proxy servers are being used, based on the `X-Forwarded-Proto` and `X-Forwarded-For` headers. For more complex proxy configurations, you might want to adapt this middleware.
#### [RateLimitMiddleware](https://github.com/abersheeran/asgi-ratelimit)
A rate limit middleware. Regular expression matches url; flexible rules; highly customizable. Very easy to use.
#### [RequestIdMiddleware](https://github.com/snok/asgi-correlation-id)
A middleware class for reading/generating request IDs and attaching them to application logs.
#### [RollbarMiddleware](https://docs.rollbar.com/docs/starlette)
A middleware class for logging exceptions, errors, and log messages to [Rollbar](https://www.rollbar.com).
#### [StarletteOpentracing](https://github.com/acidjunk/starlette-opentracing)
A middleware class that emits tracing info to [OpenTracing.io](https://opentracing.io/) compatible tracers and
can be used to profile and monitor distributed applications.
#### [SecureCookiesMiddleware](https://github.com/thearchitector/starlette-securecookies)
Customizable middleware for adding automatic cookie encryption and decryption to Starlette applications, with
extra support for existing cookie-based middleware.
#### [TimingMiddleware](https://github.com/steinnes/timing-asgi)
A middleware class to emit timing information (cpu and wall time) for each request which
passes through it. Includes examples for how to emit these timings as statsd metrics.
#### [WSGIMiddleware](https://github.com/abersheeran/a2wsgi)
A middleware class in charge of converting a WSGI application into an ASGI one.
================================================
FILE: docs/overrides/main.html
================================================
{% extends "base.html" %}
{% block extrahead %}
{{ super() }}
<script>
// Redirect starlette.io to starlette.dev
if (window.location.hostname === 'www.starlette.io' || window.location.hostname === 'starlette.io') {
const newUrl = window.location.href.replace(/^https?:\/\/(www\.)?starlette\.io/, 'https://starlette.dev');
window.location.replace(newUrl);
}
</script>
{% endblock %}
{% block announce %}
<div class="announce-wrapper">
<div id="announce-msg">
<a class="announce-link announce-item" href="/sponsorship/">
❤️ Support Starlette via <strong>sponsors</strong>!
</a>
<a class="announce-link announce-item" href="https://github.com/Kludex/starlette/discussions/3099" target="_blank">
📚 Do you like the new docs? <strong>Let us know!</strong>
</a>
</div>
</div>
{% endblock %}
================================================
FILE: docs/overrides/partials/toc-item.html
================================================
<!-- Copied from https://github.com/squidfunk/mkdocs-material/issues/4827#issuecomment-1869812019 -->
<li class="md-nav__item"></li>
<a href="{{ toc_item.url }}" class="md-nav__link">
<span class="md-ellipsis">
{{ toc_item.title }}
</span>
</a>
<!-- Table of contents list -->
{% if toc_item.children %}
<nav class="md-nav" aria-label="{{ toc_item.title | striptags }}">
<ul class="md-nav__list">
{% for toc_item in toc_item.children %}
{% if not page.meta.toc_depth or toc_item.level <= page.meta.toc_depth %} {% include "partials/toc-item.html" %}
{% endif %} {% endfor %} </ul>
</nav>
{% endif %}
</li>
================================================
FILE: docs/release-notes.md
================================================
---
toc_depth: 2
---
## 1.0.0rc1 (February 23, 2026)
We're ready! I'm thrilled to announce the first release candidate for Starlette 1.0.
Starlette was created in June 2018 by Tom Christie, and has been on ZeroVer for years. Today, it's downloaded
almost [10 million times a day](https://pypistats.org/packages/starlette), serves as the foundation for FastAPI,
and has inspired many other frameworks. In the age of AI, Starlette continues to play an important role as a
dependency of the Python MCP SDK.
This release focuses on removing deprecated features that were marked for removal in 1.0.0, along with some
last minute bug fixes. It's a release candidate, so we can gather feedback from the community before the final
1.0.0 release soon.
A huge thank you to all the contributors who have helped make Starlette what it is today.
In particular, I'd like to recognize:
* [Kim Christie](https://github.com/lovelydinosaur) - The original creator of Starlette, Uvicorn, and MkDocs, and the
current maintainer of HTTPX. Kim's work helped lay the foundation for the modern async Python ecosystem.
* [Adrian Garcia Badaracco](https://github.com/adriangb) - One of the smartest people I know, whom I have the pleasure of working with at Pydantic.
* [Thomas Grainger](https://github.com/graingert) - My async teacher, always ready to help with questions.
* [Alex Grönholm](https://github.com/agronholm) - Another async mentor, always prompt to help with questions.
* [Florimond Manca](https://github.com/florimondmanca) - Always present in the early days of both Starlette and Uvicorn, and helped a lot in the ecosystem.
* [Amin Alaee](https://github.com/aminalaee) - Contributed a lot with file-related PRs.
* [Sebastián Ramírez](https://github.com/tiangolo) - Maintains FastAPI upstream, and always in contact to help with upstream issues.
* [Alex Oleshkevich](https://github.com/alex-oleshkevich) - Helped a lot on templates and many discussions.
* [abersheeran](https://github.com/abersheeran) - My go-to person when I need help on many subjects.
I'd also like to thank my sponsors for their support. A special thanks to
[@tiangolo](https://github.com/tiangolo), [@huggingface](https://github.com/huggingface),
and [@elevenlabs](https://github.com/elevenlabs) for their generous sponsorship, and to all my other sponsors:
[@roboflow](https://github.com/roboflow),
[@ogabrielluiz](https://github.com/ogabrielluiz),
[@SaboniAmine](https://github.com/SaboniAmine),
[@russbiggs](https://github.com/russbiggs),
[@BryceBeagle](https://github.com/BryceBeagle),
[@chdsbd](https://github.com/chdsbd),
[@TheR1D](https://github.com/TheR1D),
[@ddanier](https://github.com/ddanier),
[@larsyngvelundin](https://github.com/larsyngvelundin),
[@jpizquierdo](https://github.com/jpizquierdo),
[@alixlahuec](https://github.com/alixlahuec),
[@nathanchapman](https://github.com/nathanchapman),
[@devid8642](https://github.com/devid8642),
[@comet-ml](https://github.com/comet-ml),
[@Evil0ctal](https://github.com/Evil0ctal),
[@msehnout](https://github.com/msehnout),
and [@codingjoe](https://github.com/codingjoe).
#### Removed
* Remove `on_startup` and `on_shutdown` parameters from `Starlette` and `Router`.
Use the `lifespan` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `on_event()` decorator from `Starlette` and `Router`.
Use the `lifespan` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `add_event_handler()` method from `Starlette` and `Router`.
Use the `lifespan` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `startup()` and `shutdown()` methods from `Router`
[#3117](https://github.com/encode/starlette/pull/3117).
* Remove `@app.route()` decorator from `Starlette` and `Router`.
Use `Route` in the `routes` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `@app.websocket_route()` decorator from `Starlette` and `Router`.
Use `WebSocketRoute` in the `routes` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `@app.exception_handler()` decorator from `Starlette`.
Use `exception_handlers` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `@app.middleware()` decorator from `Starlette`.
Use `middleware` parameter instead [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `iscoroutinefunction_or_partial()` from `starlette.routing` [#3117](https://github.com/encode/starlette/pull/3117).
* Remove `**env_options` parameter from `Jinja2Templates`.
Use a preconfigured `jinja2.Environment` via the `env` parameter instead [#3118](https://github.com/encode/starlette/pull/3118).
* Remove deprecated `TemplateResponse(name, context)` signature from `Jinja2Templates`.
Use `TemplateResponse(request, name, ...)` instead [#3118](https://github.com/encode/starlette/pull/3118).
* Remove deprecated `method` parameter from `FileResponse` [#3147](https://github.com/encode/starlette/pull/3147).
#### Added
* Add state generic to `WebSocket` [#3132](https://github.com/encode/starlette/pull/3132).
#### Fixed
* Include `bytes` unit in `Content-Range` header on 416 responses.
* Handle null bytes in `StaticFiles` path [#3139](https://github.com/encode/starlette/pull/3139).
* Use sort-based merge for `Range` header parsing [#3138](https://github.com/encode/starlette/pull/3138).
* Set `Content-Type` instead of `Content-Range` on multi-range responses [#3142](https://github.com/encode/starlette/pull/3142).
* Use CRLF line endings in multipart byterange boundaries [#3143](https://github.com/encode/starlette/pull/3143).
* Avoid mutating `FileResponse` headers on range requests [#3144](https://github.com/encode/starlette/pull/3144).
* Return explicit origin in CORS response when credentials are allowed [#3137](https://github.com/encode/starlette/pull/3137).
* Enable `autoescape` by default in `Jinja2Templates` [#3148](https://github.com/encode/starlette/pull/3148).
#### Changed
* `jinja2` must now be installed to import `Jinja2Templates`. Previously it would only
fail when instantiating the class [#3118](https://github.com/encode/starlette/pull/3118).
## 0.52.1 (January 18, 2026)
#### Fixed
* Only use `typing_extensions` in older Python versions [#3109](https://github.com/Kludex/starlette/pull/3109).
## 0.52.0 (January 18, 2026)
In this release, `State` can be accessed using dictionary-style syntax for improved type
safety ([#3036](https://github.com/Kludex/starlette/pull/3036)).
```python
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import TypedDict
import httpx
from starlette.applications import Starlette
from starlette.requests import Request
class State(TypedDict):
http_client: httpx.AsyncClient
@asynccontextmanager
async def lifespan(app: Starlette) -> AsyncIterator[State]:
async with httpx.AsyncClient() as client:
yield {"http_client": client}
async def homepage(request: Request[State]):
client = request.state["http_client"]
# If you run the below line with mypy or pyright, it will reveal the correct type.
reveal_type(client) # Revealed type is 'httpx.AsyncClient'
```
See [Accessing State](lifespan.md#accessing-state) for more details.
## 0.51.0 (January 10, 2026)
#### Added
* Add `allow_private_network` in `CORSMiddleware` [#3065](https://github.com/Kludex/starlette/pull/3065).
#### Changed
* Increase warning stacklevel on `DeprecationWarning` for wsgi module [#3082](https://github.com/Kludex/starlette/pull/3082).
## 0.50.0 (November 1, 2025)
#### Removed
* Drop Python 3.9 support [#3061](https://github.com/Kludex/starlette/pull/3061).
## 0.49.3 (November 1, 2025)
This is the last release that supports Python 3.9, which will be dropped in the next minor release.
#### Fixed
* Relax strictness on `Middleware` type [#3059](https://github.com/Kludex/starlette/pull/3059).
## 0.49.2 (November 1, 2025)
#### Fixed
* Ignore `if-modified-since` header if `if-none-match` is present in `StaticFiles` [#3044](https://github.com/Kludex/starlette/pull/3044).
## 0.49.1 (October 28, 2025)
This release fixes a security vulnerability in the parsing logic of the `Range` header in `FileResponse`.
You can view the full security advisory: [GHSA-7f5h-v6xp-fcq8](https://github.com/Kludex/starlette/security/advisories/GHSA-7f5h-v6xp-fcq8)
#### Fixed
* Optimize the HTTP ranges parsing logic [4ea6e22b489ec388d6004cfbca52dd5b147127c5](https://github.com/Kludex/starlette/commit/4ea6e22b489ec388d6004cfbca52dd5b147127c5)
## 0.49.0 (October 28, 2025)
#### Added
* Add `encoding` parameter to `Config` class [#2996](https://github.com/Kludex/starlette/pull/2996).
* Support multiple cookie headers in `Request.cookies` [#3029](https://github.com/Kludex/starlette/pull/3029).
* Use `Literal` type for `WebSocketEndpoint` encoding values [#3027](https://github.com/Kludex/starlette/pull/3027).
#### Changed
* Do not pollute exception context in `Middleware` when using `BaseHTTPMiddleware` [#2976](https://github.com/Kludex/starlette/pull/2976).
## 0.48.0 (September 13, 2025)
#### Added
* Add official Python 3.14 support [#3013](https://github.com/Kludex/starlette/pull/3013).
#### Changed
* Implement [RFC9110](https://www.rfc-editor.org/rfc/rfc9110) http status names [#2939](https://github.com/Kludex/starlette/pull/2939).
## 0.47.3 (August 24, 2025)
#### Fixed
* Use `asyncio.iscoroutinefunction` for Python 3.12 and older [#2984](https://github.com/Kludex/starlette/pull/2984).
## 0.47.2 (July 20, 2025)
#### Fixed
* Make `UploadFile` check for future rollover [#2962](https://github.com/Kludex/starlette/pull/2962).
## 0.47.1 (June 21, 2025)
#### Fixed
* Use `Self` in `TestClient.__enter__` [#2951](https://github.com/Kludex/starlette/pull/2951).
* Allow async exception handlers to type-check [#2949](https://github.com/Kludex/starlette/pull/2949).
## 0.47.0 (May 29, 2025)
#### Added
* Add support for ASGI `pathsend` extension [#2671](https://github.com/Kludex/starlette/pull/2671).
* Add `partitioned` attribute to `Response.set_cookie` [#2501](https://github.com/Kludex/starlette/pull/2501).
#### Changed
* Change `methods` parameter type from `list[str]` to `Collection[str]`
[#2903](https://github.com/Kludex/starlette/pull/2903).
* Replace `import typing` by `from typing import ...` in the whole codebase
[#2867](https://github.com/Kludex/starlette/pull/2867).
#### Fixed
* Mark `ExceptionMiddleware.http_exception` as async to prevent thread creation
[#2922](https://github.com/Kludex/starlette/pull/2922).
## 0.46.2 (April 13, 2025)
#### Fixed
* Prevents reraising of exception from BaseHttpMiddleware [#2911](https://github.com/Kludex/starlette/pull/2911).
* Use correct index on backwards compatible logic in `TemplateResponse` [#2909](https://github.com/Kludex/starlette/pull/2909).
## 0.46.1 (March 8, 2025)
#### Fixed
* Allow relative directory path when `follow_symlinks=True` [#2896](https://github.com/Kludex/starlette/pull/2896).
## 0.46.0 (February 22, 2025)
#### Added
* `GZipMiddleware`: Make sure `Vary` header is always added if a response can be compressed [#2865](https://github.com/Kludex/starlette/pull/2865).
#### Fixed
* Raise exception from background task on BaseHTTPMiddleware [#2812](https://github.com/Kludex/starlette/pull/2812).
* `GZipMiddleware`: Don't compress on server sent events [#2871](https://github.com/Kludex/starlette/pull/2871).
#### Changed
* `MultiPartParser`: Rename `max_file_size` to `spool_max_size` [#2780](https://github.com/Kludex/starlette/pull/2780).
#### Deprecated
* Add deprecated warning to `TestClient(timeout=...)` [#2840](https://github.com/Kludex/starlette/pull/2840).
## 0.45.3 (January 24, 2025)
#### Fixed
* Turn directory into string on `lookup_path` on commonpath comparison [#2851](https://github.com/Kludex/starlette/pull/2851).
## 0.45.2 (January 4, 2025)
#### Fixed
* Make `create_memory_object_stream` compatible with old anyio versions once again, and bump anyio minimum version to 3.6.2 [#2833](https://github.com/Kludex/starlette/pull/2833).
## 0.45.1 (December 30, 2024)
#### Fixed
* Close `MemoryObjectReceiveStream` left unclosed upon exception in `BaseHTTPMiddleware` children [#2813](https://github.com/Kludex/starlette/pull/2813).
* Collect errors more reliably from the WebSocket logic on the `TestClient` [#2814](https://github.com/Kludex/starlette/pull/2814).
#### Refactor
* Use a pair of memory object streams instead of two queues on the `TestClient` [#2829](https://github.com/Kludex/starlette/pull/2829).
## 0.45.0 (December 29, 2024)
#### Removed
* Drop Python 3.8 support [#2823](https://github.com/Kludex/starlette/pull/2823).
* Remove `ExceptionMiddleware` import proxy from `starlette.exceptions` module [#2826](https://github.com/Kludex/starlette/pull/2826).
* Remove deprecated `WS_1004_NO_STATUS_RCVD` and `WS_1005_ABNORMAL_CLOSURE` [#2827](https://github.com/Kludex/starlette/pull/2827).
## 0.44.0 (December 28, 2024)
#### Added
* Add `client` parameter to `TestClient` [#2810](https://github.com/Kludex/starlette/pull/2810).
* Add `max_part_size` parameter to `Request.form()` [#2815](https://github.com/Kludex/starlette/pull/2815).
## 0.43.0 (December 25, 2024)
#### Removed
* Remove deprecated `allow_redirects` argument from `TestClient` [#2808](https://github.com/Kludex/starlette/pull/2808).
#### Added
* Make UUID path parameter conversion more flexible [#2806](https://github.com/Kludex/starlette/pull/2806).
## 0.42.0 (December 14, 2024)
#### Added
* Raise `ClientDisconnect` on `StreamingResponse` [#2732](https://github.com/Kludex/starlette/pull/2732).
#### Fixed
* Use ETag from headers when parsing If-Range in FileResponse [#2761](https://github.com/Kludex/starlette/pull/2761).
* Follow directory symlinks in `StaticFiles` when `follow_symlinks=True` [#2711](https://github.com/Kludex/starlette/pull/2711).
* Bump minimum `python-multipart` version to `0.0.18` [0ba8395](https://github.com/Kludex/starlette/commit/0ba83959e609bbd460966f092287df1bbd564cc6).
* Bump minimum `httpx` version to `0.27.0` [#2773](https://github.com/Kludex/starlette/pull/2773).
## 0.41.3 (November 18, 2024)
#### Fixed
* Exclude the query parameters from the `scope[raw_path]` on the `TestClient` [#2716](https://github.com/Kludex/starlette/pull/2716).
* Replace `dict` by `Mapping` on `HTTPException.headers` [#2749](https://github.com/Kludex/starlette/pull/2749).
* Correct middleware argument passing and improve factory pattern [#2752](https://github.com/Kludex/starlette/pull/2752).
## 0.41.2 (October 27, 2024)
#### Fixed
* Revert bump on `python-multipart` on `starlette[full]` extras [#2737](https://github.com/Kludex/starlette/pull/2737).
## 0.41.1 (October 24, 2024)
#### Fixed
* Bump minimum `python-multipart` version to `0.0.13` [#2734](https://github.com/Kludex/starlette/pull/2734).
* Change `python-multipart` import to `python_multipart` [#2733](https://github.com/Kludex/starlette/pull/2733).
## 0.41.0 (October 15, 2024)
#### Added
- Allow to raise `HTTPException` before `websocket.accept()` [#2725](https://github.com/Kludex/starlette/pull/2725).
## 0.40.0 (October 15, 2024)
This release fixes a Denial of service (DoS) via `multipart/form-data` requests.
You can view the full security advisory:
[GHSA-f96h-pmfr-66vw](https://github.com/Kludex/starlette/security/advisories/GHSA-f96h-pmfr-66vw)
#### Fixed
- Add `max_part_size` to `MultiPartParser` to limit the size of parts in `multipart/form-data`
requests [fd038f3](https://github.com/Kludex/starlette/commit/fd038f3070c302bff17ef7d173dbb0b007617733).
## 0.39.2 (September 29, 2024)
#### Fixed
- Allow use of `request.url_for` when only "app" scope is available [#2672](https://github.com/Kludex/starlette/pull/2672).
- Fix internal type hints to support `python-multipart==0.0.12` [#2708](https://github.com/Kludex/starlette/pull/2708).
## 0.39.1 (September 25, 2024)
#### Fixed
- Avoid regex re-compilation in `responses.py` and `schemas.py` [#2700](https://github.com/Kludex/starlette/pull/2700).
- Improve performance of `get_route_path` by removing regular expression usage
[#2701](https://github.com/Kludex/starlette/pull/2701).
- Consider `FileResponse.chunk_size` when handling multiple ranges [#2703](https://github.com/Kludex/starlette/pull/2703).
- Use `token_hex` for generating multipart boundary strings [#2702](https://github.com/Kludex/starlette/pull/2702).
## 0.39.0 (September 23, 2024)
#### Added
* Add support for [HTTP Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests) to
`FileResponse` [#2697](https://github.com/Kludex/starlette/pull/2697).
## 0.38.6 (September 22, 2024)
#### Fixed
* Close unclosed `MemoryObjectReceiveStream` in `TestClient` [#2693](https://github.com/Kludex/starlette/pull/2693).
## 0.38.5 (September 7, 2024)
#### Fixed
* Schedule `BackgroundTasks` from within `BaseHTTPMiddleware` [#2688](https://github.com/Kludex/starlette/pull/2688).
This behavior was removed in 0.38.3, and is now restored.
## 0.38.4 (September 1, 2024)
#### Fixed
* Ensure accurate `root_path` removal in `get_route_path` function [#2600](https://github.com/Kludex/starlette/pull/2600).
## 0.38.3 (September 1, 2024)
#### Added
* Support for Python 3.13 [#2662](https://github.com/Kludex/starlette/pull/2662).
#### Fixed
* Don't poll for disconnects in `BaseHTTPMiddleware` via `StreamingResponse` [#2620](https://github.com/Kludex/starlette/pull/2620).
## 0.38.2 (July 27, 2024)
#### Fixed
* Not assume all routines have `__name__` on `routing.get_name()` [#2648](https://github.com/Kludex/starlette/pull/2648).
## 0.38.1 (July 23, 2024)
#### Removed
* Revert "Add support for ASGI pathsend extension" [#2649](https://github.com/Kludex/starlette/pull/2649).
## 0.38.0 (July 20, 2024)
#### Added
* Allow use of `memoryview` in `StreamingResponse` and `Response` [#2576](https://github.com/Kludex/starlette/pull/2576)
and [#2577](https://github.com/Kludex/starlette/pull/2577).
* Send 404 instead of 500 when filename requested is too long on `StaticFiles` [#2583](https://github.com/Kludex/starlette/pull/2583).
#### Changed
* Fail fast on invalid `Jinja2Template` instantiation parameters [#2568](https://github.com/Kludex/starlette/pull/2568).
* Check endpoint handler is async only once [#2536](https://github.com/Kludex/starlette/pull/2536).
#### Fixed
* Add proper synchronization to `WebSocketTestSession` [#2597](https://github.com/Kludex/starlette/pull/2597).
## 0.37.2 (March 5, 2024)
#### Added
* Add `bytes` to `_RequestData` type [#2510](https://github.com/Kludex/starlette/pull/2510).
#### Fixed
* Revert "Turn `scope["client"]` to `None` on `TestClient` (#2377)" [#2525](https://github.com/Kludex/starlette/pull/2525).
* Remove deprecated `app` argument passed to `httpx.Client` on the `TestClient` [#2526](https://github.com/Kludex/starlette/pull/2526).
## 0.37.1 (February 9, 2024)
#### Fixed
* Warn instead of raise for missing env file on `Config` [#2485](https://github.com/Kludex/starlette/pull/2485).
## 0.37.0 (February 5, 2024)
#### Added
* Support the WebSocket Denial Response ASGI extension [#2041](https://github.com/Kludex/starlette/pull/2041).
## 0.36.3 (February 4, 2024)
#### Fixed
* Create `anyio.Event` on async context [#2459](https://github.com/Kludex/starlette/pull/2459).
## 0.36.2 (February 3, 2024)
#### Fixed
* Upgrade `python-multipart` to `0.0.7` [13e5c26](http://github.com/Kludex/starlette/commit/13e5c26a27f4903924624736abd6131b2da80cc5).
* Avoid duplicate charset on `Content-Type` [#2443](https://github.com/Kludex/starlette/2443).
## 0.36.1 (January 23, 2024)
#### Fixed
* Check if "extensions" in scope before checking the extension [#2438](http://github.com/Kludex/starlette/pull/2438).
## 0.36.0 (January 22, 2024)
#### Added
* Add support for ASGI `pathsend` extension [#2435](http://github.com/Kludex/starlette/pull/2435).
* Cancel `WebSocketTestSession` on close [#2427](http://github.com/Kludex/starlette/pull/2427).
* Raise `WebSocketDisconnect` when `WebSocket.send()` excepts `IOError` [#2425](http://github.com/Kludex/starlette/pull/2425).
* Raise `FileNotFoundError` when the `env_file` parameter on `Config` is not valid [#2422](http://github.com/Kludex/starlette/pull/2422).
## 0.35.1 (January 11, 2024)
#### Fixed
* Stop using the deprecated "method" parameter in `FileResponse` inside of `StaticFiles` [#2406](https://github.com/Kludex/starlette/pull/2406).
* Make `typing-extensions` optional again [#2409](https://github.com/Kludex/starlette/pull/2409).
## 0.35.0 (January 11, 2024)
#### Added
* Add `*args` to `Middleware` and improve its type hints [#2381](https://github.com/Kludex/starlette/pull/2381).
#### Fixed
* Use `Iterable` instead `Iterator` on `iterate_in_threadpool` [#2362](https://github.com/Kludex/starlette/pull/2362).
#### Changes
* Handle `root_path` to keep compatibility with mounted ASGI applications and WSGI [#2400](https://github.com/Kludex/starlette/pull/2400).
* Turn `scope["client"]` to `None` on `TestClient` [#2377](https://github.com/Kludex/starlette/pull/2377).
## 0.34.0 (December 16, 2023)
### Added
* Use `ParamSpec` for `run_in_threadpool` [#2375](https://github.com/Kludex/starlette/pull/2375).
* Add `UploadFile.__repr__` [#2360](https://github.com/Kludex/starlette/pull/2360).
### Fixed
* Merge URLs properly on `TestClient` [#2376](https://github.com/Kludex/starlette/pull/2376).
* Take weak ETags in consideration on `StaticFiles` [#2334](https://github.com/Kludex/starlette/pull/2334).
### Deprecated
* Deprecate `FileResponse(method=...)` parameter [#2366](https://github.com/Kludex/starlette/pull/2366).
## 0.33.0 (December 1, 2023)
### Added
* Add `middleware` per `Route`/`WebSocketRoute` [#2349](https://github.com/Kludex/starlette/pull/2349).
* Add `middleware` per `Router` [#2351](https://github.com/Kludex/starlette/pull/2351).
### Fixed
* Do not overwrite `"path"` and `"root_path"` scope keys [#2352](https://github.com/Kludex/starlette/pull/2352).
* Set `ensure_ascii=False` on `json.dumps()` for `WebSocket.send_json()` [#2341](https://github.com/Kludex/starlette/pull/2341).
## 0.32.0.post1 (November 5, 2023)
### Fixed
* Revert mkdocs-material from 9.1.17 to 9.4.7 [#2326](https://github.com/Kludex/starlette/pull/2326).
## 0.32.0 (November 4, 2023)
### Added
* Send `reason` on `WebSocketDisconnect` [#2309](https://github.com/Kludex/starlette/pull/2309).
* Add `domain` parameter to `SessionMiddleware` [#2280](https://github.com/Kludex/starlette/pull/2280).
### Changed
* Inherit from `HTMLResponse` instead of `Response` on `_TemplateResponse` [#2274](https://github.com/Kludex/starlette/pull/2274).
* Restore the `Response.render` type annotation to its pre-0.31.0 state [#2264](https://github.com/Kludex/starlette/pull/2264).
## 0.31.1 (August 26, 2023)
### Fixed
* Fix import error when `exceptiongroup` isn't available [#2231](https://github.com/Kludex/starlette/pull/2231).
* Set `url_for` global for custom Jinja environments [#2230](https://github.com/Kludex/starlette/pull/2230).
## 0.31.0 (July 24, 2023)
### Added
* Officially support Python 3.12 [#2214](https://github.com/Kludex/starlette/pull/2214).
* Support AnyIO 4.0 [#2211](https://github.com/Kludex/starlette/pull/2211).
* Strictly type annotate Starlette (strict mode on mypy) [#2180](https://github.com/Kludex/starlette/pull/2180).
### Fixed
* Don't group duplicated headers on a single string when using the `TestClient` [#2219](https://github.com/Kludex/starlette/pull/2219).
## 0.30.0 (July 13, 2023)
### Removed
* Drop Python 3.7 support [#2178](https://github.com/Kludex/starlette/pull/2178).
## 0.29.0 (July 13, 2023)
### Added
* Add `follow_redirects` parameter to `TestClient` [#2207](https://github.com/Kludex/starlette/pull/2207).
* Add `__str__` to `HTTPException` and `WebSocketException` [#2181](https://github.com/Kludex/starlette/pull/2181).
* Warn users when using `lifespan` together with `on_startup`/`on_shutdown` [#2193](https://github.com/Kludex/starlette/pull/2193).
* Collect routes from `Host` to generate the OpenAPI schema [#2183](https://github.com/Kludex/starlette/pull/2183).
* Add `request` argument to `TemplateResponse` [#2191](https://github.com/Kludex/starlette/pull/2191).
### Fixed
* Stop `body_stream` in case `more_body=False` on `BaseHTTPMiddleware` [#2194](https://github.com/Kludex/starlette/pull/2194).
## 0.28.0 (June 7, 2023)
### Changed
* Reuse `Request`'s body buffer for call_next in `BaseHTTPMiddleware` [#1692](https://github.com/Kludex/starlette/pull/1692).
* Move exception handling logic to `Route` [#2026](https://github.com/Kludex/starlette/pull/2026).
### Added
* Add `env` parameter to `Jinja2Templates`, and deprecate `**env_options` [#2159](https://github.com/Kludex/starlette/pull/2159).
* Add clear error message when `httpx` is not installed [#2177](https://github.com/Kludex/starlette/pull/2177).
### Fixed
* Allow "name" argument on `templates url_for()` [#2127](https://github.com/Kludex/starlette/pull/2127).
## 0.27.0 (May 16, 2023)
This release fixes a path traversal vulnerability in `StaticFiles`. You can view the full security advisory:
https://github.com/Kludex/starlette/security/advisories/GHSA-v5gw-mw7f-84px
### Added
* Minify JSON websocket data via `send_json` https://github.com/Kludex/starlette/pull/2128
### Fixed
* Replace `commonprefix` by `commonpath` on `StaticFiles` [1797de4](https://github.com/Kludex/starlette/commit/1797de464124b090f10cf570441e8292936d63e3).
* Convert ImportErrors into ModuleNotFoundError [#2135](https://github.com/Kludex/starlette/pull/2135).
* Correct the RuntimeError message content in websockets [#2141](https://github.com/Kludex/starlette/pull/2141).
## 0.26.1 (March 13, 2023)
### Fixed
* Fix typing of Lifespan to allow subclasses of Starlette [#2077](https://github.com/Kludex/starlette/pull/2077).
## 0.26.0.post1 (March 9, 2023)
### Fixed
* Replace reference from Events to Lifespan on the mkdocs.yml [#2072](https://github.com/Kludex/starlette/pull/2072).
## 0.26.0 (March 9, 2023)
### Added
* Support [lifespan state](lifespan.md) [#2060](https://github.com/Kludex/starlette/pull/2060),
[#2065](https://github.com/Kludex/starlette/pull/2065) and [#2064](https://github.com/Kludex/starlette/pull/2064).
### Changed
* Change `url_for` signature to return a `URL` instance [#1385](https://github.com/Kludex/starlette/pull/1385).
### Fixed
* Allow "name" argument on `url_for()` and `url_path_for()` [#2050](https://github.com/Kludex/starlette/pull/2050).
### Deprecated
* Deprecate `on_startup` and `on_shutdown` events [#2070](https://github.com/Kludex/starlette/pull/2070).
## 0.25.0 (February 14, 2023)
### Fix
* Limit the number of fields and files when parsing `multipart/form-data` on the `MultipartParser` [8c74c2c](https://github.com/Kludex/starlette/commit/8c74c2c8dba7030154f8af18e016136bea1938fa) and [#2036](https://github.com/Kludex/starlette/pull/2036).
## 0.24.0 (February 6, 2023)
### Added
* Allow `StaticFiles` to follow symlinks [#1683](https://github.com/Kludex/starlette/pull/1683).
* Allow `Request.form()` as a context manager [#1903](https://github.com/Kludex/starlette/pull/1903).
* Add `size` attribute to `UploadFile` [#1405](https://github.com/Kludex/starlette/pull/1405).
* Add `env_prefix` argument to `Config` [#1990](https://github.com/Kludex/starlette/pull/1990).
* Add template context processors [#1904](https://github.com/Kludex/starlette/pull/1904).
* Support `str` and `datetime` on `expires` parameter on the `Response.set_cookie` method [#1908](https://github.com/Kludex/starlette/pull/1908).
### Changed
* Lazily build the middleware stack [#2017](https://github.com/Kludex/starlette/pull/2017).
* Make the `file` argument required on `UploadFile` [#1413](https://github.com/Kludex/starlette/pull/1413).
* Use debug extension instead of custom response template extension [#1991](https://github.com/Kludex/starlette/pull/1991).
### Fixed
* Fix url parsing of ipv6 urls on `URL.replace` [#1965](https://github.com/Kludex/starlette/pull/1965).
## 0.23.1 (December 9, 2022)
### Fixed
* Only stop receiving stream on `body_stream` if body is empty on the `BaseHTTPMiddleware` [#1940](https://github.com/Kludex/starlette/pull/1940).
## 0.23.0 (December 5, 2022)
### Added
* Add `headers` parameter to the `TestClient` [#1966](https://github.com/Kludex/starlette/pull/1966).
### Deprecated
* Deprecate `Starlette` and `Router` decorators [#1897](https://github.com/Kludex/starlette/pull/1897).
### Fixed
* Fix bug on `FloatConvertor` regex [#1973](https://github.com/Kludex/starlette/pull/1973).
## 0.22.0 (November 17, 2022)
### Changed
* Bypass `GZipMiddleware` when response includes `Content-Encoding` [#1901](https://github.com/Kludex/starlette/pull/1901).
### Fixed
* Remove unneeded `unquote()` from query parameters on the `TestClient` [#1953](https://github.com/Kludex/starlette/pull/1953).
* Make sure `MutableHeaders._list` is actually a `list` [#1917](https://github.com/Kludex/starlette/pull/1917).
* Import compatibility with the next version of `AnyIO` [#1936](https://github.com/Kludex/starlette/pull/1936).
## 0.21.0 (September 26, 2022)
This release replaces the underlying HTTP client used on the `TestClient` (`requests` :arrow_right: `httpx`), and as those clients [differ _a bit_ on their API](https://www.python-httpx.org/compatibility/), your test suite will likely break. To make the migration smoother, you can use the [`bump-testclient`](https://github.com/Kludex/bump-testclient) tool.
### Changed
* Replace `requests` with `httpx` in `TestClient` [#1376](https://github.com/Kludex/starlette/pull/1376).
### Added
* Add `WebSocketException` and support for WebSocket exception handlers [#1263](https://github.com/Kludex/starlette/pull/1263).
* Add `middleware` parameter to `Mount` class [#1649](https://github.com/Kludex/starlette/pull/1649).
* Officially support Python 3.11 [#1863](https://github.com/Kludex/starlette/pull/1863).
* Implement `__repr__` for route classes [#1864](https://github.com/Kludex/starlette/pull/1864).
### Fixed
* Fix bug on which `BackgroundTasks` were cancelled when using `BaseHTTPMiddleware` and client disconnected [#1715](https://github.com/Kludex/starlette/pull/1715).
## 0.20.4 (June 28, 2022)
### Fixed
* Remove converter from path when generating OpenAPI schema [#1648](https://github.com/Kludex/starlette/pull/1648).
## 0.20.3 (June 10, 2022)
### Fixed
* Revert "Allow `StaticFiles` to follow symlinks" [#1681](https://github.com/Kludex/starlette/pull/1681).
## 0.20.2 (June 7, 2022)
### Fixed
* Fix regression on route paths with colons [#1675](https://github.com/Kludex/starlette/pull/1675).
* Allow `StaticFiles` to follow symlinks [#1337](https://github.com/Kludex/starlette/pull/1377).
## 0.20.1 (May 28, 2022)
### Fixed
* Improve detection of async callables [#1444](https://github.com/Kludex/starlette/pull/1444).
* Send 400 (Bad Request) when `boundary` is missing [#1617](https://github.com/Kludex/starlette/pull/1617).
* Send 400 (Bad Request) when missing "name" field on `Content-Disposition` header [#1643](https://github.com/Kludex/starlette/pull/1643).
* Do not send empty data to `StreamingResponse` on `BaseHTTPMiddleware` [#1609](https://github.com/Kludex/starlette/pull/1609).
* Add `__bool__` dunder for `Secret` [#1625](https://github.com/Kludex/starlette/pull/1625).
## 0.20.0 (May 3, 2022)
### Removed
* Drop Python 3.6 support [#1357](https://github.com/Kludex/starlette/pull/1357) and [#1616](https://github.com/Kludex/starlette/pull/1616).
## 0.19.1 (April 22, 2022)
### Fixed
* Fix inference of `Route.name` when created from methods [#1553](https://github.com/Kludex/starlette/pull/1553).
* Avoid `TypeError` on `websocket.disconnect` when code is `None` [#1574](https://github.com/Kludex/starlette/pull/1574).
### Deprecated
* Deprecate `WS_1004_NO_STATUS_RCVD` and `WS_1005_ABNORMAL_CLOSURE` in favor of `WS_1005_NO_STATUS_RCVD` and `WS_1006_ABNORMAL_CLOSURE`, as the previous constants didn't match the [WebSockets specs](https://www.iana.org/assignments/websocket/websocket.xhtml) [#1580](https://github.com/Kludex/starlette/pull/1580).
## 0.19.0 (March 9, 2022)
### Added
* Error handler will always run, even if the error happens on a background task [#761](https://github.com/Kludex/starlette/pull/761).
* Add `headers` parameter to `HTTPException` [#1435](https://github.com/Kludex/starlette/pull/1435).
* Internal responses with `405` status code insert an `Allow` header, as described by [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.5) [#1436](https://github.com/Kludex/starlette/pull/1436).
* The `content` argument in `JSONResponse` is now required [#1431](https://github.com/Kludex/starlette/pull/1431).
* Add custom URL convertor register [#1437](https://github.com/Kludex/starlette/pull/1437).
* Add content disposition type parameter to `FileResponse` [#1266](https://github.com/Kludex/starlette/pull/1266).
* Add next query param with original request URL in requires decorator [#920](https://github.com/Kludex/starlette/pull/920).
* Add `raw_path` to `TestClient` scope [#1445](https://github.com/Kludex/starlette/pull/1445).
* Add union operators to `MutableHeaders` [#1240](https://github.com/Kludex/starlette/pull/1240).
* Display missing route details on debug page [#1363](https://github.com/Kludex/starlette/pull/1363).
* Change `anyio` required version range to `>=3.4.0,<5.0` [#1421](https://github.com/Kludex/starlette/pull/1421) and [#1460](https://github.com/Kludex/starlette/pull/1460).
* Add `typing-extensions>=3.10` requirement - used only on lower versions than Python 3.10 [#1475](https://github.com/Kludex/starlette/pull/1475).
### Fixed
* Prevent `BaseHTTPMiddleware` from hiding errors of `StreamingResponse` and mounted applications [#1459](https://github.com/Kludex/starlette/pull/1459).
* `SessionMiddleware` uses an explicit `path=...`, instead of defaulting to the ASGI 'root_path' [#1512](https://github.com/Kludex/starlette/pull/1512).
* `Request.client` is now compliant with the ASGI specifications [#1462](https://github.com/Kludex/starlette/pull/1462).
* Raise `KeyError` at early stage for missing boundary [#1349](https://github.com/Kludex/starlette/pull/1349).
### Deprecated
* Deprecate WSGIMiddleware in favor of a2wsgi [#1504](https://github.com/Kludex/starlette/pull/1504).
* Deprecate `run_until_first_complete` [#1443](https://github.com/Kludex/starlette/pull/1443).
## 0.18.0 (January 23, 2022)
### Added
* Change default chunk size from 4Kb to 64Kb on `FileResponse` [#1345](https://github.com/Kludex/starlette/pull/1345).
* Add support for `functools.partial` in `WebSocketRoute` [#1356](https://github.com/Kludex/starlette/pull/1356).
* Add `StaticFiles` packages with directory [#1350](https://github.com/Kludex/starlette/pull/1350).
* Allow environment options in `Jinja2Templates` [#1401](https://github.com/Kludex/starlette/pull/1401).
* Allow HEAD method on `HttpEndpoint` [#1346](https://github.com/Kludex/starlette/pull/1346).
* Accept additional headers on `websocket.accept` message [#1361](https://github.com/Kludex/starlette/pull/1361) and [#1422](https://github.com/Kludex/starlette/pull/1422).
* Add `reason` to `WebSocket` close ASGI event [#1417](https://github.com/Kludex/starlette/pull/1417).
* Add headers attribute to `UploadFile` [#1382](https://github.com/Kludex/starlette/pull/1382).
* Don't omit `Content-Length` header for `Content-Length: 0` cases [#1395](https://github.com/Kludex/starlette/pull/1395).
* Don't set headers for responses with 1xx, 204 and 304 status code [#1397](https://github.com/Kludex/starlette/pull/1397).
* `SessionMiddleware.max_age` now accepts `None`, so cookie can last as long as the browser session [#1387](https://github.com/Kludex/starlette/pull/1387).
### Fixed
* Tweak `hashlib.md5()` function on `FileResponse`s ETag generation. The parameter [`usedforsecurity`](https://bugs.python.org/issue9216) flag is set to `False`, if the flag is available on the system. This fixes an error raised on systems with [FIPS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/FIPS_Mode_-_an_explanation) enabled [#1366](https://github.com/Kludex/starlette/pull/1366) and [#1410](https://github.com/Kludex/starlette/pull/1410).
* Fix `path_params` type on `url_path_for()` method i.e. turn `str` into `Any` [#1341](https://github.com/Kludex/starlette/pull/1341).
* `Host` now ignores `port` on routing [#1322](https://github.com/Kludex/starlette/pull/1322).
## 0.17.1 (November 17, 2021)
### Fixed
* Fix `IndexError` in authentication `requires` when wrapped function arguments are distributed between `*args` and `**kwargs` [#1335](https://github.com/Kludex/starlette/pull/1335).
## 0.17.0 (November 4, 2021)
### Added
* `Response.delete_cookie` now accepts the same parameters as `Response.set_cookie` [#1228](https://github.com/Kludex/starlette/pull/1228).
* Update the `Jinja2Templates` constructor to allow `PathLike` [#1292](https://github.com/Kludex/starlette/pull/1292).
### Fixed
* Fix BadSignature exception handling in SessionMiddleware [#1264](https://github.com/Kludex/starlette/pull/1264).
* Change `HTTPConnection.__getitem__` return type from `str` to `typing.Any` [#1118](https://github.com/Kludex/starlette/pull/1118).
* Change `ImmutableMultiDict.getlist` return type from `typing.List[str]` to `typing.List[typing.Any]` [#1235](https://github.com/Kludex/starlette/pull/1235).
* Handle `OSError` exceptions on `StaticFiles` [#1220](https://github.com/Kludex/starlette/pull/1220).
* Fix `StaticFiles` 404.html in HTML mode [#1314](https://github.com/Kludex/starlette/pull/1314).
* Prevent anyio.ExceptionGroup in error views under a BaseHTTPMiddleware [#1262](https://github.com/Kludex/starlette/pull/1262).
### Removed
* Remove GraphQL support [#1198](https://github.com/Kludex/starlette/pull/1198).
## 0.16.0 (July 19, 2021)
### Added
* Added [Encode](https://github.com/sponsors/encode) funding option
[#1219](https://github.com/Kludex/starlette/pull/1219)
### Fixed
* `starlette.websockets.WebSocket` instances are now hashable and compare by identity
[#1039](https://github.com/Kludex/starlette/pull/1039)
* A number of fixes related to running task groups in lifespan
[#1213](https://github.com/Kludex/starlette/pull/1213),
[#1227](https://github.com/Kludex/starlette/pull/1227)
### Deprecated/removed
* The method `starlette.templates.Jinja2Templates.get_env` was removed
[#1218](https://github.com/Kludex/starlette/pull/1218)
* The ClassVar `starlette.testclient.TestClient.async_backend` was removed,
the backend is now configured using constructor kwargs
[#1211](https://github.com/Kludex/starlette/pull/1211)
* Passing an Async Generator Function or a Generator Function to `starlette.routing.Router(lifespan=)` is deprecated. You should wrap your lifespan in `@contextlib.asynccontextmanager`.
[#1227](https://github.com/Kludex/starlette/pull/1227)
[#1110](https://github.com/Kludex/starlette/pull/1110)
## 0.15.0 (June 23, 2021)
This release includes major changes to the low-level asynchronous parts of Starlette. As a result,
**Starlette now depends on [AnyIO](https://anyio.readthedocs.io/en/stable/)** and some minor API
changes have occurred. Another significant change with this release is the
**deprecation of built-in GraphQL support**.
### Added
* Starlette now supports [Trio](https://trio.readthedocs.io/en/stable/) as an async runtime via
AnyIO - [#1157](https://github.com/Kludex/starlette/pull/1157).
* `TestClient.websocket_connect()` now must be used as a context manager.
* Initial support for Python 3.10 - [#1201](https://github.com/Kludex/starlette/pull/1201).
* The compression level used in `GZipMiddleware` is now adjustable -
[#1128](https://github.com/Kludex/starlette/pull/1128).
### Fixed
* Several fixes to `CORSMiddleware`. See [#1111](https://github.com/Kludex/starlette/pull/1111),
[#1112](https://github.com/Kludex/starlette/pull/1112),
[#1113](https://github.com/Kludex/starlette/pull/1113),
[#1199](https://github.com/Kludex/starlette/pull/1199).
* Improved exception messages in the case of duplicated path parameter names -
[#1177](https://github.com/Kludex/starlette/pull/1177).
* `RedirectResponse` now uses `quote` instead of `quote_plus` encoding for the `Location` header
to better match the behaviour in other frameworks such as Django -
[#1164](https://github.com/Kludex/starlette/pull/1164).
* Exception causes are now preserved in more cases -
[#1158](https://github.com/Kludex/starlette/pull/1158).
* Session cookies now use the ASGI root path in the case of mounted applications -
[#1147](https://github.com/Kludex/starlette/pull/1147).
* Fixed a cache invalidation bug when static files were deleted in certain circumstances -
[#1023](https://github.com/Kludex/starlette/pull/1023).
* Improved memory usage of `BaseHTTPMiddleware` when handling large responses -
[#1012](https://github.com/Kludex/starlette/issues/1012) fixed via #1157
### Deprecated/removed
* Built-in GraphQL support via the `GraphQLApp` class has been deprecated and will be removed in a
future release. Please see [#619](https://github.com/Kludex/starlette/issues/619). GraphQL is not
supported on Python 3.10.
* The `executor` parameter to `GraphQLApp` was removed. Use `executor_class` instead.
* The `workers` parameter to `WSGIMiddleware` was removed. This hasn't had any effect since
Starlette v0.6.3.
## 0.14.2 (February 2, 2021)
### Fixed
* Fixed `ServerErrorMiddleware` compatibility with Python 3.9.1/3.8.7 when debug mode is enabled -
[#1132](https://github.com/Kludex/starlette/pull/1132).
* Fixed unclosed socket `ResourceWarning`s when using the `TestClient` with WebSocket endpoints -
#1132.
* Improved detection of `async` endpoints wrapped in `functools.partial` on Python 3.8+ -
[#1106](https://github.com/Kludex/starlette/pull/1106).
## 0.14.1 (November 9th, 2020)
### Removed
* `UJSONResponse` was removed (this change was intended to be included in 0.14.0). Please see the
[documentation](https://starlette.dev/responses/#custom-json-serialization) for how to
implement responses using custom JSON serialization -
[#1074](https://github.com/Kludex/starlette/pull/1047).
## 0.14.0 (November 8th, 2020)
### Added
* Starlette now officially supports Python3.9.
* In `StreamingResponse`, allow custom async iterator such as objects from classes implementing `__aiter__`.
* Allow usage of `functools.partial` async handlers in Python versions 3.6 and 3.7.
* Add 418 I'm A Teapot status code.
### Changed
* Create tasks from handler coroutines before sending them to `asyncio.wait`.
* Use `format_exception` instead of `format_tb` in `ServerErrorMiddleware`'s `debug` responses.
* Be more lenient with handler arguments when using the `requires` decorator.
## 0.13.8
* Revert `Queue(maxsize=1)` fix for `BaseHTTPMiddleware` middleware classes and streaming responses.
* The `StaticFiles` constructor now allows `pathlib.Path` in addition to strings for its `directory` argument.
## 0.13.7
* Fix high memory usage when using `BaseHTTPMiddleware` middleware classes and streaming responses.
## 0.13.6
* Fix 404 errors with `StaticFiles`.
## 0.13.5
* Add support for `Starlette(lifespan=...)` functions.
* More robust path-traversal check in StaticFiles app.
* Fix WSGI PATH_INFO encoding.
* RedirectResponse now accepts optional background parameter
* Allow path routes to contain regex meta characters
* Treat ASGI HTTP 'body' as an optional key.
* Don't use thread pooling for writing to in-memory upload files.
## 0.13.0
* Switch to promoting application configuration on init style everywhere.
This means dropping the decorator style in favour of declarative routing
tables and middleware definitions.
## 0.12.12
* Fix `request.url_for()` for the Mount-within-a-Mount case.
## 0.12.11
* Fix `request.url_for()` when an ASGI `root_path` is being used.
## 0.12.1
* Add `URL.include_query_params(**kwargs)`
* Add `URL.replace_query_params(**kwargs)`
* Add `URL.remove_query_params(param_names)`
* `request.state` properly persisting across middleware.
* Added `request.scope` interface.
## 0.12.0
* Switch to ASGI 3.0.
* Fixes to CORS middleware.
* Add `StaticFiles(html=True)` support.
* Fix path quoting in redirect responses.
## 0.11.1
* Add `request.state` interface, for storing arbitrary additional information.
* Support disabling GraphiQL with `GraphQLApp(..., graphiql=False)`.
## 0.11.0
* `DatabaseMiddleware` is now dropped in favour of `databases`
* Templates are no longer configured on the application instance. Use `templates = Jinja2Templates(directory=...)` and `return templates.TemplateResponse('index.html', {"request": request})`
* Schema generation is no longer attached to the application instance. Use `schemas = SchemaGenerator(...)` and `return schemas.OpenAPIResponse(request=request)`
* `LifespanMiddleware` is dropped in favor of router-based lifespan handling.
* Application instances now accept a `routes` argument, `Starlette(routes=[...])`
* Schema generation now includes mounted routes.
## 0.10.6
* Add `Lifespan` routing component.
## 0.10.5
* Ensure `templating` does not strictly require `jinja2` to be installed.
## 0.10.4
* Templates are now configured independently from the application instance. `templates = Jinja2Templates(directory=...)`. Existing API remains in place, but is no longer documented,
and will be deprecated in due course. See the template documentation for more details.
## 0.10.3
* Move to independent `databases` package instead of `DatabaseMiddleware`. Existing API
remains in place, but is no longer documented, and will be deprecated in due course.
## 0.10.2
* Don't drop explicit port numbers on redirects from `HTTPSRedirectMiddleware`.
## 0.10.1
* Add MySQL database support.
* Add host-based routing.
## 0.10.0
* WebSockets now default to sending/receiving JSON over text data frames. Use `.send_json(data, mode="binary")` and `.receive_json(mode="binary")` for binary framing.
* `GraphQLApp` now takes an `executor_class` argument, which should be used in preference to the existing `executor` argument. Resolves an issue with async executors being instantiated before the event loop was setup. The `executor` argument is expected to be deprecated in the next median or major release.
* Authentication and the `@requires` decorator now support WebSocket endpoints.
* `MultiDict` and `ImmutableMultiDict` classes are available in `uvicorn.datastructures`.
* `QueryParams` is now instantiated with standard dict-style `*args, **kwargs` arguments.
## 0.9.11
* Session cookies now include browser 'expires', in addition to the existing signed expiry.
* `request.form()` now returns a multi-dict interface.
* The query parameter multi-dict implementation now mirrors `dict` more correctly for the
behavior of `.keys()`, `.values()`, and `.items()` when multiple same-key items occur.
* Use `urlsplit` throughout in favor of `urlparse`.
## 0.9.10
* Support `@requires(...)` on class methods.
* Apply URL escaping to form data.
* Support `HEAD` requests automatically.
* Add `await request.is_disconnected()`.
* Pass operationName to GraphQL executor.
## 0.9.9
* Add `TemplateResponse`.
* Add `CommaSeparatedStrings` datatype.
* Add `BackgroundTasks` for multiple tasks.
* Common subclass for `Request` and `WebSocket`, to eg. share `session` functionality.
* Expose remote address with `request.client`.
## 0.9.8
* Add `request.database.executemany`.
## 0.9.7
* Ensure that `AuthenticationMiddleware` handles lifespan messages correctly.
## 0.9.6
* Add `AuthenticationMiddleware`, and `@requires()` decorator.
## 0.9.5
* Support either `str` or `Secret` for `SessionMiddleware(secret_key=...)`.
## 0.9.4
* Add `config.environ`.
* Add `datastructures.Secret`.
* Add `datastructures.DatabaseURL`.
## 0.9.3
* Add `config.Config(".env")`
## 0.9.2
* Add optional database support.
* Add `request` to GraphQL context.
* Hide any password component in `URL.__repr__`.
## 0.9.1
* Handle startup/shutdown errors properly.
## 0.9.0
* `TestClient` can now be used as a context manager, instead of `LifespanContext`.
* Lifespan is now handled as middleware. Startup and Shutdown events are
visible throughout the middleware stack.
## 0.8.8
* Better support for third-party API schema generators.
## 0.8.7
* Support chunked requests with TestClient.
* Cleanup asyncio tasks properly with WSGIMiddleware.
* Support using TestClient within endpoints, for service mocking.
## 0.8.6
* Session cookies are now set on the root path.
## 0.8.5
* Support URL convertors.
* Support HTTP 304 cache responses from `StaticFiles`.
* Resolve character escaping issue with form data.
## 0.8.4
* Default to empty body on responses.
## 0.8.3
* Add 'name' argument to `@app.route()`.
* Use 'Host' header for URL reconstruction.
## 0.8.2
### StaticFiles
* StaticFiles no longer reads the file for responses to `HEAD` requests.
## 0.8.1
### Templating
* Add a default templating configuration with Jinja2.
Allows the following:
```python
app = Starlette(template_directory="templates")
@app.route('/')
async def homepage(request):
# `url_for` is available inside the template.
template = app.get_template('index.html')
content = template.render(request=request)
return HTMLResponse(content)
```
## 0.8.0
### Exceptions
* Add support for `@app.exception_handler(404)`.
* Ensure handled exceptions are not seen as errors by the middleware stack.
### SessionMiddleware
* Add `max_age`, and use timestamp-signed cookies. Defaults to two weeks.
### Cookies
* Ensure cookies are strictly HTTP correct.
### StaticFiles
* Check directory exists on instantiation.
## 0.7.4
### Concurrency
* Add `starlette.concurrency.run_in_threadpool`. Now handles `contextvar` support.
## 0.7.3
### Routing
* Add `name=` support to `app.mount()`. This allows eg: `app.mount('/static', StaticFiles(directory='static'), name='static')`.
## 0.7.2
### Middleware
* Add support for `@app.middleware("http")` decorator.
### Routing
* Add "endpoint" to ASGI scope.
## 0.7.1
### Debug tracebacks
* Improve debug traceback information & styling.
### URL routing
* Support mounted URL lookups with "path=", eg. `url_for('static', path=...)`.
* Support nested URL lookups, eg. `url_for('admin:user', username=...)`.
* Add redirect slashes support.
* Add www redirect support.
### Background tasks
* Add background task support to `FileResponse` and `StreamingResponse`.
## 0.7.0
### API Schema support
* Add `app.schema_generator = SchemaGenerator(...)`.
* Add `app.schema` property.
* Add `OpenAPIResponse(...)`.
### GraphQL routing
* Drop `app.add_graphql_route("/", ...)` in favor of more consistent `app.add_route("/", GraphQLApp(...))`.
## 0.6.3
### Routing API
* Support routing to methods.
* Ensure `url_path_for` works with Mount('/{some_path_params}').
* Fix Router(default=) argument.
* Support repeated paths, like: `@app.route("/", methods=["GET"])`, `@app.route("/", methods=["POST"])`
* Use the default ThreadPoolExecutor for all sync endpoints.
## 0.6.2
### SessionMiddleware
Added support for `request.session`, with `SessionMiddleware`.
## 0.6.1
### BaseHTTPMiddleware
Added support for `BaseHTTPMiddleware`, which provides a standard
request/response interface over a regular ASGI middleware.
This means you can write ASGI middleware while still working at
a request/response level, rather than handling ASGI messages directly.
```python
from starlette.applications import Starlette
from starlette.middleware.base import BaseHTTPMiddleware
class CustomMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
response.headers['Custom-Header'] = 'Example'
return response
app = Starlette()
app.add_middleware(CustomMiddleware)
```
## 0.6.0
### request.path_params
The biggest change in 0.6 is that endpoint signatures are no longer:
```python
async def func(request: Request, **kwargs) -> Response
```
Instead we just use:
```python
async def func(request: Request) -> Response
```
The path parameters are available on the request as `request.path_params`.
This is different to most Python webframeworks, but I think it actually ends up
being much more nicely consistent all the way through.
### request.url_for()
Request and WebSocketSession now support URL reversing with `request.url_for(name, **path_params)`.
This method returns a fully qualified `URL` instance.
The URL instance is a string-like object.
### app.url_path_for()
Applications now support URL path reversing with `app.url_path_for(name, **path_params)`.
This method returns a `URL` instance with the path and scheme set.
The URL instance is a string-like object, and will return only the path if coerced to a string.
### app.routes
Applications now support a `.routes` parameter, which returns a list of `[Route|WebSocketRoute|Mount]`.
### Route, WebSocketRoute, Mount
The low level components to `Router` now match the `@app.route()`, `@app.websocket_route()`, and `app.mount()` signatures.
================================================
FILE: docs/requests.md
================================================
Starlette includes a `Request` class that gives you a nicer interface onto
the incoming request, rather than accessing the ASGI scope and receive channel directly.
### Request
Signature: `Request(scope, receive=None)`
```python
from starlette.requests import Request
from starlette.responses import Response
async def app(scope, receive, send):
assert scope['type'] == 'http'
request = Request(scope, receive)
content = '%s %s' % (request.method, request.url.path)
response = Response(content, media_type='text/plain')
await response(scope, receive, send)
```
Requests present a mapping interface, so you can use them in the same
way as a `scope`.
For instance: `request['path']` will return the ASGI path.
If you don't need to access the request body you can instantiate a request
without providing an argument to `receive`.
#### Method
The request method is accessed as `request.method`.
#### URL
The request URL is accessed as `request.url`.
The property is a string-like object that exposes all the
components that can be parsed out of the URL.
For example: `request.url.path`, `request.url.port`, `request.url.scheme`.
#### Headers
Headers are exposed as an immutable, case-insensitive, multi-dict.
For example: `request.headers['content-type']`
#### Query Parameters
Query parameters are exposed as an immutable multi-dict.
For example: `request.query_params['search']`
#### Path Parameters
Router path parameters are exposed as a dictionary interface.
For example: `request.path_params['username']`
#### Client Address
The client's remote address is exposed as a named two-tuple `request.client` (or `None`).
The hostname or IP address: `request.client.host`
The port number from which the client is connecting: `request.client.port`
#### Cookies
Cookies are exposed as a regular dictionary interface.
For example: `request.cookies.get('mycookie')`
Cookies are ignored in case of an invalid cookie. (RFC2109)
#### Body
There are a few different interfaces for returning the body of the request:
The request body as bytes: `await request.body()`
The request body, parsed as form data or multipart: `async with request.form() as form:`
The request body, parsed as JSON: `await request.json()`
You can also access the request body as a stream, using the `async for` syntax:
```python
from starlette.requests import Request
from starlette.responses import Response
async def app(scope, receive, send):
assert scope['type'] == 'http'
request = Request(scope, receive)
body = b''
async for chunk in request.stream():
body += chunk
response = Response(body, media_type='text/plain')
await response(scope, receive, send)
```
If you access `.stream()` then the byte chunks are provided without storing
the entire body to memory. Any subsequent calls to `.body()`, `.form()`, or `.json()`
will raise an error.
In some cases such as long-polling, or streaming responses you might need to
determine if the client has dropped the connection. You can determine this
state with `disconnected = await request.is_disconnected()`.
#### Request Files
Request files are normally sent as multipart form data (`multipart/form-data`).
Signature: `request.form(max_files=1000, max_fields=1000, max_part_size=1024*1024)`
You can configure the number of maximum fields or files with the parameters `max_files` and `max_fields`; and part size using `max_part_size`:
```python
async with request.form(max_files=1000, max_fields=1000, max_part_size=1024*1024):
...
```
!!! info
These limits are for security reasons, allowing an unlimited number of fields or files could lead to a denial of service attack by consuming a lot of CPU and memory parsing too many empty fields.
When you call `async with request.form() as form` you receive a `starlette.datastructures.FormData` which is an immutable
multidict, containing both file uploads and text input. File upload items are represented as instances of `starlette.datastructures.UploadFile`.
`UploadFile` has the following attributes:
* `filename`: An `str` with the original file name that was uploaded or `None` if its not available (e.g. `myimage.jpg`).
* `content_type`: An `str` with the content type (MIME type / media type) or `None` if it's not available (e.g. `image/jpeg`).
* `file`: A <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" target="_blank">`SpooledTemporaryFile`</a> (a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> object). This is the actual Python file that you can pass directly to other functions or libraries that expect a "file-like" object.
* `headers`: A `Headers` object. Often this will only be the `Content-Type` header, but if additional headers were included in the multipart field they will be included here. Note that these headers have no relationship with the headers in `Request.headers`.
* `size`: An `int` with uploaded file's size in bytes. This value is calculated from request's contents, making it better choice to find uploaded file's size than `Content-Length` header. `None` if not set.
`UploadFile` has the following `async` methods. They all call the corresponding file methods underneath (using the internal `SpooledTemporaryFile`).
* `async write(data)`: Writes `data` (`bytes`) to the file.
* `async read(size)`: Reads `size` (`int`) bytes of the file.
* `async seek(offset)`: Goes to the byte position `offset` (`int`) in the file.
* E.g., `await myfile.seek(0)` would go to the start of the file.
* `async close()`: Closes the file.
As all these methods are `async` methods, you need to "await" them.
For example, you can get the file name and the contents with:
```python
async with request.form() as form:
filename = form["upload_file"].filename
contents = await form["upload_file"].read()
```
!!! info
As settled in [RFC-7578: 4.2](https://www.ietf.org/rfc/rfc7578.txt), form-data content part that contains file
assumed to have `name` and `filename` fields in `Content-Disposition` header: `Content-Disposition: form-data;
name="user"; filename="somefile"`. Though `filename` field is optional according to RFC-7578, it helps
Starlette to differentiate which data should be treated as file. If `filename` field was supplied, `UploadFile`
object will be created to access underlying file, otherwise form-data part will be parsed and available as a raw
string.
#### Application
The originating Starlette application can be accessed via `request.app`.
#### Other state
If you want to store additional information on the request you can do so
using `request.state`.
For example:
`request.state.time_started = time.time()`
================================================
FILE: docs/responses.md
================================================
Starlette includes a few response classes that handle sending back the
appropriate ASGI messages on the `send` channel.
### Response
Signature: `Response(content, status_code=200, headers=None, media_type=None)`
* `content` - A string or bytestring.
* `status_code` - An integer HTTP status code.
* `headers` - A dictionary of strings.
* `media_type` - A string giving the media type. eg. "text/html"
Starlette will automatically include a Content-Length header. It will also
include a Content-Type header, based on the media_type and appending a charset
for text types, unless a charset has already been specified in the `media_type`.
Once you've instantiated a response, you can send it by calling it as an
ASGI application instance.
```python
from starlette.responses import Response
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = Response('Hello, world!', media_type='text/plain')
await response(scope, receive, send)
```
#### Set Cookie
Starlette provides a `set_cookie` method to allow you to set cookies on the response object.
Signature: `Response.set_cookie(key, value, max_age=None, expires=None, path="/", domain=None, secure=False, httponly=False, samesite="lax", partitioned=False)`
* `key` - A string that will be the cookie's key.
* `value` - A string that will be the cookie's value.
* `max_age` - An integer that defines the lifetime of the cookie in seconds. A negative integer or a value of `0` will discard the cookie immediately. `Optional`
* `expires` - Either an integer that defines the number of seconds until the cookie expires, or a datetime. `Optional`
* `path` - A string that specifies the subset of routes to which the cookie will apply. `Optional`
* `domain` - A string that specifies the domain for which the cookie is valid. `Optional`
* `secure` - A bool indicating that the cookie will only be sent to the server if request is made using SSL and the HTTPS protocol. `Optional`
* `httponly` - A bool indicating that the cookie cannot be accessed via JavaScript through `Document.cookie` property, the `XMLHttpRequest` or `Request` APIs. `Optional`
* `samesite` - A string that specifies the samesite strategy for the cookie. Valid values are `'lax'`, `'strict'` and `'none'`. Defaults to `'lax'`. `Optional`
* `partitioned` - A bool that indicates to user agents that these cross-site cookies should only be available in the same top-level context that the cookie was first set in. Only available for Python 3.14+, otherwise an error will be raised. `Optional`
#### Delete Cookie
Conversely, Starlette also provides a `delete_cookie` method to manually expire a set cookie.
Signature: `Response.delete_cookie(key, path='/', domain=None)`
### HTMLResponse
Takes some text or bytes and returns an HTML response.
```python
from starlette.responses import HTMLResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = HTMLResponse('<html><body><h1>Hello, world!</h1></body></html>')
await response(scope, receive, send)
```
### PlainTextResponse
Takes some text or bytes and returns a plain text response.
```python
from starlette.responses import PlainTextResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = PlainTextResponse('Hello, world!')
await response(scope, receive, send)
```
### JSONResponse
Takes some data and returns an `application/json` encoded response.
```python
from starlette.responses import JSONResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = JSONResponse({'hello': 'world'})
await response(scope, receive, send)
```
#### Custom JSON serialization
If you need fine-grained control over JSON serialization, you can subclass
`JSONResponse` and override the `render` method.
For example, if you wanted to use a third-party JSON library such as
[orjson](https://pypi.org/project/orjson/):
```python
from typing import Any
import orjson
from starlette.responses import JSONResponse
class OrjsonResponse(JSONResponse):
def render(self, content: Any) -> bytes:
return orjson.dumps(content)
```
In general you *probably* want to stick with `JSONResponse` by default unless
you are micro-optimising a particular endpoint or need to serialize non-standard
object types.
### RedirectResponse
Returns an HTTP redirect. Uses a 307 status code by default.
```python
from starlette.responses import PlainTextResponse, RedirectResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
if scope['path'] != '/':
response = RedirectResponse(url='/')
else:
response = PlainTextResponse('Hello, world!')
await response(scope, receive, send)
```
### StreamingResponse
Takes an async generator or a normal generator/iterator and streams the response body.
```python
from starlette.responses import StreamingResponse
import asyncio
async def slow_numbers(minimum, maximum):
yield '<html><body><ul>'
for number in range(minimum, maximum + 1):
yield '<li>%d</li>' % number
await asyncio.sleep(0.5)
yield '</ul></body></html>'
async def app(scope, receive, send):
assert scope['type'] == 'http'
generator = slow_numbers(1, 10)
response = StreamingResponse(generator, media_type='text/html')
await response(scope, receive, send)
```
Have in mind that <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> objects (like those created by `open()`) are normal iterators. So, you can return them directly in a `StreamingResponse`.
### FileResponse
Asynchronously streams a file as the response.
Takes a different set of arguments to instantiate than the other response types:
* `path` - The filepath to the file to stream.
* `headers` - Any custom headers to include, as a dictionary.
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
* `filename` - If set, this will be included in the response `Content-Disposition`.
* `content_disposition_type` - will be included in the response `Content-Disposition`. Can be set to "attachment" (default) or "inline".
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
```python
from starlette.responses import FileResponse
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = FileResponse('statics/favicon.ico')
await response(scope, receive, send)
```
File responses also supports [HTTP range requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests).
The `Accept-Ranges: bytes` header will be included in the response if the file exists. For now, only the `bytes`
range unit is supported.
If the request includes a `Range` header, and the file exists, the response will be a `206 Partial Content` response
with the requested range of bytes. If the range is invalid, the response will be a `416 Range Not Satisfiable` response.
## Third party responses
#### [EventSourceResponse](https://github.com/sysid/sse-starlette)
A response class that implements [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html). It enables event streaming from the server to the client without the complexity of websockets.
================================================
FILE: docs/routing.md
================================================
## HTTP Routing
Starlette has a simple but capable request routing system. A routing table
is defined as a list of routes, and passed when instantiating the application.
```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
async def homepage(request):
return PlainTextResponse("Homepage")
async def about(request):
return PlainTextResponse("About")
routes = [
Route("/", endpoint=homepage),
Route("/about", endpoint=about),
]
app = Starlette(routes=routes)
```
The `endpoint` argument can be one of:
* A regular function or async function, which accepts a single `request`
argument and which should return a response.
* A class that implements the ASGI interface, such as Starlette's [HTTPEndpoint](endpoints.md#httpendpoint).
## Path Parameters
Paths can use URI templating style to capture path components.
```python
Route('/users/{username}', user)
```
By default this will capture characters up to the end of the path or the next `/`.
You can use convertors to modify what is captured. The available convertors are:
* `str` returns a string, and is the default.
* `int` returns a Python integer.
* `float` returns a Python float.
* `uuid` return a Python `uuid.UUID` instance.
* `path` returns the rest of the path, including any additional `/` characters.
Convertors are used by prefixing them with a colon, like so:
```python
Route('/users/{user_id:int}', user)
Route('/floating-point/{number:float}', floating_point)
Route('/uploaded/{rest_of_path:path}', uploaded)
```
If you need a different converter that is not defined, you can create your own.
See below an example on how to create a `datetime` convertor, and how to register it:
```python
from datetime import datetime
from starlette.convertors import Convertor, register_url_convertor
class DateTimeConvertor(Convertor):
regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?"
def convert(self, value: str) -> datetime:
return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
def to_string(self, value: datetime) -> str:
return value.strftime("%Y-%m-%dT%H:%M:%S")
register_url_convertor("datetime", DateTimeConvertor())
```
After registering it, you'll be able to use it as:
```python
Route('/history/{date:datetime}', history)
```
Path parameters are made available in the request, as the `request.path_params`
dictionary.
```python
async def user(request):
user_id = request.path_params['user_id']
...
```
## Handling HTTP methods
Routes can also specify which HTTP methods are handled by an endpoint:
```python
Route('/users/{user_id:int}', user, methods=["GET", "POST"])
```
By default function endpoints will only accept `GET` requests, unless specified.
## Submounting routes
In large applications you might find that you want to break out parts of the
routing table, based on a common path prefix.
```python
routes = [
Route('/', homepage),
Mount('/users', routes=[
Route('/', users, methods=['GET', 'POST']),
Route('/{username}', user),
])
]
```
This style allows you to define different subsets of the routing table in
different parts of your project.
```python
from myproject import users, auth
routes = [
Route('/', homepage),
Mount('/users', routes=users.routes),
Mount('/auth', routes=auth.routes),
]
```
You can also use mounting to include sub-applications within your Starlette
application. For example...
```python
# This is a standalone static files server:
app = StaticFiles(directory="static")
# This is a static files server mounted within a Starlette application,
# underneath the "/static" path.
routes = [
...
Mount("/static", app=StaticFiles(directory="static"), name="static")
]
app = Starlette(routes=routes)
```
## Reverse URL lookups
You'll often want to be able to generate the URL for a particular route,
such as in cases where you need to return a redirect response.
* Signature: `url_for(name, **path_params) -> URL`
```python
routes = [
Route("/", homepage, name="homepage")
]
# We can use the following to return a URL...
url = request.url_for("homepage")
```
URL lookups can include path parameters...
```python
routes = [
Route("/users/{username}", user, name="user_detail")
]
# We can use the following to return a URL...
url = request.url_for("user_detail", username=...)
```
If a `Mount` includes a `name`, then submounts should use a `{prefix}:{name}`
style for reverse URL lookups.
```python
routes = [
Mount("/users", name="users", routes=[
Route("/", user, name="user_list"),
Route("/{username}", user, name="user_detail")
])
]
# We can use the following to return URLs...
url = request.url_for("users:user_list")
url = request.url_for("users:user_detail", username=...)
```
Mounted applications may include a `path=...` parameter.
```python
routes = [
...
Mount("/static", app=StaticFiles(directory="static"), name="static")
]
# We can use the following to return URLs...
url = request.url_for("static", path="/css/base.css")
```
For cases where there is no `request` instance, you can make reverse lookups
against the application, although these will only return the URL path.
```python
url = app.url_path_for("user_detail", username=...)
```
## Host-based routing
If you want to use different routes for the same path based on the `Host` header.
Note that port is removed from the `Host` header when matching.
For example, `Host (host='example.org:3600', ...)` will be processed
even if the `Host` header contains or does not contain a port other than `3600`
(`example.org:5600`, `example.org`).
Therefore, you can specify the port if you need it for use in `url_for`.
There are several ways to connect host-based routes to your application
```python
site = Router() # Use eg. `@site.route()` to configure this.
api = Router() # Use eg. `@api.route()` to configure this.
news = Router() # Use eg. `@news.route()` to configure this.
routes = [
Host('api.example.org', api, name="site_api")
]
app = Starlette(routes=routes)
app.host('www.example.org', site, name="main_site")
news_host = Host('news.example.org', news)
app.router.routes.append(news_host)
```
URL lookups can include host parameters just like path parameters
```python
routes = [
Host("{subdomain}.example.org", name="sub", app=Router(routes=[
Mount("/users", name="users", routes=[
Route("/", user, name="user_list"),
Route("/{username}", user, name="user_detail")
])
]))
]
...
url = request.url_for("sub:users:user_detail", username=..., subdomain=...)
url = request.url_for("sub:users:user_list", subdomain=...)
```
## Route priority
Incoming paths are matched against each `Route` in order.
In cases where more that one route could match an incoming path, you should
take care to ensure that more specific routes are listed before general cases.
For example:
```python
# Don't do this: `/users/me` will never match incoming requests.
routes = [
Route('/users/{username}', user),
Route('/users/me', current_user),
]
# Do this: `/users/me` is tested first.
routes = [
Route('/users/me', current_user),
Route('/users/{username}', user),
]
```
## Working with Router instances
If you're working at a low-level you might want to use a plain `Router`
instance, rather that creating a `Starlette` application. This gives you
a lightweight ASGI application that just provides the application routing,
without wrapping it up in any middleware.
```python
app = Router(routes=[
Route('/', homepage),
Mount('/users', routes=[
Route('/', users, methods=['GET', 'POST']),
Route('/{username}', user),
])
])
```
## WebSocket Routing
When working with WebSocket endpoints, you should use `WebSocketRoute`
instead of the usual `Route`.
Path parameters, and reverse URL lookups for `WebSocketRoute` work the same
as HTTP `Route`, which can be found in the HTTP [Route](#http-routing) section above.
```python
from starlette.applications import Starlette
from starlette.routing import WebSocketRoute
async def websocket_index(websocket):
await websocket.accept()
await websocket.send_text("Hello, websocket!")
await websocket.close()
async def websocket_user(websocket):
name = websocket.path_params["name"]
await websocket.accept()
await websocket.send_text(f"Hello, {name}")
await websocket.close()
routes = [
WebSocketRoute("/", endpoint=websocket_index),
WebSocketRoute("/{name}", endpoint=websocket_user),
]
app = Starlette(routes=routes)
```
The `endpoint` argument can be one of:
* An async function, which accepts a single `websocket` argument.
* A class that implements the ASGI interface, such as Starlette's [WebSocketEndpoint](endpoints.md#websocketendpoint).
================================================
FILE: docs/schemas.md
================================================
Starlette supports generating API schemas, such as the widely used [OpenAPI
specification][openapi]. (Formerly known as "Swagger".)
Schema generation works by inspecting the routes on the application through
`app.routes`, and using the docstrings or other attributes on the endpoints
in order to determine a complete API schema.
Starlette is not tied to any particular schema generation or validation tooling,
but includes a simple implementation that generates OpenAPI schemas based on
the docstrings.
```python
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.schemas import SchemaGenerator
schemas = SchemaGenerator(
{"openapi": "3.0.0", "info": {"title": "Example API", "version": "1.0"}}
)
def list_users(request):
"""
responses:
200:
description: A list of users.
examples:
[{"username": "tom"}, {"username": "lucy"}]
"""
raise NotImplementedError()
def create_user(request):
"""
responses:
200:
description: A user.
examples:
{"username": "tom"}
"""
raise NotImplementedError()
def openapi_schema(request):
return schemas.OpenAPIResponse(request=request)
routes = [
Route("/users", endpoint=list_users, methods=["GET"]),
Route("/users", endpoint=create_user, methods=["POST"]),
Route("/schema", endpoint=openapi_schema, include_in_schema=False)
]
app = Starlette(routes=routes)
```
We can now access an OpenAPI schema at the "/schema" endpoint.
You can generate the API Schema directly with `.get_schema(routes)`:
```python
schema = schemas.get_schema(routes=app.routes)
assert schema == {
"openapi": "3.0.0",
"info": {"title": "Example API", "version": "1.0"},
"paths": {
"/users": {
"get": {
"responses": {
200: {
"description": "A list of users.",
"examples": [{"username": "tom"}, {"username": "lucy"}],
}
}
},
"post": {
"responses": {
200: {"description": "A user.", "examples": {"username": "tom"}}
}
},
},
},
}
```
You might also want to be able to print out the API schema, so that you can
use tooling such as generating API documentation.
```python
if __name__ == '__main__':
assert sys.argv[-1] in ("run", "schema"), "Usage: example.py [run|schema]"
if sys.argv[-1] == "run":
uvicorn.run("example:app", host='0.0.0.0', port=8000)
elif sys.argv[-1] == "schema":
schema = schemas.get_schema(routes=app.routes)
print(yaml.dump(schema, default_flow_style=False))
```
### Third party packages
#### [starlette-apispec][starlette-apispec]
Easy APISpec integration for Starlette, which supports some object serialization libraries.
[openapi]: https://github.com/OAI/OpenAPI-Specification
[starlette-apispec]: https://github.com/Woile/starlette-apispec
================================================
FILE: docs/server-push.md
================================================
Starlette includes support for HTTP/2 and HTTP/3 server push, making it
possible to push resources to the client to speed up page load times.
### `Request.send_push_promise`
Used to initiate a server push for a resource. If server push is not available
this method does nothing.
Signature: `send_push_promise(path)`
* `path` - A string denoting the path of the resource.
```python
from starlette.applications import Starlette
from starlette.responses import HTMLResponse
from starlette.routing import Route, Mount
from starlette.staticfiles import StaticFiles
async def homepage(request):
"""
Homepage which uses server push to deliver the stylesheet.
"""
await request.send_push_promise("/static/style.css")
return HTMLResponse(
'<html><head><link rel="stylesheet" href="/static/style.css"/></head></html>'
)
routes = [
Route("/", endpoint=homepage),
Mount("/static", StaticFiles(directory="static"), name="static")
]
app = Starlette(routes=routes)
```
================================================
FILE: docs/staticfiles.md
================================================
Starlette also includes a `StaticFiles` class for serving files in a given directory:
### StaticFiles
Signature: `StaticFiles(directory=None, packages=None, html=False, check_dir=True, follow_symlink=False)`
* `directory` - A string or [os.PathLike][pathlike] denoting a directory path.
* `packages` - A list of strings or list of tuples of strings of python packages.
* `html` - Run in HTML mode. Automatically loads `index.html` for directories if such file exist.
* `check_dir` - Ensure that the directory exists upon instantiation. Defaults to `True`.
* `follow_symlink` - A boolean indicating if symbolic links for files and directories should be followed. Defaults to `False`.
You can combine this ASGI application with Starlette's routing to provide
comprehensive static file serving.
```python
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles
routes = [
...
Mount('/static', app=StaticFiles(directory='static'), name="static"),
]
app = Starlette(routes=routes)
```
Static files will respond with "404 Not found" or "405 Method not allowed"
responses for requests which do not match. In HTML mode if `404.html` file
exists it will be shown as 404 response.
The `packages` option can be used to include "static" directories contained within
a python package. The Python "bootstrap4" package is an example of this.
```python
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles
routes=[
...
Mount('/static', app=StaticFiles(directory='static', packages=['bootstrap4']), name="static"),
]
app = Starlette(routes=routes)
```
By default `StaticFiles` will look for `statics` directory in each package,
you can change the default directory by specifying a tuple of strings.
```python
routes=[
...
Mount('/static', app=StaticFiles(packages=[('bootstrap4', 'static')]), name="static"),
]
```
You may prefer to include static files directly inside the "static" directory
rather than using Python packaging to include static files, but it can be useful
for bundling up reusable components.
[pathlike]: https://docs.python.org/3/library/os.html#os.PathLike
================================================
FILE: docs/templates.md
================================================
Starlette is not _strictly_ coupled to any particular templating engine, but
Jinja2 provides an excellent choice.
??? abstract "API Reference"
::: starlette.templating.Jinja2Templates
options:
parameter_headings: false
show_root_heading: true
heading_level: 3
filters:
- "__init__"
Starlette provides a simple way to get `jinja2` configured. This is probably
what you want to use by default.
```python
from starlette.applications import Starlette
from starlette.routing import Route, Mount
from starlette.templating import Jinja2Templates
from starlette.staticfiles import StaticFiles
templates = Jinja2Templates(directory='templates')
async def homepage(request):
return templates.TemplateResponse(request, 'index.html')
routes = [
Route('/', endpoint=homepage),
Mount('/static', StaticFiles(directory='static'), name='static')
]
app = Starlette(debug=True, routes=routes)
```
Note that the incoming `request` instance must be included as part of the
template context.
The Jinja2 template context will automatically include a `url_for` function,
so we can correctly hyperlink to other pages within the application.
For example, we can link to static files from within our HTML templates:
```html
<link href="{{ url_for('static', path='/css/bootstrap.min.css') }}" rel="stylesheet" />
```
If you want to use [custom filters][jinja2], you will need to update the `env`
property of `Jinja2Templates`:
```python
from commonmark import commonmark
from starlette.templating import Jinja2Templates
def marked_filter(text):
return commonmark(text)
templates = Jinja2Templates(directory='templates')
templates.env.filters['marked'] = marked_filter
```
## Using custom jinja2.Environment instance
Starlette also accepts a preconfigured [`jinja2.Environment`](https://jinja.palletsprojects.com/en/3.0.x/api/#api) instance.
```python
import jinja2
from starlette.templating import Jinja2Templates
env = jinja2.Environment(...)
templates = Jinja2Templates(env=env)
```
## Autoescape
When using the `directory` argument, Starlette enables autoescape by default for
`.html`, `.htm`, and `.xml` templates using [`jinja2.select_autoescape()`](https://jinja.palletsprojects.com/en/stable/api/#jinja2.select_autoescape).
This protects against Cross-Site Scripting (XSS) vulnerabilities by escaping
user-provided content before rendering it in the template. For example, if a user
submits `<script>alert('XSS')</script>` as their name, it will be rendered as
`<script>alert('XSS')</script>` instead of being executed as JavaScript.
## Context processors
A context processor is a function that returns a dictionary to be merged into a template context.
Every function takes only one argument `request` and must return a dictionary to add to the context.
A common use case of template processors is to extend the template context with shared variables.
```python
import typing
from starlette.requests import Request
def app_context(request: Request) -> typing.Dict[str, typing.Any]:
return {'app': request.app}
```
### Registering context templates
Pass context processors to `context_processors` argument of the `Jinja2Templates` class.
```python
import typing
from starlette.requests import Request
from starlette.templating import Jinja2Templates
def app_context(request: Request) -> typing.Dict[str, typing.Any]:
return {'app': request.app}
templates = Jinja2Templates(
directory='templates', context_processors=[app_context]
)
```
!!! info
Asynchronous functions as context processors are not supported.
## Testing template responses
When using the test client, template responses include `.template` and `.context`
attributes.
```python
from starlette.testclient import TestClient
def test_homepage():
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.template.name == 'index.html'
assert "request" in response.context
```
## Asynchronous template rendering
Jinja2 supports async template rendering, however as a general rule
we'd recommend that you keep your templates free from logic that invokes
database lookups, or other I/O operations.
Instead we'd recommend that you ensure that your endpoints perform all I/O,
for example, strictly evaluate any database queries within the view and
include the final results in the context.
[jinja2]: https://jinja.palletsprojects.com/en/3.0.x/api/?highlight=environment#writing-filters
[pathlike]: https://docs.python.org/3/library/os.html#os.PathLike
================================================
FILE: docs/testclient.md
================================================
??? abstract "API Reference"
::: starlette.testclient.TestClient
options:
parameter_headings: false
show_bases: false
show_root_heading: true
heading_level: 3
filters:
- "__init__"
The test client allows you to make requests against your ASGI application,
using the `httpx` library.
```python
from starlette.responses import HTMLResponse
from starlette.testclient import TestClient
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = HTMLResponse('<html><body>Hello, world!</body></html>')
await response(scope, receive, send)
def test_app():
client = TestClient(app)
response = client.get('/')
assert response.status_code == 200
```
The test client exposes the same interface as any other `httpx` session.
In particular, note that the calls to make a request are just standard
function calls, not awaitables.
You can use any of `httpx` standard API, such as authentication, session
cookies handling, or file uploads.
For example, to set headers on the TestClient you can do:
```python
client = TestClient(app)
# Set headers on the client for future requests
client.headers = {"Authorization": "..."}
response = client.get("/")
# Set headers for each request separately
response = client.get("/", headers={"Authorization": "..."})
```
And for example to send files with the TestClient:
```python
client = TestClient(app)
# Send a single file
with open("example.txt", "rb") as f:
response = client.post("/form", files={"file": f})
# Send multiple files
with open("example.txt", "rb") as f1:
with open("example.png", "rb") as f2:
files = {"file1": f1, "file2": ("filename", f2, "image/png")}
response = client.post("/form", files=files)
```
For more information you can check the `httpx` [documentation](https://www.python-httpx.org/advanced/).
By default the `TestClient` will raise any exceptions that occur in the
application. Occasionally you might want to test the content of 500 error
responses, rather than allowing client to raise the server exception. In this
case you should use `client = TestClient(app, raise_server_exceptions=False)`.
!!! note
If you want the `TestClient` to run the `lifespan` handler,
you will need to use the `TestClient` as a context manager. It will
not be triggered when the `TestClient` is instantiated. You can learn more about it
[here](lifespan.md#running-lifespan-in-tests).
### Change client address
By default, the TestClient will set the client host to `"testserver"` and the port to `50000`.
You can change the client address by setting the `client` attribute of the `TestClient` instance:
```python
client = TestClient(app, client=('localhost', 8000))
```
### Selecting the Async backend
`TestClient` takes arguments `backend` (a string) and `backend_options` (a dictionary).
These options are passed to `anyio.start_blocking_portal()`.
See the [anyio documentation](https://anyio.readthedocs.io/en/stable/basics.html#backend-options)
for more information about the accepted backend options.
By default, `asyncio` is used with default options.
To run `Trio`, pass `backend="trio"`. For example:
```python
def test_app()
with TestClient(app, backend="trio") as client:
...
```
To run `asyncio` with `uvloop`, pass `backend_options={"use_uvloop": True}`. For example:
```python
def test_app()
with TestClient(app, backend_options={"use_uvloop": True}) as client:
...
```
### Testing WebSocket sessions
You can also test websocket sessions with the test client.
The `httpx` library will be used to build the initial handshake, meaning you
can use the same authentication options and other headers between both http and
websocket testing.
```python
from starlette.testclient import TestClient
from starlette.websockets import WebSocket
async def app(scope, receive, send):
assert scope['type'] == 'websocket'
websocket = WebSocket(scope, receive=receive, send=send)
await websocket.accept()
await websocket.send_text('Hello, world!')
await websocket.close()
def test_app():
client = TestClient(app)
with client.websocket_connect('/') as websocket:
data = websocket.receive_text()
assert data == 'Hello, world!'
```
The operations on session are standard function calls, not awaitables.
It's important to use the session within a context-managed `with` block. This
ensure that the background thread on which the ASGI application is properly
terminated, and that any exceptions that occur within the application are
always raised by the test client.
#### Establishing a test session
* `.websocket_connect(url, subprotocols=None, **options)` - Takes the same set of arguments as `httpx.get()`.
May raise `starlette.websockets.WebSocketDisconnect` if the application does not accept the websocket connection.
`websocket_connect()` must be used as a context manager (in a `with` block).
!!! note
The `params` argument is not supported by `websocket_connect`. If you need to pass query arguments, hard code it
directly in the URL.
```python
with client.websocket_connect('/path?foo=bar') as websocket:
...
```
#### Sending data
* `.send_text(data)` - Send the given text to the application.
* `.send_bytes(data)` - Send the given bytes to the application.
* `.send_json(data, mode="text")` - Send the given data to the application. Use `mode="binary"` to send JSON over binary data frames.
#### Receiving data
* `.receive_text()` - Wait for incoming text sent by the application and return it.
* `.receive_bytes()` - Wait for incoming bytestring sent by the application and return it.
* `.receive_json(mode="text")` - Wait for incoming json data sent by the application and return it. Use `mode="binary"` to receive JSON over binary data frames.
May raise `starlette.websockets.WebSocketDisconnect`.
#### Closing the connection
* `.close(code=1000)` - Perform a client-side close of the websocket connection.
### Asynchronous tests
Sometimes you will want to do async things outside of your application.
For example, you might want to check the state of your database after calling your app
using your existing async database client/infrastructure.
For these situations, using `TestClient` is difficult because it creates it's own event loop and async
resources (like a database connection) often cannot be shared across event loops.
The simplest way to work around this is to just make your entire test async and use an async client, like [httpx.AsyncClient].
Here is an example of such a test:
```python
from httpx import AsyncClient, ASGITransport
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import PlainTextResponse
def hello(request: Request) -> PlainTextResponse:
return PlainTextResponse("Hello World!")
app = Starlette(routes=[Route("/", hello)])
# if you're using pytest, you'll need to to add an async marker like:
# @pytest.mark.anyio # using https://github.com/agronholm/anyio
# or install and configure pytest-asyncio (https://github.com/pytest-dev/pytest-asyncio)
async def test_app() -> None:
# note: you _must_ set `base_url` for relative urls like "/" to work
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://testserver") as client:
r = await client.get("/")
assert r.status_code == 200
assert r.text == "Hello World!"
```
[httpx.AsyncClient]: https://www.python-httpx.org/advanced/#calling-into-python-web-apps
================================================
FILE: docs/third-party-packages.md
================================================
Starlette has a rapidly growing community of developers, building tools that integrate into Starlette, tools that depend on Starlette, etc.
Here are some of those third party packages:
## Plugins
### Apitally
<a href="https://github.com/apitally/apitally-py" target="_blank">GitHub</a> |
<a href="https://docs.apitally.io/frameworks/starlette" target="_blank">Documentation</a>
Analytics, request logging and monitoring for REST APIs built with Starlette (and other frameworks).
### Authlib
<a href="https://github.com/lepture/Authlib" target="_blank">GitHub</a> |
<a href="https://docs.authlib.org/en/latest/" target="_blank">Documentation</a>
The ultimate Python library in building OAuth and OpenID Connect clients and servers. Check out how to integrate with [Starlette](https://docs.authlib.org/en/latest/client/starlette.html).
### ChannelBox
<a href="https://github.com/Sobolev5/channel-box" target="_blank">GitHub Repository</a>
ChannelBox is a lightweight solution for WebSocket broadcasting in ASGI applications.
It allows sending messages to named WebSocket channel groups and integrates with Starlette and FastAPI.
### Imia
<a href="https://github.com/alex-oleshkevich/imia" target="_blank">GitHub</a>
An authentication framework for Starlette with pluggable authenticators and login/logout flow.
### Mangum
<a href="https://github.com/erm/mangum" target="_blank">GitHub</a>
Serverless ASGI adapter for AWS Lambda & API Gateway.
### Nejma
<a href="https://github.com/taoufik07/nejma" target="_blank">GitHub</a>
Manage and send messages to groups of channels using websockets.
Checkout <a href="https://github.com/taoufik07/nejma-chat" target="_blank">nejma-chat</a>, a simple chat application built using `nejma` and `starlette`.
### Scout APM
<a href="https://github.com/scoutapp/scout_apm_python" target="_blank">GitHub</a>
An APM (Application Performance Monitoring) solution that can
instrument your application to find performance bottlenecks.
### SpecTree
<a href="https://github.com/0b01001001/spectree" target="_blank">GitHub</a>
Generate OpenAPI spec document and validate request & response with Python annotations. Less boilerplate code(no need for YAML).
### Starlette APISpec
<a href="https://github.com/Woile/starlette-apispec" target="_blank">GitHub</a>
Simple APISpec integration for Starlette.
Document your REST API built with Starlette by declaring OpenAPI (Swagger)
schemas in YAML format in your endpoint's docstrings.
### Starlette Compress
<a href="https://github.com/Zaczero/starlette-compress" target="_blank">GitHub</a>
Starlette-Compress is a fast and simple middleware for compressing responses in Starlette.
It adds ZStd, Brotli, and GZip compression support with sensible default configuration.
### Starlette Context
<a href="https://github.com/tomwojcik/starlette-context" target="_blank">GitHub</a>
Middleware for Starlette that allows you to store and access the context data of a request.
Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.
### Starlette Cramjam
<a href="https://github.com/developmentseed/starlette-cramjam" target="_blank">GitHub</a>
A Starlette middleware that allows **brotli**, **gzip** and **deflate** compression algorithm with a minimal requirements.
### Starlette OAuth2 API
<a href="https://gitlab.com/jorgecarleitao/starlette-oauth2-api" target="_blank">GitLab</a>
A starlette middleware to add authentication and authorization through JWTs.
It relies solely on an auth provider to issue access and/or id tokens to clients.
### Starlette Prometheus
<a href="https://github.com/perdy/starlette-prometheus" target="_blank">GitHub</a>
A plugin for providing an endpoint that exposes [Prometheus](https://prometheus.io/) metrics based on its [official python client](https://github.com/prometheus/client_python).
### Starlette WTF
<a href="https://github.com/muicss/starlette-wtf" target="_blank">GitHub</a>
A simple tool for integrating Starlette and WTForms. It is modeled on the excellent Flask-WTF library.
### Starlette-Login
<a href="https://github.com/jockerz/Starlette-Login" target="_blank">GitHub</a> |
<a href="https://starlette-login.readthedocs.io/en/stable/" target="_blank">Documentation</a>
User session management for Starlette.
It handles the common tasks of logging in, logging out, and remembering your users' sessions over extended periods of time.
### Starsessions
<a href="https://github.com/alex-oleshkevich/starsessions" target="_blank">GitHub</a>
An alternate session support implementation with customizable storage backends.
### webargs-starlette
<a href="https://github.com/sloria/webargs-starlette" target="_blank">GitHub</a>
Declarative request parsing and validation for Starlette, built on top
of [webargs](https://github.com/
gitextract_gn9w6fxg/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-issue.md
│ │ └── config.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── CITATION.cff
├── LICENSE.md
├── README.md
├── docs/
│ ├── CNAME
│ ├── applications.md
│ ├── authentication.md
│ ├── background.md
│ ├── config.md
│ ├── contributing.md
│ ├── css/
│ │ └── custom.css
│ ├── database.md
│ ├── endpoints.md
│ ├── exceptions.md
│ ├── graphql.md
│ ├── index.md
│ ├── js/
│ │ └── custom.js
│ ├── lifespan.md
│ ├── middleware.md
│ ├── overrides/
│ │ ├── main.html
│ │ └── partials/
│ │ └── toc-item.html
│ ├── release-notes.md
│ ├── requests.md
│ ├── responses.md
│ ├── routing.md
│ ├── schemas.md
│ ├── server-push.md
│ ├── staticfiles.md
│ ├── templates.md
│ ├── testclient.md
│ ├── third-party-packages.md
│ ├── threadpool.md
│ └── websockets.md
├── mkdocs.yml
├── pyproject.toml
├── scripts/
│ ├── README.md
│ ├── build
│ ├── check
│ ├── coverage
│ ├── docs
│ ├── install
│ ├── lint
│ ├── sync-version
│ └── test
├── starlette/
│ ├── __init__.py
│ ├── _exception_handler.py
│ ├── _utils.py
│ ├── applications.py
│ ├── authentication.py
│ ├── background.py
│ ├── concurrency.py
│ ├── config.py
│ ├── convertors.py
│ ├── datastructures.py
│ ├── endpoints.py
│ ├── exceptions.py
│ ├── formparsers.py
│ ├── middleware/
│ │ ├── __init__.py
│ │ ├── authentication.py
│ │ ├── base.py
│ │ ├── cors.py
│ │ ├── errors.py
│ │ ├── exceptions.py
│ │ ├── gzip.py
│ │ ├── httpsredirect.py
│ │ ├── sessions.py
│ │ ├── trustedhost.py
│ │ └── wsgi.py
│ ├── py.typed
│ ├── requests.py
│ ├── responses.py
│ ├── routing.py
│ ├── schemas.py
│ ├── staticfiles.py
│ ├── status.py
│ ├── templating.py
│ ├── testclient.py
│ ├── types.py
│ └── websockets.py
└── tests/
├── __init__.py
├── conftest.py
├── middleware/
│ ├── __init__.py
│ ├── test_base.py
│ ├── test_cors.py
│ ├── test_errors.py
│ ├── test_gzip.py
│ ├── test_https_redirect.py
│ ├── test_middleware.py
│ ├── test_session.py
│ ├── test_trusted_host.py
│ └── test_wsgi.py
├── statics/
│ └── example.txt
├── test__utils.py
├── test_applications.py
├── test_authentication.py
├── test_background.py
├── test_concurrency.py
├── test_config.py
├── test_convertors.py
├── test_datastructures.py
├── test_endpoints.py
├── test_exceptions.py
├── test_formparsers.py
├── test_requests.py
├── test_responses.py
├── test_routing.py
├── test_schemas.py
├── test_staticfiles.py
├── test_status.py
├── test_templates.py
├── test_testclient.py
├── test_websockets.py
└── types.py
SYMBOL INDEX (1197 symbols across 64 files)
FILE: docs/js/custom.js
function shuffle (line 1) | function shuffle(array) {
function showRandomAnnouncement (line 13) | async function showRandomAnnouncement(groupId, timeInterval) {
function main (line 29) | async function main() {
FILE: starlette/_exception_handler.py
function _lookup_exception_handler (line 16) | def _lookup_exception_handler(exc_handlers: ExceptionHandlers, exc: Exce...
function wrap_app_handling_exceptions (line 23) | def wrap_app_handling_exceptions(app: ASGIApp, conn: Request | WebSocket...
FILE: starlette/_utils.py
function is_async_callable (line 31) | def is_async_callable(obj: AwaitableCallable[T]) -> TypeIs[AwaitableCall...
function is_async_callable (line 35) | def is_async_callable(obj: Any) -> TypeIs[AwaitableCallable[Any]]: ...
function is_async_callable (line 38) | def is_async_callable(obj: Any) -> Any:
class AwaitableOrContextManager (line 48) | class AwaitableOrContextManager(
class SupportsAsyncClose (line 53) | class SupportsAsyncClose(Protocol):
method close (line 54) | async def close(self) -> None: ... # pragma: no cover
class AwaitableOrContextManagerWrapper (line 60) | class AwaitableOrContextManagerWrapper(Generic[SupportsAsyncCloseType]):
method __init__ (line 63) | def __init__(self, aw: Awaitable[SupportsAsyncCloseType]) -> None:
method __await__ (line 66) | def __await__(self) -> Generator[Any, None, SupportsAsyncCloseType]:
method __aenter__ (line 69) | async def __aenter__(self) -> SupportsAsyncCloseType:
method __aexit__ (line 73) | async def __aexit__(self, *args: Any) -> None | bool:
function collapse_excgroups (line 79) | def collapse_excgroups() -> Generator[None, None, None]:
function get_route_path (line 90) | def get_route_path(scope: Scope) -> str:
FILE: starlette/applications.py
class Starlette (line 19) | class Starlette:
method __init__ (line 22) | def __init__(
method build_middleware_stack (line 57) | def build_middleware_stack(self) -> ASGIApp:
method routes (line 80) | def routes(self) -> list[BaseRoute]:
method url_path_for (line 83) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method __call__ (line 86) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method mount (line 92) | def mount(self, path: str, app: ASGIApp, name: str | None = None) -> N...
method host (line 95) | def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:
method add_middleware (line 98) | def add_middleware(self, middleware_class: _MiddlewareFactory[P], *arg...
method add_exception_handler (line 103) | def add_exception_handler(
method add_route (line 110) | def add_route(
FILE: starlette/authentication.py
function has_required_scope (line 18) | def has_required_scope(conn: HTTPConnection, scopes: Sequence[str]) -> b...
function requires (line 25) | def requires(
class AuthenticationError (line 94) | class AuthenticationError(Exception):
class AuthenticationBackend (line 98) | class AuthenticationBackend:
method authenticate (line 99) | async def authenticate(self, conn: HTTPConnection) -> tuple[AuthCreden...
class AuthCredentials (line 103) | class AuthCredentials:
method __init__ (line 104) | def __init__(self, scopes: Sequence[str] | None = None):
class BaseUser (line 108) | class BaseUser:
method is_authenticated (line 110) | def is_authenticated(self) -> bool:
method display_name (line 114) | def display_name(self) -> str:
method identity (line 118) | def identity(self) -> str:
class SimpleUser (line 122) | class SimpleUser(BaseUser):
method __init__ (line 123) | def __init__(self, username: str) -> None:
method is_authenticated (line 127) | def is_authenticated(self) -> bool:
method display_name (line 131) | def display_name(self) -> str:
class UnauthenticatedUser (line 135) | class UnauthenticatedUser(BaseUser):
method is_authenticated (line 137) | def is_authenticated(self) -> bool:
method display_name (line 141) | def display_name(self) -> str:
FILE: starlette/background.py
class BackgroundTask (line 12) | class BackgroundTask:
method __init__ (line 13) | def __init__(self, func: Callable[P, Any], *args: P.args, **kwargs: P....
method __call__ (line 19) | async def __call__(self) -> None:
class BackgroundTasks (line 26) | class BackgroundTasks(BackgroundTask):
method __init__ (line 27) | def __init__(self, tasks: Sequence[BackgroundTask] | None = None):
method add_task (line 30) | def add_task(self, func: Callable[P, Any], *args: P.args, **kwargs: P....
method __call__ (line 34) | async def __call__(self) -> None:
FILE: starlette/concurrency.py
function run_until_first_complete (line 14) | async def run_until_first_complete(*args: tuple[Callable, dict]) -> None...
function run_in_threadpool (line 30) | async def run_in_threadpool(func: Callable[P, T], *args: P.args, **kwarg...
class _StopIteration (line 35) | class _StopIteration(Exception):
function _next (line 39) | def _next(iterator: Iterator[T]) -> T:
function iterate_in_threadpool (line 49) | async def iterate_in_threadpool(
FILE: starlette/config.py
class undefined (line 10) | class undefined:
class EnvironError (line 14) | class EnvironError(Exception):
class Environ (line 18) | class Environ(MutableMapping[str, str]):
method __init__ (line 19) | def __init__(self, environ: MutableMapping[str, str] = os.environ):
method __getitem__ (line 23) | def __getitem__(self, key: str) -> str:
method __setitem__ (line 27) | def __setitem__(self, key: str, value: str) -> None:
method __delitem__ (line 32) | def __delitem__(self, key: str) -> None:
method __iter__ (line 37) | def __iter__(self) -> Iterator[str]:
method __len__ (line 40) | def __len__(self) -> int:
class Config (line 49) | class Config:
method __init__ (line 50) | def __init__(
method __call__ (line 67) | def __call__(self, key: str, *, default: None) -> str | None: ...
method __call__ (line 70) | def __call__(self, key: str, cast: type[T], default: T = ...) -> T: ...
method __call__ (line 73) | def __call__(self, key: str, cast: type[str] = ..., default: str = ......
method __call__ (line 76) | def __call__(
method __call__ (line 84) | def __call__(self, key: str, cast: type[str] = ..., default: T = ...) ...
method __call__ (line 86) | def __call__(
method get (line 94) | def get(
method _read_file (line 111) | def _read_file(self, file_name: str | Path, encoding: str) -> dict[str...
method _perform_cast (line 123) | def _perform_cast(
FILE: starlette/convertors.py
class Convertor (line 10) | class Convertor(Generic[T]):
method convert (line 13) | def convert(self, value: str) -> T:
method to_string (line 16) | def to_string(self, value: T) -> str:
class StringConvertor (line 20) | class StringConvertor(Convertor[str]):
method convert (line 23) | def convert(self, value: str) -> str:
method to_string (line 26) | def to_string(self, value: str) -> str:
class PathConvertor (line 33) | class PathConvertor(Convertor[str]):
method convert (line 36) | def convert(self, value: str) -> str:
method to_string (line 39) | def to_string(self, value: str) -> str:
class IntegerConvertor (line 43) | class IntegerConvertor(Convertor[int]):
method convert (line 46) | def convert(self, value: str) -> int:
method to_string (line 49) | def to_string(self, value: int) -> str:
class FloatConvertor (line 55) | class FloatConvertor(Convertor[float]):
method convert (line 58) | def convert(self, value: str) -> float:
method to_string (line 61) | def to_string(self, value: float) -> str:
class UUIDConvertor (line 69) | class UUIDConvertor(Convertor[uuid.UUID]):
method convert (line 72) | def convert(self, value: str) -> uuid.UUID:
method to_string (line 75) | def to_string(self, value: uuid.UUID) -> str:
function register_url_convertor (line 88) | def register_url_convertor(key: str, convertor: Convertor[Any]) -> None:
FILE: starlette/datastructures.py
class Address (line 18) | class Address(NamedTuple):
class URL (line 30) | class URL:
method __init__ (line 31) | def __init__(
method components (line 72) | def components(self) -> SplitResult:
method scheme (line 78) | def scheme(self) -> str:
method netloc (line 82) | def netloc(self) -> str:
method path (line 86) | def path(self) -> str:
method query (line 90) | def query(self) -> str:
method fragment (line 94) | def fragment(self) -> str:
method username (line 98) | def username(self) -> None | str:
method password (line 102) | def password(self) -> None | str:
method hostname (line 106) | def hostname(self) -> None | str:
method port (line 110) | def port(self) -> int | None:
method is_secure (line 114) | def is_secure(self) -> bool:
method replace (line 117) | def replace(self, **kwargs: Any) -> URL:
method include_query_params (line 145) | def include_query_params(self, **kwargs: Any) -> URL:
method replace_query_params (line 151) | def replace_query_params(self, **kwargs: Any) -> URL:
method remove_query_params (line 155) | def remove_query_params(self, keys: str | Sequence[str]) -> URL:
method __eq__ (line 164) | def __eq__(self, other: Any) -> bool:
method __str__ (line 167) | def __str__(self) -> str:
method __repr__ (line 170) | def __repr__(self) -> str:
class URLPath (line 177) | class URLPath(str):
method __new__ (line 183) | def __new__(cls, path: str, protocol: str = "", host: str = "") -> URL...
method __init__ (line 187) | def __init__(self, path: str, protocol: str = "", host: str = "") -> N...
method make_absolute_url (line 191) | def make_absolute_url(self, base_url: str | URL) -> URL:
class Secret (line 207) | class Secret:
method __init__ (line 213) | def __init__(self, value: str):
method __repr__ (line 216) | def __repr__(self) -> str:
method __str__ (line 220) | def __str__(self) -> str:
method __bool__ (line 223) | def __bool__(self) -> bool:
class CommaSeparatedStrings (line 227) | class CommaSeparatedStrings(Sequence[str]):
method __init__ (line 228) | def __init__(self, value: str | Sequence[str]):
method __len__ (line 237) | def __len__(self) -> int:
method __getitem__ (line 240) | def __getitem__(self, index: int | slice) -> Any:
method __iter__ (line 243) | def __iter__(self) -> Iterator[str]:
method __repr__ (line 246) | def __repr__(self) -> str:
method __str__ (line 251) | def __str__(self) -> str:
class ImmutableMultiDict (line 255) | class ImmutableMultiDict(Mapping[_KeyType, _CovariantValueType]):
method __init__ (line 258) | def __init__(
method getlist (line 286) | def getlist(self, key: Any) -> list[_CovariantValueType]:
method keys (line 289) | def keys(self) -> KeysView[_KeyType]:
method values (line 292) | def values(self) -> ValuesView[_CovariantValueType]:
method items (line 295) | def items(self) -> ItemsView[_KeyType, _CovariantValueType]:
method multi_items (line 298) | def multi_items(self) -> list[tuple[_KeyType, _CovariantValueType]]:
method __getitem__ (line 301) | def __getitem__(self, key: _KeyType) -> _CovariantValueType:
method __contains__ (line 304) | def __contains__(self, key: Any) -> bool:
method __iter__ (line 307) | def __iter__(self) -> Iterator[_KeyType]:
method __len__ (line 310) | def __len__(self) -> int:
method __eq__ (line 313) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 318) | def __repr__(self) -> str:
class MultiDict (line 324) | class MultiDict(ImmutableMultiDict[Any, Any]):
method __setitem__ (line 325) | def __setitem__(self, key: Any, value: Any) -> None:
method __delitem__ (line 328) | def __delitem__(self, key: Any) -> None:
method pop (line 332) | def pop(self, key: Any, default: Any = None) -> Any:
method popitem (line 336) | def popitem(self) -> tuple[Any, Any]:
method poplist (line 341) | def poplist(self, key: Any) -> list[Any]:
method clear (line 346) | def clear(self) -> None:
method setdefault (line 350) | def setdefault(self, key: Any, default: Any = None) -> Any:
method setlist (line 357) | def setlist(self, key: Any, values: list[Any]) -> None:
method append (line 365) | def append(self, key: Any, value: Any) -> None:
method update (line 369) | def update(
class QueryParams (line 380) | class QueryParams(ImmutableMultiDict[str, str]):
method __init__ (line 385) | def __init__(
method __str__ (line 403) | def __str__(self) -> str:
method __repr__ (line 406) | def __repr__(self) -> str:
class UploadFile (line 412) | class UploadFile:
method __init__ (line 417) | def __init__(
method content_type (line 435) | def content_type(self) -> str | None:
method _in_memory (line 439) | def _in_memory(self) -> bool:
method _will_roll (line 444) | def _will_roll(self, size_to_add: int) -> bool:
method write (line 453) | async def write(self, data: bytes) -> None:
method read (line 463) | async def read(self, size: int = -1) -> bytes:
method seek (line 468) | async def seek(self, offset: int) -> None:
method close (line 474) | async def close(self) -> None:
method __repr__ (line 480) | def __repr__(self) -> str:
class FormData (line 484) | class FormData(ImmutableMultiDict[str, UploadFile | str]):
method __init__ (line 489) | def __init__(
method close (line 496) | async def close(self) -> None:
class Headers (line 502) | class Headers(Mapping[str, str]):
method __init__ (line 507) | def __init__(
method raw (line 527) | def raw(self) -> list[tuple[bytes, bytes]]:
method keys (line 530) | def keys(self) -> list[str]: # type: ignore[override]
method values (line 533) | def values(self) -> list[str]: # type: ignore[override]
method items (line 536) | def items(self) -> list[tuple[str, str]]: # type: ignore[override]
method getlist (line 539) | def getlist(self, key: str) -> list[str]:
method mutablecopy (line 543) | def mutablecopy(self) -> MutableHeaders:
method __getitem__ (line 546) | def __getitem__(self, key: str) -> str:
method __contains__ (line 553) | def __contains__(self, key: Any) -> bool:
method __iter__ (line 560) | def __iter__(self) -> Iterator[Any]:
method __len__ (line 563) | def __len__(self) -> int:
method __eq__ (line 566) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 571) | def __repr__(self) -> str:
class MutableHeaders (line 579) | class MutableHeaders(Headers):
method __setitem__ (line 580) | def __setitem__(self, key: str, value: str) -> None:
method __delitem__ (line 602) | def __delitem__(self, key: str) -> None:
method __ior__ (line 616) | def __ior__(self, other: Mapping[str, str]) -> MutableHeaders:
method __or__ (line 622) | def __or__(self, other: Mapping[str, str]) -> MutableHeaders:
method raw (line 630) | def raw(self) -> list[tuple[bytes, bytes]]:
method setdefault (line 633) | def setdefault(self, key: str, value: str) -> str:
method update (line 647) | def update(self, other: Mapping[str, str]) -> None:
method append (line 651) | def append(self, key: str, value: str) -> None:
method add_vary_header (line 659) | def add_vary_header(self, vary: str) -> None:
class State (line 666) | class State:
method __init__ (line 675) | def __init__(self, state: dict[str, Any] | None = None):
method __setattr__ (line 680) | def __setattr__(self, key: Any, value: Any) -> None:
method __getattr__ (line 683) | def __getattr__(self, key: Any) -> Any:
method __delattr__ (line 690) | def __delattr__(self, key: Any) -> None:
method __getitem__ (line 693) | def __getitem__(self, key: str) -> Any:
method __setitem__ (line 696) | def __setitem__(self, key: str, value: Any) -> None:
method __delitem__ (line 699) | def __delitem__(self, key: str) -> None:
method __iter__ (line 702) | def __iter__(self) -> Iterator[str]:
method __len__ (line 705) | def __len__(self) -> int:
FILE: starlette/endpoints.py
class HTTPEndpoint (line 17) | class HTTPEndpoint:
method __init__ (line 18) | def __init__(self, scope: Scope, receive: Receive, send: Send) -> None:
method __await__ (line 29) | def __await__(self) -> Generator[Any, None, None]:
method dispatch (line 32) | async def dispatch(self) -> None:
method method_not_allowed (line 44) | async def method_not_allowed(self, request: Request) -> Response:
class WebSocketEndpoint (line 54) | class WebSocketEndpoint:
method __init__ (line 57) | def __init__(self, scope: Scope, receive: Receive, send: Send) -> None:
method __await__ (line 63) | def __await__(self) -> Generator[Any, None, None]:
method dispatch (line 66) | async def dispatch(self) -> None:
method decode (line 87) | async def decode(self, websocket: WebSocket, message: Message) -> Any:
method on_connect (line 115) | async def on_connect(self, websocket: WebSocket) -> None:
method on_receive (line 119) | async def on_receive(self, websocket: WebSocket, data: Any) -> None:
method on_disconnect (line 122) | async def on_disconnect(self, websocket: WebSocket, close_code: int) -...
FILE: starlette/exceptions.py
class HTTPException (line 7) | class HTTPException(Exception):
method __init__ (line 8) | def __init__(self, status_code: int, detail: str | None = None, header...
method __str__ (line 15) | def __str__(self) -> str:
method __repr__ (line 18) | def __repr__(self) -> str:
class WebSocketException (line 23) | class WebSocketException(Exception):
method __init__ (line 24) | def __init__(self, code: int, reason: str | None = None) -> None:
method __str__ (line 28) | def __str__(self) -> str:
method __repr__ (line 31) | def __repr__(self) -> str:
FILE: starlette/formparsers.py
class FormMessage (line 28) | class FormMessage(Enum):
class MultipartPart (line 37) | class MultipartPart:
function _user_safe_decode (line 45) | def _user_safe_decode(src: bytes | bytearray, codec: str) -> str:
class MultiPartException (line 52) | class MultiPartException(Exception):
method __init__ (line 53) | def __init__(self, message: str) -> None:
class FormParser (line 57) | class FormParser:
method __init__ (line 58) | def __init__(self, headers: Headers, stream: AsyncGenerator[bytes, Non...
method on_field_start (line 64) | def on_field_start(self) -> None:
method on_field_name (line 68) | def on_field_name(self, data: bytes, start: int, end: int) -> None:
method on_field_data (line 72) | def on_field_data(self, data: bytes, start: int, end: int) -> None:
method on_field_end (line 76) | def on_field_end(self) -> None:
method on_end (line 80) | def on_end(self) -> None:
method parse (line 84) | async def parse(self) -> FormData:
class MultiPartParser (line 125) | class MultiPartParser:
method __init__ (line 131) | def __init__(
method on_part_begin (line 157) | def on_part_begin(self) -> None:
method on_part_data (line 160) | def on_part_data(self, data: bytes, start: int, end: int) -> None:
method on_part_end (line 169) | def on_part_end(self) -> None:
method on_header_field (line 184) | def on_header_field(self, data: bytes, start: int, end: int) -> None:
method on_header_value (line 187) | def on_header_value(self, data: bytes, start: int, end: int) -> None:
method on_header_end (line 190) | def on_header_end(self) -> None:
method on_headers_finished (line 198) | def on_headers_finished(self) -> None:
method on_end (line 223) | def on_end(self) -> None:
method parse (line 226) | async def parse(self) -> FormData:
FILE: starlette/middleware/__init__.py
class _MiddlewareFactory (line 17) | class _MiddlewareFactory(Protocol[P]):
method __call__ (line 18) | def __call__(self, app: _ASGIApp, /, *args: P.args, **kwargs: P.kwargs...
class Middleware (line 21) | class Middleware:
method __init__ (line 22) | def __init__(self, cls: _MiddlewareFactory[P], *args: P.args, **kwargs...
method __iter__ (line 27) | def __iter__(self) -> Iterator[Any]:
method __repr__ (line 31) | def __repr__(self) -> str:
FILE: starlette/middleware/authentication.py
class AuthenticationMiddleware (line 16) | class AuthenticationMiddleware:
method __init__ (line 17) | def __init__(
method __call__ (line 29) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method default_on_error (line 51) | def default_on_error(conn: HTTPConnection, exc: Exception) -> Response:
FILE: starlette/middleware/base.py
class _CachedRequest (line 20) | class _CachedRequest(Request):
method __init__ (line 28) | def __init__(self, scope: Scope, receive: Receive):
method wrapped_receive (line 34) | async def wrapped_receive(self) -> Message:
class BaseHTTPMiddleware (line 96) | class BaseHTTPMiddleware:
method __init__ (line 97) | def __init__(self, app: ASGIApp, dispatch: DispatchFunction | None = N...
method __call__ (line 101) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method dispatch (line 200) | async def dispatch(self, request: Request, call_next: RequestResponseE...
class _StreamingResponse (line 204) | class _StreamingResponse(Response):
method __init__ (line 205) | def __init__(
method __call__ (line 220) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
FILE: starlette/middleware/cors.py
class CORSMiddleware (line 15) | class CORSMiddleware:
method __init__ (line 16) | def __init__(
method __call__ (line 78) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method is_allowed_origin (line 98) | def is_allowed_origin(self, origin: str) -> bool:
method preflight_response (line 107) | def preflight_response(self, request_headers: Headers) -> Response:
method simple_response (line 152) | async def simple_response(self, scope: Scope, receive: Receive, send: ...
method send (line 156) | async def send(self, message: Message, send: Send, request_headers: He...
method allow_explicit_origin (line 177) | def allow_explicit_origin(headers: MutableHeaders, origin: str) -> None:
FILE: starlette/middleware/errors.py
class ServerErrorMiddleware (line 127) | class ServerErrorMiddleware:
method __init__ (line 139) | def __init__(
method __call__ (line 149) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method format_line (line 188) | def format_line(self, index: int, line: str, frame_lineno: int, frame_...
method generate_frame_html (line 199) | def generate_frame_html(self, frame: inspect.FrameInfo, is_collapsed: ...
method generate_html (line 224) | def generate_html(self, exc: Exception, limit: int = 7) -> str:
method generate_plain_text (line 246) | def generate_plain_text(self, exc: Exception) -> str:
method debug_response (line 249) | def debug_response(self, request: Request, exc: Exception) -> Response:
method error_response (line 258) | def error_response(self, request: Request, exc: Exception) -> Response:
FILE: starlette/middleware/exceptions.py
class ExceptionMiddleware (line 18) | class ExceptionMiddleware:
method __init__ (line 19) | def __init__(
method add_exception_handler (line 36) | def add_exception_handler(
method __call__ (line 47) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method http_exception (line 65) | async def http_exception(self, request: Request, exc: Exception) -> Re...
method websocket_exception (line 71) | async def websocket_exception(self, websocket: WebSocket, exc: Excepti...
FILE: starlette/middleware/gzip.py
class GZipMiddleware (line 11) | class GZipMiddleware:
method __init__ (line 12) | def __init__(self, app: ASGIApp, minimum_size: int = 500, compressleve...
method __call__ (line 17) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class IdentityResponder (line 32) | class IdentityResponder:
method __init__ (line 35) | def __init__(self, app: ASGIApp, minimum_size: int) -> None:
method __call__ (line 44) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method send_with_compression (line 48) | async def send_with_compression(self, message: Message) -> None:
method apply_compression (line 109) | def apply_compression(self, body: bytes, *, more_body: bool) -> bytes:
class GZipResponder (line 119) | class GZipResponder(IdentityResponder):
method __init__ (line 122) | def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int...
method __call__ (line 128) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method apply_compression (line 132) | def apply_compression(self, body: bytes, *, more_body: bool) -> bytes:
function unattached_send (line 144) | async def unattached_send(message: Message) -> NoReturn:
FILE: starlette/middleware/httpsredirect.py
class HTTPSRedirectMiddleware (line 6) | class HTTPSRedirectMiddleware:
method __init__ (line 7) | def __init__(self, app: ASGIApp) -> None:
method __call__ (line 10) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
FILE: starlette/middleware/sessions.py
class SessionMiddleware (line 16) | class SessionMiddleware:
method __init__ (line 17) | def __init__(
method __call__ (line 39) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class Session (line 91) | class Session(dict[str, typing.Any]):
method mark_accessed (line 95) | def mark_accessed(self) -> None:
method mark_modified (line 98) | def mark_modified(self) -> None:
method __setitem__ (line 102) | def __setitem__(self, key: str, value: typing.Any) -> None:
method __delitem__ (line 106) | def __delitem__(self, key: str) -> None:
method clear (line 110) | def clear(self) -> None:
method pop (line 114) | def pop(self, key: str, *args: typing.Any) -> typing.Any:
method setdefault (line 118) | def setdefault(self, key: str, default: typing.Any = None) -> typing.Any:
method update (line 123) | def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
FILE: starlette/middleware/trustedhost.py
class TrustedHostMiddleware (line 12) | class TrustedHostMiddleware:
method __init__ (line 13) | def __init__(
method __call__ (line 31) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
FILE: starlette/middleware/wsgi.py
function build_environ (line 23) | def build_environ(scope: Scope, body: bytes) -> dict[str, Any]:
class WSGIMiddleware (line 75) | class WSGIMiddleware:
method __init__ (line 76) | def __init__(self, app: Callable[..., Any]) -> None:
method __call__ (line 79) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class WSGIResponder (line 85) | class WSGIResponder:
method __init__ (line 89) | def __init__(self, app: Callable[..., Any], scope: Scope) -> None:
method __call__ (line 98) | async def __call__(self, receive: Receive, send: Send) -> None:
method sender (line 114) | async def sender(self, send: Send) -> None:
method start_response (line 119) | def start_response(
method wsgi (line 143) | def wsgi(
FILE: starlette/requests.py
function cookie_parser (line 46) | def cookie_parser(cookie_string: str) -> dict[str, str]:
class ClientDisconnect (line 73) | class ClientDisconnect(Exception):
class HTTPConnection (line 80) | class HTTPConnection(Mapping[str, Any], Generic[StateT]):
method __init__ (line 86) | def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
method __getitem__ (line 90) | def __getitem__(self, key: str) -> Any:
method __iter__ (line 93) | def __iter__(self) -> Iterator[str]:
method __len__ (line 96) | def __len__(self) -> int:
method app (line 106) | def app(self) -> Any:
method url (line 110) | def url(self) -> URL:
method base_url (line 116) | def base_url(self) -> URL:
method headers (line 133) | def headers(self) -> Headers:
method query_params (line 139) | def query_params(self) -> QueryParams:
method path_params (line 145) | def path_params(self) -> dict[str, Any]:
method cookies (line 149) | def cookies(self) -> dict[str, str]:
method client (line 161) | def client(self) -> Address | None:
method session (line 169) | def session(self) -> dict[str, Any]:
method auth (line 178) | def auth(self) -> Any:
method user (line 183) | def user(self) -> Any:
method state (line 188) | def state(self) -> StateT:
method url_for (line 197) | def url_for(self, name: str, /, **path_params: Any) -> URL:
function empty_receive (line 205) | async def empty_receive() -> NoReturn:
function empty_send (line 209) | async def empty_send(message: Message) -> NoReturn:
class Request (line 213) | class Request(HTTPConnection[StateT]):
method __init__ (line 216) | def __init__(self, scope: Scope, receive: Receive = empty_receive, sen...
method method (line 226) | def method(self) -> str:
method receive (line 230) | def receive(self) -> Receive:
method stream (line 233) | async def stream(self) -> AsyncGenerator[bytes, None]:
method body (line 253) | async def body(self) -> bytes:
method json (line 261) | async def json(self) -> Any:
method _get_form (line 267) | async def _get_form(
method form (line 302) | def form(
method close (line 313) | async def close(self) -> None:
method is_disconnected (line 317) | async def is_disconnected(self) -> bool:
method send_push_promise (line 331) | async def send_push_promise(self, path: str) -> None:
FILE: starlette/responses.py
class Response (line 29) | class Response:
method __init__ (line 33) | def __init__(
method render (line 48) | def render(self, content: Any) -> bytes | memoryview:
method init_headers (line 55) | def init_headers(self, headers: Mapping[str, str] | None = None) -> None:
method headers (line 84) | def headers(self) -> MutableHeaders:
method set_cookie (line 89) | def set_cookie(
method delete_cookie (line 134) | def delete_cookie(
method _wrap_websocket_denial_send (line 154) | def _wrap_websocket_denial_send(self, send: Send) -> Send:
method __call__ (line 163) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class HTMLResponse (line 173) | class HTMLResponse(Response):
class PlainTextResponse (line 177) | class PlainTextResponse(Response):
class JSONResponse (line 181) | class JSONResponse(Response):
method __init__ (line 184) | def __init__(
method render (line 194) | def render(self, content: Any) -> bytes:
class RedirectResponse (line 204) | class RedirectResponse(Response):
method __init__ (line 205) | def __init__(
class StreamingResponse (line 222) | class StreamingResponse(Response):
method __init__ (line 225) | def __init__(
method listen_for_disconnect (line 242) | async def listen_for_disconnect(self, receive: Receive) -> None:
method stream_response (line 248) | async def stream_response(self, send: Send) -> None:
method __call__ (line 257) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class MalformedRangeHeader (line 287) | class MalformedRangeHeader(Exception):
method __init__ (line 288) | def __init__(self, content: str = "Malformed range header.") -> None:
class RangeNotSatisfiable (line 292) | class RangeNotSatisfiable(Exception):
method __init__ (line 293) | def __init__(self, max_size: int) -> None:
class FileResponse (line 297) | class FileResponse(Response):
method __init__ (line 300) | def __init__(
method set_stat_headers (line 331) | def set_stat_headers(self, stat_result: os.stat_result) -> None:
method __call__ (line 341) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method _handle_simple (line 385) | async def _handle_simple(self, send: Send, send_header_only: bool, sen...
method _handle_single_range (line 399) | async def _handle_single_range(
method _handle_multiple_ranges (line 418) | async def _handle_multiple_ranges(
method _should_use_range (line 454) | def _should_use_range(self, http_if_range: str) -> bool:
method _parse_range_header (line 458) | def _parse_range_header(cls, http_range: str, file_size: int) -> list[...
method _parse_ranges (line 497) | def _parse_ranges(cls, range_: str, file_size: int) -> list[tuple[int,...
method generate_multipart (line 525) | def generate_multipart(
FILE: starlette/routing.py
class NoMatchFound (line 29) | class NoMatchFound(Exception):
method __init__ (line 35) | def __init__(self, name: str, path_params: dict[str, Any]) -> None:
class Match (line 40) | class Match(Enum):
function request_response (line 46) | def request_response(
function websocket_session (line 69) | def websocket_session(
function get_name (line 88) | def get_name(endpoint: Callable[..., Any]) -> str:
function replace_params (line 92) | def replace_params(
function compile_path (line 110) | def compile_path(
class BaseRoute (line 166) | class BaseRoute:
method matches (line 167) | def matches(self, scope: Scope) -> tuple[Match, Scope]:
method url_path_for (line 170) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method handle (line 173) | async def handle(self, scope: Scope, receive: Receive, send: Send) -> ...
method __call__ (line 176) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class Route (line 196) | class Route(BaseRoute):
method __init__ (line 197) | def __init__(
method matches (line 238) | def matches(self, scope: Scope) -> tuple[Match, Scope]:
method url_path_for (line 256) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method handle (line 267) | async def handle(self, scope: Scope, receive: Receive, send: Send) -> ...
method __eq__ (line 278) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 286) | def __repr__(self) -> str:
class WebSocketRoute (line 293) | class WebSocketRoute(BaseRoute):
method __init__ (line 294) | def __init__(
method matches (line 323) | def matches(self, scope: Scope) -> tuple[Match, Scope]:
method url_path_for (line 338) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method handle (line 349) | async def handle(self, scope: Scope, receive: Receive, send: Send) -> ...
method __eq__ (line 352) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 355) | def __repr__(self) -> str:
class Mount (line 359) | class Mount(BaseRoute):
method __init__ (line 360) | def __init__(
method routes (line 384) | def routes(self) -> list[BaseRoute]:
method matches (line 387) | def matches(self, scope: Scope) -> tuple[Match, Scope]:
method url_path_for (line 420) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method handle (line 447) | async def handle(self, scope: Scope, receive: Receive, send: Send) -> ...
method __eq__ (line 450) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 453) | def __repr__(self) -> str:
class Host (line 459) | class Host(BaseRoute):
method __init__ (line 460) | def __init__(self, host: str, app: ASGIApp, name: str | None = None) -...
method routes (line 468) | def routes(self) -> list[BaseRoute]:
method matches (line 471) | def matches(self, scope: Scope) -> tuple[Match, Scope]:
method url_path_for (line 486) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method handle (line 509) | async def handle(self, scope: Scope, receive: Receive, send: Send) -> ...
method __eq__ (line 512) | def __eq__(self, other: Any) -> bool:
method __repr__ (line 515) | def __repr__(self) -> str:
class _AsyncLiftContextManager (line 524) | class _AsyncLiftContextManager(AbstractAsyncContextManager[_T]):
method __init__ (line 525) | def __init__(self, cm: AbstractContextManager[_T]):
method __aenter__ (line 528) | async def __aenter__(self) -> _T:
method __aexit__ (line 531) | async def __aexit__(
function _wrap_gen_lifespan_context (line 540) | def _wrap_gen_lifespan_context(
class _DefaultLifespan (line 552) | class _DefaultLifespan:
method __init__ (line 553) | def __init__(self, router: Router):
method __aenter__ (line 556) | async def __aenter__(self) -> None:
method __aexit__ (line 559) | async def __aexit__(self, *exc_info: object) -> None:
method __call__ (line 562) | def __call__(self: _T, app: object) -> _T:
class Router (line 566) | class Router:
method __init__ (line 567) | def __init__(
method not_found (line 606) | async def not_found(self, scope: Scope, receive: Receive, send: Send) ...
method url_path_for (line 621) | def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
method lifespan (line 629) | async def lifespan(self, scope: Scope, receive: Receive, send: Send) -...
method __call__ (line 656) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method app (line 662) | async def app(self, scope: Scope, receive: Receive, send: Send) -> None:
method __eq__ (line 712) | def __eq__(self, other: Any) -> bool:
method mount (line 715) | def mount(self, path: str, app: ASGIApp, name: str | None = None) -> N...
method host (line 719) | def host(self, host: str, app: ASGIApp, name: str | None = None) -> No...
method add_route (line 723) | def add_route(
method add_websocket_route (line 740) | def add_websocket_route(
FILE: starlette/schemas.py
class OpenAPIResponse (line 18) | class OpenAPIResponse(Response):
method render (line 21) | def render(self, content: Any) -> bytes:
class EndpointInfo (line 27) | class EndpointInfo(NamedTuple):
class BaseSchemaGenerator (line 36) | class BaseSchemaGenerator:
method get_schema (line 37) | def get_schema(self, routes: list[BaseRoute]) -> dict[str, Any]:
method get_endpoints (line 40) | def get_endpoints(self, routes: list[BaseRoute]) -> list[EndpointInfo]:
method _remove_converter (line 89) | def _remove_converter(self, path: str) -> str:
method parse_docstring (line 98) | def parse_docstring(self, func_or_method: Callable[..., Any]) -> dict[...
method OpenAPIResponse (line 122) | def OpenAPIResponse(self, request: Request) -> Response:
class SchemaGenerator (line 128) | class SchemaGenerator(BaseSchemaGenerator):
method __init__ (line 129) | def __init__(self, base_schema: dict[str, Any]) -> None:
method get_schema (line 132) | def get_schema(self, routes: list[BaseRoute]) -> dict[str, Any]:
FILE: starlette/staticfiles.py
class NotModifiedResponse (line 22) | class NotModifiedResponse(Response):
method __init__ (line 32) | def __init__(self, headers: Headers):
class StaticFiles (line 39) | class StaticFiles:
method __init__ (line 40) | def __init__(
method get_directories (line 58) | def get_directories(
method __call__ (line 87) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
method get_path (line 101) | def get_path(self, scope: Scope) -> str:
method get_response (line 109) | async def get_response(self, path: str, scope: Scope) -> Response:
method lookup_path (line 154) | def lookup_path(self, path: str) -> tuple[str, os.stat_result | None]:
method file_response (line 172) | def file_response(
method check_config (line 186) | async def check_config(self) -> None:
method is_not_modified (line 202) | def is_not_modified(self, response_headers: Headers, request_headers: ...
FILE: starlette/status.py
function __getattr__ (line 188) | def __getattr__(name: str) -> int:
function __dir__ (line 208) | def __dir__() -> list[str]:
FILE: starlette/templating.py
class _TemplateResponse (line 31) | class _TemplateResponse(HTMLResponse):
method __init__ (line 32) | def __init__(
method __call__ (line 46) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class Jinja2Templates (line 54) | class Jinja2Templates:
method __init__ (line 69) | def __init__(
method __init__ (line 77) | def __init__(
method __init__ (line 84) | def __init__(
method _setup_env_defaults (line 101) | def _setup_env_defaults(self, env: jinja2.Environment) -> None:
method get_template (line 114) | def get_template(self, name: str) -> jinja2.Template:
method TemplateResponse (line 117) | def TemplateResponse(
FILE: starlette/testclient.py
function _is_asgi3 (line 55) | def _is_asgi3(app: ASGI2App | ASGI3App) -> TypeGuard[ASGI3App]:
class _WrapASGI2 (line 61) | class _WrapASGI2:
method __init__ (line 66) | def __init__(self, app: ASGI2App) -> None:
method __call__ (line 69) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class _AsyncBackend (line 74) | class _AsyncBackend(TypedDict):
class _Upgrade (line 79) | class _Upgrade(Exception):
method __init__ (line 80) | def __init__(self, session: WebSocketTestSession) -> None:
class WebSocketDenialResponse (line 84) | class WebSocketDenialResponse( # type: ignore[misc]
class WebSocketTestSession (line 94) | class WebSocketTestSession:
method __init__ (line 95) | def __init__(
method __enter__ (line 107) | def __enter__(self) -> WebSocketTestSession:
method __exit__ (line 122) | def __exit__(self, *args: Any) -> bool | None:
method _run (line 125) | async def _run(self, *, task_status: anyio.abc.TaskStatus[anyio.Cancel...
method _raise_on_close (line 142) | def _raise_on_close(self, message: Message) -> None:
method send (line 157) | def send(self, message: Message) -> None:
method send_text (line 160) | def send_text(self, data: str) -> None:
method send_bytes (line 163) | def send_bytes(self, data: bytes) -> None:
method send_json (line 166) | def send_json(self, data: Any, mode: Literal["text", "binary"] = "text...
method close (line 173) | def close(self, code: int = 1000, reason: str | None = None) -> None:
method receive (line 176) | def receive(self) -> Message:
method receive_text (line 179) | def receive_text(self) -> str:
method receive_bytes (line 184) | def receive_bytes(self) -> bytes:
method receive_json (line 189) | def receive_json(self, mode: Literal["text", "binary"] = "text") -> Any:
class _TestClientTransport (line 199) | class _TestClientTransport(httpx.BaseTransport):
method __init__ (line 200) | def __init__(
method handle_request (line 217) | def handle_request(self, request: httpx.Request) -> httpx.Response:
class TestClient (line 368) | class TestClient(httpx.Client):
method __init__ (line 373) | def __init__(
method _portal_factory (line 414) | def _portal_factory(self) -> Generator[anyio.abc.BlockingPortal, None,...
method request (line 421) | def request( # type: ignore[override]
method get (line 461) | def get( # type: ignore[override]
method options (line 484) | def options( # type: ignore[override]
method head (line 507) | def head( # type: ignore[override]
method post (line 530) | def post( # type: ignore[override]
method put (line 561) | def put( # type: ignore[override]
method patch (line 592) | def patch( # type: ignore[override]
method delete (line 623) | def delete( # type: ignore[override]
method websocket_connect (line 646) | def websocket_connect(
method __enter__ (line 669) | def __enter__(self) -> Self:
method __exit__ (line 698) | def __exit__(self, *args: Any) -> None:
method lifespan (line 701) | async def lifespan(self) -> None:
method wait_startup (line 708) | async def wait_startup(self) -> None:
method wait_shutdown (line 725) | async def wait_shutdown(self) -> None:
FILE: starlette/websockets.py
class WebSocketState (line 13) | class WebSocketState(enum.Enum):
class WebSocketDisconnect (line 20) | class WebSocketDisconnect(Exception):
method __init__ (line 21) | def __init__(self, code: int = 1000, reason: str | None = None) -> None:
class WebSocket (line 26) | class WebSocket(HTTPConnection[StateT]):
method __init__ (line 27) | def __init__(self, scope: Scope, receive: Receive, send: Send) -> None:
method receive (line 35) | async def receive(self) -> Message:
method send (line 59) | async def send(self, message: Message) -> None:
method accept (line 100) | async def accept(
method _raise_on_disconnect (line 112) | def _raise_on_disconnect(self, message: Message) -> None:
method receive_text (line 116) | async def receive_text(self) -> str:
method receive_bytes (line 123) | async def receive_bytes(self) -> bytes:
method receive_json (line 130) | async def receive_json(self, mode: str = "text") -> Any:
method iter_text (line 144) | async def iter_text(self) -> AsyncIterator[str]:
method iter_bytes (line 151) | async def iter_bytes(self) -> AsyncIterator[bytes]:
method iter_json (line 158) | async def iter_json(self) -> AsyncIterator[Any]:
method send_text (line 165) | async def send_text(self, data: str) -> None:
method send_bytes (line 168) | async def send_bytes(self, data: bytes) -> None:
method send_json (line 171) | async def send_json(self, data: Any, mode: str = "text") -> None:
method close (line 180) | async def close(self, code: int = 1000, reason: str | None = None) -> ...
method send_denial_response (line 183) | async def send_denial_response(self, response: Response) -> None:
class WebSocketClose (line 190) | class WebSocketClose:
method __init__ (line 191) | def __init__(self, code: int = 1000, reason: str | None = None) -> None:
method __call__ (line 195) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
FILE: tests/conftest.py
function test_client_factory (line 13) | def test_client_factory(
FILE: tests/middleware/test_base.py
class CustomMiddleware (line 26) | class CustomMiddleware(BaseHTTPMiddleware):
method dispatch (line 27) | async def dispatch(
function homepage (line 37) | def homepage(request: Request) -> PlainTextResponse:
function exc (line 41) | def exc(request: Request) -> None:
function exc_stream (line 45) | def exc_stream(request: Request) -> StreamingResponse:
function _generate_faulty_stream (line 49) | def _generate_faulty_stream() -> Generator[bytes, None, None]:
class NoResponse (line 54) | class NoResponse:
method __init__ (line 55) | def __init__(
method __await__ (line 63) | def __await__(self) -> Generator[Any, None, None]:
method dispatch (line 66) | async def dispatch(self) -> None:
function websocket_endpoint (line 70) | async def websocket_endpoint(session: WebSocket) -> None:
function test_custom_middleware (line 88) | def test_custom_middleware(test_client_factory: TestClientFactory) -> None:
function test_state_data_across_multiple_middlewares (line 109) | def test_state_data_across_multiple_middlewares(
function test_app_middleware_argument (line 165) | def test_app_middleware_argument(test_client_factory: TestClientFactory)...
function test_fully_evaluated_response (line 176) | def test_fully_evaluated_response(test_client_factory: TestClientFactory...
class CustomMiddlewareWithoutBaseHTTPMiddleware (line 197) | class CustomMiddlewareWithoutBaseHTTPMiddleware:
method __init__ (line 198) | def __init__(self, app: ASGIApp) -> None:
method __call__ (line 201) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
class CustomMiddlewareUsingBaseHTTPMiddleware (line 207) | class CustomMiddlewareUsingBaseHTTPMiddleware(BaseHTTPMiddleware):
method dispatch (line 208) | async def dispatch(
function test_contextvars (line 235) | def test_contextvars(
function test_run_background_tasks_even_if_client_disconnects (line 255) | async def test_run_background_tasks_even_if_client_disconnects() -> None:
function test_run_background_tasks_raise_exceptions (line 301) | def test_run_background_tasks_raise_exceptions(test_client_factory: Test...
function test_exception_can_be_caught (line 324) | def test_exception_can_be_caught(test_client_factory: TestClientFactory)...
function test_do_not_block_on_background_tasks (line 346) | async def test_do_not_block_on_background_tasks() -> None:
function test_run_context_manager_exit_even_if_client_disconnects (line 401) | async def test_run_context_manager_exit_even_if_client_disconnects() -> ...
function test_app_receives_http_disconnect_while_sending_if_discarded (line 459) | def test_app_receives_http_disconnect_while_sending_if_discarded(
function test_app_receives_http_disconnect_after_sending_if_discarded (line 535) | def test_app_receives_http_disconnect_after_sending_if_discarded(
function test_read_request_stream_in_app_after_middleware_calls_stream (line 585) | def test_read_request_stream_in_app_after_middleware_calls_stream(
function test_read_request_stream_in_app_after_middleware_calls_body (line 617) | def test_read_request_stream_in_app_after_middleware_calls_body(
function test_read_request_body_in_app_after_middleware_calls_stream (line 646) | def test_read_request_body_in_app_after_middleware_calls_stream(
function test_read_request_body_in_app_after_middleware_calls_body (line 675) | def test_read_request_body_in_app_after_middleware_calls_body(
function test_read_request_stream_in_dispatch_after_app_calls_stream (line 701) | def test_read_request_stream_in_dispatch_after_app_calls_stream(
function test_read_request_stream_in_dispatch_after_app_calls_body (line 733) | def test_read_request_stream_in_dispatch_after_app_calls_body(
function test_read_request_stream_in_dispatch_wrapping_app_calls_body (line 763) | async def test_read_request_stream_in_dispatch_wrapping_app_calls_body()...
function test_read_request_stream_in_dispatch_after_app_calls_body_with_middleware_calling_body_before_call_next (line 821) | def test_read_request_stream_in_dispatch_after_app_calls_body_with_middl...
function test_read_request_body_in_dispatch_after_app_calls_body_with_middleware_calling_body_before_call_next (line 851) | def test_read_request_body_in_dispatch_after_app_calls_body_with_middlew...
function test_read_request_disconnected_client (line 880) | async def test_read_request_disconnected_client() -> None:
function test_read_request_disconnected_after_consuming_steam (line 924) | async def test_read_request_disconnected_after_consuming_steam() -> None:
function test_downstream_middleware_modifies_receive (line 965) | def test_downstream_middleware_modifies_receive(
function test_pr_1519_comment_1236166180_example (line 1006) | def test_pr_1519_comment_1236166180_example() -> None:
function test_multiple_middlewares_stacked_client_disconnected (line 1049) | async def test_multiple_middlewares_stacked_client_disconnected() -> None:
function test_poll_for_disconnect_repeated (line 1156) | async def test_poll_for_disconnect_repeated(send_body: bool) -> None:
function test_asgi_pathsend_events (line 1205) | async def test_asgi_pathsend_events(tmpdir: Path) -> None:
function test_error_context_propagation (line 1248) | def test_error_context_propagation(test_client_factory: TestClientFactor...
FILE: tests/middleware/test_cors.py
function test_cors_allow_all (line 10) | def test_cors_allow_all(
function test_cors_allow_all_except_credentials (line 71) | def test_cors_allow_all_except_credentials(
function test_cors_allow_specific_origin (line 122) | def test_cors_allow_specific_origin(
function test_cors_disallowed_preflight (line 171) | def test_cors_disallowed_preflight(
function test_preflight_allows_request_origin_if_origins_wildcard_and_credentials_allowed (line 212) | def test_preflight_allows_request_origin_if_origins_wildcard_and_credent...
function test_cors_preflight_allow_all_methods (line 247) | def test_cors_preflight_allow_all_methods(
function test_cors_allow_all_methods (line 271) | def test_cors_allow_all_methods(
function test_cors_allow_origin_regex (line 300) | def test_cors_allow_origin_regex(
function test_cors_allow_origin_regex_fullmatch (line 372) | def test_cors_allow_origin_regex_fullmatch(
function test_cors_vary_header_defaults_to_origin (line 407) | def test_cors_vary_header_defaults_to_origin(test_client_factory: TestCl...
function test_cors_vary_header_is_not_set_for_non_credentialed_request (line 425) | def test_cors_vary_header_is_not_set_for_non_credentialed_request(test_c...
function test_cors_vary_header_is_properly_set_for_credentialed_request (line 440) | def test_cors_vary_header_is_properly_set_for_credentialed_request(test_...
function test_cors_vary_header_is_properly_set_when_allow_origins_is_not_wildcard (line 455) | def test_cors_vary_header_is_properly_set_when_allow_origins_is_not_wild...
function test_cors_allowed_origin_does_not_leak_between_requests (line 474) | def test_cors_allowed_origin_does_not_leak_between_requests(test_client_...
function test_cors_private_network_access_allowed (line 495) | def test_cors_private_network_access_allowed(test_client_factory: TestCl...
function test_cors_private_network_access_disallowed (line 536) | def test_cors_private_network_access_disallowed(test_client_factory: Tes...
FILE: tests/middleware/test_errors.py
function test_handler (line 15) | def test_handler(
function test_debug_text (line 31) | def test_debug_text(test_client_factory: TestClientFactory) -> None:
function test_debug_html (line 43) | def test_debug_html(test_client_factory: TestClientFactory) -> None:
function test_debug_after_response_sent (line 55) | def test_debug_after_response_sent(test_client_factory: TestClientFactor...
function test_debug_not_http (line 67) | def test_debug_not_http(test_client_factory: TestClientFactory) -> None:
function test_background_task (line 83) | def test_background_task(test_client_factory: TestClientFactory) -> None:
FILE: tests/middleware/test_gzip.py
function test_gzip_responses (line 17) | def test_gzip_responses(test_client_factory: TestClientFactory) -> None:
function test_gzip_not_in_accept_encoding (line 35) | def test_gzip_not_in_accept_encoding(test_client_factory: TestClientFact...
function test_gzip_ignored_for_small_responses (line 53) | def test_gzip_ignored_for_small_responses(
function test_gzip_streaming_response (line 73) | def test_gzip_streaming_response(test_client_factory: TestClientFactory)...
function test_gzip_streaming_response_identity (line 96) | def test_gzip_streaming_response_identity(test_client_factory: TestClien...
function test_gzip_ignored_for_responses_with_encoding_set (line 119) | def test_gzip_ignored_for_responses_with_encoding_set(
function test_gzip_ignored_on_server_sent_events (line 144) | def test_gzip_ignored_on_server_sent_events(test_client_factory: TestCli...
function test_gzip_ignored_for_pathsend_responses (line 167) | async def test_gzip_ignored_for_pathsend_responses(tmpdir: Path) -> None:
FILE: tests/middleware/test_https_redirect.py
function test_https_redirect_middleware (line 10) | def test_https_redirect_middleware(test_client_factory: TestClientFactor...
FILE: tests/middleware/test_middleware.py
class CustomMiddleware (line 5) | class CustomMiddleware: # pragma: no cover
method __init__ (line 6) | def __init__(self, app: ASGIApp, foo: str, *, bar: int) -> None:
method __call__ (line 11) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
function test_middleware_repr (line 15) | def test_middleware_repr() -> None:
function test_middleware_iter (line 20) | def test_middleware_iter() -> None:
FILE: tests/middleware/test_session.py
function view_session (line 13) | def view_session(request: Request) -> JSONResponse:
function update_session (line 17) | async def update_session(request: Request) -> JSONResponse:
function clear_session (line 23) | async def clear_session(request: Request) -> JSONResponse:
function no_session_access (line 28) | def no_session_access(request: Request) -> JSONResponse:
function test_session (line 32) | def test_session(test_client_factory: TestClientFactory) -> None:
function test_session_expires (line 65) | def test_session_expires(test_client_factory: TestClientFactory) -> None:
function test_secure_session (line 89) | def test_secure_session(test_client_factory: TestClientFactory) -> None:
function test_session_cookie_subpath (line 126) | def test_session_cookie_subpath(test_client_factory: TestClientFactory) ...
function test_invalid_session_cookie (line 144) | def test_invalid_session_cookie(test_client_factory: TestClientFactory) ...
function test_session_cookie (line 163) | def test_session_cookie(test_client_factory: TestClientFactory) -> None:
function test_domain_cookie (line 185) | def test_domain_cookie(test_client_factory: TestClientFactory) -> None:
function test_set_cookie_only_on_modification (line 207) | def test_set_cookie_only_on_modification(test_client_factory: TestClient...
function test_vary_cookie_on_access (line 227) | def test_vary_cookie_on_access(test_client_factory: TestClientFactory) -...
function test_session_tracks_modification (line 251) | def test_session_tracks_modification() -> None:
FILE: tests/middleware/test_trusted_host.py
function test_trusted_host_middleware (line 10) | def test_trusted_host_middleware(test_client_factory: TestClientFactory)...
function test_default_allowed_hosts (line 32) | def test_default_allowed_hosts() -> None:
function test_www_redirect (line 38) | def test_www_redirect(test_client_factory: TestClientFactory) -> None:
FILE: tests/middleware/test_wsgi.py
function hello_world (line 16) | def hello_world(
function echo_body (line 30) | def echo_body(
function raise_exception (line 44) | def raise_exception(
function return_exc_info (line 51) | def return_exc_info(
function test_wsgi_get (line 68) | def test_wsgi_get(test_client_factory: TestClientFactory) -> None:
function test_wsgi_post (line 76) | def test_wsgi_post(test_client_factory: TestClientFactory) -> None:
function test_wsgi_exception (line 84) | def test_wsgi_exception(test_client_factory: TestClientFactory) -> None:
function test_wsgi_exc_info (line 93) | def test_wsgi_exc_info(test_client_factory: TestClientFactory) -> None:
function test_build_environ (line 108) | def test_build_environ() -> None:
function test_build_environ_encoding (line 153) | def test_build_environ_encoding() -> None:
FILE: tests/test__utils.py
function test_async_func (line 11) | def test_async_func() -> None:
function test_async_partial (line 20) | def test_async_partial() -> None:
function test_async_method (line 32) | def test_async_method() -> None:
function test_async_object_call (line 43) | def test_async_object_call() -> None:
function test_async_partial_object_call (line 54) | def test_async_partial_object_call() -> None:
function test_async_nested_partial (line 76) | def test_async_nested_partial() -> None:
function test_async_mocked_async_function (line 87) | def test_async_mocked_async_function() -> None:
function test_get_route_path (line 103) | def test_get_route_path(scope: Scope, expected_result: str) -> None:
FILE: tests/test_applications.py
function error_500 (line 28) | async def error_500(request: Request, exc: HTTPException) -> JSONResponse:
function method_not_allowed (line 32) | async def method_not_allowed(request: Request, exc: HTTPException) -> JS...
function http_exception (line 36) | async def http_exception(request: Request, exc: HTTPException) -> JSONRe...
function func_homepage (line 40) | def func_homepage(request: Request) -> PlainTextResponse:
function async_homepage (line 44) | async def async_homepage(request: Request) -> PlainTextResponse:
class Homepage (line 48) | class Homepage(HTTPEndpoint):
method get (line 49) | def get(self, request: Request) -> PlainTextResponse:
function all_users_page (line 53) | def all_users_page(request: Request) -> PlainTextResponse:
function user_page (line 57) | def user_page(request: Request) -> PlainTextResponse:
function custom_subdomain (line 62) | def custom_subdomain(request: Request) -> PlainTextResponse:
function runtime_error (line 66) | def runtime_error(request: Request) -> None:
function websocket_endpoint (line 70) | async def websocket_endpoint(session: WebSocket) -> None:
function websocket_raise_websocket_exception (line 76) | async def websocket_raise_websocket_exception(websocket: WebSocket) -> N...
function websocket_raise_http_exception (line 81) | async def websocket_raise_http_exception(websocket: WebSocket) -> None:
class CustomWSException (line 85) | class CustomWSException(Exception):
function websocket_raise_custom (line 89) | async def websocket_raise_custom(websocket: WebSocket) -> None:
function websocket_state (line 94) | async def websocket_state(websocket: WebSocket[CustomState]) -> None:
function custom_ws_exception_handler (line 100) | def custom_ws_exception_handler(websocket: WebSocket, exc: CustomWSExcep...
class CustomState (line 104) | class CustomState(TypedDict):
function lifespan (line 109) | async def lifespan(app: Starlette) -> AsyncGenerator[CustomState]:
function state_count (line 113) | async def state_count(request: Request[CustomState]) -> JSONResponse:
function client (line 161) | def client(test_client_factory: TestClientFactory) -> Generator[TestClie...
function test_url_path_for (line 166) | def test_url_path_for() -> None:
function test_func_route (line 170) | def test_func_route(client: TestClient) -> None:
function test_async_route (line 180) | def test_async_route(client: TestClient) -> None:
function test_class_route (line 186) | def test_class_route(client: TestClient) -> None:
function test_mounted_route (line 192) | def test_mounted_route(client: TestClient) -> None:
function test_mounted_route_path_params (line 198) | def test_mounted_route_path_params(client: TestClient) -> None:
function test_subdomain_route (line 204) | def test_subdomain_route(test_client_factory: TestClientFactory) -> None:
function test_websocket_route (line 212) | def test_websocket_route(client: TestClient) -> None:
function test_400 (line 218) | def test_400(client: TestClient) -> None:
function test_405 (line 224) | def test_405(client: TestClient) -> None:
function test_500 (line 234) | def test_500(test_client_factory: TestClientFactory) -> None:
function test_request_state (line 241) | def test_request_state(client: TestClient) -> None:
function test_websocket_raise_websocket_exception (line 247) | def test_websocket_raise_websocket_exception(client: TestClient) -> None:
function test_websocket_state (line 257) | def test_websocket_state(client: TestClient) -> None:
function test_websocket_raise_http_exception (line 263) | def test_websocket_raise_http_exception(client: TestClient) -> None:
function test_websocket_raise_custom_exception (line 271) | def test_websocket_raise_custom_exception(client: TestClient) -> None:
function test_middleware (line 281) | def test_middleware(test_client_factory: TestClientFactory) -> None:
function test_routes (line 288) | def test_routes() -> None:
function test_app_mount (line 316) | def test_app_mount(tmpdir: Path, test_client_factory: TestClientFactory)...
function test_app_debug (line 338) | def test_app_debug(test_client_factory: TestClientFactory) -> None:
function test_app_add_route (line 356) | def test_app_add_route(test_client_factory: TestClientFactory) -> None:
function test_app_add_websocket_route (line 372) | def test_app_add_websocket_route(test_client_factory: TestClientFactory)...
function test_app_async_cm_lifespan (line 390) | def test_app_async_cm_lifespan(test_client_factory: TestClientFactory) -...
function test_app_async_gen_lifespan (line 422) | def test_app_async_gen_lifespan(test_client_factory: TestClientFactory) ...
function test_app_sync_gen_lifespan (line 444) | def test_app_sync_gen_lifespan(test_client_factory: TestClientFactory) -...
function test_middleware_stack_init (line 465) | def test_middleware_stack_init(test_client_factory: TestClientFactory) -...
function test_middleware_args (line 507) | def test_middleware_args(test_client_factory: TestClientFactory) -> None:
function test_middleware_factory (line 529) | def test_middleware_factory(test_client_factory: TestClientFactory) -> N...
function test_lifespan_app_subclass (line 552) | def test_lifespan_app_subclass() -> None:
FILE: tests/test_authentication.py
class BasicAuth (line 26) | class BasicAuth(AuthenticationBackend):
method authenticate (line 27) | async def authenticate(
function homepage (line 45) | def homepage(request: Request) -> JSONResponse:
function dashboard (line 55) | async def dashboard(request: Request) -> JSONResponse:
function admin (line 65) | async def admin(request: Request) -> JSONResponse:
function dashboard_sync (line 75) | def dashboard_sync(request: Request) -> JSONResponse:
class Dashboard (line 84) | class Dashboard(HTTPEndpoint):
method get (line 86) | def get(self, request: Request) -> JSONResponse:
function admin_sync (line 96) | def admin_sync(request: Request) -> JSONResponse:
function websocket_endpoint (line 106) | async def websocket_endpoint(websocket: WebSocket) -> None:
function async_inject_decorator (line 116) | def async_inject_decorator(
function decorated_async (line 130) | async def decorated_async(request: Request, additional: str) -> JSONResp...
function sync_inject_decorator (line 140) | def sync_inject_decorator(
function decorated_sync (line 154) | def decorated_sync(request: Request, additional: str) -> JSONResponse:
function ws_inject_decorator (line 164) | def ws_inject_decorator(**kwargs: Any) -> Callable[..., AsyncEndpoint]:
function websocket_endpoint_decorated (line 176) | async def websocket_endpoint_decorated(websocket: WebSocket, additional:...
function test_invalid_decorator_usage (line 204) | def test_invalid_decorator_usage() -> None:
function test_user_interface (line 212) | def test_user_interface(test_client_factory: TestClientFactory) -> None:
function test_authentication_required (line 223) | def test_authentication_required(test_client_factory: TestClientFactory)...
function test_websocket_authentication_required (line 273) | def test_websocket_authentication_required(
function test_authentication_redirect (line 306) | def test_authentication_redirect(test_client_factory: TestClientFactory)...
function on_auth_error (line 327) | def on_auth_error(request: HTTPConnection, exc: AuthenticationError) -> ...
function control_panel (line 332) | def control_panel(request: Request) -> JSONResponse:
function test_custom_on_error (line 347) | def test_custom_on_error(test_client_factory: TestClientFactory) -> None:
FILE: tests/test_background.py
function test_async_task (line 9) | def test_async_task(test_client_factory: TestClientFactory) -> None:
function test_sync_task (line 28) | def test_sync_task(test_client_factory: TestClientFactory) -> None:
function test_multiple_tasks (line 47) | def test_multiple_tasks(test_client_factory: TestClientFactory) -> None:
function test_multi_tasks_failure_avoids_next_execution (line 68) | def test_multi_tasks_failure_avoids_next_execution(
FILE: tests/test_concurrency.py
function test_run_until_first_complete (line 16) | async def test_run_until_first_complete() -> None:
function test_accessing_context_from_threaded_sync_endpoint (line 33) | def test_accessing_context_from_threaded_sync_endpoint(
function test_iterate_in_threadpool (line 50) | async def test_iterate_in_threadpool() -> None:
FILE: tests/test_config.py
function test_config_types (line 12) | def test_config_types() -> None:
function test_config (line 40) | def test_config(tmpdir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
function test_missing_env_file_raises (line 105) | def test_missing_env_file_raises(tmpdir: Path) -> None:
function test_environ (line 112) | def test_environ() -> None:
function test_config_with_env_prefix (line 137) | def test_config_with_env_prefix(tmpdir: Path, monkeypatch: pytest.Monkey...
function test_config_with_encoding (line 145) | def test_config_with_encoding(tmpdir: Path) -> None:
FILE: tests/test_convertors.py
function refresh_convertor_types (line 16) | def refresh_convertor_types() -> Iterator[None]:
class DateTimeConvertor (line 22) | class DateTimeConvertor(Convertor[datetime]):
method convert (line 25) | def convert(self, value: str) -> datetime:
method to_string (line 28) | def to_string(self, value: datetime) -> str:
function app (line 33) | def app() -> Router:
function test_datetime_convertor (line 52) | def test_datetime_convertor(test_client_factory: TestClientFactory, app:...
function test_default_float_convertor (line 63) | def test_default_float_convertor(test_client_factory: TestClientFactory,...
function test_default_uuid_convertor (line 86) | def test_default_uuid_convertor(test_client_factory: TestClientFactory, ...
FILE: tests/test_datastructures.py
function test_url (line 19) | def test_url() -> None:
function test_url_query_params (line 65) | def test_url_query_params() -> None:
function test_hidden_password (line 82) | def test_hidden_password() -> None:
function test_csv (line 93) | def test_csv() -> None:
function test_url_from_scope (line 117) | def test_url_from_scope() -> None:
function test_headers (line 162) | def test_headers() -> None:
function test_mutable_headers (line 189) | def test_mutable_headers() -> None:
function test_mutable_headers_merge (line 205) | def test_mutable_headers_merge() -> None:
function test_mutable_headers_merge_dict (line 214) | def test_mutable_headers_merge_dict() -> None:
function test_mutable_headers_update (line 223) | def test_mutable_headers_update() -> None:
function test_mutable_headers_update_dict (line 232) | def test_mutable_headers_update_dict() -> None:
function test_mutable_headers_merge_not_mapping (line 241) | def test_mutable_headers_merge_not_mapping() -> None:
function test_headers_mutablecopy (line 249) | def test_headers_mutablecopy() -> None:
function test_mutable_headers_from_scope (line 257) | def test_mutable_headers_from_scope() -> None:
function test_url_blank_params (line 267) | def test_url_blank_params() -> None:
function test_queryparams (line 280) | def test_queryparams() -> None:
function test_upload_file_file_input (line 309) | async def test_upload_file_file_input() -> None:
function test_upload_file_without_size (line 323) | async def test_upload_file_without_size() -> None:
function test_uploadfile_rolling (line 338) | async def test_uploadfile_rolling(max_size: int) -> None:
function test_formdata (line 362) | def test_formdata() -> None:
function test_upload_file_repr (line 386) | async def test_upload_file_repr() -> None:
function test_upload_file_repr_headers (line 393) | async def test_upload_file_repr_headers() -> None:
function test_multidict (line 399) | def test_multidict() -> None:
FILE: tests/test_endpoints.py
class Homepage (line 14) | class Homepage(HTTPEndpoint):
method get (line 15) | async def get(self, request: Request) -> PlainTextResponse:
function client (line 26) | def client(test_client_factory: TestClientFactory) -> Iterator[TestClient]:
function test_http_endpoint_route (line 31) | def test_http_endpoint_route(client: TestClient) -> None:
function test_http_endpoint_route_path_params (line 37) | def test_http_endpoint_route_path_params(client: TestClient) -> None:
function test_http_endpoint_route_method (line 43) | def test_http_endpoint_route_method(client: TestClient) -> None:
function test_websocket_endpoint_on_connect (line 50) | def test_websocket_endpoint_on_connect(test_client_factory: TestClientFa...
function test_websocket_endpoint_on_receive_bytes (line 61) | def test_websocket_endpoint_on_receive_bytes(
function test_websocket_endpoint_on_receive_json (line 81) | def test_websocket_endpoint_on_receive_json(
function test_websocket_endpoint_on_receive_json_binary (line 101) | def test_websocket_endpoint_on_receive_json_binary(
function test_websocket_endpoint_on_receive_text (line 117) | def test_websocket_endpoint_on_receive_text(
function test_websocket_endpoint_on_default (line 137) | def test_websocket_endpoint_on_default(test_client_factory: TestClientFa...
function test_websocket_endpoint_on_disconnect (line 151) | def test_websocket_endpoint_on_disconnect(
FILE: tests/test_exceptions.py
function raise_runtime_error (line 17) | def raise_runtime_error(request: Request) -> None:
function not_acceptable (line 21) | def not_acceptable(request: Request) -> None:
function no_content (line 25) | def no_content(request: Request) -> None:
function not_modified (line 29) | def not_modified(request: Request) -> None:
function with_headers (line 33) | def with_headers(request: Request) -> None:
class BadBodyException (line 37) | class BadBodyException(HTTPException):
function read_body_and_raise_exc (line 41) | async def read_body_and_raise_exc(request: Request) -> None:
function handler_that_reads_body (line 46) | async def handler_that_reads_body(request: Request, exc: BadBodyExceptio...
class HandledExcAfterResponse (line 51) | class HandledExcAfterResponse:
method __call__ (line 52) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
function client (line 79) | def client(test_client_factory: TestClientFactory) -> Generator[TestClie...
function test_not_acceptable (line 84) | def test_not_acceptable(client: TestClient) -> None:
function test_no_content (line 90) | def test_no_content(client: TestClient) -> None:
function test_not_modified (line 96) | def test_not_modified(client: TestClient) -> None:
function test_with_headers (line 102) | def test_with_headers(client: TestClient) -> None:
function test_websockets_should_raise (line 108) | def test_websockets_should_raise(client: TestClient) -> None:
function test_handled_exc_after_response (line 114) | def test_handled_exc_after_response(test_client_factory: TestClientFacto...
function test_force_500_response (line 128) | def test_force_500_response(test_client_factory: TestClientFactory) -> N...
function test_http_str (line 146) | def test_http_str() -> None:
function test_http_repr (line 152) | def test_http_repr() -> None:
function test_websocket_str (line 166) | def test_websocket_str() -> None:
function test_websocket_repr (line 171) | def test_websocket_repr() -> None:
function test_request_in_app_and_handler_is_the_same_object (line 185) | def test_request_in_app_and_handler_is_the_same_object(client: TestClien...
function test_http_exception_does_not_use_threadpool (line 191) | def test_http_exception_does_not_use_threadpool(client: TestClient, monk...
function test_handlers_annotations (line 210) | def test_handlers_annotations() -> None:
FILE: tests/test_formparsers.py
class ForceMultipartDict (line 25) | class ForceMultipartDict(dict[Any, Any]):
method __bool__ (line 26) | def __bool__(self) -> bool:
function app (line 34) | async def app(scope: Scope, receive: Receive, send: Send) -> None:
function multi_items_app (line 54) | async def multi_items_app(scope: Scope, receive: Receive, send: Send) ->...
function app_with_headers (line 78) | async def app_with_headers(scope: Scope, receive: Receive, send: Send) -...
function app_read_body (line 99) | async def app_read_body(scope: Scope, receive: Receive, send: Send) -> N...
function app_monitor_thread (line 112) | async def app_monitor_thread(scope: Scope, receive: Receive, send: Send)...
function make_app_max_parts (line 128) | def make_app_max_parts(max_files: int = 1000, max_fields: int = 1000, ma...
function test_multipart_request_data (line 151) | def test_multipart_request_data(tmpdir: Path, test_client_factory: TestC...
function test_multipart_request_files (line 157) | def test_multipart_request_files(tmpdir: Path, test_client_factory: Test...
function test_multipart_request_files_with_content_type (line 175) | def test_multipart_request_files_with_content_type(tmpdir: Path, test_cl...
function test_multipart_request_multiple_files (line 193) | def test_multipart_request_multiple_files(tmpdir: Path, test_client_fact...
function test_multipart_request_multiple_files_with_headers (line 221) | def test_multipart_request_multiple_files_with_headers(tmpdir: Path, tes...
function test_multi_items (line 258) | def test_multi_items(tmpdir: Path, test_client_factory: TestClientFactor...
function test_multipart_request_mixed_files_and_data (line 293) | def test_multipart_request_mixed_files_and_data(tmpdir: Path, test_clien...
class ThreadTrackingSpooledTemporaryFile (line 327) | class ThreadTrackingSpooledTemporaryFile(SpooledTemporaryFile[bytes]):
method rollover (line 335) | def rollover(self) -> None:
function mock_spooled_temporary_file (line 341) | def mock_spooled_temporary_file() -> Generator[None]:
function test_multipart_request_large_file_rollover_in_background_thread (line 349) | def test_multipart_request_large_file_rollover_in_background_thread(
function test_multipart_request_with_charset_for_filename (line 368) | def test_multipart_request_with_charset_for_filename(tmpdir: Path, test_...
function test_multipart_request_without_charset_for_filename (line 392) | def test_multipart_request_without_charset_for_filename(tmpdir: Path, te...
function test_multipart_request_with_encoded_value (line 416) | def test_multipart_request_with_encoded_value(tmpdir: Path, test_client_...
function test_urlencoded_request_data (line 432) | def test_urlencoded_request_data(tmpdir: Path, test_client_factory: Test...
function test_no_request_data (line 438) | def test_no_request_data(tmpdir: Path, test_client_factory: TestClientFa...
function test_urlencoded_percent_encoding (line 444) | def test_urlencoded_percent_encoding(tmpdir: Path, test_client_factory: ...
function test_urlencoded_percent_encoding_keys (line 450) | def test_urlencoded_percent_encoding_keys(tmpdir: Path, test_client_fact...
function test_urlencoded_multi_field_app_reads_body (line 456) | def test_urlencoded_multi_field_app_reads_body(tmpdir: Path, test_client...
function test_multipart_multi_field_app_reads_body (line 462) | def test_multipart_multi_field_app_reads_body(tmpdir: Path, test_client_...
function test_user_safe_decode_helper (line 468) | def test_user_safe_decode_helper() -> None:
function test_user_safe_decode_ignores_wrong_charset (line 473) | def test_user_safe_decode_ignores_wrong_charset() -> None:
function test_missing_boundary_parameter (line 485) | def test_missing_boundary_parameter(
function test_missing_name_parameter_on_content_disposition (line 513) | def test_missing_name_parameter_on_content_disposition(
function test_too_many_fields_raise (line 541) | def test_too_many_fields_raise(
function test_too_many_files_raise (line 568) | def test_too_many_files_raise(
function test_too_many_files_single_field_raise (line 595) | def test_too_many_files_single_field_raise(
function test_too_many_files_and_fields_raise (line 624) | def test_too_many_files_and_fields_raise(
function test_max_fields_is_customizable_low_raises (line 655) | def test_max_fields_is_customizable_low_raises(
function test_max_files_is_customizable_low_raises (line 685) | def test_max_files_is_customizable_low_raises(
function test_max_fields_is_customizable_high (line 705) | def test_max_fields_is_customizable_high(test_client_factory: TestClient...
function test_max_part_size_exceeds_limit (line 736) | def test_max_part_size_exceeds_limit(
function test_max_part_size_exceeds_custom_limit (line 776) | def test_max_part_size_exceeds_custom_limit(
FILE: tests/test_requests.py
function test_request_url (line 17) | def test_request_url(test_client_factory: TestClientFactory) -> None:
function test_request_query_params (line 32) | def test_request_query_params(test_client_factory: TestClientFactory) ->...
function test_request_headers (line 48) | def test_request_headers(test_client_factory: TestClientFactory) -> None:
function test_request_client (line 76) | def test_request_client(scope: Scope, expected_client: Address | None) -...
function test_request_body (line 82) | def test_request_body(test_client_factory: TestClientFactory) -> None:
function test_request_stream (line 101) | def test_request_stream(test_client_factory: TestClientFactory) -> None:
function test_request_form_urlencoded (line 122) | def test_request_form_urlencoded(test_client_factory: TestClientFactory)...
function test_request_form_context_manager (line 135) | def test_request_form_context_manager(test_client_factory: TestClientFac...
function test_request_body_then_stream (line 148) | def test_request_body_then_stream(test_client_factory: TestClientFactory...
function test_request_stream_then_body (line 164) | def test_request_stream_then_body(test_client_factory: TestClientFactory...
function test_request_json (line 183) | def test_request_json(test_client_factory: TestClientFactory) -> None:
function test_request_scope_interface (line 195) | def test_request_scope_interface() -> None:
function test_request_raw_path (line 206) | def test_request_raw_path(test_client_factory: TestClientFactory) -> None:
function test_request_without_setting_receive (line 219) | def test_request_without_setting_receive(
function test_request_disconnect (line 241) | def test_request_disconnect(
function test_request_is_disconnected (line 269) | def test_request_is_disconnected(test_client_factory: TestClientFactory)...
function test_request_state_object (line 292) | def test_request_state_object() -> None:
function test_request_state (line 328) | def test_request_state(test_client_factory: TestClientFactory) -> None:
function test_request_cookies (line 340) | def test_request_cookies(test_client_factory: TestClientFactory) -> None:
function test_cookie_lenient_parsing (line 359) | def test_cookie_lenient_parsing(test_client_factory: TestClientFactory) ...
function test_cookies_edge_cases (line 415) | def test_cookies_edge_cases(
function test_cookies_invalid (line 454) | def test_cookies_invalid(
function test_multiple_cookie_headers (line 475) | def test_multiple_cookie_headers(test_client_factory: TestClientFactory)...
function test_chunked_encoding (line 488) | def test_chunked_encoding(test_client_factory: TestClientFactory) -> None:
function test_request_send_push_promise (line 505) | def test_request_send_push_promise(test_client_factory: TestClientFactor...
function test_request_send_push_promise_without_push_extension (line 521) | def test_request_send_push_promise_without_push_extension(
function test_request_send_push_promise_without_setting_send (line 541) | def test_request_send_push_promise_without_setting_send(
function test_request_rcv (line 581) | async def test_request_rcv(messages: list[Message]) -> None:
function test_request_stream_called_twice (line 595) | async def test_request_stream_called_twice() -> None:
function test_request_url_outside_starlette_context (line 633) | def test_request_url_outside_starlette_context(test_client_factory: Test...
function test_request_url_starlette_context (line 646) | def test_request_url_starlette_context(test_client_factory: TestClientFa...
FILE: tests/test_responses.py
function test_text_response (line 26) | def test_text_response(test_client_factory: TestClientFactory) -> None:
function test_bytes_response (line 36) | def test_bytes_response(test_client_factory: TestClientFactory) -> None:
function test_json_none_response (line 46) | def test_json_none_response(test_client_factory: TestClientFactory) -> N...
function test_redirect_response (line 57) | def test_redirect_response(test_client_factory: TestClientFactory) -> None:
function test_quoting_redirect_response (line 71) | def test_quoting_redirect_response(test_client_factory: TestClientFactor...
function test_redirect_response_content_length_header (line 85) | def test_redirect_response_content_length_header(
function test_streaming_response (line 101) | def test_streaming_response(test_client_factory: TestClientFactory) -> N...
function test_streaming_response_custom_iterator (line 129) | def test_streaming_response_custom_iterator(
function test_streaming_response_custom_iterable (line 154) | def test_streaming_response_custom_iterable(
function test_sync_streaming_response (line 171) | def test_sync_streaming_response(test_client_factory: TestClientFactory)...
function test_response_headers (line 188) | def test_response_headers(test_client_factory: TestClientFactory) -> None:
function test_response_phrase (line 201) | def test_response_phrase(test_client_factory: TestClientFactory) -> None:
function test_file_response (line 213) | def test_file_response(tmp_path: Path, test_client_factory: TestClientFa...
function test_file_response_on_head_method (line 253) | async def test_file_response_on_head_method(tmp_path: Path) -> None:
function test_file_response_set_media_type (line 281) | def test_file_response_set_media_type(tmp_path: Path, test_client_factor...
function test_file_response_with_directory_raises_error (line 293) | def test_file_response_with_directory_raises_error(tmp_path: Path, test_...
function test_file_response_with_missing_file_raises_error (line 301) | def test_file_response_with_missing_file_raises_error(tmp_path: Path, te...
function test_file_response_with_chinese_filename (line 310) | def test_file_response_with_chinese_filename(tmp_path: Path, test_client...
function test_file_response_with_inline_disposition (line 324) | def test_file_response_with_inline_disposition(tmp_path: Path, test_clie...
function test_file_response_with_range_header (line 338) | def test_file_response_with_range_header(tmp_path: Path, test_client_fac...
function test_file_response_with_pathsend (line 355) | async def test_file_response_with_pathsend(tmpdir: Path) -> None:
function test_set_cookie (line 386) | def test_set_cookie(test_client_factory: TestClientFactory, monkeypatch:...
function test_set_cookie_raises_for_invalid_python_version (line 419) | def test_set_cookie_raises_for_invalid_python_version(
function test_set_cookie_path_none (line 434) | def test_set_cookie_path_none(test_client_factory: TestClientFactory) ->...
function test_set_cookie_samesite_none (line 446) | def test_set_cookie_samesite_none(test_client_factory: TestClientFactory...
function test_expires_on_set_cookie (line 466) | def test_expires_on_set_cookie(
function test_delete_cookie (line 486) | def test_delete_cookie(test_client_factory: TestClientFactory) -> None:
function test_populate_headers (line 503) | def test_populate_headers(test_client_factory: TestClientFactory) -> None:
function test_head_method (line 512) | def test_head_method(test_client_factory: TestClientFactory) -> None:
function test_empty_response (line 519) | def test_empty_response(test_client_factory: TestClientFactory) -> None:
function test_empty_204_response (line 528) | def test_empty_204_response(test_client_factory: TestClientFactory) -> N...
function test_non_empty_response (line 535) | def test_non_empty_response(test_client_factory: TestClientFactory) -> N...
function test_response_do_not_add_redundant_charset (line 542) | def test_response_do_not_add_redundant_charset(
function test_file_response_known_size (line 551) | def test_file_response_known_size(tmp_path: Path, test_client_factory: T...
function test_streaming_response_unknown_size (line 562) | def test_streaming_response_unknown_size(
function test_streaming_response_known_size (line 571) | def test_streaming_response_known_size(test_client_factory: TestClientFa...
function test_response_memoryview (line 578) | def test_response_memoryview(test_client_factory: TestClientFactory) -> ...
function test_streaming_response_memoryview (line 585) | def test_streaming_response_memoryview(test_client_factory: TestClientFa...
function test_streaming_response_stops_if_receiving_http_disconnect (line 593) | async def test_streaming_response_stops_if_receiving_http_disconnect() -...
function test_streaming_response_on_client_disconnects (line 624) | async def test_streaming_response_on_client_disconnects() -> None:
function test_streaming_response_runs_background_on_websocket_scope (line 657) | async def test_streaming_response_runs_background_on_websocket_scope() -...
function readme_file (line 698) | def readme_file(tmp_path: Path) -> Path:
function file_response_client (line 705) | def file_response_client(readme_file: Path, test_client_factory: TestCli...
function test_file_response_without_range (line 709) | def test_file_response_without_range(file_response_client: TestClient) -...
function test_file_response_head (line 718) | def test_file_response_head(file_response_client: TestClient) -> None:
function test_file_response_range (line 727) | def test_file_response_range(file_response_client: TestClient) -> None:
function test_file_response_range_head (line 736) | def test_file_response_range_head(file_response_client: TestClient) -> N...
function test_file_response_range_multi (line 745) | def test_file_response_range_multi(file_response_client: TestClient) -> ...
function test_file_response_range_multi_head (line 753) | def test_file_response_range_multi_head(file_response_client: TestClient...
function test_file_response_range_invalid (line 773) | def test_file_response_range_invalid(file_response_client: TestClient) -...
function test_file_response_range_head_max (line 778) | def test_file_response_range_head_max(file_response_client: TestClient) ...
function test_file_response_range_416 (line 783) | def test_file_response_range_416(file_response_client: TestClient) -> None:
function test_file_response_only_support_bytes_range (line 789) | def test_file_response_only_support_bytes_range(file_response_client: Te...
function test_file_response_range_must_be_requested (line 795) | def test_file_response_range_must_be_requested(file_response_client: Tes...
function test_file_response_start_must_be_less_than_end (line 801) | def test_file_response_start_must_be_less_than_end(file_response_client:...
function test_file_response_merge_ranges (line 807) | def test_file_response_merge_ranges(file_response_client: TestClient) ->...
class MultipartPart (line 815) | class MultipartPart:
function parse_multipart_data (line 820) | def parse_multipart_data(data: bytes, boundary: bytes | str) -> list[Mul...
function test_file_response_insert_ranges (line 861) | def test_file_response_insert_ranges(file_response_client: TestClient) -...
function test_file_response_range_without_dash (line 901) | def test_file_response_range_without_dash(file_response_client: TestClie...
function test_file_response_range_empty_start_and_end (line 907) | def test_file_response_range_empty_start_and_end(file_response_client: T...
function test_file_response_range_ignore_non_numeric (line 913) | def test_file_response_range_ignore_non_numeric(file_response_client: Te...
function test_file_response_suffix_range (line 919) | def test_file_response_suffix_range(file_response_client: TestClient) ->...
function test_file_response_multiple_calls (line 929) | def test_file_response_multiple_calls(file_response_client: TestClient) ...
function test_file_response_multi_small_chunk_size (line 941) | async def test_file_response_multi_small_chunk_size(readme_file: Path) -...
FILE: tests/test_routing.py
function homepage (line 25) | def homepage(request: Request) -> Response:
function users (line 29) | def users(request: Request) -> Response:
function user (line 33) | def user(request: Request) -> Response:
function user_me (line 38) | def user_me(request: Request) -> Response:
function disable_user (line 43) | def disable_user(request: Request) -> Response:
function user_no_match (line 48) | def user_no_match(request: Request) -> Response: # pragma: no cover
function partial_endpoint (line 53) | async def partial_endpoint(arg: str, request: Request) -> JSONResponse:
function partial_ws_endpoint (line 57) | async def partial_ws_endpoint(websocket: WebSocket) -> None:
class PartialRoutes (line 63) | class PartialRoutes:
method async_endpoint (line 65) | async def async_endpoint(cls, arg: str, request: Request) -> JSONRespo...
method async_ws_endpoint (line 69) | async def async_ws_endpoint(cls, websocket: WebSocket) -> None:
function func_homepage (line 75) | def func_homepage(request: Request) -> Response:
function contact (line 79) | def contact(request: Request) -> Response:
function int_convertor (line 83) | def int_convertor(request: Request) -> JSONResponse:
function float_convertor (line 88) | def float_convertor(request: Request) -> JSONResponse:
function path_convertor (line 93) | def path_convertor(request: Request) -> JSONResponse:
function uuid_converter (line 98) | def uuid_converter(request: Request) -> JSONResponse:
function path_with_parentheses (line 103) | def path_with_parentheses(request: Request) -> JSONResponse:
function websocket_endpoint (line 108) | async def websocket_endpoint(session: WebSocket) -> None:
function websocket_params (line 114) | async def websocket_params(session: WebSocket) -> None:
function client (line 168) | def client(
function test_router (line 181) | def test_router(client: TestClient) -> None:
function test_route_converters (line 226) | def test_route_converters(client: TestClient) -> None:
function test_url_path_for (line 261) | def test_url_path_for() -> None:
function test_url_for (line 275) | def test_url_for() -> None:
function test_router_add_route (line 295) | def test_router_add_route(client: TestClient) -> None:
function test_router_duplicate_path (line 301) | def test_router_duplicate_path(client: TestClient) -> None:
function test_router_add_websocket_route (line 307) | def test_router_add_websocket_route(client: TestClient) -> None:
function test_router_middleware (line 317) | def test_router_middleware(test_client_factory: TestClientFactory) -> None:
function http_endpoint (line 337) | def http_endpoint(request: Request) -> Response:
class WebSocketEndpoint (line 342) | class WebSocketEndpoint:
method __call__ (line 343) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
function test_protocol_switch (line 358) | def test_protocol_switch(test_client_factory: TestClientFactory) -> None:
function test_mount_urls (line 376) | def test_mount_urls(test_client_factory: TestClientFactory) -> None:
function test_reverse_mount_urls (line 386) | def test_reverse_mount_urls() -> None:
function test_mount_at_root (line 404) | def test_mount_at_root(test_client_factory: TestClientFactory) -> None:
function users_api (line 410) | def users_api(request: Request) -> JSONResponse:
function test_host_routing (line 439) | def test_host_routing(test_client_factory: TestClientFactory) -> None:
function test_host_reverse_urls (line 479) | def test_host_reverse_urls() -> None:
function subdomain_app (line 496) | async def subdomain_app(scope: Scope, receive: Receive, send: Send) -> N...
function test_subdomain_routing (line 504) | def test_subdomain_routing(test_client_factory: TestClientFactory) -> None:
function test_subdomain_reverse_urls (line 512) | def test_subdomain_reverse_urls() -> None:
function echo_urls (line 521) | async def echo_urls(request: Request) -> JSONResponse:
function test_url_for_with_root_path (line 540) | def test_url_for_with_root_path(test_client_factory: TestClientFactory) ...
function stub_app (line 555) | async def stub_app(scope: Scope, receive: Receive, send: Send) -> None:
function test_url_for_with_double_mount (line 564) | def test_url_for_with_double_mount() -> None:
function test_url_for_with_root_path_ending_with_slash (line 570) | def test_url_for_with_root_path_ending_with_slash(test_client_factory: T...
function test_standalone_route_matches (line 580) | def test_standalone_route_matches(
function test_standalone_route_does_not_match (line 590) | def test_standalone_route_does_not_match(
function ws_helloworld (line 600) | async def ws_helloworld(websocket: WebSocket) -> None:
function test_standalone_ws_route_matches (line 606) | def test_standalone_ws_route_matches(
function test_standalone_ws_route_does_not_match (line 616) | def test_standalone_ws_route_does_not_match(
function test_lifespan_state_unsupported (line 626) | def test_lifespan_state_unsupported(test_client_factory: TestClientFacto...
function test_lifespan_state_async_cm (line 645) | def test_lifespan_state_async_cm(test_client_factory: TestClientFactory)...
function test_raise_on_startup (line 693) | def test_raise_on_startup(test_client_factory: TestClientFactory) -> None:
function test_raise_on_shutdown (line 717) | def test_raise_on_shutdown(test_client_factory: TestClientFactory) -> None:
function test_partial_async_endpoint (line 730) | def test_partial_async_endpoint(test_client_factory: TestClientFactory) ...
function test_partial_async_ws_endpoint (line 741) | def test_partial_async_ws_endpoint(
function test_duplicated_param_names (line 754) | def test_duplicated_param_names() -> None:
class Endpoint (line 768) | class Endpoint:
method my_method (line 769) | async def my_method(self, request: Request) -> None: ... # pragma: no...
method my_classmethod (line 772) | async def my_classmethod(cls, request: Request) -> None: ... # pragma...
method my_staticmethod (line 775) | async def my_staticmethod(request: Request) -> None: ... # pragma: no...
method __call__ (line 777) | def __call__(self, request: Request) -> None: ... # pragma: no cover
function test_route_name (line 795) | def test_route_name(endpoint: Callable[..., Response], expected_name: st...
class AddHeadersMiddleware (line 799) | class AddHeadersMiddleware:
method __init__ (line 800) | def __init__(self, app: ASGIApp) -> None:
method __call__ (line 803) | async def __call__(self, scope: Scope, receive: Receive, send: Send) -...
function assert_middleware_header_route (line 814) | def assert_middleware_header_route(request: Request) -> Response:
function test_base_route_middleware (line 875) | def test_base_route_middleware(
function test_mount_routes_with_middleware_url_path_for (line 890) | def test_mount_routes_with_middleware_url_path_for() -> None:
function test_mount_asgi_app_with_middleware_url_path_for (line 895) | def test_mount_asgi_app_with_middleware_url_path_for() -> None:
function test_add_route_to_app_after_mount (line 903) | def test_add_route_to_app_after_mount(
function test_exception_on_mounted_apps (line 921) | def test_exception_on_mounted_apps(
function test_mounted_middleware_does_not_catch_exception (line 936) | def test_mounted_middleware_does_not_catch_exception(
function test_websocket_route_middleware (line 991) | def test_websocket_route_middleware(
function test_route_repr (line 1029) | def test_route_repr() -> None:
function test_route_repr_without_methods (line 1034) | def test_route_repr_without_methods() -> None:
function test_websocket_route_repr (line 1039) | def test_websocket_route_repr() -> None:
function test_mount_repr (line 1044) | def test_mount_repr() -> None:
function test_mount_named_repr (line 1055) | def test_mount_named_repr() -> None:
function test_host_repr (line 1067) | def test_host_repr() -> None:
function test_host_named_repr (line 1080) | def test_host_named_repr() -> None:
function echo_paths (line 1094) | async def echo_paths(request: Request, name: str) -> JSONResponse:
function pure_asgi_echo_paths (line 1104) | async def pure_asgi_echo_paths(scope: Scope, receive: Receive, send: Sen...
function test_paths_with_root_path (line 1146) | def test_paths_with_root_path(test_client_factory: TestClientFactory) ->...
FILE: tests/test_schemas.py
function ws (line 13) | def ws(session: WebSocket) -> None:
function get_user (line 18) | def get_user(request: Request) -> None:
function list_users (line 29) | def list_users(request: Request) -> None:
function create_user (line 40) | def create_user(request: Request) -> None:
class OrganisationsEndpoint (line 51) | class OrganisationsEndpoint(HTTPEndpoint):
method get (line 52) | def get(self, request: Request) -> None:
method post (line 62) | def post(self, request: Request) -> None:
function regular_docstring_and_schema (line 73) | def regular_docstring_and_schema(request: Request) -> None:
function regular_docstring (line 86) | def regular_docstring(request: Request) -> None:
function no_docstring (line 93) | def no_docstring(request: Request) -> None:
function subapp_endpoint (line 97) | def subapp_endpoint(request: Request) -> None:
function schema (line 106) | def schema(request: Request) -> Response:
function test_schema_generation (line 133) | def test_schema_generation() -> None:
function test_schema_endpoint (line 250) | def test_schema_endpoint(test_client_factory: TestClientFactory) -> None:
FILE: tests/test_staticfiles.py
function test_staticfiles (line 22) | def test_staticfiles(tmpdir: Path, test_client_factory: TestClientFactor...
function test_staticfiles_with_pathlib (line 34) | def test_staticfiles_with_pathlib(tmp_path: Path, test_client_factory: T...
function test_staticfiles_head_with_middleware (line 46) | def test_staticfiles_head_with_middleware(tmpdir: Path, test_client_fact...
function test_staticfiles_with_package (line 68) | def test_staticfiles_with_package(test_client_factory: TestClientFactory...
function test_staticfiles_post (line 82) | def test_staticfiles_post(tmpdir: Path, test_client_factory: TestClientF...
function test_staticfiles_with_directory_returns_404 (line 96) | def test_staticfiles_with_directory_returns_404(tmpdir: Path, test_clien...
function test_staticfiles_with_missing_file_returns_404 (line 110) | def test_staticfiles_with_missing_file_returns_404(tmpdir: Path, test_cl...
function test_staticfiles_instantiated_with_missing_directory (line 124) | def test_staticfiles_instantiated_with_missing_directory(tmpdir: Path) -...
function test_staticfiles_configured_with_missing_directory (line 131) | def test_staticfiles_configured_with_missing_directory(tmpdir: Path, tes...
function test_staticfiles_configured_with_file_instead_of_directory (line 140) | def test_staticfiles_configured_with_file_instead_of_directory(
function test_staticfiles_config_check_occurs_only_once (line 154) | def test_staticfiles_config_check_occurs_only_once(tmpdir: Path, test_cl...
function test_staticfiles_prevents_breaking_out_of_directory (line 168) | def test_staticfiles_prevents_breaking_out_of_directory(tmpdir: Path) ->...
function test_staticfiles_never_read_file_for_head_method (line 188) | def test_staticfiles_never_read_file_for_head_method(tmpdir: Path, test_...
function test_staticfiles_304_with_etag_match (line 201) | def test_staticfiles_304_with_etag_match(tmpdir: Path, test_client_facto...
function test_staticfiles_200_with_etag_mismatch (line 219) | def test_staticfiles_200_with_etag_mismatch(tmpdir: Path, test_client_fa...
function test_staticfiles_200_with_etag_mismatch_and_timestamp_match (line 234) | def test_staticfiles_200_with_etag_mismatch_and_timestamp_match(
function test_staticfiles_304_with_last_modified_compare_last_req (line 252) | def test_staticfiles_304_with_last_modified_compare_last_req(
function test_staticfiles_html_normal (line 273) | def test_staticfiles_html_normal(tmpdir: Path, test_client_factory: Test...
function test_staticfiles_html_without_index (line 306) | def test_staticfiles_html_without_index(tmpdir: Path, test_client_factor...
function test_staticfiles_html_without_404 (line 331) | def test_staticfiles_html_without_404(tmpdir: Path, test_client_factory:...
function test_staticfiles_html_only_files (line 356) | def test_staticfiles_html_only_files(tmpdir: Path, test_client_factory: ...
function test_staticfiles_cache_invalidation_for_deleted_file_html_mode (line 373) | def test_staticfiles_cache_invalidation_for_deleted_file_html_mode(
function test_staticfiles_with_invalid_dir_permissions_returns_401 (line 410) | def test_staticfiles_with_invalid_dir_permissions_returns_401(
function test_staticfiles_with_missing_dir_returns_404 (line 435) | def test_staticfiles_with_missing_dir_returns_404(tmpdir: Path, test_cli...
function test_staticfiles_access_file_as_dir_returns_404 (line 449) | def test_staticfiles_access_file_as_dir_returns_404(tmpdir: Path, test_c...
function test_staticfiles_null_byte_in_path (line 463) | def test_staticfiles_null_byte_in_path(tmpdir: Path, test_client_factory...
function test_staticfiles_filename_too_long (line 472) | def test_staticfiles_filename_too_long(tmpdir: Path, test_client_factory...
function test_staticfiles_unhandled_os_error_returns_500 (line 483) | def test_staticfiles_unhandled_os_error_returns_500(
function test_staticfiles_follows_symlinks (line 506) | def test_staticfiles_follows_symlinks(tmpdir: Path, test_client_factory:...
function test_staticfiles_follows_symlink_directories (line 527) | def test_staticfiles_follows_symlink_directories(tmpdir: Path, test_clie...
function test_staticfiles_disallows_path_traversal_with_symlinks (line 548) | def test_staticfiles_disallows_path_traversal_with_symlinks(tmpdir: Path...
function test_staticfiles_avoids_path_traversal (line 573) | def test_staticfiles_avoids_path_traversal(tmp_path: Path) -> None:
function test_staticfiles_self_symlinks (line 606) | def test_staticfiles_self_symlinks(tmp_path: Path, test_client_factory: ...
function test_staticfiles_relative_directory_symlinks (line 625) | def test_staticfiles_relative_directory_symlinks(test_client_factory: Te...
FILE: tests/test_status.py
function test_deprecated_types (line 19) | def test_deprecated_types(constant: str, msg: str) -> None:
function test_unknown_status (line 26) | def test_unknown_status() -> None:
FILE: tests/test_templates.py
function test_templates (line 19) | def test_templates(tmpdir: Path, test_client_factory: TestClientFactory)...
function test_templates_autoescape (line 37) | def test_templates_autoescape(tmp_path: Path) -> None:
function test_calls_context_processors (line 49) | def test_calls_context_processors(tmp_path: Path, test_client_factory: T...
function test_template_with_middleware (line 77) | def test_template_with_middleware(tmpdir: Path, test_client_factory: Tes...
function test_templates_with_directories (line 103) | def test_templates_with_directories(tmp_path: Path, test_client_factory:...
function test_templates_require_directory_or_environment (line 138) | def test_templates_require_directory_or_environment() -> None:
function test_templates_require_directory_or_environment_not_both (line 143) | def test_templates_require_directory_or_environment_not_both() -> None:
function test_templates_with_directory (line 148) | def test_templates_with_directory(tmpdir: Path) -> None:
function test_templates_with_environment (line 158) | def test_templates_with_environment(tmpdir: Path, test_client_factory: T...
FILE: tests/test_testclient.py
function mock_service_endpoint (line 27) | def mock_service_endpoint(request: Request) -> JSONResponse:
function current_task (line 34) | def current_task() -> Task[Any] | trio.lowlevel.Task:
function test_use_testclient_in_endpoint (line 49) | def test_use_testclient_in_endpoint(test_client_factory: TestClientFacto...
function test_testclient_headers_behavior (line 69) | def test_testclient_headers_behavior() -> None:
function test_use_testclient_as_contextmanager (line 88) | def test_use_testclient_as_contextmanager(test_client_factory: TestClien...
function test_error_on_startup (line 166) | def test_error_on_startup(test_client_factory: TestClientFactory) -> None:
function test_exception_in_middleware (line 179) | def test_exception_in_middleware(test_client_factory: TestClientFactory)...
function test_testclient_asgi2 (line 197) | def test_testclient_asgi2(test_client_factory: TestClientFactory) -> None:
function test_testclient_asgi3 (line 216) | def test_testclient_asgi3(test_client_factory: TestClientFactory) -> None:
function test_websocket_blocking_receive (line 232) | def test_websocket_blocking_receive(test_client_factory: TestClientFacto...
function test_websocket_not_block_on_close (line 257) | def test_websocket_not_block_on_close(test_client_factory: TestClientFac...
function test_client (line 279) | def test_client(test_client_factory: TestClientFactory) -> None:
function test_client_custom_client (line 292) | def test_client_custom_client(test_client_factory: TestClientFactory) ->...
function test_query_params (line 306) | def test_query_params(test_client_factory: TestClientFactory, param: str...
function test_domain_restricted_cookies (line 334) | def test_domain_restricted_cookies(test_client_factory: TestClientFactor...
function test_forward_follow_redirects (line 360) | def test_forward_follow_redirects(test_client_factory: TestClientFactory...
function test_forward_nofollow_redirects (line 373) | def test_forward_nofollow_redirects(test_client_factory: TestClientFacto...
function test_with_duplicate_headers (line 383) | def test_with_duplicate_headers(test_client_factory: TestClientFactory) ...
function test_merge_url (line 393) | def test_merge_url(test_client_factory: TestClientFactory) -> None:
function test_raw_path_with_querystring (line 403) | def test_raw_path_with_querystring(test_client_factory: TestClientFactor...
function test_websocket_raw_path_without_params (line 413) | def test_websocket_raw_path_without_params(test_client_factory: TestClie...
function test_timeout_deprecation (line 427) | def test_timeout_deprecation() -> None:
FILE: tests/test_websockets.py
function test_websocket_url (line 18) | def test_websocket_url(test_client_factory: TestClientFactory) -> None:
function test_websocket_binary_json (line 31) | def test_websocket_binary_json(test_client_factory: TestClientFactory) -...
function test_websocket_ensure_unicode_on_send_json (line 46) | def test_websocket_ensure_unicode_on_send_json(
function test_websocket_query_params (line 64) | def test_websocket_query_params(test_client_factory: TestClientFactory) ...
function test_websocket_headers (line 82) | def test_websocket_headers(test_client_factory: TestClientFactory) -> None:
function test_websocket_port (line 105) | def test_websocket_port(test_client_factory: TestClientFactory) -> None:
function test_websocket_send_and_receive_text (line 118) | def test_websocket_send_and_receive_text(
function test_websocket_send_and_receive_bytes (line 135) | def test_websocket_send_and_receive_bytes(
function test_websocket_send_and_receive_json (line 152) | def test_websocket_send_and_receive_json(
function test_websocket_iter_text (line 169) | def test_websocket_iter_text(test_client_factory: TestClientFactory) -> ...
function test_websocket_iter_bytes (line 183) | def test_websocket_iter_bytes(test_client_factory: TestClientFactory) ->...
function test_websocket_iter_json (line 197) | def test_websocket_iter_json(test_client_factory: TestClientFactory) -> ...
function test_websocket_concurrency_pattern (line 211) | def test_websocket_concurrency_pattern(test_client_factory: TestClientFa...
function test_client_close (line 241) | def test_client_close(test_client_factory: TestClientFactory) -> None:
function test_client_disconnect_on_send (line 263) | async def test_client_disconnect_on_send() -> None:
function test_application_close (line 283) | def test_application_close(test_client_factory: TestClientFactory) -> None:
function test_rejected_connection (line 296) | def test_rejected_connection(test_client_factory: TestClientFactory) -> ...
function test_send_denial_response (line 310) | def test_send_denial_response(test_client_factory: TestClientFactory) ->...
function test_send_denial_response_with_streaming_response (line 326) | def test_send_denial_response_with_streaming_response(test_client_factor...
function test_send_denial_response_with_file_response (line 346) | def test_send_denial_response_with_file_response(test_client_factory: Te...
function test_send_response_multi (line 364) | def test_send_response_multi(test_client_factory: TestClientFactory) -> ...
function test_send_response_unsupported (line 388) | def test_send_response_unsupported(test_client_factory: TestClientFactor...
function test_send_response_duplicate_start (line 409) | def test_send_response_duplicate_start(test_client_factory: TestClientFa...
function test_subprotocol (line 439) | def test_subprotocol(test_client_factory: TestClientFactory) -> None:
function test_additional_headers (line 451) | def test_additional_headers(test_client_factory: TestClientFactory) -> N...
function test_no_additional_headers (line 462) | def test_no_additional_headers(test_client_factory: TestClientFactory) -...
function test_websocket_exception (line 473) | def test_websocket_exception(test_client_factory: TestClientFactory) -> ...
function test_duplicate_close (line 483) | def test_duplicate_close(test_client_factory: TestClientFactory) -> None:
function test_duplicate_disconnect (line 496) | def test_duplicate_disconnect(test_client_factory: TestClientFactory) ->...
function test_websocket_scope_interface (line 510) | def test_websocket_scope_interface() -> None:
function test_websocket_close_reason (line 537) | def test_websocket_close_reason(test_client_factory: TestClientFactory) ...
function test_send_json_invalid_mode (line 551) | def test_send_json_invalid_mode(test_client_factory: TestClientFactory) ...
function test_receive_json_invalid_mode (line 563) | def test_receive_json_invalid_mode(test_client_factory: TestClientFactor...
function test_receive_text_before_accept (line 575) | def test_receive_text_before_accept(test_client_factory: TestClientFacto...
function test_receive_bytes_before_accept (line 586) | def test_receive_bytes_before_accept(test_client_factory: TestClientFact...
function test_receive_json_before_accept (line 597) | def test_receive_json_before_accept(test_client_factory: TestClientFacto...
function test_send_before_accept (line 608) | def test_send_before_accept(test_client_factory: TestClientFactory) -> N...
function test_send_wrong_message_type (line 619) | def test_send_wrong_message_type(test_client_factory: TestClientFactory)...
function test_receive_before_accept (line 631) | def test_receive_before_accept(test_client_factory: TestClientFactory) -...
function test_receive_wrong_message_type (line 644) | def test_receive_wrong_message_type(test_client_factory: TestClientFacto...
FILE: tests/types.py
class TestClientFactory (line 12) | class TestClientFactory(Protocol): # pragma: no cover
method __call__ (line 13) | def __call__(
class TestClientFactory (line 26) | class TestClientFactory:
method __call__ (line 13) | def __call__(
Condensed preview — 120 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (883K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 15,
"preview": "github: Kludex\n"
},
{
"path": ".github/ISSUE_TEMPLATE/1-issue.md",
"chars": 589,
"preview": "---\nname: Issue\nabout: Please only raise an issue if you've been advised to do so after discussion. Thanks! 🙏\n---\n\nThe s"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 444,
"preview": "# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#con"
},
{
"path": ".github/dependabot.yml",
"chars": 341,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"uv\"\n directory: \"/\"\n schedule:\n interval: \"monthly\"\n groups:\n "
},
{
"path": ".github/pull_request_template.md",
"chars": 541,
"preview": "<!-- Thanks for contributing to Starlette! 💚\nGiven this is a project maintained by volunteers, please read this template"
},
{
"path": ".github/workflows/main.yml",
"chars": 1283,
"preview": "---\nname: Test Suite\n\non:\n push:\n branches: [\"main\"]\n pull_request:\n branches: [\"main\"]\n\njobs:\n tests:\n name"
},
{
"path": ".github/workflows/publish.yml",
"chars": 3121,
"preview": "name: Publish\n\non:\n push:\n tags:\n - \"*\"\n workflow_dispatch:\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n st"
},
{
"path": ".gitignore",
"chars": 135,
"preview": "*.pyc\ntest.db\n.coverage\n.pytest_cache/\n.mypy_cache/\n__pycache__/\nhtmlcov/\nsite/\n*.egg-info/\nvenv*/\n.venv/\n.python-versio"
},
{
"path": "CITATION.cff",
"chars": 627,
"preview": "# This CITATION.cff file was generated with cffinit.\n# Visit https://bit.ly/cffinit to generate yours today!\n\ncff-versio"
},
{
"path": "LICENSE.md",
"chars": 1517,
"preview": "Copyright © 2018, [Encode OSS Ltd](https://www.encode.io/).\nAll rights reserved.\n\nRedistribution and use in source and b"
},
{
"path": "README.md",
"chars": 4779,
"preview": "<p align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.co"
},
{
"path": "docs/CNAME",
"chars": 17,
"preview": "www.starlette.io\n"
},
{
"path": "docs/applications.md",
"chars": 1786,
"preview": "\n??? abstract \"API Reference\"\n ::: starlette.applications.Starlette\n options:\n parameter_headings: "
},
{
"path": "docs/authentication.md",
"chars": 5478,
"preview": "Starlette offers a simple but powerful interface for handling authentication\nand permissions. Once you've installed `Aut"
},
{
"path": "docs/background.md",
"chars": 1961,
"preview": "\nStarlette includes a `BackgroundTask` class for in-process background tasks.\n\nA background task should be attached to a"
},
{
"path": "docs/config.md",
"chars": 6960,
"preview": "Starlette encourages a strict separation of configuration from code,\nfollowing [the twelve-factor pattern][twelve-factor"
},
{
"path": "docs/contributing.md",
"chars": 5590,
"preview": "# Contributing\n\nThank you for being interested in contributing to Starlette.\nThere are many ways you can contribute to t"
},
{
"path": "docs/css/custom.css",
"chars": 839,
"preview": "/* Lighter dark mode colors */\n[data-md-color-scheme=\"slate\"] {\n --md-default-bg-color: #263238;\n --md-default-fg-colo"
},
{
"path": "docs/database.md",
"chars": 690,
"preview": "Starlette is not strictly tied to any particular database implementation.\n\nYou are free to use any async database librar"
},
{
"path": "docs/endpoints.md",
"chars": 4159,
"preview": "\nStarlette includes the classes `HTTPEndpoint` and `WebSocketEndpoint` that provide a class-based view pattern for\nhandl"
},
{
"path": "docs/exceptions.md",
"chars": 5160,
"preview": "\nStarlette allows you to install custom exception handlers to deal with\nhow you return responses when errors or handled "
},
{
"path": "docs/graphql.md",
"chars": 612,
"preview": "GraphQL support in Starlette was deprecated in version 0.15.0, and removed in version 0.17.0.\n\nAlthough GraphQL support "
},
{
"path": "docs/index.md",
"chars": 5244,
"preview": "<p align=\"center\">\n <img width=\"400px\" src=\"/img/starlette.svg#only-light\" alt=\"starlette\"/>\n <img width=\"400px\" src=\""
},
{
"path": "docs/js/custom.js",
"chars": 1078,
"preview": "function shuffle(array) {\n var currentIndex = array.length, temporaryValue, randomIndex;\n while (0 !== currentInde"
},
{
"path": "docs/lifespan.md",
"chars": 4502,
"preview": "\nStarlette applications can register a lifespan handler for dealing with\ncode that needs to run before the application s"
},
{
"path": "docs/middleware.md",
"chars": 36315,
"preview": "\nStarlette includes several middleware classes for adding behavior that is applied across\nyour entire application. These"
},
{
"path": "docs/overrides/main.html",
"chars": 832,
"preview": "{% extends \"base.html\" %}\n\n{% block extrahead %}\n{{ super() }}\n<script>\n // Redirect starlette.io to starlette.dev\n if"
},
{
"path": "docs/overrides/partials/toc-item.html",
"chars": 655,
"preview": "<!-- Copied from https://github.com/squidfunk/mkdocs-material/issues/4827#issuecomment-1869812019 -->\n<li class=\"md-nav_"
},
{
"path": "docs/release-notes.md",
"chars": 53461,
"preview": "---\ntoc_depth: 2\n---\n\n## 1.0.0rc1 (February 23, 2026)\n\nWe're ready! I'm thrilled to announce the first release candidate"
},
{
"path": "docs/requests.md",
"chars": 6760,
"preview": "\nStarlette includes a `Request` class that gives you a nicer interface onto\nthe incoming request, rather than accessing "
},
{
"path": "docs/responses.md",
"chars": 7366,
"preview": "\nStarlette includes a few response classes that handle sending back the\nappropriate ASGI messages on the `send` channel."
},
{
"path": "docs/routing.md",
"chars": 8902,
"preview": "## HTTP Routing\n\nStarlette has a simple but capable request routing system. A routing table\nis defined as a list of rout"
},
{
"path": "docs/schemas.md",
"chars": 3035,
"preview": "Starlette supports generating API schemas, such as the widely used [OpenAPI\nspecification][openapi]. (Formerly known as "
},
{
"path": "docs/server-push.md",
"chars": 1004,
"preview": "\nStarlette includes support for HTTP/2 and HTTP/3 server push, making it\npossible to push resources to the client to spe"
},
{
"path": "docs/staticfiles.md",
"chars": 2248,
"preview": "\nStarlette also includes a `StaticFiles` class for serving files in a given directory:\n\n### StaticFiles\n\nSignature: `Sta"
},
{
"path": "docs/templates.md",
"chars": 4620,
"preview": "Starlette is not _strictly_ coupled to any particular templating engine, but\nJinja2 provides an excellent choice.\n\n??? a"
},
{
"path": "docs/testclient.md",
"chars": 7692,
"preview": "\n??? abstract \"API Reference\"\n ::: starlette.testclient.TestClient\n options:\n parameter_headings: f"
},
{
"path": "docs/third-party-packages.md",
"chars": 12221,
"preview": "\nStarlette has a rapidly growing community of developers, building tools that integrate into Starlette, tools that depen"
},
{
"path": "docs/threadpool.md",
"chars": 1384,
"preview": "# Thread Pool\n\nStarlette uses a thread pool in several scenarios to avoid blocking the event loop:\n\n- When you create a "
},
{
"path": "docs/websockets.md",
"chars": 4820,
"preview": "\nStarlette includes a `WebSocket` class that fulfils a similar role\nto the HTTP request, but that allows sending and rec"
},
{
"path": "mkdocs.yml",
"chars": 3215,
"preview": "site_name: Starlette\nsite_description: The little ASGI library that shines.\nsite_url: https://starlette.dev\n\nrepo_name: "
},
{
"path": "pyproject.toml",
"chars": 3546,
"preview": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"starlette\"\ndynamic = [\"vers"
},
{
"path": "scripts/README.md",
"chars": 485,
"preview": "# Development Scripts\n\n* `scripts/install` - Install dependencies in a virtual environment.\n* `scripts/test` - Run the t"
},
{
"path": "scripts/build",
"chars": 87,
"preview": "#!/bin/sh -e\n\nset -x\n\nuv build\nuv run twine check dist/*\nuv run zensical build --clean\n"
},
{
"path": "scripts/check",
"chars": 190,
"preview": "#!/bin/sh -e\n\nexport SOURCE_FILES=\"starlette tests\"\n\nset -x\n\n./scripts/sync-version\nuv run ruff format --check --diff $S"
},
{
"path": "scripts/coverage",
"chars": 92,
"preview": "#!/bin/sh -e\n\nset -x\n\nuv run coverage report --show-missing --skip-covered --fail-under=100\n"
},
{
"path": "scripts/docs",
"chars": 44,
"preview": "#!/bin/sh -e\n\nset -x\n\nuv run zensical serve\n"
},
{
"path": "scripts/install",
"chars": 39,
"preview": "#!/bin/sh -e\n\nset -x\n\nuv sync --frozen\n"
},
{
"path": "scripts/lint",
"chars": 132,
"preview": "#!/bin/sh -e\n\nexport SOURCE_FILES=\"starlette tests\"\n\nset -x\n\nuv run ruff format $SOURCE_FILES\nuv run ruff check --fix $S"
},
{
"path": "scripts/sync-version",
"chars": 402,
"preview": "#!/bin/sh -e\n\nSEMVER_REGEX=\"([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*))?(\\+[0-9A-Za-z-]+)?\"\nCHANGEL"
},
{
"path": "scripts/test",
"chars": 164,
"preview": "#!/bin/sh\n\nset -ex\n\nif [ -z $GITHUB_ACTIONS ]; then\n scripts/check\nfi\n\nuv run coverage run -m pytest $@\n\nif [ -z $GIT"
},
{
"path": "starlette/__init__.py",
"chars": 25,
"preview": "__version__ = \"1.0.0rc1\"\n"
},
{
"path": "starlette/_exception_handler.py",
"chars": 2205,
"preview": "from __future__ import annotations\n\nfrom typing import Any\n\nfrom starlette._utils import is_async_callable\nfrom starlett"
},
{
"path": "starlette/_utils.py",
"chars": 2833,
"preview": "from __future__ import annotations\n\nimport functools\nimport sys\nfrom collections.abc import Awaitable, Callable, Generat"
},
{
"path": "starlette/applications.py",
"chars": 5291,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Awaitable, Callable, Mapping, Sequence\nfrom typing impor"
},
{
"path": "starlette/authentication.py",
"chars": 4750,
"preview": "from __future__ import annotations\n\nimport functools\nimport inspect\nfrom collections.abc import Callable, Sequence\nfrom "
},
{
"path": "starlette/background.py",
"chars": 1122,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Sequence\nfrom typing import Any, ParamSpec\n\nfr"
},
{
"path": "starlette/concurrency.py",
"chars": 1630,
"preview": "from __future__ import annotations\n\nimport functools\nimport warnings\nfrom collections.abc import AsyncIterator, Callable"
},
{
"path": "starlette/config.py",
"chars": 4426,
"preview": "from __future__ import annotations\n\nimport os\nimport warnings\nfrom collections.abc import Callable, Iterator, Mapping, M"
},
{
"path": "starlette/convertors.py",
"chars": 2304,
"preview": "from __future__ import annotations\n\nimport math\nimport uuid\nfrom typing import Any, ClassVar, Generic, TypeVar\n\nT = Type"
},
{
"path": "starlette/datastructures.py",
"chars": 22829,
"preview": "from __future__ import annotations\n\nfrom collections.abc import ItemsView, Iterable, Iterator, KeysView, Mapping, Mutabl"
},
{
"path": "starlette/endpoints.py",
"chars": 5099,
"preview": "from __future__ import annotations\n\nimport json\nfrom collections.abc import Callable, Generator\nfrom typing import Any, "
},
{
"path": "starlette/exceptions.py",
"chars": 1066,
"preview": "from __future__ import annotations\n\nimport http\nfrom collections.abc import Mapping\n\n\nclass HTTPException(Exception):\n "
},
{
"path": "starlette/formparsers.py",
"chars": 11132,
"preview": "from __future__ import annotations\n\nfrom collections.abc import AsyncGenerator\nfrom dataclasses import dataclass, field\n"
},
{
"path": "starlette/middleware/__init__.py",
"chars": 1349,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Awaitable, Callable, Iterator\nfrom typing import Any, Pa"
},
{
"path": "starlette/middleware/authentication.py",
"chars": 1800,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Callable\n\nfrom starlette.authentication import (\n Aut"
},
{
"path": "starlette/middleware/base.py",
"chars": 10333,
"preview": "from __future__ import annotations\n\nfrom collections.abc import AsyncGenerator, AsyncIterable, Awaitable, Callable, Mapp"
},
{
"path": "starlette/middleware/cors.py",
"chars": 7509,
"preview": "from __future__ import annotations\n\nimport functools\nimport re\nfrom collections.abc import Sequence\n\nfrom starlette.data"
},
{
"path": "starlette/middleware/errors.py",
"chars": 8037,
"preview": "from __future__ import annotations\n\nimport html\nimport inspect\nimport sys\nimport traceback\n\nfrom starlette._utils import"
},
{
"path": "starlette/middleware/exceptions.py",
"chars": 2784,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Mapping\nfrom typing import Any\n\nfrom starlette._exceptio"
},
{
"path": "starlette/middleware/gzip.py",
"chars": 5899,
"preview": "import gzip\nimport io\nfrom typing import NoReturn\n\nfrom starlette.datastructures import Headers, MutableHeaders\nfrom sta"
},
{
"path": "starlette/middleware/httpsredirect.py",
"chars": 848,
"preview": "from starlette.datastructures import URL\nfrom starlette.responses import RedirectResponse\nfrom starlette.types import AS"
},
{
"path": "starlette/middleware/sessions.py",
"chars": 4778,
"preview": "from __future__ import annotations\n\nimport json\nimport typing\nfrom base64 import b64decode, b64encode\nfrom typing import"
},
{
"path": "starlette/middleware/trustedhost.py",
"chars": 2219,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Sequence\n\nfrom starlette.datastructures import URL, Head"
},
{
"path": "starlette/middleware/wsgi.py",
"chars": 5368,
"preview": "from __future__ import annotations\n\nimport io\nimport math\nimport sys\nimport warnings\nfrom collections.abc import Callabl"
},
{
"path": "starlette/py.typed",
"chars": 0,
"preview": ""
},
{
"path": "starlette/requests.py",
"chars": 12253,
"preview": "from __future__ import annotations\n\nimport json\nimport sys\nfrom collections.abc import AsyncGenerator, Iterator, Mapping"
},
{
"path": "starlette/responses.py",
"chars": 21757,
"preview": "from __future__ import annotations\n\nimport hashlib\nimport http.cookies\nimport json\nimport os\nimport stat\nimport sys\nfrom"
},
{
"path": "starlette/routing.py",
"chars": 29493,
"preview": "from __future__ import annotations\n\nimport contextlib\nimport functools\nimport inspect\nimport re\nimport traceback\nimport "
},
{
"path": "starlette/schemas.py",
"chars": 5168,
"preview": "from __future__ import annotations\n\nimport inspect\nimport re\nfrom collections.abc import Callable\nfrom typing import Any"
},
{
"path": "starlette/staticfiles.py",
"chars": 8627,
"preview": "from __future__ import annotations\n\nimport errno\nimport importlib.util\nimport os\nimport stat\nfrom email.utils import par"
},
{
"path": "starlette/status.py",
"chars": 6359,
"preview": "\"\"\"\nHTTP codes\nSee HTTP Status Code Registry:\nhttps://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml"
},
{
"path": "starlette/templating.py",
"chars": 5447,
"preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Mapping, Sequence\nfrom os import PathLike\nfrom"
},
{
"path": "starlette/testclient.py",
"chars": 27850,
"preview": "from __future__ import annotations\n\nimport contextlib\nimport inspect\nimport io\nimport json\nimport math\nimport sys\nimport"
},
{
"path": "starlette/types.py",
"chars": 1041,
"preview": "from collections.abc import Awaitable, Callable, Mapping, MutableMapping\nfrom contextlib import AbstractAsyncContextMana"
},
{
"path": "starlette/websockets.py",
"chars": 8352,
"preview": "from __future__ import annotations\n\nimport enum\nimport json\nfrom collections.abc import AsyncIterator, Iterable\nfrom typ"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/conftest.py",
"chars": 617,
"preview": "from __future__ import annotations\n\nimport functools\nfrom typing import Any, Literal\n\nimport pytest\n\nfrom starlette.test"
},
{
"path": "tests/middleware/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/middleware/test_base.py",
"chars": 42823,
"preview": "from __future__ import annotations\n\nimport contextvars\nfrom collections.abc import AsyncGenerator, AsyncIterator, Genera"
},
{
"path": "tests/middleware/test_cors.py",
"chars": 20832,
"preview": "from starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors "
},
{
"path": "tests/middleware/test_errors.py",
"chars": 3799,
"preview": "from typing import Any\n\nimport pytest\n\nfrom starlette.applications import Starlette\nfrom starlette.background import Bac"
},
{
"path": "tests/middleware/test_gzip.py",
"chars": 7161,
"preview": "from __future__ import annotations\n\nfrom pathlib import Path\n\nimport pytest\n\nfrom starlette.applications import Starlett"
},
{
"path": "tests/middleware/test_https_redirect.py",
"chars": 1697,
"preview": "from starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.https"
},
{
"path": "tests/middleware/test_middleware.py",
"chars": 776,
"preview": "from starlette.middleware import Middleware\nfrom starlette.types import ASGIApp, Receive, Scope, Send\n\n\nclass CustomMidd"
},
{
"path": "tests/middleware/test_session.py",
"chars": 10433,
"preview": "import re\n\nfrom starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.middl"
},
{
"path": "tests/middleware/test_trusted_host.py",
"chars": 1799,
"preview": "from starlette.applications import Starlette\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.trust"
},
{
"path": "tests/middleware/test_wsgi.py",
"chars": 5068,
"preview": "import sys\nfrom collections.abc import Callable, Iterable\nfrom typing import Any\n\nimport pytest\n\nfrom starlette._utils i"
},
{
"path": "tests/statics/example.txt",
"chars": 4,
"preview": "123\n"
},
{
"path": "tests/test__utils.py",
"chars": 2780,
"preview": "import functools\nfrom typing import Any\nfrom unittest.mock import create_autospec\n\nimport pytest\n\nfrom starlette._utils "
},
{
"path": "tests/test_applications.py",
"chars": 17187,
"preview": "from __future__ import annotations\n\nimport os\nfrom collections.abc import AsyncGenerator, AsyncIterator, Callable, Gener"
},
{
"path": "tests/test_authentication.py",
"chars": 12138,
"preview": "from __future__ import annotations\n\nimport base64\nimport binascii\nfrom collections.abc import Awaitable, Callable\nfrom t"
},
{
"path": "tests/test_background.py",
"chars": 2829,
"preview": "import pytest\n\nfrom starlette.background import BackgroundTask, BackgroundTasks\nfrom starlette.responses import Response"
},
{
"path": "tests/test_concurrency.py",
"chars": 1577,
"preview": "from collections.abc import Iterator\nfrom contextvars import ContextVar\n\nimport anyio\nimport pytest\n\nfrom starlette.appl"
},
{
"path": "tests/test_config.py",
"chars": 5173,
"preview": "import os\nfrom pathlib import Path\nfrom typing import Any\n\nimport pytest\nfrom typing_extensions import assert_type\n\nfrom"
},
{
"path": "tests/test_convertors.py",
"chars": 3264,
"preview": "from collections.abc import Iterator\nfrom datetime import datetime\nfrom uuid import UUID\n\nimport pytest\n\nfrom starlette "
},
{
"path": "tests/test_datastructures.py",
"chars": 17006,
"preview": "import io\nfrom tempfile import SpooledTemporaryFile\nfrom typing import BinaryIO\n\nimport pytest\n\nfrom starlette.datastruc"
},
{
"path": "tests/test_endpoints.py",
"chars": 5780,
"preview": "from collections.abc import Iterator\n\nimport pytest\n\nfrom starlette.endpoints import HTTPEndpoint, WebSocketEndpoint\nfro"
},
{
"path": "tests/test_exceptions.py",
"chars": 7936,
"preview": "from collections.abc import Generator\nfrom typing import Any\n\nimport pytest\nfrom pytest import MonkeyPatch\n\nfrom starlet"
},
{
"path": "tests/test_formparsers.py",
"chars": 27805,
"preview": "from __future__ import annotations\n\nimport os\nimport threading\nfrom collections.abc import Generator\nfrom contextlib imp"
},
{
"path": "tests/test_requests.py",
"chars": 23214,
"preview": "from __future__ import annotations\n\nimport sys\nfrom collections.abc import Iterator\nfrom typing import Any\n\nimport anyio"
},
{
"path": "tests/test_responses.py",
"chars": 38592,
"preview": "from __future__ import annotations\n\nimport datetime as dt\nimport sys\nimport time\nfrom collections.abc import AsyncGenera"
},
{
"path": "tests/test_routing.py",
"chars": 38581,
"preview": "from __future__ import annotations\n\nimport contextlib\nimport functools\nimport json\nimport uuid\nfrom collections.abc impo"
},
{
"path": "tests/test_schemas.py",
"chars": 6933,
"preview": "from starlette.applications import Starlette\nfrom starlette.endpoints import HTTPEndpoint\nfrom starlette.requests import"
},
{
"path": "tests/test_staticfiles.py",
"chars": 22801,
"preview": "import os\nimport stat\nimport tempfile\nimport time\nfrom pathlib import Path\nfrom typing import Any\n\nimport anyio\nimport p"
},
{
"path": "tests/test_status.py",
"chars": 972,
"preview": "import importlib\n\nimport pytest\n\n\n@pytest.mark.parametrize(\n \"constant,msg\",\n (\n (\n \"HTTP_413_RE"
},
{
"path": "tests/test_templates.py",
"chars": 6825,
"preview": "from __future__ import annotations\n\nimport os\nfrom pathlib import Path\n\nimport jinja2\nimport pytest\n\nfrom starlette.appl"
},
{
"path": "tests/test_testclient.py",
"chars": 15154,
"preview": "from __future__ import annotations\n\nimport itertools\nimport sys\nfrom asyncio import Task, current_task as asyncio_curren"
},
{
"path": "tests/test_websockets.py",
"chars": 25981,
"preview": "import sys\nfrom collections.abc import AsyncGenerator, MutableMapping\nfrom pathlib import Path\nfrom typing import Any\n\ni"
},
{
"path": "tests/types.py",
"chars": 777,
"preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Protocol\n\nimport httpx\n\nfrom starlette.testclient "
}
]
About this extraction
This page contains the full source code of the Kludex/starlette GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 120 files (817.8 KB), approximately 197.8k tokens, and a symbol index with 1197 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.