Started work on new request dispatching. Unittests not yet updated

This commit is contained in:
Armin Ronacher 2011-05-27 20:10:53 +02:00
parent e3f2dd8f08
commit e71a5ff8de
11 changed files with 436 additions and 153 deletions

View file

@ -310,6 +310,9 @@ Configuration
Useful Internals
----------------
.. autoclass:: flask.ctx.RequestContext
:members:
.. data:: _request_ctx_stack
The internal :class:`~werkzeug.local.LocalStack` that is used to implement
@ -347,23 +350,6 @@ Useful Internals
if ctx is not None:
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
-------
@ -401,6 +387,12 @@ Signals
in debug mode, where no exception handling happens. The 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
.. class:: flask.signals.Namespace
@ -418,28 +410,3 @@ Signals
operations, including connecting.
.. _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)

View file

@ -51,27 +51,36 @@ The following configuration values are used internally by Flask:
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
=============================== =========================================
``DEBUG`` enable/disable debug mode
``TESTING`` enable/disable testing mode
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
propagation of exceptions. If not set or
explicitly set to `None` this is
implicitly true if either `TESTING` or
`DEBUG` is true.
``SECRET_KEY`` the secret key
``SESSION_COOKIE_NAME`` the name of the session cookie
``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.
=============================== =========================================
================================= =========================================
``DEBUG`` enable/disable debug mode
``TESTING`` enable/disable testing mode
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
propagation of exceptions. If not set or
explicitly set to `None` this is
implicitly true if either `TESTING` or
`DEBUG` is true.
``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in
debug mode the request context is not
popped on exceptions to enable debuggers
to introspect the data. This can be
disabled by this key. You can also use
this setting to force-enable it for non
debug execution which might be useful to
debug production applications (but also
very risky).
``SECRET_KEY`` the secret key
``SESSION_COOKIE_NAME`` the name of the session cookie
``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``
@ -102,7 +111,7 @@ The following configuration values are used internally by Flask:
``MAX_CONTENT_LENGTH``
.. versionadded:: 0.7
``PROPAGATE_EXCEPTIONS``
``PROPAGATE_EXCEPTIONS``, ``PRESERVE_CONTEXT_ON_EXCEPTION``
Configuring from Files
----------------------

View file

@ -17,6 +17,7 @@ instructions for web development with Flask.
errorhandling
config
signals
reqcontext
shell
patterns/index
deploying/index

230
docs/reqcontext.rst Normal file
View 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.

View file

@ -1,3 +1,5 @@
.. _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
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
redirected to. Imagine it would always redirect to the URL's ``next``
parameter or the HTTP referrer or the index page::
The easiest way to create a proper request context from the shell is by
using the :attr:`~flask.Flask.test_request_context` method which creates
us a :class:`~flask.ctx.RequestContext`:
from flask import request, url_for
>>> ctx = app.test_request_context()
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 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:
Normally you would use the `with` statement to make this request object
active, but in the shell it's easier to use the
:meth:`~flask.ctx.RequestContext.push` and
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
>>> ctx.push()
From that point onwards you can work with the request object:
>>> redirect_url()
u'http://example.com/'
Until you call `pop`:
From that point onwards you can work with the request object until you
call `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
---------------------------
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
being unavailable, the current user not being stored on the
is normally run before a request. This might result in your database
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.
This however can easily be done yourself. Just call
@ -96,6 +74,11 @@ a response object:
<Response 0 bytes [200 OK]>
>>> 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
--------------------------------------

View file

@ -236,4 +236,20 @@ The following signals exist in Flask:
from flask import got_request_exception
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