starlette

✨ The little ASGI library that shines. ✨

Build Status Coverage Package version

--- Starlette is a small library for working with [ASGI](https://asgi.readthedocs.io/en/latest/). It gives you `Request` and `Response` classes, request routing, a test client, and a decorator for writing super-minimal applications. **Requirements:** Python 3.6+ **Installation:** ```shell pip3 install starlette ``` **Example:** ```python from starlette import Response class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = Response('Hello, world!', media_type='text/plain') await response(receive, send) ``` You can run the application with any ASGI server, including [uvicorn](http://www.uvicorn.org/), [daphne](https://github.com/django/daphne/), or [hypercorn](https://pgjones.gitlab.io/hypercorn/).

— ⭐️ —

## Responses 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. Once you've instantiated a response, you can send it by calling it as an ASGI application instance. ```python class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = Response('Hello, world!', media_type='text/plain') await response(receive, send) ``` ### HTMLResponse Takes some text or bytes and returns an HTML response. ```python from starlette import HTMLResponse class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = HTMLResponse('

Hello, world!

') await response(receive, send) ``` ### PlainTextResponse Takes some text or bytes and returns an plain text response. ```python from starlette import PlainTextResponse class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = PlainTextResponse('Hello, world!') await response(receive, send) ``` ### JSONResponse Takes some data and returns an `application/json` encoded response. ```python from starlette import JSONResponse class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = JSONResponse({'hello': 'world'}) await response(receive, send) ``` ### StreamingResponse Takes an async generator and streams the response body. ```python from starlette import Request, StreamingResponse import asyncio async def slow_numbers(minimum, maximum): yield('') class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): generator = slow_numbers(1, 10) response = StreamingResponse(generator, media_type='text/html') await response(receive, send) ``` ### 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`. ```python from starlette import FileResponse class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = FileResponse('/statics/favicon.ico') await response(receive, send) ``` --- ## Requests 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 class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): request = Request(self.scope, receive) content = '%s %s' % (request.method, request.url.path) response = Response(content, media_type='text/plain') await response(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 actually a subclass of `str`, and also 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 Headers are exposed as an immutable multi-dict. For example: `request.query_params['abc']` #### 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 JSON: `await request.json()` You can also access the request body as a stream, using the `async for` syntax: ```python class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): request = Request(self.scope, receive) body = b'' async for chunk in request.stream(): body += chunk response = Response(body, media_type='text/plain') await response(receive, send) ``` If you access `.stream()` then the byte chunks are provided without storing the entire body to memory. Any subsequent calls to `.body()` and `.json()` will raise an error. --- ## Routing Starlette includes a `Router` class which is an ASGI application that dispatches to other ASGI applications. ```python from starlette import Router, Path, PathPrefix from myproject import Homepage, StaticFiles app = Router([ Path('/', app=Homepage, methods=['GET']), PathPrefix('/static', app=StaticFiles, methods=['GET']) ]) ``` Paths can use URI templating style to capture path components. ```python Path('/users/{username}', app=User, methods=['GET']) ``` Path components are made available in the scope, as `scope["kwargs"]`. Because each target of the router is an ASGI instance itself, routers allow for easy composition. For example: ```python app = Router([ Path('/', app=Homepage, methods=['GET']), PathPrefix('/users', app=Router([ Path('/', app=Users, methods=['GET', 'POST']), Path('/{username}', app=User, methods=['GET']), ])) ]) ``` The router will respond with "404 Not found" or "406 Method not allowed" responses for requests which do not match. --- ## Test Client The test client allows you to make requests against your ASGI application, using the `requests` library. ```python from starlette import HTMLResponse, TestClient class App: def __init__(self, scope): self.scope = scope async def __call__(self, receive, send): response = HTMLResponse('Hello, world!') await response(receive, send) def test_app(): client = TestClient(App) response = client.get('/') assert response.status_code == 200 ``` --- ## Decorators The `asgi_application` decorator takes a request/response function and turns it into an ASGI application. The function must take a single `request` argument, and return a response. The decorator can be applied to either `async` functions, or to standard functions. ```python from starlette import asgi_application, HTMLResponse @asgi_application async def app(request): return HTMLResponse('Hello, world!') ``` ---

Starlette is BSD licensed code.
Designed & built in Brighton, England.

— ⭐️ —