forked from orbit-oss/flask
Started work on new request dispatching. Unittests not yet updated
This commit is contained in:
parent
e3f2dd8f08
commit
e71a5ff8de
11 changed files with 436 additions and 153 deletions
51
docs/api.rst
51
docs/api.rst
|
|
@ -310,6 +310,9 @@ Configuration
|
||||||
Useful Internals
|
Useful Internals
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
.. autoclass:: flask.ctx.RequestContext
|
||||||
|
:members:
|
||||||
|
|
||||||
.. data:: _request_ctx_stack
|
.. data:: _request_ctx_stack
|
||||||
|
|
||||||
The internal :class:`~werkzeug.local.LocalStack` that is used to implement
|
The internal :class:`~werkzeug.local.LocalStack` that is used to implement
|
||||||
|
|
@ -347,23 +350,6 @@ Useful Internals
|
||||||
if ctx is not None:
|
if ctx is not None:
|
||||||
return ctx.session
|
return ctx.session
|
||||||
|
|
||||||
.. versionchanged:: 0.4
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Signals
|
Signals
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
@ -401,6 +387,12 @@ Signals
|
||||||
in debug mode, where no exception handling happens. The exception
|
in debug mode, where no exception handling happens. The exception
|
||||||
itself is passed to the subscriber as `exception`.
|
itself is passed to the subscriber as `exception`.
|
||||||
|
|
||||||
|
.. data:: request_tearing_down
|
||||||
|
|
||||||
|
This signal is sent when the application is tearing down the request.
|
||||||
|
This is always called, even if an error happened. No arguments are
|
||||||
|
provided.
|
||||||
|
|
||||||
.. currentmodule:: None
|
.. currentmodule:: None
|
||||||
|
|
||||||
.. class:: flask.signals.Namespace
|
.. class:: flask.signals.Namespace
|
||||||
|
|
@ -418,28 +410,3 @@ Signals
|
||||||
operations, including connecting.
|
operations, including connecting.
|
||||||
|
|
||||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||||
|
|
||||||
.. _notes-on-proxies:
|
|
||||||
|
|
||||||
Notes On Proxies
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Some of the objects provided by Flask are proxies to other objects. The
|
|
||||||
reason behind this is that these proxies are shared between threads and
|
|
||||||
they have to dispatch to the actual object bound to a thread behind the
|
|
||||||
scenes as necessary.
|
|
||||||
|
|
||||||
Most of the time you don't have to care about that, but there are some
|
|
||||||
exceptions where it is good to know that this object is an actual proxy:
|
|
||||||
|
|
||||||
- The proxy objects do not fake their inherited types, so if you want to
|
|
||||||
perform actual instance checks, you have to do that on the instance
|
|
||||||
that is being proxied (see `_get_current_object` below).
|
|
||||||
- if the object reference is important (so for example for sending
|
|
||||||
:ref:`signals`)
|
|
||||||
|
|
||||||
If you need to get access to the underlying object that is proxied, you
|
|
||||||
can use the :meth:`~werkzeug.local.LocalProxy._get_current_object` method::
|
|
||||||
|
|
||||||
app = current_app._get_current_object()
|
|
||||||
my_signal.send(app)
|
|
||||||
|
|
|
||||||
|
|
@ -51,27 +51,36 @@ The following configuration values are used internally by Flask:
|
||||||
|
|
||||||
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
||||||
|
|
||||||
=============================== =========================================
|
================================= =========================================
|
||||||
``DEBUG`` enable/disable debug mode
|
``DEBUG`` enable/disable debug mode
|
||||||
``TESTING`` enable/disable testing mode
|
``TESTING`` enable/disable testing mode
|
||||||
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
||||||
propagation of exceptions. If not set or
|
propagation of exceptions. If not set or
|
||||||
explicitly set to `None` this is
|
explicitly set to `None` this is
|
||||||
implicitly true if either `TESTING` or
|
implicitly true if either `TESTING` or
|
||||||
`DEBUG` is true.
|
`DEBUG` is true.
|
||||||
``SECRET_KEY`` the secret key
|
``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in
|
||||||
``SESSION_COOKIE_NAME`` the name of the session cookie
|
debug mode the request context is not
|
||||||
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
|
popped on exceptions to enable debuggers
|
||||||
:class:`datetime.timedelta` object.
|
to introspect the data. This can be
|
||||||
``USE_X_SENDFILE`` enable/disable x-sendfile
|
disabled by this key. You can also use
|
||||||
``LOGGER_NAME`` the name of the logger
|
this setting to force-enable it for non
|
||||||
``SERVER_NAME`` the name of the server. Required for
|
debug execution which might be useful to
|
||||||
subdomain support (e.g.: ``'localhost'``)
|
debug production applications (but also
|
||||||
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
|
very risky).
|
||||||
reject incoming requests with a
|
``SECRET_KEY`` the secret key
|
||||||
content length greater than this by
|
``SESSION_COOKIE_NAME`` the name of the session cookie
|
||||||
returning a 413 status code.
|
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
|
||||||
=============================== =========================================
|
:class:`datetime.timedelta` object.
|
||||||
|
``USE_X_SENDFILE`` enable/disable x-sendfile
|
||||||
|
``LOGGER_NAME`` the name of the logger
|
||||||
|
``SERVER_NAME`` the name of the server. Required for
|
||||||
|
subdomain support (e.g.: ``'localhost'``)
|
||||||
|
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
|
||||||
|
reject incoming requests with a
|
||||||
|
content length greater than this by
|
||||||
|
returning a 413 status code.
|
||||||
|
================================= =========================================
|
||||||
|
|
||||||
.. admonition:: More on ``SERVER_NAME``
|
.. admonition:: More on ``SERVER_NAME``
|
||||||
|
|
||||||
|
|
@ -102,7 +111,7 @@ The following configuration values are used internally by Flask:
|
||||||
``MAX_CONTENT_LENGTH``
|
``MAX_CONTENT_LENGTH``
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
.. versionadded:: 0.7
|
||||||
``PROPAGATE_EXCEPTIONS``
|
``PROPAGATE_EXCEPTIONS``, ``PRESERVE_CONTEXT_ON_EXCEPTION``
|
||||||
|
|
||||||
Configuring from Files
|
Configuring from Files
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ instructions for web development with Flask.
|
||||||
errorhandling
|
errorhandling
|
||||||
config
|
config
|
||||||
signals
|
signals
|
||||||
|
reqcontext
|
||||||
shell
|
shell
|
||||||
patterns/index
|
patterns/index
|
||||||
deploying/index
|
deploying/index
|
||||||
|
|
|
||||||
230
docs/reqcontext.rst
Normal file
230
docs/reqcontext.rst
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
.. _request-context:
|
||||||
|
|
||||||
|
The Request Context
|
||||||
|
===================
|
||||||
|
|
||||||
|
This document describes the behavior in Flask 0.7 which is mostly in line
|
||||||
|
with the old behavior but has some small, subtle differences.
|
||||||
|
|
||||||
|
One of the design ideas behind Flask is that there are two different
|
||||||
|
“states” in which code is executed. The application setup state in which
|
||||||
|
the application implicitly is on the module level. It starts when the
|
||||||
|
:class:`Flask` object is instantiated, and it implicitly ends when the
|
||||||
|
first request comes in. While the application is in this state a few
|
||||||
|
assumptions are true:
|
||||||
|
|
||||||
|
- the programmer can modify the application object safely.
|
||||||
|
- no request handling happened so far
|
||||||
|
- you have to have a reference to the application object in order to
|
||||||
|
modify it, there is no magic proxy that can give you a reference to
|
||||||
|
the application object you're currently creating or modifying.
|
||||||
|
|
||||||
|
On the contrast, during request handling, a couple of other rules exist:
|
||||||
|
|
||||||
|
- while a request is active, the context local objects
|
||||||
|
(:data:`flask.request` and others) point to the current request.
|
||||||
|
- any code can get hold of these objects at any time.
|
||||||
|
|
||||||
|
The magic that makes this works is internally referred in Flask as the
|
||||||
|
“request context”.
|
||||||
|
|
||||||
|
Diving into Context Locals
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Say you have a utility function that returns the URL the user should be
|
||||||
|
redirected to. Imagine it would always redirect to the URL's ``next``
|
||||||
|
parameter or the HTTP referrer or the index page::
|
||||||
|
|
||||||
|
from flask import request, url_for
|
||||||
|
|
||||||
|
def redirect_url():
|
||||||
|
return request.args.get('next') or \
|
||||||
|
request.referrer or \
|
||||||
|
url_for('index')
|
||||||
|
|
||||||
|
As you can see, it accesses the request object. If you try to run this
|
||||||
|
from a plain Python shell, this is the exception you will see:
|
||||||
|
|
||||||
|
>>> redirect_url()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
AttributeError: 'NoneType' object has no attribute 'request'
|
||||||
|
|
||||||
|
That makes a lot of sense because we currently do not have a request we
|
||||||
|
could access. So we have to make a request and bind it to the current
|
||||||
|
context. The :attr:`~flask.Flask.test_request_context` method can create
|
||||||
|
us a :class:`~flask.ctx.RequestContext`:
|
||||||
|
|
||||||
|
>>> ctx = app.test_request_context('/?next=http://example.com/')
|
||||||
|
|
||||||
|
This context can be used in two ways. Either with the `with` statement
|
||||||
|
or by calling the :meth:`~flask.ctx.RequestContext.push` and
|
||||||
|
:meth:`~flask.ctx.RequestContext.pop` methods:
|
||||||
|
|
||||||
|
>>> ctx.push()
|
||||||
|
|
||||||
|
From that point onwards you can work with the request object:
|
||||||
|
|
||||||
|
>>> redirect_url()
|
||||||
|
u'http://example.com/'
|
||||||
|
|
||||||
|
Until you call `pop`:
|
||||||
|
|
||||||
|
>>> ctx.pop()
|
||||||
|
|
||||||
|
Because the request context is internally maintained as a stack you can
|
||||||
|
push and pop multiple times. This is very handy to implement things like
|
||||||
|
internal redirects.
|
||||||
|
|
||||||
|
For more information of how to utilize the request context from the
|
||||||
|
interactive Python shell, head over to the :ref:`shell` chapter.
|
||||||
|
|
||||||
|
How the Context Works
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
If you look into how the Flask WSGI application internally works, you will
|
||||||
|
find a piece of code that looks very much like this::
|
||||||
|
|
||||||
|
def wsgi_app(self, environ):
|
||||||
|
with self.request_context(environ):
|
||||||
|
try:
|
||||||
|
response = self.full_dispatch_request()
|
||||||
|
except Exception, e:
|
||||||
|
response = self.make_response(self.handle_exception(e))
|
||||||
|
return response(environ, start_response)
|
||||||
|
|
||||||
|
The method :meth:`~Flask.request_context` returns a new
|
||||||
|
:class:`~flask.ctx.RequestContext` object and uses it in combination with
|
||||||
|
the `with` statement to bind the context. Everything that is called from
|
||||||
|
the same thread from this point onwards until the end of the `with`
|
||||||
|
statement will have access to the request globals (:data:`flask.request`
|
||||||
|
and others).
|
||||||
|
|
||||||
|
The request context internally works like a stack: The topmost level on
|
||||||
|
the stack is the current active request.
|
||||||
|
:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on
|
||||||
|
the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the
|
||||||
|
stack again. On popping the application's
|
||||||
|
:func:`~flask.Flask.teardown_request` functions are also executed.
|
||||||
|
|
||||||
|
.. _callbacks-and-errors:
|
||||||
|
|
||||||
|
Callbacks and Errors
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
What happens if an error occurs in Flask during request processing? This
|
||||||
|
particular behavior changed in 0.7 because we wanted to make it easier to
|
||||||
|
understand what is actually happening. The new behavior is quite simple:
|
||||||
|
|
||||||
|
1. Before each request, :meth:`~flask.Flask.before_request` functions are
|
||||||
|
executed. If one of these functions return a response, the other
|
||||||
|
functions are no longer called. In any case however the return value
|
||||||
|
is treated as a replacement for the view's return value.
|
||||||
|
|
||||||
|
2. If the :meth:`~flask.Flask.before_request` functions did not return a
|
||||||
|
response, the regular request handling kicks in and the view function
|
||||||
|
that was matched has the chance to return a response.
|
||||||
|
|
||||||
|
3. The return value of the view is then converted into an actual response
|
||||||
|
object and handed over to the :meth:`~flask.Flask.after_request`
|
||||||
|
functions which have the chance to replace it or modify it in place.
|
||||||
|
|
||||||
|
4. At the end of the request the :meth:`~flask.Flask.teardown_request`
|
||||||
|
functions are executed. This always happens, even in case of an
|
||||||
|
unhandled exception down the road.
|
||||||
|
|
||||||
|
Now what happens on errors? In production mode if an exception is not
|
||||||
|
caught, the 500 internal server handler is called. In development mode
|
||||||
|
however the exception is not further processed and bubbles up to the WSGI
|
||||||
|
server. That way things like the interactive debugger can provide helpful
|
||||||
|
debug information.
|
||||||
|
|
||||||
|
An important change in 0.7 is that the internal server error is now no
|
||||||
|
longer post processed by the after request callbacks and after request
|
||||||
|
callbacks are no longer guaranteed to be executed. This way the internal
|
||||||
|
dispatching code looks cleaner and is easier to customize and understand.
|
||||||
|
|
||||||
|
The new teardown functions are supposed to be used as a replacement for
|
||||||
|
things that absolutely need to happen at the end of request.
|
||||||
|
|
||||||
|
Teardown Callbacks
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The teardown callbacks are special callbacks in that they are executed at
|
||||||
|
at different point. Strictly speaking they are independent of the actual
|
||||||
|
request handling as they are bound to the lifecycle of the
|
||||||
|
:class:`~flask.ctx.RequestContext` object. When the request context is
|
||||||
|
popped, the :meth:`~flask.Flask.teardown_request` functions are called.
|
||||||
|
|
||||||
|
This is important to know if the life of the request context is prolonged
|
||||||
|
by using the test client in a with statement of when using the request
|
||||||
|
context from the command line::
|
||||||
|
|
||||||
|
with app.test_client() as client:
|
||||||
|
resp = client.get('/foo')
|
||||||
|
# the teardown functions are still not called at that point
|
||||||
|
# even though the response ended and you have the response
|
||||||
|
# object in your hand
|
||||||
|
|
||||||
|
# only when the code reaches this point the teardown functions
|
||||||
|
# are called. Alternatively the same thing happens if another
|
||||||
|
# request was triggered from the test client
|
||||||
|
|
||||||
|
It's easy to see the behavior from the command line:
|
||||||
|
|
||||||
|
>>> app = Flask(__name__)
|
||||||
|
>>> @app.teardown_request
|
||||||
|
... def after_request(exception=None):
|
||||||
|
... print 'after request'
|
||||||
|
...
|
||||||
|
>>> ctx = app.test_request_context()
|
||||||
|
>>> ctx.push()
|
||||||
|
>>> ctx.pop()
|
||||||
|
after request
|
||||||
|
|
||||||
|
.. _notes-on-proxies:
|
||||||
|
|
||||||
|
Notes On Proxies
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Some of the objects provided by Flask are proxies to other objects. The
|
||||||
|
reason behind this is that these proxies are shared between threads and
|
||||||
|
they have to dispatch to the actual object bound to a thread behind the
|
||||||
|
scenes as necessary.
|
||||||
|
|
||||||
|
Most of the time you don't have to care about that, but there are some
|
||||||
|
exceptions where it is good to know that this object is an actual proxy:
|
||||||
|
|
||||||
|
- The proxy objects do not fake their inherited types, so if you want to
|
||||||
|
perform actual instance checks, you have to do that on the instance
|
||||||
|
that is being proxied (see `_get_current_object` below).
|
||||||
|
- if the object reference is important (so for example for sending
|
||||||
|
:ref:`signals`)
|
||||||
|
|
||||||
|
If you need to get access to the underlying object that is proxied, you
|
||||||
|
can use the :meth:`~werkzeug.local.LocalProxy._get_current_object` method::
|
||||||
|
|
||||||
|
app = current_app._get_current_object()
|
||||||
|
my_signal.send(app)
|
||||||
|
|
||||||
|
Context Preservation on Error
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
If an error occurs or not, at the end of the request the request context
|
||||||
|
is popped and all data associated with it is destroyed. During
|
||||||
|
development however that can be problematic as you might want to have the
|
||||||
|
information around for a longer time in case an exception occurred. In
|
||||||
|
Flask 0.6 and earlier in debug mode, if an exception occurred, the
|
||||||
|
request context was not popped so that the interactive debugger can still
|
||||||
|
provide you with important information.
|
||||||
|
|
||||||
|
Starting with Flask 0.7 you have finer control over that behavior by
|
||||||
|
setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
|
||||||
|
default it's linked to the setting of ``DEBUG``. If the application is in
|
||||||
|
debug mode the context is preserved, in production mode it's not.
|
||||||
|
|
||||||
|
Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode
|
||||||
|
as it will cause your application to leak memory on exceptions. However
|
||||||
|
it can be useful during development to get the same error preserving
|
||||||
|
behavior as in development mode when attempting to debug an error that
|
||||||
|
only occurs under production settings.
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _shell:
|
||||||
|
|
||||||
Working with the Shell
|
Working with the Shell
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
@ -21,61 +23,37 @@ that these functions are not only there for interactive shell usage, but
|
||||||
also for unittesting and other situations that require a faked request
|
also for unittesting and other situations that require a faked request
|
||||||
context.
|
context.
|
||||||
|
|
||||||
Diving into Context Locals
|
Generally it's recommended that you read the :ref:`request-context`
|
||||||
|
chapter of the documentation first.
|
||||||
|
|
||||||
|
Creating a Request Context
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Say you have a utility function that returns the URL the user should be
|
The easiest way to create a proper request context from the shell is by
|
||||||
redirected to. Imagine it would always redirect to the URL's ``next``
|
using the :attr:`~flask.Flask.test_request_context` method which creates
|
||||||
parameter or the HTTP referrer or the index page::
|
us a :class:`~flask.ctx.RequestContext`:
|
||||||
|
|
||||||
from flask import request, url_for
|
>>> ctx = app.test_request_context()
|
||||||
|
|
||||||
def redirect_url():
|
Normally you would use the `with` statement to make this request object
|
||||||
return request.args.get('next') or \
|
active, but in the shell it's easier to use the
|
||||||
request.referrer or \
|
:meth:`~flask.ctx.RequestContext.push` and
|
||||||
url_for('index')
|
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
|
||||||
|
|
||||||
As you can see, it accesses the request object. If you try to run this
|
|
||||||
from a plain Python shell, this is the exception you will see:
|
|
||||||
|
|
||||||
>>> redirect_url()
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
AttributeError: 'NoneType' object has no attribute 'request'
|
|
||||||
|
|
||||||
That makes a lot of sense because we currently do not have a request we
|
|
||||||
could access. So we have to make a request and bind it to the current
|
|
||||||
context. The :attr:`~flask.Flask.test_request_context` method can create
|
|
||||||
us a request context:
|
|
||||||
|
|
||||||
>>> ctx = app.test_request_context('/?next=http://example.com/')
|
|
||||||
|
|
||||||
This context can be used in two ways. Either with the `with` statement
|
|
||||||
(which unfortunately is not very handy for shell sessions). The
|
|
||||||
alternative way is to call the `push` and `pop` methods:
|
|
||||||
|
|
||||||
>>> ctx.push()
|
>>> ctx.push()
|
||||||
|
|
||||||
From that point onwards you can work with the request object:
|
From that point onwards you can work with the request object until you
|
||||||
|
call `pop`:
|
||||||
>>> redirect_url()
|
|
||||||
u'http://example.com/'
|
|
||||||
|
|
||||||
Until you call `pop`:
|
|
||||||
|
|
||||||
>>> ctx.pop()
|
>>> ctx.pop()
|
||||||
>>> redirect_url()
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
AttributeError: 'NoneType' object has no attribute 'request'
|
|
||||||
|
|
||||||
|
|
||||||
Firing Before/After Request
|
Firing Before/After Request
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
By just creating a request context, you still don't have run the code that
|
By just creating a request context, you still don't have run the code that
|
||||||
is normally run before a request. This probably results in your database
|
is normally run before a request. This might result in your database
|
||||||
being unavailable, the current user not being stored on the
|
being unavailable if you are connecting to the database in a
|
||||||
|
before-request callback or the current user not being stored on the
|
||||||
:data:`~flask.g` object etc.
|
:data:`~flask.g` object etc.
|
||||||
|
|
||||||
This however can easily be done yourself. Just call
|
This however can easily be done yourself. Just call
|
||||||
|
|
@ -96,6 +74,11 @@ a response object:
|
||||||
<Response 0 bytes [200 OK]>
|
<Response 0 bytes [200 OK]>
|
||||||
>>> ctx.pop()
|
>>> ctx.pop()
|
||||||
|
|
||||||
|
The functions registered as :meth:`~flask.Flask.teardown_request` are
|
||||||
|
automatically called when the context is popped. So this is the perfect
|
||||||
|
place to automatically tear down resources that were needed by the request
|
||||||
|
context (such as database connections).
|
||||||
|
|
||||||
|
|
||||||
Further Improving the Shell Experience
|
Further Improving the Shell Experience
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -236,4 +236,20 @@ The following signals exist in Flask:
|
||||||
from flask import got_request_exception
|
from flask import got_request_exception
|
||||||
got_request_exception.connect(log_exception, app)
|
got_request_exception.connect(log_exception, app)
|
||||||
|
|
||||||
|
.. data:: flask.request_tearing_down
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
This signal is sent when the request is tearing down. This is always
|
||||||
|
called, even if an exception is caused. Currently functions listening
|
||||||
|
to this signal are called after the regular teardown handlers, but this
|
||||||
|
is not something you can rely on.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def close_db_connection(sender):
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
from flask import request_tearing_down
|
||||||
|
request_tearing_down.connect(close_db_connection, app)
|
||||||
|
|
||||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ from .session import Session
|
||||||
|
|
||||||
# the signals
|
# the signals
|
||||||
from .signals import signals_available, template_rendered, request_started, \
|
from .signals import signals_available, template_rendered, request_started, \
|
||||||
request_finished, got_request_exception
|
request_finished, got_request_exception, request_tearing_down
|
||||||
|
|
||||||
# only import json if it's available
|
# only import json if it's available
|
||||||
if json_available:
|
if json_available:
|
||||||
|
|
|
||||||
130
flask/app.py
130
flask/app.py
|
|
@ -27,13 +27,14 @@ from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
|
||||||
_tojson_filter, _endpoint_from_view_func
|
_tojson_filter, _endpoint_from_view_func
|
||||||
from .wrappers import Request, Response
|
from .wrappers import Request, Response
|
||||||
from .config import ConfigAttribute, Config
|
from .config import ConfigAttribute, Config
|
||||||
from .ctx import _RequestContext
|
from .ctx import RequestContext
|
||||||
from .globals import _request_ctx_stack, request
|
from .globals import _request_ctx_stack, request
|
||||||
from .session import Session, _NullSession
|
from .session import Session, _NullSession
|
||||||
from .module import _ModuleSetupState
|
from .module import _ModuleSetupState
|
||||||
from .templating import _DispatchingJinjaLoader, \
|
from .templating import _DispatchingJinjaLoader, \
|
||||||
_default_template_ctx_processor
|
_default_template_ctx_processor
|
||||||
from .signals import request_started, request_finished, got_request_exception
|
from .signals import request_started, request_finished, got_request_exception, \
|
||||||
|
request_tearing_down
|
||||||
|
|
||||||
# a lock used for logger initialization
|
# a lock used for logger initialization
|
||||||
_logger_lock = Lock()
|
_logger_lock = Lock()
|
||||||
|
|
@ -126,6 +127,9 @@ class Flask(_PackageBoundObject):
|
||||||
#: For example this might activate unittest helpers that have an
|
#: For example this might activate unittest helpers that have an
|
||||||
#: additional runtime cost which should not be enabled by default.
|
#: additional runtime cost which should not be enabled by default.
|
||||||
#:
|
#:
|
||||||
|
#: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
|
||||||
|
#: default it's implicitly enabled.
|
||||||
|
#:
|
||||||
#: This attribute can also be configured from the config with the
|
#: This attribute can also be configured from the config with the
|
||||||
#: `TESTING` configuration key. Defaults to `False`.
|
#: `TESTING` configuration key. Defaults to `False`.
|
||||||
testing = ConfigAttribute('TESTING')
|
testing = ConfigAttribute('TESTING')
|
||||||
|
|
@ -191,6 +195,7 @@ class Flask(_PackageBoundObject):
|
||||||
'DEBUG': False,
|
'DEBUG': False,
|
||||||
'TESTING': False,
|
'TESTING': False,
|
||||||
'PROPAGATE_EXCEPTIONS': None,
|
'PROPAGATE_EXCEPTIONS': None,
|
||||||
|
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
|
||||||
'SECRET_KEY': None,
|
'SECRET_KEY': None,
|
||||||
'SESSION_COOKIE_NAME': 'session',
|
'SESSION_COOKIE_NAME': 'session',
|
||||||
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
|
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
|
||||||
|
|
@ -334,6 +339,19 @@ class Flask(_PackageBoundObject):
|
||||||
return rv
|
return rv
|
||||||
return self.testing or self.debug
|
return self.testing or self.debug
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preserve_context_on_exception(self):
|
||||||
|
"""Returns the value of the `PRESERVE_CONTEXT_ON_EXCEPTION`
|
||||||
|
configuration value in case it's set, otherwise a sensible default
|
||||||
|
is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
"""
|
||||||
|
rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION']
|
||||||
|
if rv is not None:
|
||||||
|
return rv
|
||||||
|
return self.debug
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logger(self):
|
def logger(self):
|
||||||
"""A :class:`logging.Logger` object for this application. The
|
"""A :class:`logging.Logger` object for this application. The
|
||||||
|
|
@ -713,16 +731,38 @@ class Flask(_PackageBoundObject):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def after_request(self, f):
|
def after_request(self, f):
|
||||||
"""Register a function to be run after each request. Your function
|
"""Register a function to be run after each request. Your function
|
||||||
must take one parameter, a :attr:`response_class` object and return
|
must take one parameter, a :attr:`response_class` object and return
|
||||||
a new response object or the same (see :meth:`process_response`).
|
a new response object or the same (see :meth:`process_response`).
|
||||||
|
|
||||||
|
As of Flask 0.7 this function might not be executed at the end of the
|
||||||
|
request in case an unhandled exception ocurred.
|
||||||
"""
|
"""
|
||||||
self.after_request_funcs.setdefault(None, []).append(f)
|
self.after_request_funcs.setdefault(None, []).append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def teardown_request(self, f):
|
def teardown_request(self, f):
|
||||||
"""Register a function to be run at the end of each request,
|
"""Register a function to be run at the end of each request,
|
||||||
regardless of whether there was an exception or not.
|
regardless of whether there was an exception or not. These functions
|
||||||
|
are executed when the request context is popped, even if not an
|
||||||
|
actual request was performed.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
ctx = app.test_request_context()
|
||||||
|
ctx.push()
|
||||||
|
...
|
||||||
|
ctx.pop()
|
||||||
|
|
||||||
|
When ``ctx.pop()`` is executed in the above example, the teardown
|
||||||
|
functions are called just before the request context moves from the
|
||||||
|
stack of active contexts. This becomes relevant if you are using
|
||||||
|
such constructs in tests.
|
||||||
|
|
||||||
|
Generally teardown functions must take every necesary step to avoid
|
||||||
|
that they will fail. If they do execute code that might fail they
|
||||||
|
will have to surround the execution of these code by try/except
|
||||||
|
statements and log ocurring errors.
|
||||||
"""
|
"""
|
||||||
self.teardown_request_funcs.setdefault(None, []).append(f)
|
self.teardown_request_funcs.setdefault(None, []).append(f)
|
||||||
return f
|
return f
|
||||||
|
|
@ -770,21 +810,39 @@ class Flask(_PackageBoundObject):
|
||||||
return value of the view or error handler. This does not have to
|
return value of the view or error handler. This does not have to
|
||||||
be a response object. In order to convert the return value to a
|
be a response object. In order to convert the return value to a
|
||||||
proper response object, call :func:`make_response`.
|
proper response object, call :func:`make_response`.
|
||||||
|
|
||||||
|
.. versionchanged:: 0.7
|
||||||
|
This no longer does the exception handling, this code was
|
||||||
|
moved to the new :meth:`full_dispatch_request`.
|
||||||
"""
|
"""
|
||||||
req = _request_ctx_stack.top.request
|
req = _request_ctx_stack.top.request
|
||||||
|
if req.routing_exception is not None:
|
||||||
|
raise req.routing_exception
|
||||||
|
rule = req.url_rule
|
||||||
|
# if we provide automatic options for this URL and the
|
||||||
|
# request came with the OPTIONS method, reply automatically
|
||||||
|
if getattr(rule, 'provide_automatic_options', False) \
|
||||||
|
and req.method == 'OPTIONS':
|
||||||
|
return self.make_default_options_response()
|
||||||
|
# otherwise dispatch to the handler for that endpoint
|
||||||
|
return self.view_functions[rule.endpoint](**req.view_args)
|
||||||
|
|
||||||
|
def full_dispatch_request(self):
|
||||||
|
"""Dispatches the request and on top of that performs request
|
||||||
|
pre and postprocessing as well as HTTP exception catching and
|
||||||
|
error handling.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
if req.routing_exception is not None:
|
request_started.send(self)
|
||||||
raise req.routing_exception
|
rv = self.preprocess_request()
|
||||||
rule = req.url_rule
|
if rv is None:
|
||||||
# if we provide automatic options for this URL and the
|
rv = self.dispatch_request()
|
||||||
# request came with the OPTIONS method, reply automatically
|
|
||||||
if getattr(rule, 'provide_automatic_options', False) \
|
|
||||||
and req.method == 'OPTIONS':
|
|
||||||
return self.make_default_options_response()
|
|
||||||
# otherwise dispatch to the handler for that endpoint
|
|
||||||
return self.view_functions[rule.endpoint](**req.view_args)
|
|
||||||
except HTTPException, e:
|
except HTTPException, e:
|
||||||
return self.handle_http_exception(e)
|
rv = self.handle_http_exception(e)
|
||||||
|
response = self.make_response(rv)
|
||||||
|
response = self.process_response(response)
|
||||||
|
request_finished.send(self, response=response)
|
||||||
|
return response
|
||||||
|
|
||||||
def make_default_options_response(self):
|
def make_default_options_response(self):
|
||||||
"""This method is called to create the default `OPTIONS` response.
|
"""This method is called to create the default `OPTIONS` response.
|
||||||
|
|
@ -894,7 +952,10 @@ class Flask(_PackageBoundObject):
|
||||||
|
|
||||||
def do_teardown_request(self):
|
def do_teardown_request(self):
|
||||||
"""Called after the actual request dispatching and will
|
"""Called after the actual request dispatching and will
|
||||||
call every as :meth:`teardown_request` decorated function.
|
call every as :meth:`teardown_request` decorated function. This is
|
||||||
|
not actually called by the :class:`Flask` object itself but is always
|
||||||
|
triggered when the request context is popped. That way we have a
|
||||||
|
tighter control over certain resources under testing environments.
|
||||||
"""
|
"""
|
||||||
funcs = reversed(self.teardown_request_funcs.get(None, ()))
|
funcs = reversed(self.teardown_request_funcs.get(None, ()))
|
||||||
mod = request.module
|
mod = request.module
|
||||||
|
|
@ -905,12 +966,13 @@ class Flask(_PackageBoundObject):
|
||||||
rv = func(exc)
|
rv = func(exc)
|
||||||
if rv is not None:
|
if rv is not None:
|
||||||
return rv
|
return rv
|
||||||
|
request_tearing_down.send(self)
|
||||||
|
|
||||||
def request_context(self, environ):
|
def request_context(self, environ):
|
||||||
"""Creates a request context from the given environment and binds
|
"""Creates a :class:`~flask.ctx.RequestContext` from the given
|
||||||
it to the current context. This must be used in combination with
|
environment and binds it to the current context. This must be used in
|
||||||
the `with` statement because the request is only bound to the
|
combination with the `with` statement because the request is only bound
|
||||||
current context for the duration of the `with` block.
|
to the current context for the duration of the `with` block.
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
|
|
@ -934,7 +996,7 @@ class Flask(_PackageBoundObject):
|
||||||
|
|
||||||
:param environ: a WSGI environment
|
:param environ: a WSGI environment
|
||||||
"""
|
"""
|
||||||
return _RequestContext(self, environ)
|
return RequestContext(self, environ)
|
||||||
|
|
||||||
def test_request_context(self, *args, **kwargs):
|
def test_request_context(self, *args, **kwargs):
|
||||||
"""Creates a WSGI environment from the given values (see
|
"""Creates a WSGI environment from the given values (see
|
||||||
|
|
@ -969,16 +1031,11 @@ class Flask(_PackageBoundObject):
|
||||||
Then you still have the original application object around and
|
Then you still have the original application object around and
|
||||||
can continue to call methods on it.
|
can continue to call methods on it.
|
||||||
|
|
||||||
.. versionchanged:: 0.4
|
|
||||||
The :meth:`after_request` functions are now called even if an
|
|
||||||
error handler took over request processing. This ensures that
|
|
||||||
even if an exception happens database have the chance to
|
|
||||||
properly close the connection.
|
|
||||||
|
|
||||||
.. versionchanged:: 0.7
|
.. versionchanged:: 0.7
|
||||||
The :meth:`teardown_request` functions get called at the very end of
|
The behavior of the before and after request callbacks was changed
|
||||||
processing the request. If an exception was thrown, it gets passed to
|
under error conditions and a new callback was added that will
|
||||||
each teardown_request function.
|
always execute at the end of the request, independent on if an
|
||||||
|
error ocurred or not. See :ref:`callbacks-and-errors`.
|
||||||
|
|
||||||
:param environ: a WSGI environment
|
:param environ: a WSGI environment
|
||||||
:param start_response: a callable accepting a status code,
|
:param start_response: a callable accepting a status code,
|
||||||
|
|
@ -987,20 +1044,9 @@ class Flask(_PackageBoundObject):
|
||||||
"""
|
"""
|
||||||
with self.request_context(environ):
|
with self.request_context(environ):
|
||||||
try:
|
try:
|
||||||
request_started.send(self)
|
response = self.full_dispatch_request()
|
||||||
rv = self.preprocess_request()
|
|
||||||
if rv is None:
|
|
||||||
rv = self.dispatch_request()
|
|
||||||
response = self.make_response(rv)
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
response = self.make_response(self.handle_exception(e))
|
response = self.make_response(self.handle_exception(e))
|
||||||
try:
|
|
||||||
response = self.process_response(response)
|
|
||||||
except Exception, e:
|
|
||||||
response = self.make_response(self.handle_exception(e))
|
|
||||||
finally:
|
|
||||||
self.do_teardown_request()
|
|
||||||
request_finished.send(self, response=response)
|
|
||||||
return response(environ, start_response)
|
return response(environ, start_response)
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
|
|
|
||||||
35
flask/ctx.py
35
flask/ctx.py
|
|
@ -51,11 +51,34 @@ def has_request_context():
|
||||||
return _request_ctx_stack.top is not None
|
return _request_ctx_stack.top is not None
|
||||||
|
|
||||||
|
|
||||||
class _RequestContext(object):
|
class RequestContext(object):
|
||||||
"""The request context contains all request relevant information. It is
|
"""The request context contains all request relevant information. It is
|
||||||
created at the beginning of the request and pushed to the
|
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
|
`_request_ctx_stack` and removed at the end of it. It will create the
|
||||||
URL adapter and request object for the WSGI environment provided.
|
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):
|
def __init__(self, app, environ):
|
||||||
|
|
@ -74,7 +97,7 @@ class _RequestContext(object):
|
||||||
self.request.routing_exception = e
|
self.request.routing_exception = e
|
||||||
|
|
||||||
def push(self):
|
def push(self):
|
||||||
"""Binds the request context."""
|
"""Binds the request context to the current context."""
|
||||||
_request_ctx_stack.push(self)
|
_request_ctx_stack.push(self)
|
||||||
|
|
||||||
# Open the session at the moment that the request context is
|
# Open the session at the moment that the request context is
|
||||||
|
|
@ -85,7 +108,11 @@ class _RequestContext(object):
|
||||||
self.session = _NullSession()
|
self.session = _NullSession()
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
"""Pops the request context."""
|
"""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.
|
||||||
|
"""
|
||||||
|
self.app.do_teardown_request()
|
||||||
_request_ctx_stack.pop()
|
_request_ctx_stack.pop()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
|
@ -99,5 +126,5 @@ class _RequestContext(object):
|
||||||
# the context can be force kept alive for the test client.
|
# the context can be force kept alive for the test client.
|
||||||
# See flask.testing for how this works.
|
# See flask.testing for how this works.
|
||||||
if not self.request.environ.get('flask._preserve_context') and \
|
if not self.request.environ.get('flask._preserve_context') and \
|
||||||
(tb is None or not self.app.debug):
|
(tb is None or not self.app.preserve_context_on_exception):
|
||||||
self.pop()
|
self.pop()
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,5 @@ _signals = Namespace()
|
||||||
template_rendered = _signals.signal('template-rendered')
|
template_rendered = _signals.signal('template-rendered')
|
||||||
request_started = _signals.signal('request-started')
|
request_started = _signals.signal('request-started')
|
||||||
request_finished = _signals.signal('request-finished')
|
request_finished = _signals.signal('request-finished')
|
||||||
|
request_tearing_down = _signals.signal('request-tearing-down')
|
||||||
got_request_exception = _signals.signal('got-request-exception')
|
got_request_exception = _signals.signal('got-request-exception')
|
||||||
|
|
|
||||||
|
|
@ -784,8 +784,11 @@ class BasicFunctionalityTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_max_content_length(self):
|
def test_max_content_length(self):
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
app.debug = True
|
|
||||||
app.config['MAX_CONTENT_LENGTH'] = 64
|
app.config['MAX_CONTENT_LENGTH'] = 64
|
||||||
|
@app.before_request
|
||||||
|
def always_first():
|
||||||
|
flask.request.form['myfile']
|
||||||
|
assert False
|
||||||
@app.route('/accept', methods=['POST'])
|
@app.route('/accept', methods=['POST'])
|
||||||
def accept_file():
|
def accept_file():
|
||||||
flask.request.form['myfile']
|
flask.request.form['myfile']
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue