From eea23c243a2c2667c8eee1250574967740eec758 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 23 Dec 2009 23:14:01 +0000 Subject: Thread locals chapter roles. --- docs/narr/threadlocals.rst | 176 ++++++++++++++++++++++++--------------------- docs/narr/views.rst | 2 +- docs/whatsnew-1.1.rst | 2 +- 3 files changed, 95 insertions(+), 85 deletions(-) diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index b97d9e4f2..9eef3e233 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -3,14 +3,14 @@ Thread Locals ============= -A thread local variable is a variable that appears to be a "global" -variable to an application which uses it. However, unlike a true -global variable, one thread or process serving the application may -receive a different value than another thread or process when that +A :term:`thread local` variable is a variable that appears to be a +"global" variable to an application which uses it. However, unlike a +true global variable, one thread or process serving the application +may receive a different value than another thread or process when that variable is "thread local". -When a request is processed, :mod:`repoze.bfg` makes two "thread -local" variables available to the application: a "registry" and a +When a request is processed, :mod:`repoze.bfg` makes two :term:`thread +local` variables available to the application: a "registry" and a "request". Why and How :mod:`repoze.bfg` Uses Thread Local Variables @@ -27,20 +27,21 @@ bad idea, at least if code readability counts as an important concern. For historical reasons, however, thread local variables are indeed consulted by various :mod:`repoze.bfg` API functions. For example, the implementation of the :mod:`repoze.bfg.security` function named -``authenticated_userid`` retrieves the thread local :term:`application -registry` as a matter of course to find a :term:`authentication -policy`. It uses the ``repoze.bfg.threadlocal.get_current_registry`` -function to retrieve the registry, from which it looks up the +:func:`repoze.bfg.security.authenticated_userid` retrieves the thread +local :term:`application registry` as a matter of course to find a +:term:`authentication policy`. It uses the +:func:`repoze.bfg.threadlocal.get_current_registry` function to +retrieve the application registry, from which it looks up the authentication policy; it then uses the authentication policy to retrieve the authenticated user id. This is how :mod:`repoze.bfg` allows arbitrary authentication policies to be "plugged in". When they need to do so, :mod:`repoze.bfg` internals use two API functions to retrieve the :term:`request` and :term:`application -registry`: ``repoze.bfg.threadlocal.get_current_request`` and -``repoze.bfg.threadlocal.get_current_registry``. The former returns -the "current" request; the latter returns the "current" registry. -Both ``get_current_*`` functions retrieve an object from a +registry`: :func:`repoze.bfg.threadlocal.get_current_request` and +:func:`repoze.bfg.threadlocal.get_current_registry`. The former +returns the "current" request; the latter returns the "current" +registry. Both ``get_current_*`` functions retrieve an object from a thread-local data structure. These API functions are documented in :ref:`threadlocal_module`. @@ -48,15 +49,15 @@ These values are thread locals rather than true globals because one Python process may be handling multiple simultaneous requests or even multiple :mod:`repoze.bfg` applications. If they were true globals, :mod:`repoze.bfg` could not handle multiple simultaneous requests or -allow more than one BFG application instance to exist in a single -Python process. +allow more than one :mod:`repoze.bfg` application instance to exist in +a single Python process. Because one :mod:`repoze.bfg` application is permitted to call *another* :mod:`repoze.bfg` application from its own :term:`view` code -(perhaps as a :term:`WSGI` app with help from the ``wsgiapp2`` -decorator in :ref:`wsgi_module`), these variables are managed in a -*stack* during normal system operations. The stack instance itself is -a `threading.local +(perhaps as a :term:`WSGI` app with help from the +:func:`repoze.bfg.wsgi.wsgiapp2` decorator), these variables are +managed in a *stack* during normal system operations. The stack +instance itself is a `threading.local `_. During normal operations, the thread locals stack is managed by a @@ -69,10 +70,11 @@ defined entirely by the behavior of a repoze.bfg :term:`Router`. However, during unit testing, no Router code is ever invoked, and the definition of "current" is defined by the boundary between calls to -the ``begin`` and ``end`` methods of a :term:`Configurator` (or, -pre-1.2a6, between calls to the ``repoze.bfg.testing.setUp`` and -``repoze.bfg.testing.tearDown`` functions). These functions push and -pop the threadlocal stack when the system is under test. See +the :meth:`repoze.bfg.configuration.Configurator.begin` and +:meth:`repoze.bfg.configuration.Configurator.end` methods (or between +calls to the :func:`repoze.bfg.testing.setUp` and +:func:`repoze.bfg.testing.tearDown` functions). These functions push +and pop the threadlocal stack when the system is under test. See :ref:`test_setup_and_teardown` for the definitions of these functions. Scripts which use :mod:`repoze.bfg` machinery but never actually start @@ -80,71 +82,79 @@ a WSGI server or receive requests via HTTP such as scripts which use the :mod:`repoze.bfg.scripting`` API will never cause any Router code to be executed. However, the :mod:`repoze.bfg.scripting` APIs also push some values on to the thread locals stack as a matter of course. -Such scripts should expect the ``get_current_request`` function to -always return ``None``, and should expect the ``get_current_registry`` -function to return exactly the same :term:`application registry` for -every request. +Such scripts should expect the +:func:`repoze.bfg.threadlocal.get_current_request` function to always +return ``None``, and should expect the +:func:`repoze.bfg.threadlocal.get_current_registry` function to return +exactly the same :term:`application registry` for every request. Why You Shouldn't Abuse Thread Locals ------------------------------------- -You probably should almost never use the ``get_current_request`` or -``get_current_registry`` functions, except perhaps in tests. In -particular, it's almost always a mistake to use -``get_current_request`` or ``get_current_registry`` in application +You probably should almost never use the +:func:`repoze.bfg.threadlocal.get_current_request` or +:func:`repoze.bfg.threadlocal.get_current_registry` functions, except +perhaps in tests. In particular, it's almost always a mistake to use +:func:`repoze.bfg.threadlocal.get_current_request` or +:func:`repoze.bfg.threadlocal.get_current_registry` in application code because its usage makes it possible to write code that can be neither easily tested nor scripted. Inappropriate usage is defined as follows: -- ``get_current_request`` should never be called within :term:`view` - code, or code called by view code. View code already has access to - the request (it's passed in). - -- ``get_current_request`` should never be called in :term:`model` - code. Model code should never require any access to the request; if - your model code requires access to a request object, you've almost - certainly factored something wrong, and you should change your code - rather than using this function. - -- The ``get_current_request`` function should never be called because - it's "easier" or "more elegant" to think about calling it than to - pass a request through a series of function calls when creating some - API design. Your application should instead almost certainly pass - data derived from the request around rather than relying on being - able to call this function to obtain the request in places that - actually have no business knowing about it. Parameters are *meant* - to be passed around as function arguments, this is why they exist. - Don't try to "save typing" or create "nicer APIs" by using this - function in the place where a request is required; this will only - lead to sadness later. - -- Neither ``get_current_request`` nor ``get_current_registry`` should - ever be called within application-specific forks of third-party - library code. The library you've forked almost certainly has - nothing to do with :mod:`repoze.bfg`, and making it dependent on - repoze.bfg (rather than making your :mod:`repoze.bfg` application - depend upon it) means you're forming a dependency in the wrong - direction. - -Use of the ``get_current_request`` function in application code *is* -still useful in very limited circumstances. As a rule of thumb, usage -of ``get_current_request`` is useful **within code which is meant to -eventually be removed**. For instance, you may find yourself wanting -to deprecate some API that expects to be passed a request object in -favor of one that does not expect to be passed a request object. But -you need to keep implementations of the old API working for some -period of time while you deprecate the older API. So you write a -"facade" implementation of the new API which calls into the code which -implements the older API. Since the new API does not require the -request, your facade implementation doesn't have local access to the -request when it needs to pass it into the older API implementation. -After some period of time, the older implementation code is disused -and the hack that uses ``get_current_request`` is removed. This would -be an appropriate place to use the ``get_current_request`` function. - -Use of the ``get_current_registry`` function should be limited to -testing scenarios. The registry made current by use of a -Configurator's ``begin`` method during a test (or pre-1.2a6, via -``repoze.bfg.testing.setUp``) when you do not pass one in is available -to you via this API. +- :func:`repoze.bfg.threadlocal.get_current_request` should never be + called within the body of a :term:`view callable`, or within code + called by a view callable. View callables already have access to + the request (it's passed in to each as ``request``). + +- :func:`repoze.bfg.threadlocal.get_current_request` should never be + called in :term:`model` code. Model code should never require any + access to the request; if your model code requires access to a + request object, you've almost certainly factored something wrong, + and you should change your code rather than using this function. + +- :func:`repoze.bfg.threadlocal.get_current_request` function should + never be called because it's "easier" or "more elegant" to think + about calling it than to pass a request through a series of function + calls when creating some API design. Your application should + instead almost certainly pass data derived from the request around + rather than relying on being able to call this function to obtain + the request in places that actually have no business knowing about + it. Parameters are *meant* to be passed around as function + arguments, this is why they exist. Don't try to "save typing" or + create "nicer APIs" by using this function in the place where a + request is required; this will only lead to sadness later. + +- Neither :func:`repoze.bfg.threadlocal.get_current_request` nor + :func:`repoze.bfg.threadlocal.get_current_registry` should ever be + called within application-specific forks of third-party library + code. The library you've forked almost certainly has nothing to do + with :mod:`repoze.bfg`, and making it dependent on repoze.bfg + (rather than making your :mod:`repoze.bfg` application depend upon + it) means you're forming a dependency in the wrong direction. + +Use of the :func:`repoze.bfg.threadlocal.get_current_request` function +in application code *is* still useful in very limited circumstances. +As a rule of thumb, usage of +:func:`repoze.bfg.threadlocal.get_current_request` is useful **within +code which is meant to eventually be removed**. For instance, you may +find yourself wanting to deprecate some API that expects to be passed +a request object in favor of one that does not expect to be passed a +request object. But you need to keep implementations of the old API +working for some period of time while you deprecate the older API. So +you write a "facade" implementation of the new API which calls into +the code which implements the older API. Since the new API does not +require the request, your facade implementation doesn't have local +access to the request when it needs to pass it into the older API +implementation. After some period of time, the older implementation +code is disused and the hack that uses +:func:`repoze.bfg.threadlocal.get_current_request` is removed. This +would be an appropriate place to use the +:func:`repoze.bfg.threadlocal.get_current_request` function. + +Use of the :func:`repoze.bfg.threadlocal.get_current_registry` +function should be limited to testing scenarios. The registry made +current by use of the +:meth:`repoze.bfg.configuration.Configurator.begin` method during a +test (or via :func:`repoze.bfg.testing.setUp`) when you do not pass +one in is available to you via this API. diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 5c512c902..8fda32240 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -224,7 +224,7 @@ the ``renderer`` attribute. For example, this ZCML associates the There is a ``json`` renderer, which renders view return values to a :term:`JSON` serialization. Other built-in renderers include renderers which use the :term:`Chameleon` templating language to -render a dictionary to a response. See :ref:`built_in_renders` for +render a dictionary to a response. See :ref:`built_in_renderers` for the available built-in renderers. If the ``view`` callable associated with a ``view`` directive returns diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst index 1847bfc1c..fa2af3e94 100644 --- a/docs/whatsnew-1.1.rst +++ b/docs/whatsnew-1.1.rst @@ -278,7 +278,7 @@ language to render a dictionary to a response. For example: def my_view(context, request): return {'abc':123} -See :ref:`built_in_renders` for the available built-in renderers. +See :ref:`built_in_renderers` for the available built-in renderers. If the ``view`` callable associated with a ``view`` directive returns a Response object (an object with the attributes ``status``, -- cgit v1.2.3