ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.01.2026

Просмотров: 2755

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

CHAPTER

THIRTYFOUR

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 variable is “thread local”.

When a request is processed, Pyramid makes two thread local variables available to the application: a “registry” and a “request”.

34.1 Why and How Pyramid Uses Thread Local Variables

How are thread locals beneficial to Pyramid and application developers who use Pyramid? Well, usually they’re decidedly not. Using a global or a thread local variable in any application usually makes it a lot harder to understand for a casual reader. Use of a thread local or a global is usually just a way to avoid passing some value around between functions, which is itself usually a very bad idea, at least if code readability counts as an important concern.

For historical reasons, however, thread local variables are indeed consulted by various Pyramid API functions. For example, the implementation of the pyramid.security function named authenticated_userid() retrieves the thread local application registry as a matter of course to find an authentication policy. It uses the pyramid.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 Pyramid allows arbitrary authentication policies to be “plugged in”.

When they need to do so, Pyramid internals use two API functions to retrieve the request and application registry: get_current_request() and get_current_registry(). The former returns the

377

34. THREAD LOCALS

“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 pyramid.threadlocal.

These values are thread locals rather than true globals because one Python process may be handling multiple simultaneous requests or even multiple Pyramid applications. If they were true globals, Pyramid could not handle multiple simultaneous requests or allow more than one Pyramid application instance to exist in a single Python process.

Because one Pyramid application is permitted to call another Pyramid application from its own view code (perhaps as a WSGI app with help from the pyramid.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 Router object. At the beginning of a request, the Router pushes the application’s registry and the request on to the stack. At the end of a request, the stack is popped. The topmost request and registry on the stack are considered “current”. Therefore, when the system is operating normally, the very definition of “current” is defined entirely by the behavior of a pyramid 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 pyramid.config.Configurator.begin() and pyramid.config.Configurator.end() methods (or between calls to the pyramid.testing.setUp() and pyramid.testing.tearDown() functions). These functions push and pop the threadlocal stack when the system is under test. See Test Set Up and Tear Down for the definitions of these functions.

Scripts which use Pyramid machinery but never actually start a WSGI server or receive requests via HTTP such as scripts which use the pyramid.scripting API will never cause any Router code to be executed. However, the pyramid.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 application registry for every request.

34.2 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 code because its usage makes it possible to write code that can be neither easily tested nor scripted. Inappropriate usage is defined as follows:

378


34.2.WHY YOU SHOULDN’T ABUSE THREAD LOCALS

get_current_request should never be called within the body of a 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).

get_current_request should never be called in resource code. If a resource needs access to the request, it should be passed the request by a view callable.

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 Pyramid, and making it dependent on Pyramid (rather than making your pyramid 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.

Use of the get_current_registry() function should be limited to testing scenarios. The registry made current by use of the pyramid.config.Configurator.begin() method during a test (or via pyramid.testing.setUp()) when you do not pass one in is available to you via this API.

379

34. THREAD LOCALS

380



CHAPTER

THIRTYFIVE

USING THE ZOPE COMPONENT ARCHITECTURE IN PYRAMID

Under the hood, Pyramid uses a Zope Component Architecture component registry as its application registry. The Zope Component Architecture is referred to colloquially as the “ZCA.”

The zope.component API used to access data in a traditional Zope application can be opaque. For example, here is a typical “unnamed utility” lookup using the zope.component.getUtility() global API as it might appear in a traditional Zope application:

1

2

3

from pyramid.interfaces import ISettings from zope.component import getUtility settings = getUtility(ISettings)

After this code runs, settings will be a Python dictionary. But it’s unlikely that any “civilian” will be able to figure this out just by reading the code casually. When the zope.component.getUtility API is used by a developer, the conceptual load on a casual reader of code is high.

While the ZCA is an excellent tool with which to build a framework such as Pyramid, it is not always the best tool with which to build an application due to the opacity of the zope.component APIs. Accordingly, Pyramid tends to hide the presence of the ZCA from application developers. You needn’t understand the ZCA to create a Pyramid application; its use is effectively only a framework implementation detail.

However, developers who are already used to writing Zope applications often still wish to use the ZCA while building a Pyramid application; pyramid makes this possible.

381


35. USING THE ZOPE COMPONENT ARCHITECTURE IN PYRAMID

35.1 Using the ZCA Global API in a Pyramid Application

Zope uses a single ZCA registry – the “global” ZCA registry – for all Zope applications that run in the same Python process, effectively making it impossible to run more than one Zope application in a single process.

However, for ease of deployment, it’s often useful to be able to run more than a single application per process. For example, use of a PasteDeploy “composite” allows you to run separate individual WSGI applications in the same process, each answering requests for some URL prefix. This makes it possible to run, for example, a TurboGears application at /turbogears and a Pyramid application at /pyramid, both served up using the same WSGI server within a single Python process.

Most production Zope applications are relatively large, making it impractical due to memory constraints to run more than one Zope application per Python process. However, a Pyramid application may be very small and consume very little memory, so it’s a reasonable goal to be able to run more than one Pyramid application per process.

In order to make it possible to run more than one Pyramid application in a single process, Pyramid defaults to using a separate ZCA registry per application.

While this services a reasonable goal, it causes some issues when trying to use patterns which you might use to build a typical Zope application to build a Pyramid application. Without special help, ZCA “global” APIs such as zope.component.getUtility and zope.component.getSiteManager will use the ZCA “global” registry. Therefore, these APIs will appear to fail when used in a Pyramid application, because they’ll be consulting the ZCA global registry rather than the component registry associated with your Pyramid application.

There are three ways to fix this: by disusing the ZCA global API entirely, by using pyramid.config.Configurator.hook_zca() or by passing the ZCA global registry to the

Configurator constructor at startup time. We’ll describe all three methods in this section.

35.1.1 Disusing the Global ZCA API

ZCA “global” API functions such as zope.component.getSiteManager, zope.component.getUtility, zope.component.getAdapter, and zope.component.getMultiAdapter aren’t strictly necessary. Every component registry has a method API that offers the same functionality; it can be used instead. For example, presuming the registry value below is a Zope Component Architecture component registry, the following bit of code is equivalent to zope.component.getUtility(IFoo):

382

35.1. USING THE ZCA GLOBAL API IN A PYRAMID APPLICATION

1 registry.getUtility(IFoo)

The full method API is documented in the zope.component package, but it largely mirrors the “global” API almost exactly.

If you are willing to disuse the “global” ZCA APIs and use the method interface of a registry instead, you need only know how to obtain the Pyramid component registry.

There are two ways of doing so:

use the pyramid.threadlocal.get_current_registry() function within Pyramid view or resource code. This will always return the “current” Pyramid application registry.

use the attribute of the request object named registry in your Pyramid view code, eg. request.registry. This is the ZCA component registry related to the running Pyramid application.

See Thread Locals for more information about pyramid.threadlocal.get_current_registry().

35.1.2 Enabling the ZCA Global API by Using hook_zca

Consider the following bit of idiomatic Pyramid startup code:

1 from zope.component import getGlobalSiteManager

2 from pyramid.config import Configurator

3

4 def app(global_settings, **settings):

5config = Configurator(settings=settings)

6 config.include(’some.other.package’)

7return config.make_wsgi_app()

When the app function above is run, a Configurator is constructed. When the configurator is created, it creates a new application registry (a ZCA component registry). A new registry is constructed whenever the registry argument is omitted when a Configurator constructor is called, or when a registry argument with a value of None is passed to a Configurator constructor.

During a request, the application registry created by the Configurator is “made current”. This means calls to get_current_registry() in the thread handling the request will return the component registry associated with the application.

383