From 3b0eb0f3ca45786135cacfc380bfdb51e7744e46 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 20 Jul 2010 10:12:58 +0100 Subject: [PATCH] Added notes on proxies --- docs/api.rst | 35 +++++++++++++++++++++++++++++++++++ docs/signals.rst | 46 ++++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index ca57e8bf..ed2a5037 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -38,6 +38,8 @@ Incoming Request Data sure that you always get the correct data for the active thread if you are in a multithreaded environment. + This is a proxy. See :ref:`notes-on-proxies` for more information. + The request object is an instance of a :class:`~werkzeug.Request` subclass and provides all of the attributes Werkzeug defines. This just shows a quick overview of the most important ones. @@ -164,6 +166,8 @@ To access the current session you can use the :class:`session` object: The session object works pretty much like an ordinary dict, with the difference that it keeps track on modifications. + This is a proxy. See :ref:`notes-on-proxies` for more information. + The following attributes are interesting: .. attribute:: new @@ -206,6 +210,8 @@ thing, like it does for :class:`request` and :class:`session`. Just store on this whatever you want. For example a database connection or the user that is currently logged in. + This is a proxy. See :ref:`notes-on-proxies` for more information. + Useful Functions and Classes ---------------------------- @@ -216,6 +222,8 @@ Useful Functions and Classes extensions that want to support multiple applications running side by side. + This is a proxy. See :ref:`notes-on-proxies` for more information. + .. autofunction:: url_for .. function:: abort(code) @@ -389,6 +397,8 @@ Signals in debug mode, where no exception handling happens. The exception itself is passed to the subscriber as `exception`. +.. currentmodule:: None + .. class:: flask.signals.Namespace An alias for :class:`blinker.base.Namespace` if blinker is available, @@ -404,3 +414,28 @@ 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 +- 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.LocalProxy._get_current_object` method:: + + app = current_app._get_current_object() + my_signal.send(app) diff --git a/docs/signals.rst b/docs/signals.rst index eeed7343..513d8694 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -41,13 +41,9 @@ the optional second argument specifies a sender. To unsubscribe from a signal, you can use the :meth:`~blinker.base.Signal.disconnect` method. For all core Flask signals, the sender is the application that issued the -signal. This however might not be true for Flask extensions, so consult -the documentation when subscribing to signals. - -Additionally there is a convenient helper method that allows you to -temporarily subscribe a function to a signal. This is especially helpful -for unittests (:meth:`~blinker.base.Signal.temporarily_connected_to`). -This has to be used in combination with the `with` statement. +signal. When you subscribe to a signal, be sure to also provide a sender +unless you really want to listen for signals of all applications. This is +especially true if you are developing an extension. Here for example a helper context manager that can be used to figure out in a unittest which templates were rendered and what variables were passed @@ -57,19 +53,19 @@ to the template:: from contextlib import contextmanager @contextmanager - def captured_templates(): + def captured_templates(app): recorded = [] def record(template, context): recorded.append((template, context)) - template_rendered.connect(record) + template_rendered.connect(record, app) try: yield recorded finally: - template_rendered.disconnect(record) + template_rendered.disconnect(record, app) This can now easily be paired with a test client:: - with captured_templates() as templates: + with captured_templates(app) as templates: rv = app.test_client().get('/') assert rv.status_code == 200 assert len(templates) == 1 @@ -77,9 +73,23 @@ This can now easily be paired with a test client:: assert template.name == 'index.html' assert len(context['items']) == 10 -All the template rendering in the code, the `with` block wraps will now be -recorded in the `templates` variable. Whenever a template is rendered, -the template object as well as context is appended to it. +All the template rendering in the code issued by the application `app` +in the body of the `with` block will now be recorded in the `templates` +variable. Whenever a template is rendered, the template object as well as +context are appended to it. + +Additionally there is a convenient helper method +(:meth:`~blinker.base.Signal.temporarily_connected_to`). that allows you +to temporarily subscribe a function to a signal with is a context manager +on its own which simplifies the example above:: + + from flask import template_rendered + + def captured_templates(app): + recorded = [] + def record(template, context): + recorded.append((template, context)) + return template_rendered.temporarily_connected_to(record, app) Creating Signals ---------------- @@ -153,7 +163,7 @@ The following signals exist in Flask: context) from flask import request_started - request_started.connect(log_template_renders) + request_started.connect(log_template_renders, app) .. data:: flask.request_started :noindex: @@ -169,7 +179,7 @@ The following signals exist in Flask: sender.logger.debug('Request context is set up') from flask import request_started - request_started.connect(log_request) + request_started.connect(log_request, app) .. data:: flask.request_finished :noindex: @@ -184,7 +194,7 @@ The following signals exist in Flask: 'Response: %s', response) from flask import request_finished - request_finished.connect(log_response) + request_finished.connect(log_response, app) .. data:: flask.got_request_exception :noindex: @@ -200,6 +210,6 @@ The following signals exist in Flask: sender.logger.debug('Got exception during processing: %s', exception) from flask import got_request_exception - got_request_exception.connect(log_exception) + got_request_exception.connect(log_exception, app) .. _blinker: http://pypi.python.org/pypi/blinker