Skip to content
This repository
branch: 1.5-branch
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 259 lines (200 sloc) 10.269 kb

Using the Zope Component Architecture in :app:`Pyramid`

System Message: ERROR/3 (<string>, line 1)

Unknown directive type "index".

.. index::
   single: ZCA
   single: Zope Component Architecture
   single: zope.component
   single: application registry
   single: getSiteManager
   single: getUtility

System Message: ERROR/3 (<string>, line 11); backlink

Unknown interpreted text role "app".

Under the hood, :app:`Pyramid` uses a :term:`Zope Component Architecture` component registry as its :term:`application registry`. The Zope Component Architecture is referred to colloquially as the "ZCA."

System Message: ERROR/3 (<string>, line 14); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 14); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 14); backlink

Unknown interpreted text role "term".

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 :func:`zope.component.getUtility` global API as it might appear in a traditional Zope application:

System Message: ERROR/3 (<string>, line 19); backlink

Unknown interpreted text role "func".

System Message: ERROR/3 (<string>, line 24)

Error in "code-block" directive: unknown option: "linenos".

.. code-block:: python
   :linenos:

   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 :app:`Pyramid`, it is not always the best tool with which to build an application due to the opacity of the zope.component APIs. Accordingly, :app:`Pyramid` tends to hide the presence of the ZCA from application developers. You needn't understand the ZCA to create a :app:`Pyramid` application; its use is effectively only a framework implementation detail.

System Message: ERROR/3 (<string>, line 37); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 37); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 37); backlink

Unknown interpreted text role "app".

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

System Message: ERROR/3 (<string>, line 45); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 45); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 45); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 49)

Unknown directive type "index".

.. index::
   single: get_current_registry
   single: getUtility
   single: getSiteManager
   single: ZCA global API

Using the ZCA Global API in a :app:`Pyramid` Application

System Message: ERROR/3 (<string>, line 55); backlink

Unknown interpreted text role "app".

:term:`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.

System Message: ERROR/3 (<string>, line 58); backlink

Unknown interpreted text role "term".

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 :term:`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 :app:`Pyramid` application at /pyramid, both served up using the same :term:`WSGI` server within a single Python process.

System Message: ERROR/3 (<string>, line 63); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 63); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 63); backlink

Unknown interpreted text role "term".

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 :app:`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 :app:`Pyramid` application per process.

System Message: ERROR/3 (<string>, line 71); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 71); backlink

Unknown interpreted text role "app".

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

System Message: ERROR/3 (<string>, line 78); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 78); backlink

Unknown interpreted text role "app".

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

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "func".

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "func".

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 82); backlink

Unknown interpreted text role "app".

There are three ways to fix this: by disusing the ZCA global API entirely, by using :meth:`pyramid.config.Configurator.hook_zca` or by passing the ZCA global registry to the :term:`Configurator` constructor at startup time. We'll describe all three methods in this section.

System Message: ERROR/3 (<string>, line 92); backlink

Unknown interpreted text role "meth".

System Message: ERROR/3 (<string>, line 92); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 98)

Unknown directive type "index".

.. index::
   single: request.registry

Disusing the Global ZCA API

ZCA "global" API functions such as zope.component.getSiteManager, zope.component.getUtility, :func:`zope.component.getAdapter`, and :func:`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):

System Message: ERROR/3 (<string>, line 106); backlink

Unknown interpreted text role "func".

System Message: ERROR/3 (<string>, line 106); backlink

Unknown interpreted text role "func".
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 :app:`Pyramid` component registry.

System Message: ERROR/3 (<string>, line 122); backlink

Unknown interpreted text role "app".

There are two ways of doing so:

  • use the :func:`pyramid.threadlocal.get_current_registry` function within :app:`Pyramid` view or resource code. This will always return the "current" :app:`Pyramid` application registry.

    System Message: ERROR/3 (<string>, line 128); backlink

    Unknown interpreted text role "func".

    System Message: ERROR/3 (<string>, line 128); backlink

    Unknown interpreted text role "app".

    System Message: ERROR/3 (<string>, line 128); backlink

    Unknown interpreted text role "app".

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

    System Message: ERROR/3 (<string>, line 132); backlink

    Unknown interpreted text role "term".

    System Message: ERROR/3 (<string>, line 132); backlink

    Unknown interpreted text role "app".

    System Message: ERROR/3 (<string>, line 132); backlink

    Unknown interpreted text role "app".

See :ref:`threadlocals_chapter` for more information about :func:`pyramid.threadlocal.get_current_registry`.

System Message: ERROR/3 (<string>, line 137); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<string>, line 137); backlink

Unknown interpreted text role "func".

System Message: ERROR/3 (<string>, line 140)

Unknown directive type "index".

.. index::
   single: hook_zca (configurator method)

Enabling the ZCA Global API by Using hook_zca

Consider the following bit of idiomatic :app:`Pyramid` startup code:

System Message: ERROR/3 (<string>, line 148); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 150)

Error in "code-block" directive: unknown option: "linenos".

.. code-block:: python
   :linenos:

   from pyramid.config import Configurator

   def app(global_settings, **settings):
       config = Configurator(settings=settings)
       config.include('some.other.package')
       return config.make_wsgi_app()

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

System Message: ERROR/3 (<string>, line 160); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 160); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 160); backlink

Unknown interpreted text role "term".

System Message: ERROR/3 (<string>, line 160); backlink

Unknown interpreted text role "term".

During a request, the application registry created by the Configurator is "made current". This means calls to :func:`~pyramid.threadlocal.get_current_registry` in the thread handling the request will return the component registry associated with the application.

System Message: ERROR/3 (<string>, line 168); backlink

Unknown interpreted text role "func".

As a result, application developers can use get_current_registry to get the registry and thus get access to utilities and such, as per :ref:`disusing_the_global_zca_api`. But they still cannot use the global ZCA API. Without special treatment, the ZCA global APIs will always return the global ZCA registry (the one in zope.component.globalregistry.base).

System Message: ERROR/3 (<string>, line 174); backlink

Unknown interpreted text role "ref".

To "fix" this and make the ZCA global APIs use the "current" :app:`Pyramid` registry, you need to call :meth:`~pyramid.config.Configurator.hook_zca` within your setup code. For example:

System Message: ERROR/3 (<string>, line 181); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 181); backlink

Unknown interpreted text role "meth".

System Message: ERROR/3 (<string>, line 186)

Error in "code-block" directive: unknown option: "linenos".

.. code-block:: python
   :linenos:

   from pyramid.config import Configurator

   def app(global_settings, **settings):
       config = Configurator(settings=settings)
       config.hook_zca()
       config.include('some.other.application')
       return config.make_wsgi_app()

We've added a line to our original startup code, line number 6, which calls config.hook_zca(). The effect of this line under the hood is that an analogue of the following code is executed:

System Message: ERROR/3 (<string>, line 201)

Error in "code-block" directive: unknown option: "linenos".

.. code-block:: python
   :linenos:

   from zope.component import getSiteManager
   from pyramid.threadlocal import get_current_registry
   getSiteManager.sethook(get_current_registry)

This causes the ZCA global API to start using the :app:`Pyramid` application registry in threads which are running a :app:`Pyramid` request.

System Message: ERROR/3 (<string>, line 208); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 208); backlink

Unknown interpreted text role "app".

Calling hook_zca is usually sufficient to "fix" the problem of being able to use the global ZCA API within a :app:`Pyramid` application. However, it also means that a Zope application that is running in the same process may start using the :app:`Pyramid` global registry instead of the Zope global registry, effectively inverting the original problem. In such a case, follow the steps in the next section, :ref:`using_the_zca_global_registry`.

System Message: ERROR/3 (<string>, line 212); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 212); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 212); backlink

Unknown interpreted text role "ref".

System Message: ERROR/3 (<string>, line 220)

Unknown directive type "index".

.. index::
   single: get_current_registry
   single: getGlobalSiteManager
   single: ZCA global registry

Enabling the ZCA Global API by Using The ZCA Global Registry

You can tell your :app:`Pyramid` application to use the ZCA global registry at startup time instead of constructing a new one:

System Message: ERROR/3 (<string>, line 230); backlink

Unknown interpreted text role "app".

System Message: ERROR/3 (<string>, line 233)

Error in "code-block" directive: unknown option: "linenos".

.. code-block:: python
   :linenos:

   from zope.component import getGlobalSiteManager
   from pyramid.config import Configurator

   def app(global_settings, **settings):
       globalreg = getGlobalSiteManager()
       config = Configurator(registry=globalreg)
       config.setup_registry(settings=settings)
       config.include('some.other.application')
       return config.make_wsgi_app()

Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves the global ZCA component registry. Line 6 creates a :term:`Configurator`, passing the global ZCA registry into its constructor as the registry argument. Line 7 "sets up" the global registry with Pyramid-specific registrations; this is code that is normally executed when a registry is constructed rather than created, but we must call it "by hand" when we pass an explicit registry.

System Message: ERROR/3 (<string>, line 246); backlink

Unknown interpreted text role "term".

At this point, :app:`Pyramid` will use the ZCA global registry rather than creating a new application-specific registry; since by default the ZCA global API will use this registry, things will work as you might expect a Zope app to when you use the global ZCA API.

System Message: ERROR/3 (<string>, line 254); backlink

Unknown interpreted text role "app".
Something went wrong with that request. Please try again.