Request state (#404)

* Add Mount(routes=...)

* Lifespan route instance

* Lifespan as a standard routing component

* Linting

* Linting

* Version 0.10.6

* Release notes

* Version 0.11.0

* Drop redundant import

* Drop redundant database requirements

* Include htmlcov in scripts/clean

* Drop redundant import

* Release notes

* Linting

* Add request.state
This commit is contained in:
Tom Christie 2019-02-19 13:14:53 +00:00 committed by GitHub
parent ff8336fb6b
commit 82c610559c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 10 deletions

View File

@ -5,6 +5,7 @@
* 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

View File

@ -115,3 +115,12 @@ 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()`.
#### 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()`

View File

@ -18,6 +18,10 @@ class ClientDisconnect(Exception):
pass
class State:
pass
class HTTPConnection(Mapping):
"""
A base class for incoming HTTP connections, that is used to provide
@ -88,16 +92,6 @@ class HTTPConnection(Mapping):
), "SessionMiddleware must be installed to access request.session"
return self._scope["session"]
@property
def database(self) -> typing.Any: # pragma: no cover
# NOTE: Pending deprecation. You probably want to look at the
# stand-alone `databases` package instead.
# https://github.com/encode/databases
assert (
"database" in self._scope
), "DatabaseMiddleware must be installed to access request.database"
return self._scope["database"]
@property
def auth(self) -> typing.Any:
assert (
@ -112,6 +106,12 @@ class HTTPConnection(Mapping):
), "AuthenticationMiddleware must be installed to access request.user"
return self._scope["user"]
@property
def state(self) -> State:
if "state" not in self._scope:
self._scope["state"] = State()
return self._scope["state"]
def url_for(self, name: str, **path_params: typing.Any) -> str:
router = self._scope["router"]
url_path = router.url_path_for(name, **path_params)

View File

@ -280,6 +280,21 @@ def test_request_is_disconnected():
assert disconnected_after_response
def test_request_state():
def app(scope):
async def asgi(receive, send):
request = Request(scope, receive)
request.state.example = 123
response = JSONResponse({"state.example": request["state"].example})
await response(receive, send)
return asgi
client = TestClient(app)
response = client.get("/123?a=abc")
assert response.json() == {"state.example": 123}
def test_request_cookies():
def app(scope):
async def asgi(receive, send):