---
[](https://github.com/Kludex/starlette/actions)
[](https://pypi.python.org/pypi/starlette)
[](https://pypi.org/project/starlette)
[](https://discord.gg/RxKUF5JuHs)
---
**Documentation**: https://starlette.dev
**Source Code**: https://github.com/Kludex/starlette
---
# 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.
---
Starlette is BSD licensed code. Designed & crafted with care.— ⭐️ —
[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
starlette-1.0.0/docs/ 0000775 0000000 0000000 00000000000 15160031440 0014441 5 ustar 00root root 0000000 0000000 starlette-1.0.0/docs/CNAME 0000664 0000000 0000000 00000000021 15160031440 0015200 0 ustar 00root root 0000000 0000000 www.starlette.io
starlette-1.0.0/docs/applications.md 0000664 0000000 0000000 00000003372 15160031440 0017456 0 ustar 00root root 0000000 0000000
??? 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`.
starlette-1.0.0/docs/authentication.md 0000664 0000000 0000000 00000012546 15160031440 0020012 0 ustar 00root root 0000000 0000000 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),
],
)
```
starlette-1.0.0/docs/background.md 0000664 0000000 0000000 00000003651 15160031440 0017107 0 ustar 00root root 0000000 0000000
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.
starlette-1.0.0/docs/config.md 0000664 0000000 0000000 00000015460 15160031440 0016236 0 ustar 00root root 0000000 0000000 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
starlette-1.0.0/docs/contributing.md 0000664 0000000 0000000 00000012731 15160031440 0017476 0 ustar 00root root 0000000 0000000 # 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.
Here are some common ways the test suite can fail:
### Check Job Failed
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
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.
starlette-1.0.0/docs/css/ 0000775 0000000 0000000 00000000000 15160031440 0015231 5 ustar 00root root 0000000 0000000 starlette-1.0.0/docs/css/custom.css 0000664 0000000 0000000 00000001507 15160031440 0017260 0 ustar 00root root 0000000 0000000 /* 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;
}
starlette-1.0.0/docs/database.md 0000664 0000000 0000000 00000001262 15160031440 0016530 0 ustar 00root root 0000000 0000000 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.
starlette-1.0.0/docs/endpoints.md 0000664 0000000 0000000 00000010077 15160031440 0016773 0 ustar 00root root 0000000 0000000
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 = """
Chat
WebSocket Chat
"""
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)
```
starlette-1.0.0/docs/exceptions.md 0000664 0000000 0000000 00000012050 15160031440 0017142 0 ustar 00root root 0000000 0000000
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).
starlette-1.0.0/docs/graphql.md 0000664 0000000 0000000 00000001144 15160031440 0016421 0 ustar 00root root 0000000 0000000 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)
starlette-1.0.0/docs/img/ 0000775 0000000 0000000 00000000000 15160031440 0015215 5 ustar 00root root 0000000 0000000 starlette-1.0.0/docs/img/gh-actions-fail-check.png 0000664 0000000 0000000 00003550635 15160031440 0021764 0 ustar 00root root 0000000 0000000 PNG
IHDR
> Q miCCPICC Profile HWXS[BzDj )! ҋ`#$cBP
(;veQ}Pyu_ϙ3)w& 4?p%