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

.. _zca_chapter:

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

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."

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:

.. 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.

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.

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

Using the ZCA global API in a :app:`Pyramid` application
--------------------------------------------------------

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

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.

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.

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*.

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.

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.

.. index::
   single: request.registry

.. _disusing_the_global_zca_api:

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)``:

.. code-block:: python

   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.

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.

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

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

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

.. _hook_zca:

Enabling the ZCA global API by using ``hook_zca``
+++++++++++++++++++++++++++++++++++++++++++++++++

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

.. 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.

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.

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``).

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:

.. code-block:: python
   :linenos:
   :emphasize-lines: 5

   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 5, which calls
``config.hook_zca()``.  The effect of this line under the hood is that an
analogue of the following code is executed:

.. 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.

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

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

.. _using_the_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:

.. code-block:: python
   :linenos:
   :emphasize-lines: 5-7

   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.

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 in a Zope app
when you use the global ZCA API.
