You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					458 lines
				
				16 KiB
			
		
		
			
		
	
	
					458 lines
				
				16 KiB
			| 
											8 years ago
										 | # -*- coding: utf-8 -*-
 | ||
|  | """
 | ||
|  |     flask.ctx
 | ||
|  |     ~~~~~~~~~
 | ||
|  | 
 | ||
|  |     Implements the objects required to keep the context.
 | ||
|  | 
 | ||
|  |     :copyright: © 2010 by the Pallets team.
 | ||
|  |     :license: BSD, see LICENSE for more details.
 | ||
|  | """
 | ||
|  | 
 | ||
|  | import sys
 | ||
|  | from functools import update_wrapper
 | ||
|  | 
 | ||
|  | from werkzeug.exceptions import HTTPException
 | ||
|  | 
 | ||
|  | from .globals import _request_ctx_stack, _app_ctx_stack
 | ||
|  | from .signals import appcontext_pushed, appcontext_popped
 | ||
|  | from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise
 | ||
|  | 
 | ||
|  | 
 | ||
|  | # 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.
 | ||
|  | 
 | ||
|  |     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 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):
 | ||
|  |         self.app = app
 | ||
|  |         if request is None:
 | ||
|  |             request = app.request_class(environ)
 | ||
|  |         self.request = request
 | ||
|  |         self.url_adapter = app.create_url_adapter(self.request)
 | ||
|  |         self.flashes = None
 | ||
|  |         self.session = None
 | ||
|  | 
 | ||
|  |         # 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 = []
 | ||
|  | 
 | ||
|  |         self.match_request()
 | ||
|  | 
 | ||
|  |     def _get_g(self):
 | ||
|  |         return _app_ctx_stack.top.g
 | ||
|  |     def _set_g(self, value):
 | ||
|  |         _app_ctx_stack.top.g = value
 | ||
|  |     g = property(_get_g, _set_g)
 | ||
|  |     del _get_g, _set_g
 | ||
|  | 
 | ||
|  |     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
 | ||
|  |         """
 | ||
|  |         return self.__class__(self.app,
 | ||
|  |             environ=self.request.environ,
 | ||
|  |             request=self.request
 | ||
|  |         )
 | ||
|  | 
 | ||
|  |     def match_request(self):
 | ||
|  |         """Can be overridden by a subclass to hook into the matching
 | ||
|  |         of the request.
 | ||
|  |         """
 | ||
|  |         try:
 | ||
|  |             url_rule, self.request.view_args = \
 | ||
|  |                 self.url_adapter.match(return_rule=True)
 | ||
|  |             self.request.url_rule = url_rule
 | ||
|  |         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)
 | ||
|  | 
 | ||
|  |     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,
 | ||
|  |         )
 |