Steve Piercy
2015-12-04 34d2784b73963feb5e38b4e5ea31dcd2856a65d9
- add emphasize lines, minor grammar, rewrap 79 columns
(cherry picked from commit acfdc70)
1 files modified
239 ■■■■ changed files
docs/narr/zca.rst 239 ●●●● patch | view | raw | blame | history
docs/narr/zca.rst
@@ -9,17 +9,16 @@
.. _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."
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:
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:
@@ -28,23 +27,21 @@
   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.
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.
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.
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
@@ -52,87 +49,81 @@
   single: getSiteManager
   single: ZCA global API
Using the ZCA Global API in a :app:`Pyramid` Application
-----------------------------------------------------------
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.
: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.
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.
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*.
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.
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.
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
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)``:
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.
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.
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 :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, eg. ``request.registry``.  This
  is the ZCA component registry related to the running
  :app:`Pyramid` application.
- 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`.
@@ -142,7 +133,7 @@
.. _hook_zca:
Enabling the ZCA Global API by Using ``hook_zca``
Enabling the ZCA global API by using ``hook_zca``
+++++++++++++++++++++++++++++++++++++++++++++++++
Consider the following bit of idiomatic :app:`Pyramid` startup code:
@@ -157,34 +148,31 @@
       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.
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.
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``).
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:
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
@@ -194,9 +182,9 @@
       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:
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:
@@ -205,17 +193,15 @@
   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.
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`.
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
@@ -224,14 +210,15 @@
.. _using_the_zca_global_registry:
Enabling the ZCA Global API by 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:
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
@@ -243,16 +230,14 @@
       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,
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 a Zope app to when you use the global ZCA API.
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.