From bc7f4bd3d35b2b06272328e1e701edcf51d48cd2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 27 Aug 2019 09:41:04 +0100 Subject: [PATCH] Application state --- docs/applications.md | 11 +++++++++++ starlette/applications.py | 3 ++- starlette/datastructures.py | 26 ++++++++++++++++++++++++++ starlette/middleware/errors.py | 2 +- starlette/requests.py | 20 +------------------- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/docs/applications.md b/docs/applications.md index 0092a3b4..e19928f5 100644 --- a/docs/applications.md +++ b/docs/applications.md @@ -76,3 +76,14 @@ exceptions that occur within the application: * `app.add_exception_handler(exc_class_or_status_code, handler)` - Add an error handler. The handler function may be either a coroutine or a regular function, with a signature like `func(request, exc) -> response`. * `@app.exception_handler(exc_class_or_status_code)` - Add an error handler, decorator style. * `app.debug` - Enable or disable error tracebacks in the browser. + +### 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' +``` diff --git a/starlette/applications.py b/starlette/applications.py index 8e7694bc..4b19fdc1 100644 --- a/starlette/applications.py +++ b/starlette/applications.py @@ -1,6 +1,6 @@ import typing -from starlette.datastructures import URLPath +from starlette.datastructures import State, URLPath from starlette.exceptions import ExceptionMiddleware from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.errors import ServerErrorMiddleware @@ -13,6 +13,7 @@ class Starlette: self, debug: bool = False, routes: typing.List[BaseRoute] = None ) -> None: self._debug = debug + self.state = State() self.router = Router(routes) self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware( diff --git a/starlette/datastructures.py b/starlette/datastructures.py index ae23e237..bc5ae36f 100644 --- a/starlette/datastructures.py +++ b/starlette/datastructures.py @@ -628,3 +628,29 @@ class MutableHeaders(Headers): if existing is not None: vary = ", ".join([existing, vary]) self["vary"] = vary + + +class State(object): + """ + An object that can be used to store arbitrary state. + + Used for `request.state` and `app.state`. + """ + + def __init__(self, state: typing.Dict = None): + if state is None: + state = {} + super(State, self).__setattr__("_state", state) + + def __setattr__(self, key: typing.Any, value: typing.Any) -> None: + self._state[key] = value + + def __getattr__(self, key: typing.Any) -> typing.Any: + try: + return self._state[key] + except KeyError: + message = "'{}' object has no attribute '{}'" + raise AttributeError(message.format(self.__class__.__name__, key)) + + def __delattr__(self, key: typing.Any) -> None: + del self._state[key] diff --git a/starlette/middleware/errors.py b/starlette/middleware/errors.py index a3fe4f35..d90bf275 100644 --- a/starlette/middleware/errors.py +++ b/starlette/middleware/errors.py @@ -1,5 +1,5 @@ -import html import asyncio +import html import inspect import traceback import typing diff --git a/starlette/requests.py b/starlette/requests.py index be05f291..a71c9075 100644 --- a/starlette/requests.py +++ b/starlette/requests.py @@ -4,7 +4,7 @@ import json import typing from collections.abc import Mapping -from starlette.datastructures import URL, Address, FormData, Headers, QueryParams +from starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State from starlette.formparsers import FormParser, MultiPartParser from starlette.types import Message, Receive, Scope @@ -18,24 +18,6 @@ class ClientDisconnect(Exception): pass -class State(object): - def __init__(self, state: typing.Dict = {}): - super(State, self).__setattr__("_state", state) - - def __setattr__(self, key: typing.Any, value: typing.Any) -> None: - self._state[key] = value - - def __getattr__(self, key: typing.Any) -> typing.Any: - try: - return self._state[key] - except KeyError: - message = "'{}' object has no attribute '{}'" - raise AttributeError(message.format(self.__class__.__name__, key)) - - def __delattr__(self, key: typing.Any) -> None: - del self._state[key] - - class HTTPConnection(Mapping): """ A base class for incoming HTTP connections, that is used to provide