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
|
||||
----------------
|
||||
|
||||
.. 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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----------------------
|
||||
|
|
|
|||
|
|
@ -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
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
|
||||
======================
|
||||
|
||||
|
|
@ -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
|
||||
--------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue