From 2411707492d59eff68a3cf6d306eed91fceb6344 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 8 Apr 2018 10:43:40 -0700 Subject: [PATCH] rework context docs --- docs/api.rst | 93 +++++------ docs/appcontext.rst | 211 ++++++++++++------------ docs/reqcontext.rst | 381 ++++++++++++++++++++++++-------------------- flask/app.py | 163 ++++++++++++------- flask/ctx.py | 44 ++++- flask/globals.py | 4 +- flask/testing.py | 21 ++- 7 files changed, 542 insertions(+), 375 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 0db2f6b6..c8820218 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -211,29 +211,22 @@ thing, like it does for :class:`request` and :class:`session`. .. data:: g - Just store on this whatever you want. For example a database - connection or the user that is currently logged in. + A namespace object that can store data during an + :doc:`application context `. This is an instance of + :attr:`Flask.app_ctx_globals_class`, which defaults to + :class:`ctx._AppCtxGlobals`. - Starting with Flask 0.10 this is stored on the application context and - no longer on the request context which means it becomes available if - only the application context is bound and not yet a request. This - is especially useful when combined with the :ref:`faking-resources` - pattern for testing. + This is a good place to store resources during a request. During + testing, you can use the :ref:`faking-resources` pattern to + pre-configure such resources. - Additionally as of 0.10 you can use the :meth:`get` method to - get an attribute or ``None`` (or the second argument) if it's not set. - These two usages are now equivalent:: + This is a proxy. See :ref:`notes-on-proxies` for more information. - user = getattr(flask.g, 'user', None) - user = flask.g.get('user', None) + .. versionchanged:: 0.10 + Bound to the application context instead of the request context. - It's now also possible to use the ``in`` operator on it to see if an - attribute is defined and it yields all keys on iteration. - - As of 0.11 you can use :meth:`pop` and :meth:`setdefault` in the same - way you would use them on a dictionary. - - This is a proxy. See :ref:`notes-on-proxies` for more information. +.. autoclass:: flask.ctx._AppCtxGlobals + :members: Useful Functions and Classes @@ -241,13 +234,17 @@ Useful Functions and Classes .. data:: current_app - Points to the application handling the request. This is useful for - extensions that want to support multiple applications running side - by side. This is powered by the application context and not by the - request context, so you can change the value of this proxy by - using the :meth:`~flask.Flask.app_context` method. + A proxy to the application handling the current request. This is + useful to access the application without needing to import it, or if + it can't be imported, such as when using the application factory + pattern or in blueprints and extensions. - This is a proxy. See :ref:`notes-on-proxies` for more information. + This is only available when an + :doc:`application context ` is pushed. This happens + automatically during requests and CLI commands. It can be controlled + manually with :meth:`~flask.Flask.app_context`. + + This is a proxy. See :ref:`notes-on-proxies` for more information. .. autofunction:: has_request_context @@ -384,50 +381,54 @@ Useful Internals .. data:: _request_ctx_stack - The internal :class:`~werkzeug.local.LocalStack` that is used to implement - all the context local objects used in Flask. This is a documented - instance and can be used by extensions and application code but the - use is discouraged in general. + The internal :class:`~werkzeug.local.LocalStack` that holds + :class:`~flask.ctx.RequestContext` instances. Typically, the + :data:`request` and :data:`session` proxies should be accessed + instead of the stack. It may be useful to access the stack in + extension code. - The following attributes are always present on each layer of the - stack: + The following attributes are always present on each layer of the + stack: - `app` + `app` the active Flask application. - `url_adapter` + `url_adapter` the URL adapter that was used to match the request. - `request` + `request` the current request object. - `session` + `session` the active session object. - `g` + `g` an object with all the attributes of the :data:`flask.g` object. - `flashes` + `flashes` an internal cache for the flashed messages. - Example usage:: + Example usage:: - from flask import _request_ctx_stack + from flask import _request_ctx_stack - def get_session(): - ctx = _request_ctx_stack.top - if ctx is not None: - return ctx.session + def get_session(): + ctx = _request_ctx_stack.top + if ctx is not None: + return ctx.session .. autoclass:: flask.ctx.AppContext :members: .. data:: _app_ctx_stack - Works similar to the request context but only binds the application. - This is mainly there for extensions to store data. + The internal :class:`~werkzeug.local.LocalStack` that holds + :class:`~flask.ctx.AppContext` instances. Typically, the + :data:`current_app` and :data:`g` proxies should be accessed instead + of the stack. Extensions can access the contexts on the stack as a + namespace to store data. - .. versionadded:: 0.9 + .. versionadded:: 0.9 .. autoclass:: flask.blueprints.BlueprintSetupState :members: diff --git a/docs/appcontext.rst b/docs/appcontext.rst index 976609b6..63006ad4 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -1,144 +1,159 @@ +.. currentmodule:: flask + .. _app-context: The Application Context ======================= -.. versionadded:: 0.9 - -One of the design ideas behind Flask is that there are at least two -different “states” in which code is executed: - -1. The application setup state, in which the application implicitly is -on the module level. +The application context keeps track of the application-level data during +a request, CLI command, or other activity. Rather than passing the +application around to each function, the :data:`current_app` and +:data:`g` proxies are accessed instead. - This state 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: +This is similar to the :doc:`/reqcontext`, which keeps track of +request-level data during a request. A corresponding application context +is pushed when a request context is pushed. - - 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. +Purpose of the Context +---------------------- -2. In contrast, in the request handling state, a couple of other rules -exist: +The :class:`Flask` application object has attributes, such as +:attr:`~Flask.config`, that are useful to access within views and +:doc:`CLI commands `. However, importing the ``app`` instance +within the modules in your project is prone to circular import issues. +When using the :doc:`app factory pattern ` or +writing reusable :doc:`blueprints ` or +:doc:`extensions ` there won't be an ``app`` instance to +import at all. - - 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. +Flask solves this issue with the *application context*. Rather than +referring to an ``app`` directly, you use the the :data:`current_app` +proxy, which points to the application handling the current activity. -3. There is also a third state somewhere in between 'module-level' and -'request-handling': +Flask automatically *pushes* an application context when handling a +request. View functions, error handlers, and other functions that run +during a request will have access to :data:`current_app`. - Sometimes you are dealing with an application in a way that is similar to - how you interact with applications during request handling, but without - there being an active request. Consider, for instance, that you're - sitting in an interactive Python shell and interacting with the - application, or a command line application. +Flask will also automatically push an app context when running CLI +commands registered with :attr:`Flask.cli` using ``@app.cli.command()``. -The application context is what powers the :data:`~flask.current_app` -context local. -Purpose of the Application Context ----------------------------------- +Lifetime of the Context +----------------------- -The main reason for the application's context existence is that in the -past a bunch of functionality was attached to the request context for lack -of a better solution. Since one of the pillars of Flask's design is that -you can have more than one application in the same Python process. +The application context is created and destroyed as necessary. When a +Flask application begins handling a request, it pushes an application +context and a :doc:`request context `. When the request +ends it pops the request context then the application context. +Typically, an application context will have the same lifetime as a +request. -So how does the code find the “right” application? In the past we -recommended passing applications around explicitly, but that caused issues -with libraries that were not designed with that in mind. +See :doc:`/reqcontext` for more information about how the contexts work +and the full lifecycle of a request. -A common workaround for that problem was to use the -:data:`~flask.current_app` proxy later on, which was bound to the current -request's application reference. Since creating such a request context is -an unnecessarily expensive operation in case there is no request around, -the application context was introduced. -Creating an Application Context -------------------------------- +Manually Push a Context +----------------------- -There are two ways to make an application context. The first one is -implicit: whenever a request context is pushed, an application context -will be created alongside if this is necessary. As a result, you can -ignore the existence of the application context unless you need it. +If you try to access :data:`current_app`, or anything that uses it, +outside an application context, you'll get this error message: -The second way is the explicit way using the -:meth:`~flask.Flask.app_context` method:: +.. code-block:: pytb - from flask import Flask, current_app + RuntimeError: Working outside of application context. - app = Flask(__name__) - with app.app_context(): - # within this block, current_app points to app. - print current_app.name + This typically means that you attempted to use functionality that + needed to interface with the current application object in some way. + To solve this, set up an application context with app.app_context(). -The application context is also used by the :func:`~flask.url_for` -function in case a ``SERVER_NAME`` was configured. This allows you to -generate URLs even in the absence of a request. +If you see that error while configuring your application, such as when +initializing an extension, you can push a context manually since you +have direct access to the ``app``. Use :meth:`~Flask.app_context` in a +``with`` block, and everything that runs in the block will have access +to :data:`current_app`. :: -If no request context has been pushed and an application context has -not been explicitly set, a ``RuntimeError`` will be raised. :: + def create_app(): + app = Flask(__name__) - RuntimeError: Working outside of application context. + with app.app_context(): + init_db() -Locality of the Context ------------------------ + return app -The application context is created and destroyed as necessary. It never -moves between threads and it will not be shared between requests. As such -it is the perfect place to store database connection information and other -things. The internal stack object is called :data:`flask._app_ctx_stack`. -Extensions are free to store additional information on the topmost level, -assuming they pick a sufficiently unique name and should put their -information there, instead of on the :data:`flask.g` object which is reserved -for user code. +If you see that error somewhere else in your code not related to +configuring the application, it most likely indicates that you should +move that code into a view function or CLI command. -For more information about that, see :ref:`extension-dev`. -Context Usage -------------- +Storing Data +------------ -The context is typically used to cache resources that need to be created -on a per-request or usage case. For instance, database connections are -destined to go there. When storing things on the application context -unique names should be chosen as this is a place that is shared between -Flask applications and extensions. +The application context is a good place to store common data during a +request or CLI command. Flask provides the :data:`g object ` for this +purpose. It is a simple namespace object that has the same lifetime as +an application context. -The most common usage is to split resource management into two parts: +.. note:: + The ``g`` name stands for "global", but that is referring to the + data being global *within a context*. The data on ``g`` is lost + after the context ends, and it is not an appropriate place to store + data between requests. Use the :data:`session` or a database to + store data across requests. -1. an implicit resource caching on the context. -2. a context teardown based resource deallocation. +A common use for :data:`g` is to manage resources during a request. -Generally there would be a ``get_X()`` function that creates resource -``X`` if it does not exist yet and otherwise returns the same resource, -and a ``teardown_X()`` function that is registered as teardown handler. +1. ``get_X()`` creates resource ``X`` if it does not exist, caching it + as ``g.X``. +2. ``teardown_X()`` closes or otherwise deallocates the resource if it + exists. It is registered as a :meth:`~Flask.teardown_appcontext` + handler. -This is an example that connects to a database:: +For example, you can manage a database connection using this pattern:: - import sqlite3 from flask import g def get_db(): - db = getattr(g, '_database', None) - if db is None: - db = g._database = connect_to_database() - return db + if 'db' not in g: + g.db = connect_to_database() + + return g.db @app.teardown_appcontext - def teardown_db(exception): - db = getattr(g, '_database', None) + def teardown_db(): + db = g.pop('db', None) + if db is not None: db.close() -The first time ``get_db()`` is called the connection will be established. -To make this implicit a :class:`~werkzeug.local.LocalProxy` can be used:: +During a request, every call to ``get_db()`` will return the same +connection, and it will be closed automatically at the end of the +request. + +You can use :class:`~werkzeug.local.LocalProxy` to make a new context +local from ``get_db()``:: from werkzeug.local import LocalProxy db = LocalProxy(get_db) -That way a user can directly access ``db`` which internally calls -``get_db()``. +Accessing ``db`` will call ``get_db`` internally, in the same way that +:data:`current_app` works. + +---- + +If you're writing an extension, :data:`g` should be reserved for user +code. You may store internal data on the context itself, but be sure to +use a sufficiently unique name. The current context is accessed with +:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see +:doc:`extensiondev`. + + +Events and Signals +------------------ + +The application will call functions registered with +:meth:`~Flask.teardown_appcontext` when the application context is +popped. + +If :data:`~signals.signals_available` is true, the following signals are +sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and +:data:`appcontext_popped`. diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index c3d37297..2724fe0c 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -1,224 +1,267 @@ +.. currentmodule:: flask + .. _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. +The request context keeps track of the request-level data during a +request. Rather than passing the request object to each function that +runs during a request, the :data:`request` and :data:`session` proxies +are accessed instead. + +This is similar to the :doc:`/appcontext`, which keeps track of the +application-level data independent of a request. A corresponding +application context is pushed when a request context is pushed. + + +Purpose of the Context +---------------------- + +When the :class:`Flask` application handles a request, it creates a +:class:`Request` object based on the environment it received from the +WSGI server. Because a *worker* (thread, process, or coroutine depending +on the server) handles only one request at a time, the request data can +be considered global to that worker during that request. Flask uses the +term *context local* for this. + +Flask automatically *pushes* a request context when handling a request. +View functions, error handlers, and other functions that run during a +request will have access to the :data:`request` proxy, which points to +the request object for the current request. -It is recommended that you read the :ref:`app-context` chapter first. -Diving into Context Locals --------------------------- +Lifetime of the 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:: +When a Flask application begins handling a request, it pushes a request +context, which also pushes an :doc:`/appcontext`. When the request ends +it pops the request context then the application context. - from flask import request, url_for +The context is unique to each thread (or other worker type). +:data:`request` cannot be passed to another thread, the other thread +will have a different context stack and will not know about the request +the parent thread was pointing to. - def redirect_url(): - return request.args.get('next') or \ - request.referrer or \ - url_for('index') +Context locals are implemented in Werkzeug. See :doc:`werkzeug:local` +for more information on how this works internally. -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 "", line 1, in -AttributeError: 'NoneType' object has no attribute 'request' +Manually Push a Context +----------------------- -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`: +If you try to access :data:`request`, or anything that uses it, outside +a request context, you'll get this error message: ->>> ctx = app.test_request_context('/?next=http://example.com/') +.. code-block:: pytb -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: + RuntimeError: Working outside of request context. ->>> ctx.push() + This typically means that you attempted to use functionality that + needed an active HTTP request. Consult the documentation on testing + for information about how to avoid this problem. -From that point onwards you can work with the request object: +This should typically only happen when testing code that expects an +active request. One option is to use the +:meth:`test client ` to simulate a full request. Or +you can use :meth:`~Flask.test_request_context` in a ``with`` block, and +everything that runs in the block will have access to :data:`request`, +populated with your test data. :: ->>> redirect_url() -u'http://example.com/' + def generate_report(year): + format = request.args.get('format') + ... -Until you call `pop`: + with app.test_request_context( + '/make_report/2017', data={'format': 'short'}): + generate_report() ->>> ctx.pop() +If you see that error somewhere else in your code not related to +testing, it most likely indicates that you should move that code into a +view function. -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 information on how to use the request context from the interactive +Python shell, see :doc:`/shell`. -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 as 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. - -Another thing of note is that the request context will automatically also -create an :ref:`application context ` when it's pushed and -there is no application context for that application so far. +The :meth:`Flask.wsgi_app` method is called to handle each request. It +manages the contexts during the request. Internally, the request and +application contexts work as stacks, :data:`_request_ctx_stack` and +:data:`_app_ctx_stack`. When contexts are pushed onto the stack, the +proxies that depend on them are available and point at information from +the top context on the stack. + +When the request starts, a :class:`~ctx.RequestContext` is created and +pushed, which creates and pushes an :class:`~ctx.AppContext` first if +a context for that application is not already the top context. While +these contexts are pushed, the :data:`current_app`, :data:`g`, +:data:`request`, and :data:`session` proxies are available to the +original thread handling the request. + +Because the contexts are stacks, other contexts may be pushed to change +the proxies during a request. While this is not a common pattern, it +can be used in advanced applications to, for example, do internal +redirects or chain different applications together. + +After the request is dispatched and a response is generated and sent, +the request context is popped, which then pops the application context. +Immediately before they are popped, the :meth:`~Flask.teardown_request` +and :meth:`~Flask.teardown_appcontext` functions are are executed. These +execute even if an unhandled exception occurred during dispatch. + .. _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: +Flask dispatches a request in multiple stages which can affect the +request, response, and how errors are handled. The contexts are active +during all of these stages. + +A :class:`Blueprint` can add handlers for these events that are specific +to the blueprint. The handlers for a blueprint will run if the blueprint +owns the route that matches the request. -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. +#. Before each request, :meth:`~Flask.before_request` functions are + called. If one of these functions return a value, the other + functions are skipped. The return value is treated as the response + and the view function is not called. -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. +#. If the :meth:`~Flask.before_request` functions did not return a + response, the view function for the matched route is called and + returns 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. +#. The return value of the view is converted into an actual response + object and passed to the :meth:`~Flask.after_request` + functions. Each function returns a modified or new response object. -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 or if a before-request handler was - not executed yet or at all (for example in test environments sometimes - you might want to not execute before-request callbacks). +#. After the response is returned, the contexts are popped, which calls + the :meth:`~Flask.teardown_request` and + :meth:`~Flask.teardown_appcontext` functions. These functions are + called even if an unhandled exception was raised at any point above. -Now what happens on errors? If you are not in debug mode and an exception is not -caught, the 500 internal server handler is called. In debug 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. +If an exception is raised before the teardown functions, Flask tries to +match it with an :meth:`~Flask.errorhandler` function to handle the +exception and return a response. If no error handler is found, or the +handler itself raises an exception, Flask returns a generic +``500 Internal Server Error`` response. The teardown functions are still +called, and are passed the exception object. -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. +If debug mode is enabled, unhandled exceptions are not converted to a +``500`` response and instead are propagated to the WSGI server. This +allows the development server to present the interactive debugger with +the traceback. -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 -a 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 or 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 teardown_request(exception=None): -... print 'this runs after request' -... ->>> ctx = app.test_request_context() ->>> ctx.push() ->>> ctx.pop() -this runs after request ->>> - -Keep in mind that teardown callbacks are always executed, even if -before-request callbacks were not executed yet but an exception happened. -Certain parts of the test system might also temporarily create a request -context without calling the before-request handlers. Make sure to write -your teardown-request handlers in a way that they will never fail. +~~~~~~~~~~~~~~~~~~ + +The teardown callbacks are independent of the request dispatch, and are +instead called by the contexts when they are popped. The functions are +called even if there is an unhandled exception during dispatch, and for +manually pushed contexts. This means there is no guarantee that any +other parts of the request dispatch have run first. Be sure to write +these functions in a way that does not depend on other callbacks and +will not fail. + +During testing, it can be useful to defer popping the contexts after the +request ends, so that their data can be accessed in the test function. +Using the :meth:`~Flask.test_client` as a ``with`` block to preserve the +contexts until the with block exits. + +.. code-block:: python + + from flask import Flask, request + + app = Flask(__name__) + + @app.route('/') + def hello(): + print('during view') + return 'Hello, World!' + + @app.teardown_request + def show_teardown(exception): + print('after with block') + + with app.test_request_context(): + print('during with block') + + # teardown functions are called after the context with block exits + + with app.test_client(): + client.get('/') + # the contexts are not popped even though the request ended + print(request.path) + + # the contexts are popped and teardown functions are called after + # the client with block exists + + +Signals +~~~~~~~ + +If :data:`~signals.signals_available` is true, the following signals are +sent: + +#. :data:`request_started` is sent before the + :meth:`~Flask.before_request` functions are called. + +#. :data:`request_finished` is sent after the + :meth:`~Flask.after_request` functions are called. + +#. :data:`got_request_exception` is sent when an exception begins to + be handled, but before an :meth:`~Flask.errorhandler` is looked up or + called. + +#. :data:`request_tearing_down` is sent after the + :meth:`~Flask.teardown_request` functions are called. + + +Context Preservation on Error +----------------------------- + +At the end of a request, the request context is popped and all data +associated with it is destroyed. If an error occurs during development, +it is useful to delay destroying the data for debugging purposes. + +When the development server is running in development mode (the +``FLASK_ENV`` environment variable is set to ``'development'``), the +error and data will be preserved and shown in the interactive debugger. + +This behavior can be controlled with the +:data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it +defaults to ``True`` in the development environment. + +Do not enable :data:`PRESERVE_CONTEXT_ON_EXCEPTION` in production, as it +will cause your application to leak memory on exceptions. + .. _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. +Some of the objects provided by Flask are proxies to other objects. The +proxies are accessed in the same way for each worker thread, but +point to the unique object bound to each worker behind the scenes as +described on this page. 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`) +- The proxy objects cannot fake their type as the actual object types. + If you want to perform instance checks, you have to do that on the + object being proxied. +- If the specific object reference is important, for example for + sending :ref:`signals` or passing data to a background thread. -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:: +If you need to access the underlying object that is proxied, 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. If debug mode is set to off, the context -is not preserved. - -Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` if debug mode is set to off -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 debug mode when attempting to debug an error that -only occurs under production settings. diff --git a/flask/app.py b/flask/app.py index 89be4dc5..22183dcc 100644 --- a/flask/app.py +++ b/flask/app.py @@ -2106,15 +2106,25 @@ class Flask(_PackageBoundObject): return response def do_teardown_request(self, exc=_sentinel): - """Called after the actual request dispatching and will - 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. + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. .. versionchanged:: 0.9 - Added the `exc` argument. Previously this was always using the - current exception information. + Added the ``exc`` argument. """ if exc is _sentinel: exc = sys.exc_info()[1] @@ -2127,9 +2137,17 @@ class Flask(_PackageBoundObject): request_tearing_down.send(self, exc=exc) def do_teardown_appcontext(self, exc=_sentinel): - """Called when an application context is popped. This works pretty - much the same as :meth:`do_teardown_request` but for the application - context. + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. .. versionadded:: 0.9 """ @@ -2140,62 +2158,89 @@ class Flask(_PackageBoundObject): appcontext_tearing_down.send(self, exc=exc) def app_context(self): - """Binds the application only. For as long as the application is bound - to the current context the :data:`flask.current_app` points to that - application. An application context is automatically created when a - request context is pushed if necessary. + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. - Example usage:: + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: with app.app_context(): - ... + init_db() + + See :doc:`/appcontext`. .. versionadded:: 0.9 """ return AppContext(self) def request_context(self, environ): - """Creates a :class:`~flask.ctx.RequestContext` from the given - environment and binds it to the current context. This must be used in - combination with the ``with`` statement because the request is only bound - to the current context for the duration of the ``with`` block. - - Example usage:: - - with app.request_context(environ): - do_something_with(request) - - The object returned can also be used without the ``with`` statement - which is useful for working in the shell. The example above is - doing exactly the same as this code:: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. - ctx = app.request_context(environ) - ctx.push() - try: - do_something_with(request) - finally: - ctx.pop() + See :doc:`/reqcontext`. - .. versionchanged:: 0.3 - Added support for non-with statement usage and ``with`` statement - is now passed the ctx object. + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. :param environ: a WSGI environment """ return RequestContext(self, environ) def test_request_context(self, *args, **kwargs): - """Creates a :class:`~flask.ctx.RequestContext` from the given values - (see :class:`werkzeug.test.EnvironBuilder` for more information, this - function accepts the same arguments plus two additional). + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. - Additional arguments (only if ``base_url`` is not specified): + See :doc:`/reqcontext`. - :param subdomain: subdomain to use for route matching - :param url_scheme: scheme for the request, default - ``PREFERRED_URL_SCHEME`` or ``http``. - """ + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ from flask.testing import make_test_environ_builder builder = make_test_environ_builder(self, *args, **kwargs) @@ -2206,9 +2251,9 @@ class Flask(_PackageBoundObject): builder.close() def wsgi_app(self, environ, start_response): - """The actual WSGI application. This is not implemented in - `__call__` so that middlewares can be applied without losing a - reference to the class. So instead of doing this:: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: app = MyMiddleware(app) @@ -2220,15 +2265,15 @@ class Flask(_PackageBoundObject): can continue to call methods on it. .. versionchanged:: 0.7 - The behavior of the before and after request callbacks was changed - under error conditions and a new callback was added that will - always execute at the end of the request, independent on if an - error occurred or not. See :ref:`callbacks-and-errors`. + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. - :param environ: a WSGI environment - :param start_response: a callable accepting a status code, - a list of headers and an optional - exception context to start the response + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. """ ctx = self.request_context(environ) error = None @@ -2249,7 +2294,9 @@ class Flask(_PackageBoundObject): ctx.auto_pop(error) def __call__(self, environ, start_response): - """Shortcut for :attr:`wsgi_app`.""" + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app` which can be + wrapped to applying middleware.""" return self.wsgi_app(environ, start_response) def __repr__(self): diff --git a/flask/ctx.py b/flask/ctx.py index 3438d63f..8472c920 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -24,18 +24,60 @@ _sentinel = object() class _AppCtxGlobals(object): - """A plain 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): diff --git a/flask/globals.py b/flask/globals.py index f99238cd..7d50a6f6 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -25,8 +25,8 @@ _app_ctx_err_msg = '''\ Working outside of application context. This typically means that you attempted to use functionality that needed -to interface with the current application object in a way. To solve -this set up an application context with app.app_context(). See the +to interface with the current application object in some way. To solve +this, set up an application context with app.app_context(). See the documentation for more information.\ ''' diff --git a/flask/testing.py b/flask/testing.py index cd346ce0..4bf0ebc1 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -25,7 +25,26 @@ def make_test_environ_builder( app, path='/', base_url=None, subdomain=None, url_scheme=None, *args, **kwargs ): - """Creates a new test builder with some application defaults thrown in.""" + """Create a :class:`~werkzeug.test.EnvironBuilder`, taking some + defaults from the application. + + :param app: The Flask application to configure the environment from. + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ assert ( not (base_url or subdomain or url_scheme)