| # -*- coding: utf-8 -*- |
| """ |
| flask.ctx |
| ~~~~~~~~~ |
| |
| Implements the objects required to keep the context. |
| |
| :copyright: 2010 Pallets |
| :license: BSD-3-Clause |
| """ |
| import sys |
| from functools import update_wrapper |
| |
| from werkzeug.exceptions import HTTPException |
| |
| from ._compat import BROKEN_PYPY_CTXMGR_EXIT |
| from ._compat import reraise |
| from .globals import _app_ctx_stack |
| from .globals import _request_ctx_stack |
| from .signals import appcontext_popped |
| from .signals import appcontext_pushed |
| |
| |
| # a singleton sentinel value for parameter defaults |
| _sentinel = object() |
| |
| |
| class _AppCtxGlobals(object): |
| """A plain object. Used as a namespace for storing data during an |
| application context. |
| |
| Creating an app context automatically creates this object, which is |
| made available as the :data:`g` proxy. |
| |
| .. describe:: 'key' in g |
| |
| Check whether an attribute is present. |
| |
| .. versionadded:: 0.10 |
| |
| .. describe:: iter(g) |
| |
| Return an iterator over the attribute names. |
| |
| .. versionadded:: 0.10 |
| """ |
| |
| def get(self, name, default=None): |
| """Get an attribute by name, or a default value. Like |
| :meth:`dict.get`. |
| |
| :param name: Name of attribute to get. |
| :param default: Value to return if the attribute is not present. |
| |
| .. versionadded:: 0.10 |
| """ |
| return self.__dict__.get(name, default) |
| |
| def pop(self, name, default=_sentinel): |
| """Get and remove an attribute by name. Like :meth:`dict.pop`. |
| |
| :param name: Name of attribute to pop. |
| :param default: Value to return if the attribute is not present, |
| instead of raise a ``KeyError``. |
| |
| .. versionadded:: 0.11 |
| """ |
| if default is _sentinel: |
| return self.__dict__.pop(name) |
| else: |
| return self.__dict__.pop(name, default) |
| |
| def setdefault(self, name, default=None): |
| """Get the value of an attribute if it is present, otherwise |
| set and return a default value. Like :meth:`dict.setdefault`. |
| |
| :param name: Name of attribute to get. |
| :param: default: Value to set and return if the attribute is not |
| present. |
| |
| .. versionadded:: 0.11 |
| """ |
| return self.__dict__.setdefault(name, default) |
| |
| def __contains__(self, item): |
| return item in self.__dict__ |
| |
| def __iter__(self): |
| return iter(self.__dict__) |
| |
| def __repr__(self): |
| top = _app_ctx_stack.top |
| if top is not None: |
| return "<flask.g of %r>" % top.app.name |
| return object.__repr__(self) |
| |
| |
| def after_this_request(f): |
| """Executes a function after this request. This is useful to modify |
| response objects. The function is passed the response object and has |
| to return the same or a new one. |
| |
| Example:: |
| |
| @app.route('/') |
| def index(): |
| @after_this_request |
| def add_header(response): |
| response.headers['X-Foo'] = 'Parachute' |
| return response |
| return 'Hello World!' |
| |
| This is more useful if a function other than the view function wants to |
| modify a response. For instance think of a decorator that wants to add |
| some headers without converting the return value into a response object. |
| |
| .. versionadded:: 0.9 |
| """ |
| _request_ctx_stack.top._after_request_functions.append(f) |
| return f |
| |
| |
| def copy_current_request_context(f): |
| """A helper function that decorates a function to retain the current |
| request context. This is useful when working with greenlets. The moment |
| the function is decorated a copy of the request context is created and |
| then pushed when the function is called. The current session is also |
| included in the copied request context. |
| |
| Example:: |
| |
| import gevent |
| from flask import copy_current_request_context |
| |
| @app.route('/') |
| def index(): |
| @copy_current_request_context |
| def do_some_work(): |
| # do some work here, it can access flask.request or |
| # flask.session like you would otherwise in the view function. |
| ... |
| gevent.spawn(do_some_work) |
| return 'Regular response' |
| |
| .. versionadded:: 0.10 |
| """ |
| top = _request_ctx_stack.top |
| if top is None: |
| raise RuntimeError( |
| "This decorator can only be used at local scopes " |
| "when a request context is on the stack. For instance within " |
| "view functions." |
| ) |
| reqctx = top.copy() |
| |
| def wrapper(*args, **kwargs): |
| with reqctx: |
| return f(*args, **kwargs) |
| |
| return update_wrapper(wrapper, f) |
| |
| |
| def has_request_context(): |
| """If you have code that wants to test if a request context is there or |
| not this function can be used. For instance, you may want to take advantage |
| of request information if the request object is available, but fail |
| silently if it is unavailable. |
| |
| :: |
| |
| class User(db.Model): |
| |
| def __init__(self, username, remote_addr=None): |
| self.username = username |
| if remote_addr is None and has_request_context(): |
| remote_addr = request.remote_addr |
| self.remote_addr = remote_addr |
| |
| Alternatively you can also just test any of the context bound objects |
| (such as :class:`request` or :class:`g`) for truthness:: |
| |
| class User(db.Model): |
| |
| def __init__(self, username, remote_addr=None): |
| self.username = username |
| if remote_addr is None and request: |
| remote_addr = request.remote_addr |
| self.remote_addr = remote_addr |
| |
| .. versionadded:: 0.7 |
| """ |
| return _request_ctx_stack.top is not None |
| |
| |
| def has_app_context(): |
| """Works like :func:`has_request_context` but for the application |
| context. You can also just do a boolean check on the |
| :data:`current_app` object instead. |
| |
| .. versionadded:: 0.9 |
| """ |
| return _app_ctx_stack.top is not None |
| |
| |
| class AppContext(object): |
| """The application context binds an application object implicitly |
| to the current thread or greenlet, similar to how the |
| :class:`RequestContext` binds request information. The application |
| context is also implicitly created if a request context is created |
| but the application is not on top of the individual application |
| context. |
| """ |
| |
| def __init__(self, app): |
| self.app = app |
| self.url_adapter = app.create_url_adapter(None) |
| self.g = app.app_ctx_globals_class() |
| |
| # Like request context, app contexts can be pushed multiple times |
| # but there a basic "refcount" is enough to track them. |
| self._refcnt = 0 |
| |
| def push(self): |
| """Binds the app context to the current context.""" |
| self._refcnt += 1 |
| if hasattr(sys, "exc_clear"): |
| sys.exc_clear() |
| _app_ctx_stack.push(self) |
| appcontext_pushed.send(self.app) |
| |
| def pop(self, exc=_sentinel): |
| """Pops the app context.""" |
| try: |
| self._refcnt -= 1 |
| if self._refcnt <= 0: |
| if exc is _sentinel: |
| exc = sys.exc_info()[1] |
| self.app.do_teardown_appcontext(exc) |
| finally: |
| rv = _app_ctx_stack.pop() |
| assert rv is self, "Popped wrong app context. (%r instead of %r)" % (rv, self) |
| appcontext_popped.send(self.app) |
| |
| def __enter__(self): |
| self.push() |
| return self |
| |
| def __exit__(self, exc_type, exc_value, tb): |
| self.pop(exc_value) |
| |
| if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: |
| reraise(exc_type, exc_value, tb) |
| |
| |
| class RequestContext(object): |
| """The request context contains all request relevant information. It is |
| created at the beginning of the request and pushed to the |
| `_request_ctx_stack` and removed at the end of it. It will create the |
| URL adapter and request object for the WSGI environment provided. |
| |
| Do not attempt to use this class directly, instead use |
| :meth:`~flask.Flask.test_request_context` and |
| :meth:`~flask.Flask.request_context` to create this object. |
| |
| When the request context is popped, it will evaluate all the |
| functions registered on the application for teardown execution |
| (:meth:`~flask.Flask.teardown_request`). |
| |
| The request context is automatically popped at the end of the request |
| for you. In debug mode the request context is kept around if |
| exceptions happen so that interactive debuggers have a chance to |
| introspect the data. With 0.4 this can also be forced for requests |
| that did not fail and outside of ``DEBUG`` mode. By setting |
| ``'flask._preserve_context'`` to ``True`` on the WSGI environment the |
| context will not pop itself at the end of the request. This is used by |
| the :meth:`~flask.Flask.test_client` for example to implement the |
| deferred cleanup functionality. |
| |
| You might find this helpful for unittests where you need the |
| information from the context local around for a little longer. Make |
| sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in |
| that situation, otherwise your unittests will leak memory. |
| """ |
| |
| def __init__(self, app, environ, request=None, session=None): |
| self.app = app |
| if request is None: |
| request = app.request_class(environ) |
| self.request = request |
| self.url_adapter = None |
| try: |
| self.url_adapter = app.create_url_adapter(self.request) |
| except HTTPException as e: |
| self.request.routing_exception = e |
| self.flashes = None |
| self.session = session |
| |
| # Request contexts can be pushed multiple times and interleaved with |
| # other request contexts. Now only if the last level is popped we |
| # get rid of them. Additionally if an application context is missing |
| # one is created implicitly so for each level we add this information |
| self._implicit_app_ctx_stack = [] |
| |
| # indicator if the context was preserved. Next time another context |
| # is pushed the preserved context is popped. |
| self.preserved = False |
| |
| # remembers the exception for pop if there is one in case the context |
| # preservation kicks in. |
| self._preserved_exc = None |
| |
| # Functions that should be executed after the request on the response |
| # object. These will be called before the regular "after_request" |
| # functions. |
| self._after_request_functions = [] |
| |
| @property |
| def g(self): |
| return _app_ctx_stack.top.g |
| |
| @g.setter |
| def g(self, value): |
| _app_ctx_stack.top.g = value |
| |
| def copy(self): |
| """Creates a copy of this request context with the same request object. |
| This can be used to move a request context to a different greenlet. |
| Because the actual request object is the same this cannot be used to |
| move a request context to a different thread unless access to the |
| request object is locked. |
| |
| .. versionadded:: 0.10 |
| |
| .. versionchanged:: 1.1 |
| The current session object is used instead of reloading the original |
| data. This prevents `flask.session` pointing to an out-of-date object. |
| """ |
| return self.__class__( |
| self.app, |
| environ=self.request.environ, |
| request=self.request, |
| session=self.session, |
| ) |
| |
| def match_request(self): |
| """Can be overridden by a subclass to hook into the matching |
| of the request. |
| """ |
| try: |
| result = self.url_adapter.match(return_rule=True) |
| self.request.url_rule, self.request.view_args = result |
| except HTTPException as e: |
| self.request.routing_exception = e |
| |
| def push(self): |
| """Binds the request context to the current context.""" |
| # If an exception occurs in debug mode or if context preservation is |
| # activated under exception situations exactly one context stays |
| # on the stack. The rationale is that you want to access that |
| # information under debug situations. However if someone forgets to |
| # pop that context again we want to make sure that on the next push |
| # it's invalidated, otherwise we run at risk that something leaks |
| # memory. This is usually only a problem in test suite since this |
| # functionality is not active in production environments. |
| top = _request_ctx_stack.top |
| if top is not None and top.preserved: |
| top.pop(top._preserved_exc) |
| |
| # Before we push the request context we have to ensure that there |
| # is an application context. |
| app_ctx = _app_ctx_stack.top |
| if app_ctx is None or app_ctx.app != self.app: |
| app_ctx = self.app.app_context() |
| app_ctx.push() |
| self._implicit_app_ctx_stack.append(app_ctx) |
| else: |
| self._implicit_app_ctx_stack.append(None) |
| |
| if hasattr(sys, "exc_clear"): |
| sys.exc_clear() |
| |
| _request_ctx_stack.push(self) |
| |
| # Open the session at the moment that the request context is available. |
| # This allows a custom open_session method to use the request context. |
| # Only open a new session if this is the first time the request was |
| # pushed, otherwise stream_with_context loses the session. |
| if self.session is None: |
| session_interface = self.app.session_interface |
| self.session = session_interface.open_session(self.app, self.request) |
| |
| if self.session is None: |
| self.session = session_interface.make_null_session(self.app) |
| |
| if self.url_adapter is not None: |
| self.match_request() |
| |
| def pop(self, exc=_sentinel): |
| """Pops the request context and unbinds it by doing that. This will |
| also trigger the execution of functions registered by the |
| :meth:`~flask.Flask.teardown_request` decorator. |
| |
| .. versionchanged:: 0.9 |
| Added the `exc` argument. |
| """ |
| app_ctx = self._implicit_app_ctx_stack.pop() |
| |
| try: |
| clear_request = False |
| if not self._implicit_app_ctx_stack: |
| self.preserved = False |
| self._preserved_exc = None |
| if exc is _sentinel: |
| exc = sys.exc_info()[1] |
| self.app.do_teardown_request(exc) |
| |
| # If this interpreter supports clearing the exception information |
| # we do that now. This will only go into effect on Python 2.x, |
| # on 3.x it disappears automatically at the end of the exception |
| # stack. |
| if hasattr(sys, "exc_clear"): |
| sys.exc_clear() |
| |
| request_close = getattr(self.request, "close", None) |
| if request_close is not None: |
| request_close() |
| clear_request = True |
| finally: |
| rv = _request_ctx_stack.pop() |
| |
| # get rid of circular dependencies at the end of the request |
| # so that we don't require the GC to be active. |
| if clear_request: |
| rv.request.environ["werkzeug.request"] = None |
| |
| # Get rid of the app as well if necessary. |
| if app_ctx is not None: |
| app_ctx.pop(exc) |
| |
| assert rv is self, "Popped wrong request context. (%r instead of %r)" % ( |
| rv, |
| self, |
| ) |
| |
| def auto_pop(self, exc): |
| if self.request.environ.get("flask._preserve_context") or ( |
| exc is not None and self.app.preserve_context_on_exception |
| ): |
| self.preserved = True |
| self._preserved_exc = exc |
| else: |
| self.pop(exc) |
| |
| def __enter__(self): |
| self.push() |
| return self |
| |
| def __exit__(self, exc_type, exc_value, tb): |
| # do not pop the request stack if we are in debug mode and an |
| # exception happened. This will allow the debugger to still |
| # access the request object in the interactive shell. Furthermore |
| # the context can be force kept alive for the test client. |
| # See flask.testing for how this works. |
| self.auto_pop(exc_value) |
| |
| if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: |
| reraise(exc_type, exc_value, tb) |
| |
| def __repr__(self): |
| return "<%s '%s' [%s] of %s>" % ( |
| self.__class__.__name__, |
| self.request.url, |
| self.request.method, |
| self.app.name, |
| ) |