线程局部

A thread local 变量是对使用它的应用程序而言似乎是“全局”变量的变量。但是,与真正的全局变量不同,当应用程序的一个线程或进程是“线程本地”时,该线程或进程可能会收到与另一个线程或进程不同的值。

处理请求时, Pyramid 使二 thread local 应用程序可用的变量:一个“注册表”和一个“请求”。

原因何在 Pyramid 使用线程局部变量

线程局部变量如何有益于 Pyramid 以及使用 Pyramid ?好吧,通常他们都是 not . 在任何应用程序中使用全局或线程局部变量通常会使临时读者更难理解。使用线程局部或全局通常只是避免在函数之间传递一些值的一种方法,这本身就是一个非常糟糕的主意,至少在代码可读性被视为一个重要问题时。

然而,由于历史原因,线程局部变量实际上是由 Pyramid API函数。例如,实现 pyramid.security 函数命名 authenticated_userid() (从1.5起已弃用)检索本地线程 application registry 当然,要找到一个 security policy . 它使用 pyramid.threadlocal.get_current_registry() 函数检索应用程序注册表,从中查找安全策略;然后使用安全策略检索经过身份验证的用户id Pyramid 允许“插入”任意安全策略。

当他们需要这样做的时候, Pyramid 内部使用两个API函数来检索 requestapplication registryget_current_request()get_current_registry() . 前者返回“当前”请求;后者返回“当前”注册表。两个 get_current_* 函数从线程本地数据结构中检索对象。这些API函数记录在 pyramid.threadlocal .

这些值是线程局部变量,而不是真正的全局变量,因为一个Python进程可能处理多个同时请求,甚至多个请求。 Pyramid 应用。如果他们是真正的全球人, Pyramid 无法处理多个同时请求或允许多个同时请求 Pyramid 应用程序实例将存在于单个Python进程中。

因为一 Pyramid 允许应用程序调用 另一个 Pyramid 从自己的应用程序 view 代码(可能是 WSGI 应用程序的帮助 pyramid.wsgi.wsgiapp2() decorator),这些变量在 堆栈 在系统正常运行期间。堆栈实例本身是 threading.local .

在正常操作期间,线程局部变量堆栈由 Router 对象。在请求开始时,路由器将应用程序的注册表和请求推送到堆栈上。在请求结束时,将弹出堆栈。堆栈上最顶层的请求和注册表被视为“当前”。因此,当系统正常运行时,“电流”的定义完全由金字塔的行为来定义。 Router .

但是,在单元测试期间,从来没有调用过路由器代码,并且“current”的定义是由对 pyramid.config.Configurator.begin()pyramid.config.Configurator.end() 方法(或在调用 pyramid.testing.setUp()pyramid.testing.tearDown() 函数)。这些函数在系统测试时推送和弹出线程本地堆栈。见 测试设置和拆卸 对于这些函数的定义。

使用的脚本 Pyramid 但从不实际启动WSGi服务器或通过HTTP接收请求,例如使用 pyramid.scripting API不会导致任何路由器代码被执行。然而, pyramid.scripting 当然,API也会将一些值推送到线程局部变量堆栈中。这样的脚本应该 get_current_request() 始终返回的函数 None 以及应该预期的 get_current_registry() 函数返回完全相同的 application registry 每一个请求。

为什么你不应该滥用线程本地

你可能根本不应该使用 get_current_request()get_current_registry() 函数,可能在测试中除外。尤其是,使用它几乎总是一个错误 get_current_requestget_current_registry 在应用程序代码中,因为它的使用使得编写既不容易测试也不容易编写脚本的代码成为可能。不当使用定义如下:

  • get_current_request 不应在 view callable 或者在可调用视图调用的代码中。视图可调用文件已经具有访问请求的权限(它作为 request

  • get_current_request 不应该被叫来 resource 代码。如果资源需要访问请求,则应通过 view callable .

  • get_current_request 永远不应该调用函数,因为在创建一些API设计时,考虑调用函数比通过一系列函数调用传递请求更容易或更优雅。相反,几乎可以肯定的是,应用程序应该传递从请求派生的数据,而不是依赖于能够调用此函数在实际上没有业务了解它的地方获取请求。参数是 意味 作为函数参数传递;这就是它们存在的原因。不要试图在需要请求的地方使用这个函数来“保存输入”或创建“更好的API”;这只会在以后导致悲伤。

  • 既不 get_current_request 也不 get_current_registry 应该在第三方库代码的特定于应用程序的分支中调用。你分出的类库几乎肯定与此无关。 Pyramid 使它依赖于 Pyramid (而不是 pyramid 应用程序依赖于它)意味着你正在形成一个错误方向的依赖。

使用 get_current_request() 应用程序代码中的函数 is 在非常有限的情况下仍然有用。根据经验,使用 get_current_request 是有用的 在最终要删除的代码中 . 例如,您可能会发现自己想要拒绝某些期望传递请求对象的API,而不是期望传递请求对象的API。但是,当您弃用旧的API时,需要让旧API的实现工作一段时间。因此,您编写了新API的“facade”实现,它调用实现旧API的代码。由于新的API不需要请求,因此当需要将请求传递到旧的API实现中时,Facade实现没有对请求的本地访问权。经过一段时间后,旧的实现代码将被废弃,并使用 get_current_request 被移除。这将是使用 get_current_request .

使用 get_current_registry() 功能应限于测试场景。通过使用 pyramid.config.Configurator.begin() 试验过程中的方法(或通过 pyramid.testing.setUp() )如果您没有通过此API,则可以使用一个。