1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
|
.. 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.
|