summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorTres Seaver <tseaver@palladion.com>2012-06-29 14:47:56 +0000
committerTres Seaver <tseaver@palladion.com>2012-06-29 14:47:56 +0000
commit2e39b26054c8c1da36886b7ba8ba0d346d62d2d7 (patch)
tree370e3c664f4bec9fb91effde230daf4cd56c6688 /docs
parent33d9fc041301b310e4cf28f8e28b0f915c58d4fd (diff)
downloadzope-component-2e39b26054c8c1da36886b7ba8ba0d346d62d2d7.tar.gz
Merge tseaver-test_cleanup branch.
Diffstat (limited to 'docs')
-rw-r--r--docs/Makefile153
-rw-r--r--docs/api.rst15
-rw-r--r--docs/api/adapter.rst405
-rw-r--r--docs/api/factory.rst9
-rw-r--r--docs/api/interface.rst184
-rw-r--r--docs/api/interfaces.rst31
-rw-r--r--docs/api/persistent.rst236
-rw-r--r--docs/api/security.rst96
-rw-r--r--docs/api/sitemanager.rst88
-rw-r--r--docs/api/utility.rst290
-rw-r--r--docs/conf.py248
-rw-r--r--docs/configure.rst30
-rw-r--r--docs/event.rst176
-rw-r--r--docs/factory.rst153
-rw-r--r--docs/hooks.rst112
-rw-r--r--docs/index.rst26
-rw-r--r--docs/make.bat190
-rw-r--r--docs/narr.rst449
-rw-r--r--docs/persistentregistry.rst6
-rw-r--r--docs/socketexample.rst705
-rw-r--r--docs/testlayer.rst111
-rw-r--r--docs/zcml.rst1153
22 files changed, 4866 insertions, 0 deletions
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..70f2ba7
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopeconfiguration.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopeconfiguration.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/zopeconfiguration"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopeconfiguration"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..286255f
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,15 @@
+:mod:`zope.component` API Reference
+===================================
+
+
+.. toctree::
+ :maxdepth: 2
+
+ api/interfaces
+ api/sitemanager
+ api/utility
+ api/adapter
+ api/factory
+ api/interface
+ api/security
+ api/persistent
diff --git a/docs/api/adapter.rst b/docs/api/adapter.rst
new file mode 100644
index 0000000..992d9e5
--- /dev/null
+++ b/docs/api/adapter.rst
@@ -0,0 +1,405 @@
+Adapter Registration APIs
+=========================
+
+.. testsetup::
+
+ from zope.component.testing import setUp
+ setUp()
+
+Conforming Adapter Lookup
+-------------------------
+
+.. autofunction:: zope.component.getAdapterInContext
+
+.. autofunction:: zope.component.queryAdapterInContext
+
+The :func:`~zope.component.getAdapterInContext` and
+:func:`~zope.component.queryAdapterInContext` APIs first check the
+context object to see if it already conforms to the requested interface.
+If so, the object is returned immediately. Otherwise, the adapter factory
+is looked up in the site manager, and called.
+
+Let's start by creating a component that supports the `__conform__()` method:
+
+.. doctest::
+
+ >>> from zope.interface import implementer
+ >>> from zope.component.tests.examples import I1
+ >>> from zope.component.tests.examples import I2
+ >>> @implementer(I1)
+ ... class Component(object):
+ ... def __conform__(self, iface, default=None):
+ ... if iface == I2:
+ ... return 42
+ >>> ob = Component()
+
+We also gave the component a custom representation, so it will be easier
+to use in these tests.
+
+We now have to create a site manager (other than the default global one)
+with which we can register adapters for `I1`.
+
+.. doctest::
+
+ >>> from zope.component.globalregistry import BaseGlobalComponents
+ >>> sitemanager = BaseGlobalComponents()
+
+Now we create a new `context` that knows how to get to our custom site
+manager.
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import ConformsToIComponentLookup
+ >>> context = ConformsToIComponentLookup(sitemanager)
+
+If an object implements the interface you want to adapt to,
+`getAdapterInContext()` should simply return the object.
+
+.. doctest::
+
+ >>> from zope.component import getAdapterInContext
+ >>> from zope.component import queryAdapterInContext
+ >>> getAdapterInContext(ob, I1, context) is ob
+ True
+ >>> queryAdapterInContext(ob, I1, context) is ob
+ True
+
+If an object conforms to the interface you want to adapt to,
+`getAdapterInContext()` should simply return the conformed object.
+
+.. doctest::
+
+ >>> getAdapterInContext(ob, I2, context)
+ 42
+ >>> queryAdapterInContext(ob, I2, context)
+ 42
+
+If an adapter isn't registered for the given object and interface, and you
+provide no default, the `getAdapterInContext` API raises ComponentLookupError:
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> class I4(Interface):
+ ... pass
+
+ >>> getAdapterInContext(ob, I4, context)
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<Component implementing 'I1'>,
+ <InterfaceClass ...I4>)
+
+While the `queryAdapterInContext` API returns the default:
+
+.. doctest::
+
+ >>> queryAdapterInContext(ob, I4, context, 44)
+ 44
+
+If you ask for an adapter for which something's registered you get the
+registered adapter:
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import I3
+ >>> sitemanager.registerAdapter(lambda x: 43, (I1,), I3, '')
+ >>> getAdapterInContext(ob, I3, context)
+ 43
+ >>> queryAdapterInContext(ob, I3, context)
+ 43
+
+Named Adapter Lookup
+--------------------
+
+.. autofunction:: zope.component.getAdapter
+
+.. autofunction:: zope.component.queryAdapter
+
+The ``getAdapter`` and ``queryAdapter`` API functions are similar to
+``{get|query}AdapterInContext()`` functions, except that they do not care
+about the ``__conform__()`` but also handle named adapters. (Actually, the
+name is a required argument.)
+
+If no adapter is registered for the given object, interface, and name,
+``getAdapter`` raises ``ComponentLookupError``, while ``queryAdapter``
+returns the default:
+
+.. doctest::
+
+ >>> from zope.component import getAdapter
+ >>> from zope.component import queryAdapter
+ >>> from zope.component.tests.examples import I2
+ >>> from zope.component.tests.examples import ob
+ >>> getAdapter(ob, I2, '')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance Ob>,
+ <InterfaceClass zope.component.tests.examples.I2>,
+ '')
+ >>> queryAdapter(ob, I2, '', '<default>')
+ '<default>'
+
+The 'requires' argument to `registerAdapter` must be a sequence, rather than
+a single interface:
+
+.. doctest::
+
+ >>> from zope.component import getGlobalSiteManager
+ >>> from zope.component.tests.examples import Comp
+ >>> gsm = getGlobalSiteManager()
+ >>> gsm.registerAdapter(Comp, I1, I2, '')
+ Traceback (most recent call last):
+ ...
+ TypeError: the required argument should be a list of interfaces, not a single interface
+
+After register an adapter from `I1` to `I2` with the global site manager:
+
+.. doctest::
+
+ >>> from zope.component import getGlobalSiteManager
+ >>> from zope.component.tests.examples import Comp
+ >>> gsm = getGlobalSiteManager()
+ >>> gsm.registerAdapter(Comp, (I1,), I2, '')
+
+We can access the adapter using the `getAdapter()` API:
+
+.. doctest::
+
+ >>> adapted = getAdapter(ob, I2, '')
+ >>> adapted.__class__ is Comp
+ True
+ >>> adapted.context is ob
+ True
+ >>> adapted = queryAdapter(ob, I2, '')
+ >>> adapted.__class__ is Comp
+ True
+ >>> adapted.context is ob
+ True
+
+If we search using a non-anonymous name, before registering:
+
+.. doctest::
+
+ >>> getAdapter(ob, I2, 'named')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance Ob>,
+ <InterfaceClass ....I2>,
+ 'named')
+ >>> queryAdapter(ob, I2, 'named', '<default>')
+ '<default>'
+
+After registering under that name:
+
+.. doctest::
+
+ >>> gsm.registerAdapter(Comp, (I1,), I2, 'named')
+ >>> adapted = getAdapter(ob, I2, 'named')
+ >>> adapted.__class__ is Comp
+ True
+ >>> adapted.context is ob
+ True
+ >>> adapted = queryAdapter(ob, I2, 'named')
+ >>> adapted.__class__ is Comp
+ True
+ >>> adapted.context is ob
+ True
+
+Invoking an Interface to Perform Adapter Lookup
+-----------------------------------------------
+
+:mod:`zope.component` registers an adapter hook with
+:mod:`zope.interface.interface`, allowing a convenient spelling for
+adapter lookup: just "call" the interface, passing the context:
+
+.. doctest::
+
+ >>> adapted = I2(ob)
+ >>> adapted.__class__ is Comp
+ True
+ >>> adapted.context is ob
+ True
+
+If the lookup fails, we get a `TypeError`:
+
+.. doctest::
+
+ >>> I2(object())
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt'...
+
+unless we pass a default:
+
+.. doctest::
+
+ >>> marker = object()
+ >>> adapted = I2(object(), marker)
+ >>> adapted is marker
+ True
+
+Registering Adapters For Arbitrary Objects
+------------------------------------------
+
+Providing an adapter for None says that your adapter can adapt anything
+to `I2`.
+
+.. doctest::
+
+ >>> gsm.registerAdapter(Comp, (None,), I2, '')
+ >>> adapter = I2(ob)
+ >>> adapter.__class__ is Comp
+ True
+ >>> adapter.context is ob
+ True
+
+It can really adapt any arbitrary object:
+
+.. doctest::
+
+ >>> something = object()
+ >>> adapter = I2(something)
+ >>> adapter.__class__ is Comp
+ True
+ >>> adapter.context is something
+ True
+
+Looking Up Adapters Using Multiple Objects
+------------------------------------------
+
+.. autofunction:: zope.component.getMultiAdapter
+
+.. autofunction:: zope.component.queryMultiAdapter
+
+Multi-adapters adapt one or more objects to another interface. To make
+this demonstration non-trivial, we need to create a second object to be
+adapted:
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import Ob2
+ >>> ob2 = Ob2()
+
+As with regular adapters, if an adapter isn't registered for the given
+objects and interface, the :func:`~zope.component.getMultiAdapter` API
+raises `ComponentLookupError`:
+
+.. doctest::
+
+ >>> from zope.component import getMultiAdapter
+ >>> getMultiAdapter((ob, ob2), I3)
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError:
+ ((<instance Ob>, <instance Ob2>),
+ <InterfaceClass zope.component.tests.examples.I3>,
+ u'')
+
+while the :func:`~zope.component.queryMultiAdapter` API returns the default:
+
+.. doctest::
+
+ >>> from zope.component import queryMultiAdapter
+ >>> queryMultiAdapter((ob, ob2), I3, default='<default>')
+ '<default>'
+
+Note that ``name`` is not a required attribute here.
+
+To test multi-adapters, we also have to create an adapter class that
+handles to context objects:
+
+.. doctest::
+
+ >>> from zope.interface import implementer
+ >>> @implementer(I3)
+ ... class DoubleAdapter(object):
+ ... def __init__(self, first, second):
+ ... self.first = first
+ ... self.second = second
+
+Now we can register the multi-adapter:
+
+.. doctest::
+
+ >>> from zope.component import getGlobalSiteManager
+ >>> getGlobalSiteManager().registerAdapter(DoubleAdapter, (I1, I2), I3, '')
+
+Notice how the required interfaces are simply provided by a tuple.
+
+Now we can get the adapter:
+
+.. doctest::
+
+ >>> adapter = getMultiAdapter((ob, ob2), I3)
+ >>> adapter.__class__ is DoubleAdapter
+ True
+ >>> adapter.first is ob
+ True
+ >>> adapter.second is ob2
+ True
+
+
+Finding More Than One Adapter
+-----------------------------
+
+.. autofunction:: zope.component.getAdapters
+
+It is sometimes desireable to get a list of all adapters that are
+registered for a particular output interface, given a set of
+objects.
+
+Let's register some adapters first:
+
+.. doctest::
+
+ >>> class I5(I1):
+ ... pass
+ >>> gsm.registerAdapter(Comp, [I1], I5, '')
+ >>> gsm.registerAdapter(Comp, [None], I5, 'foo')
+
+Now we get all the adapters that are registered for ``ob`` that provide
+``I5``:
+
+.. doctest::
+
+ >>> from zope.component import getAdapters
+ >>> adapters = sorted(getAdapters((ob,), I5))
+ >>> [(name, adapter.__class__.__name__) for name, adapter in adapters]
+ [(u'', 'Comp'), (u'foo', 'Comp')]
+
+Note that the output doesn't include None values. If an adapter
+factory returns None, it is as if it wasn't present.
+
+.. doctest::
+
+ >>> gsm.registerAdapter(lambda context: None, [I1], I5, 'nah')
+ >>> adapters = sorted(getAdapters((ob,), I5))
+ >>> [(name, adapter.__class__.__name__) for name, adapter in adapters]
+ [(u'', 'Comp'), (u'foo', 'Comp')]
+
+
+Subscription Adapters
+---------------------
+
+.. autofunction:: zope.component.subscribers
+
+Event handlers
+--------------
+
+.. autofunction:: zope.component.handle
+
+Helpers for Declaring / Testing Adapters
+----------------------------------------
+
+.. autofunction:: zope.component.adapter
+
+.. autofunction:: zope.component.adaptedBy
+
+.. autofunction:: zope.component.adapts
+
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
+
diff --git a/docs/api/factory.rst b/docs/api/factory.rst
new file mode 100644
index 0000000..fc5c1f8
--- /dev/null
+++ b/docs/api/factory.rst
@@ -0,0 +1,9 @@
+Factory APIs
+============
+
+.. autofunction:: zope.component.createObject
+
+.. autofunction:: zope.component.getFactoryInterfaces
+
+.. autofunction:: zope.component.getFactoriesFor
+
diff --git a/docs/api/interface.rst b/docs/api/interface.rst
new file mode 100644
index 0000000..fbaf57f
--- /dev/null
+++ b/docs/api/interface.rst
@@ -0,0 +1,184 @@
+Interface Registration APIs
+===========================
+
+.. testsetup::
+
+ from zope.component.testing import setUp
+ setUp()
+
+Registering Interfaces as Utilities
+-----------------------------------
+
+.. autofunction:: zope.component.interface.provideInterface
+
+We can register a given interface with the global site manager as a utility.
+First, declare a new interface, which itself provides only the core API,
+:class:`zope.interface.interfaces.IInterface`:
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.interface.interfaces import IInterface
+ >>> from zope.component.tests.examples import ITestType
+ >>> from zope.component import getGlobalSiteManager
+ >>> gsm = getGlobalSiteManager()
+
+ >>> class IDemo(Interface):
+ ... pass
+ >>> IInterface.providedBy(IDemo)
+ True
+ >>> ITestType.providedBy(IDemo)
+ False
+ >>> list(gsm.getUtilitiesFor(ITestType))
+ []
+
+Now, register ``IDemo`` as providing ``ITestType``
+
+.. doctest::
+
+ >>> from zope.component.interface import provideInterface
+ >>> provideInterface('', IDemo, ITestType)
+ >>> ITestType.providedBy(IDemo)
+ True
+ >>> interfaces = list(gsm.getUtilitiesFor(ITestType))
+ >>> [iface.__name__ for (name, iface) in interfaces]
+ ['IDemo']
+
+We can register ``IDemo`` as providing more than one interface:
+
+.. doctest::
+
+ >>> class IOtherType(IInterface):
+ ... pass
+ >>> provideInterface('', IDemo, IOtherType)
+ >>> ITestType.providedBy(IDemo)
+ True
+ >>> IOtherType.providedBy(IDemo)
+ True
+ >>> interfaces = list(gsm.getUtilitiesFor(ITestType))
+ >>> [iface.__name__ for (name, iface) in interfaces]
+ ['IDemo']
+ >>> interfaces = list(gsm.getUtilitiesFor(IOtherType))
+ >>> [iface.__name__ for (name, iface) in interfaces]
+ ['IDemo']
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
+
+
+.. autofunction:: zope.component.interface.getInterface
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.component.interface import getInterface
+ >>> from zope.component.tests.examples import ITestType
+ >>> from zope.component.tests.examples import IGI
+
+ >>> IInterface.providedBy(IGI)
+ True
+ >>> ITestType.providedBy(IGI)
+ False
+ >>> getInterface(None, 'zope.component.tests.examples.IGI')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: zope.component.tests.examples.interface.IGI
+ >>> provideInterface('', IGI, ITestType)
+ >>> ITestType.providedBy(IGI)
+ True
+ >>> iface = getInterface(None,
+ ... 'zope.component.tests.examples.IGI')
+ >>> iface.__name__
+ 'IGI'
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
+
+
+.. autofunction:: zope.component.interface.queryInterface
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.interface.interfaces import IInterface
+ >>> from zope.component.interface import queryInterface
+ >>> from zope.component.tests.examples import ITestType
+ >>> from zope.component.tests.examples import IQI
+
+ >>> IInterface.providedBy(IQI)
+ True
+ >>> ITestType.providedBy(IQI)
+ False
+ >>> queryInterface('zope.component.tests.examples.IQI') is None
+ True
+
+ >>> provideInterface('', IQI, ITestType)
+ >>> ITestType.providedBy(IQI)
+ True
+ >>> iface = queryInterface('zope.component.tests.examples.IQI')
+ >>> iface.__name__
+ 'IQI'
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
+
+
+.. autofunction:: zope.component.interface.searchInterface
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.interface.interfaces import IInterface
+ >>> from zope.component.interface import searchInterface
+ >>> from zope.component.tests.examples import ITestType
+ >>> from zope.component.tests.examples import ISI
+
+ >>> IInterface.providedBy(ISI)
+ True
+ >>> ITestType.providedBy(ISI)
+ False
+ >>> searchInterface(None, 'zope.component.tests.examples.ISI')
+ []
+ >>> provideInterface('', ISI, ITestType)
+ >>> ITestType.providedBy(ISI)
+ True
+ >>> searchInterface(None, 'zope.component.tests.examples.ISI') == [ISI]
+ True
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
+
+.. autofunction:: zope.component.interface.searchInterfaceIds
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.interface.interfaces import IInterface
+ >>> from zope.component.interface import searchInterfaceIds
+ >>> from zope.component.tests.examples import ITestType
+ >>> from zope.component.tests.examples import ISII
+
+ >>> IInterface.providedBy(ISII)
+ True
+ >>> ITestType.providedBy(ISII)
+ False
+ >>> searchInterfaceIds(None, 'zope.component.tests.examples.ISII')
+ []
+ >>> provideInterface('', ISII, ITestType)
+ >>> ITestType.providedBy(ISII)
+ True
+ >>> searchInterfaceIds(None, 'zope.component.tests.examples.ISII')
+ [u'zope.component.tests.examples.ISII']
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
new file mode 100644
index 0000000..addfc8f
--- /dev/null
+++ b/docs/api/interfaces.rst
@@ -0,0 +1,31 @@
+Interface Definitions
+=====================
+
+.. automodule:: zope.component.interfaces
+
+ .. autointerface:: IComponentArchitecture
+ :members:
+ :member-order: bysource
+
+ .. autointerface:: IRegistry
+ :members:
+ :member-order: bysource
+
+ .. autointerface:: IComponentRegistrationConvenience
+ :members:
+ :member-order: bysource
+
+ .. autointerface:: IPossibleSite
+ :members:
+ :member-order: bysource
+
+ .. autointerface:: ISite
+ :members:
+ :member-order: bysource
+
+ .. autoexception:: Misused
+
+ .. autointerface:: IFactory
+ :members:
+ :member-order: bysource
+
diff --git a/docs/api/persistent.rst b/docs/api/persistent.rst
new file mode 100644
index 0000000..69c9e4b
--- /dev/null
+++ b/docs/api/persistent.rst
@@ -0,0 +1,236 @@
+Persistent Registries
+=====================
+
+.. testsetup::
+
+ from zope.component.testing import setUp
+ setUp()
+
+Conforming Adapter Lookup
+-------------------------
+Here, we'll demonstrate that changes work even when data are stored in
+a database and when accessed from multiple connections.
+
+Start by setting up a database and creating two transaction
+managers and database connections to work with.
+
+.. doctest::
+
+ >>> import ZODB.tests.util
+ >>> db = ZODB.tests.util.DB()
+ >>> import transaction
+ >>> t1 = transaction.TransactionManager()
+ >>> c1 = db.open(transaction_manager=t1)
+ >>> r1 = c1.root()
+ >>> t2 = transaction.TransactionManager()
+ >>> c2 = db.open(transaction_manager=t2)
+ >>> r2 = c2.root()
+
+Create a set of components registries in the database, alternating
+connections.
+
+.. doctest::
+
+ >>> from zope.component.persistentregistry import PersistentComponents
+ >>> from zope.component.tests.examples import I1
+ >>> from zope.component.tests.examples import I2
+ >>> from zope.component.tests.examples import U
+ >>> from zope.component.tests.examples import U1
+ >>> from zope.component.tests.examples import U12
+ >>> from zope.component.tests.examples import handle1
+ >>> from zope.component.tests.examples import handle2
+ >>> from zope.component.tests.examples import handle3
+ >>> from zope.component.tests.examples import handle4
+
+ >>> _ = t1.begin()
+ >>> r1[1] = PersistentComponents('1')
+ >>> t1.commit()
+
+ >>> _ = t2.begin()
+ >>> r2[2] = PersistentComponents('2', (r2[1], ))
+ >>> t2.commit()
+
+ >>> _ = t1.begin()
+ >>> r1[3] = PersistentComponents('3', (r1[1], ))
+ >>> t1.commit()
+
+ >>> _ = t2.begin()
+ >>> r2[4] = PersistentComponents('4', (r2[2], r2[3]))
+ >>> t2.commit()
+
+ >>> _ = t1.begin()
+ >>> r1[1].__bases__
+ ()
+ >>> r1[2].__bases__ == (r1[1], )
+ True
+
+ >>> r1[1].registerUtility(U1(1))
+ >>> r1[1].queryUtility(I1)
+ U1(1)
+ >>> r1[2].queryUtility(I1)
+ U1(1)
+ >>> t1.commit()
+
+ >>> _ = t2.begin()
+ >>> r2[1].registerUtility(U1(2))
+ >>> r2[2].queryUtility(I1)
+ U1(2)
+
+ >>> r2[4].queryUtility(I1)
+ U1(2)
+ >>> t2.commit()
+
+
+ >>> _ = t1.begin()
+ >>> r1[1].registerUtility(U12(1), I2)
+ >>> r1[4].queryUtility(I2)
+ U12(1)
+ >>> t1.commit()
+
+
+ >>> _ = t2.begin()
+ >>> r2[3].registerUtility(U12(3), I2)
+ >>> r2[4].queryUtility(I2)
+ U12(3)
+ >>> t2.commit()
+
+ >>> _ = t1.begin()
+
+ >>> r1[1].registerHandler(handle1, info="First handler")
+ >>> r1[2].registerHandler(handle2, required=[U])
+
+ >>> r1[3].registerHandler(handle3)
+
+ >>> r1[4].registerHandler(handle4)
+
+ >>> r1[4].handle(U1(1))
+ handle1 U1(1)
+ handle3 U1(1)
+ handle2 (U1(1),)
+ handle4 U1(1)
+
+ >>> t1.commit()
+
+ >>> _ = t2.begin()
+ >>> r2[4].handle(U1(1))
+ handle1 U1(1)
+ handle3 U1(1)
+ handle2 (U1(1),)
+ handle4 U1(1)
+ >>> t2.abort()
+
+ >>> db.close()
+
+Subscription to Events in Persistent Registries
+-----------------------------------------------
+
+.. doctest::
+
+ >>> import ZODB.tests.util
+ >>> db = ZODB.tests.util.DB()
+ >>> import transaction
+ >>> t1 = transaction.TransactionManager()
+ >>> c1 = db.open(transaction_manager=t1)
+ >>> r1 = c1.root()
+ >>> t2 = transaction.TransactionManager()
+ >>> c2 = db.open(transaction_manager=t2)
+ >>> r2 = c2.root()
+
+ >>> from zope.component.persistentregistry import PersistentComponents
+
+ >>> _ = t1.begin()
+ >>> r1[1] = PersistentComponents('1')
+ >>> r1[1].registerHandler(handle1)
+ >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2)
+ >>> _ = r1[1].unregisterHandler(handle1)
+ >>> _ = r1[1].unregisterSubscriptionAdapter(handle1, provided=I2)
+ >>> t1.commit()
+ >>> _ = t1.begin()
+ >>> r1[1].registerHandler(handle1)
+ >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2)
+ >>> t1.commit()
+
+ >>> _ = t2.begin()
+ >>> len(list(r2[1].registeredHandlers()))
+ 1
+ >>> len(list(r2[1].registeredSubscriptionAdapters()))
+ 1
+ >>> t2.abort()
+
+Adapter Registrations after Serialization / Deserialization
+-----------------------------------------------------------
+
+We want to make sure that we see updates corrextly.
+
+.. doctest::
+
+ >>> import persistent
+ >>> import transaction
+ >>> from zope.interface import Interface
+ >>> from zope.interface import implements
+ >>> class IFoo(Interface):
+ ... pass
+ >>> class Foo(persistent.Persistent):
+ ... implements(IFoo)
+ ... name = ''
+ ... def __init__(self, name=''):
+ ... self.name = name
+ ... def __repr__(self):
+ ... return 'Foo(%r)' % self.name
+
+ >>> from zope.component.tests.examples import base
+ >>> from zope.component.tests.examples import clear_base
+ >>> len(base._v_subregistries)
+ 0
+
+ >>> import ZODB.tests.util
+ >>> db = ZODB.tests.util.DB()
+ >>> tm1 = transaction.TransactionManager()
+ >>> c1 = db.open(transaction_manager=tm1)
+ >>> from zope.component.persistentregistry import PersistentAdapterRegistry
+ >>> r1 = PersistentAdapterRegistry((base,))
+ >>> r2 = PersistentAdapterRegistry((r1,))
+ >>> c1.root()[1] = r1
+ >>> c1.root()[2] = r2
+ >>> tm1.commit()
+ >>> r1._p_deactivate()
+
+ >>> len(base._v_subregistries)
+ 0
+
+ >>> tm2 = transaction.TransactionManager()
+ >>> c2 = db.open(transaction_manager=tm2)
+ >>> r1 = c2.root()[1]
+ >>> r2 = c2.root()[2]
+
+ >>> r1.lookup((), IFoo, '')
+
+ >>> base.register((), IFoo, '', Foo(''))
+ >>> r1.lookup((), IFoo, '')
+ Foo('')
+
+ >>> r2.lookup((), IFoo, '1')
+
+ >>> r1.register((), IFoo, '1', Foo('1'))
+
+ >>> r2.lookup((), IFoo, '1')
+ Foo('1')
+
+ >>> r1.lookup((), IFoo, '2')
+ >>> r2.lookup((), IFoo, '2')
+
+ >>> base.register((), IFoo, '2', Foo('2'))
+
+ >>> r1.lookup((), IFoo, '2')
+ Foo('2')
+
+ >>> r2.lookup((), IFoo, '2')
+ Foo('2')
+
+ >>> db.close()
+ >>> clear_base()
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
diff --git a/docs/api/security.rst b/docs/api/security.rst
new file mode 100644
index 0000000..6489799
--- /dev/null
+++ b/docs/api/security.rst
@@ -0,0 +1,96 @@
+Security APIs
+=============
+
+.. automodule:: zope.component.security
+
+ .. autofunction:: securityAdapterFactory
+
+If a permission is provided when wrapping the adapter, it will be
+wrapped in a LocatingAdapterFactory.
+
+.. doctest::
+
+ >>> class Factory(object):
+ ... pass
+
+If both locate and trusted are False and a non-public
+permission is provided, then the factory is wrapped into a
+LocatingUntrustedAdapterFactory:
+
+.. doctest::
+
+ >>> from zope.component.security import securityAdapterFactory
+ >>> from zope.security.adapter import LocatingUntrustedAdapterFactory
+ >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission',
+ ... locate=False, trusted=False)
+ >>> isinstance(factory, LocatingUntrustedAdapterFactory)
+ True
+
+If a PublicPermission is provided, then the factory is not touched.
+
+.. doctest::
+
+ >>> from zope.component.security import PublicPermission
+ >>> factory = securityAdapterFactory(Factory, PublicPermission,
+ ... locate=False, trusted=False)
+ >>> factory is Factory
+ True
+
+Same for CheckerPublic:
+
+.. doctest::
+
+ >>> from zope.security.checker import CheckerPublic
+ >>> factory = securityAdapterFactory(Factory, CheckerPublic,
+ ... locate=False, trusted=False)
+ >>> factory is Factory
+ True
+
+If the permission is None, the factory isn't touched:
+
+.. doctest::
+
+ >>> factory = securityAdapterFactory(Factory, None,
+ ... locate=False, trusted=False)
+ >>> factory is Factory
+ True
+
+If the factory is trusted and a no permission is provided then the
+adapter is wrapped into a TrustedAdapterFactory:
+
+.. doctest::
+
+ >>> from zope.security.adapter import TrustedAdapterFactory
+ >>> factory = securityAdapterFactory(Factory, None,
+ ... locate=False, trusted=True)
+ >>> isinstance(factory, TrustedAdapterFactory)
+ True
+
+Same for PublicPermission:
+
+.. doctest::
+
+ >>> factory = securityAdapterFactory(Factory, PublicPermission,
+ ... locate=False, trusted=True)
+ >>> isinstance(factory, TrustedAdapterFactory)
+ True
+
+Same for CheckerPublic:
+
+.. doctest::
+
+ >>> factory = securityAdapterFactory(Factory, CheckerPublic,
+ ... locate=False, trusted=True)
+ >>> isinstance(factory, TrustedAdapterFactory)
+ True
+
+If the factory is trusted and a locate is true, then the
+adapter is wrapped into a LocatingTrustedAdapterFactory:
+
+.. doctest::
+
+ >>> from zope.security.adapter import LocatingTrustedAdapterFactory
+ >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission',
+ ... locate=True, trusted=True)
+ >>> isinstance(factory, LocatingTrustedAdapterFactory)
+ True
diff --git a/docs/api/sitemanager.rst b/docs/api/sitemanager.rst
new file mode 100644
index 0000000..6e683ec
--- /dev/null
+++ b/docs/api/sitemanager.rst
@@ -0,0 +1,88 @@
+Site Manager APIs
+=================
+
+.. autofunction:: zope.component.getGlobalSiteManager
+
+ The API returns the module-scope global registry:
+
+ .. doctest::
+
+ >>> from zope.component.interfaces import IComponentLookup
+ >>> from zope.component.globalregistry import base
+ >>> from zope.component import getGlobalSiteManager
+ >>> gsm = getGlobalSiteManager()
+ >>> gsm is base
+ True
+
+ The registry implements the
+ :class:`~zope.component.interfaces.IComponentLookup` interface:
+
+ .. doctest::
+
+ >>> IComponentLookup.providedBy(gsm)
+ True
+
+ The same registry is returned each time we call the function:
+
+ .. doctest::
+
+ >>> getGlobalSiteManager() is gsm
+ True
+
+.. autofunction:: zope.component.getSiteManager(context=None)
+
+ We don't know anything about the default service manager, except that it
+ is an `IComponentLookup`.
+
+ .. doctest::
+
+ >>> from zope.component import getSiteManager
+ >>> from zope.component.interfaces import IComponentLookup
+ >>> IComponentLookup.providedBy(getSiteManager())
+ True
+
+ Calling `getSiteManager()` with no args is equivalent to calling it with a
+ context of `None`.
+
+ .. doctest::
+
+ >>> getSiteManager() is getSiteManager(None)
+ True
+
+ If the context passed to `getSiteManager()` is not `None`, it is
+ adapted to `IComponentLookup` and this adapter returned. So, we
+ create a context that can be adapted to `IComponentLookup` using
+ the `__conform__` API.
+
+ Let's create the simplest stub-implementation of a site manager possible:
+
+ .. doctest::
+
+ >>> sitemanager = object()
+
+ Now create a context that knows how to adapt to our newly created site
+ manager.
+
+ .. doctest::
+
+ >>> from zope.component.tests.examples import ConformsToIComponentLookup
+ >>> context = ConformsToIComponentLookup(sitemanager)
+
+ Now make sure that the `getSiteManager()` API call returns the correct
+ site manager.
+
+ .. doctest::
+
+ >>> getSiteManager(context) is sitemanager
+ True
+
+ Using a context that is not adaptable to `IComponentLookup` should fail.
+
+ .. doctest::
+
+ >>> getSiteManager(sitemanager)
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: ('Could not adapt', <instance Ob>,
+ <InterfaceClass zope...interfaces.IComponentLookup>)
+
diff --git a/docs/api/utility.rst b/docs/api/utility.rst
new file mode 100644
index 0000000..0d7ba1c
--- /dev/null
+++ b/docs/api/utility.rst
@@ -0,0 +1,290 @@
+Utility Registration APIs
+=========================
+
+.. autofunction:: zope.component.getUtility
+
+.. autofunction:: zope.component.queryUtility
+
+Utilities are components that simply provide an interface. They are
+instantiated at the time or before they are registered. Here we test the
+simple query interface.
+
+Before we register any utility, there is no utility available, of
+course. The pure instatiation of an object does not make it a utility. If
+you do not specify a default, you get a `ComponentLookupError`.
+
+.. testsetup::
+
+ from zope.component.testing import setUp
+ setUp()
+
+.. doctest::
+
+ >>> from zope.component import getUtility
+ >>> from zope.component import queryUtility
+ >>> from zope.component.tests.examples import I1
+ >>> getUtility(I1) #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: \
+ (<InterfaceClass zope.component.tests.examples.I1>, '')
+
+Otherwise, you get the default:
+
+.. doctest::
+
+ >>> queryUtility(I1, default='<default>')
+ '<default>'
+
+Now we declare `ob` to be the utility providing `I1`:
+
+.. doctest::
+
+ >>> ob = object()
+ >>> from zope.component import getGlobalSiteManager
+ >>> getGlobalSiteManager().registerUtility(ob, I1)
+
+Now the component is available:
+
+.. doctest::
+
+ >>> getUtility(I1) is ob
+ True
+ >>> queryUtility(I1) is ob
+ True
+
+
+
+Named Utilities
+---------------
+
+Registering a utility without a name does not mean that it is available
+when looking for the utility with a name:
+
+.. doctest::
+
+ >>> getUtility(I1, name='foo')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError:
+ (<InterfaceClass zope.component.tests.examples.I1>, 'foo')
+
+ >>> queryUtility(I1, name='foo', default='<default>')
+ '<default>'
+
+Registering the utility under the correct name makes it available:
+
+.. doctest::
+
+ >>> ob2 = object()
+ >>> getGlobalSiteManager().registerUtility(ob2, I1, name='foo')
+ >>> getUtility(I1, 'foo') is ob2
+ True
+ >>> queryUtility(I1, 'foo') is ob2
+ True
+
+Querying Multiple Utilities
+---------------------------
+
+.. autofunction:: zope.component.getUtilitiesFor
+
+.. autofunction:: zope.component.getAllUtilitiesRegisteredFor
+
+Sometimes it may be useful to query all utilities, both anonymous and named
+for a given interface. The :func:`~zope.component.getUtilitiesFor` API
+returns a sequence of ``(name, utility)`` tuples, where ``name`` is the
+empty string for the anonymous utility:
+
+.. doctest::
+
+ >>> from zope.component import getUtilitiesFor
+ >>> tuples = list(getUtilitiesFor(I1))
+ >>> len(tuples)
+ 2
+ >>> ('', ob) in tuples
+ True
+ >>> ('foo', ob2) in tuples
+ True
+
+The :func:`~zope.component.getAllUtilitiesRegisteredFor` API returns
+utilities that have been registered for a particular interface. Utilities
+providing a derived interface are also listed.
+
+.. doctest::
+
+ >>> from zope.interface import implementer
+ >>> from zope.component.tests.examples import Comp
+ >>> from zope.component.tests.examples import I2
+ >>> from zope.component.tests.examples import Ob
+ >>> class I11(I1):
+ ... pass
+
+ >>> @implementer(I11)
+ ... class Ob11(Ob):
+ ... pass
+
+ >>> ob11 = Ob11()
+ >>> ob_bob = Ob()
+
+Now we register the new utilities:
+
+.. doctest::
+
+ >>> from zope.component import getGlobalSiteManager
+ >>> gsm = getGlobalSiteManager()
+ >>> gsm.registerUtility(ob, I1)
+ >>> gsm.registerUtility(ob11, I11)
+ >>> gsm.registerUtility(ob_bob, I1, name='bob')
+ >>> gsm.registerUtility(Comp(2), I2)
+
+We can now get all the utilities that provide interface `I1`:
+
+.. doctest::
+
+ >>> from zope.component import getAllUtilitiesRegisteredFor
+ >>> uts = list(getAllUtilitiesRegisteredFor(I1))
+ >>> len(uts)
+ 4
+ >>> ob in uts
+ True
+ >>> ob2 in uts
+ True
+ >>> ob_bob in uts
+ True
+ >>> ob11 in uts
+ True
+
+Note that `getAllUtilitiesRegisteredFor()` does not return the names of
+the utilities.
+
+
+Delegated Utility Lookup
+------------------------
+
+.. autofunction:: zope.component.getNextUtility
+
+.. autofunction:: zope.component.queryNextUtility
+
+It is common for a utility to delegate its answer to a utility
+providing the same interface in one of the component registry's
+bases. Let's first create a global utility:
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.interface import implementer
+ >>> class IMyUtility(Interface):
+ ... pass
+
+ >>> from zope.component.tests.examples import ConformsToIComponentLookup
+ >>> @implementer(IMyUtility)
+ ... class MyUtility(ConformsToIComponentLookup):
+ ... def __init__(self, id, sm):
+ ... self.id = id
+ ... self.sitemanager = sm
+ ... def __repr__(self):
+ ... return "%s('%s')" % (self.__class__.__name__, self.id)
+
+ >>> gutil = MyUtility('global', gsm)
+ >>> gsm.registerUtility(gutil, IMyUtility, 'myutil')
+
+Now, let's create two registries and set up the bases hierarchy:
+
+.. doctest::
+
+ >>> from zope.interface.registry import Components
+ >>> sm1 = Components('sm1', bases=(gsm, ))
+ >>> sm1_1 = Components('sm1_1', bases=(sm1, ))
+
+Now we create two utilities and insert them in our folder hierarchy:
+
+.. doctest::
+
+ >>> from zope.component.interfaces import IComponentLookup
+ >>> util1 = MyUtility('one', sm1)
+ >>> sm1.registerUtility(util1, IMyUtility, 'myutil')
+ >>> IComponentLookup(util1) is sm1
+ True
+
+ >>> util1_1 = MyUtility('one-one', sm1_1)
+ >>> sm1_1.registerUtility(util1_1, IMyUtility, 'myutil')
+ >>> IComponentLookup(util1_1) is sm1_1
+ True
+
+Now, if we ask `util1_1` for its next available utility we get the
+``one`` utility:
+
+.. doctest::
+
+ >>> from zope.component import getNextUtility
+ >>> getNextUtility(util1_1, IMyUtility, 'myutil')
+ MyUtility('one')
+
+Next we ask `util1` for its next utility and we should get the global version:
+
+.. doctest::
+
+ >>> getNextUtility(util1, IMyUtility, 'myutil')
+ MyUtility('global')
+
+However, if we ask the global utility for the next one, an error is raised
+
+.. doctest::
+
+ >>> getNextUtility(gutil, IMyUtility,
+ ... 'myutil') #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError:
+ No more utilities for <InterfaceClass zope.component.tests.examples.IMyUtility>,
+ 'myutil' have been found.
+
+You can also use `queryNextUtility` and specify a default:
+
+.. doctest::
+
+ >>> from zope.component import queryNextUtility
+ >>> queryNextUtility(gutil, IMyUtility, 'myutil', 'default')
+ 'default'
+
+Let's now ensure that the function also works with multiple registries. First
+we create another base registry:
+
+.. doctest::
+
+ >>> myregistry = Components()
+
+We now set up another utility into that registry:
+
+.. doctest::
+
+ >>> custom_util = MyUtility('my_custom_util', myregistry)
+ >>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util')
+
+We add it as a base to the local site manager:
+
+.. doctest::
+
+ >>> sm1.__bases__ = (myregistry,) + sm1.__bases__
+
+Both the ``myregistry`` and global utilities should be available:
+
+.. doctest::
+
+ >>> queryNextUtility(sm1, IMyUtility, 'my_custom_util')
+ MyUtility('my_custom_util')
+ >>> queryNextUtility(sm1, IMyUtility, 'myutil')
+ MyUtility('global')
+
+Note, if the context cannot be converted to a site manager, the default is
+retruned:
+
+.. doctest::
+
+ >>> queryNextUtility(object(), IMyUtility, 'myutil', 'default')
+ 'default'
+
+.. testcleanup::
+
+ from zope.component.testing import tearDown
+ tearDown()
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..ff6a13b
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+#
+# zope.configuration documentation build configuration file, created by
+# sphinx-quickstart on Thu May 10 21:03:35 2012.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.viewcode',
+ 'repoze.sphinx.autointerface',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'zope.configuration'
+copyright = u'2012, Zope Foundation Contributors'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '4.0'
+# The full version, including alpha/beta/rc tags.
+release = '4.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'zopeconfigurationdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'zopeconfiguration.tex', u'zope.configuration Documentation',
+ u'Zope Foundation Contributors', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'zopeconfiguration', u'zope.configuration Documentation',
+ [u'Zope Foundation Contributors'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'zopeconfiguration', u'zope.configuration Documentation',
+ u'Zope Foundation Contributors', 'zopeconfiguration', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
diff --git a/docs/configure.rst b/docs/configure.rst
new file mode 100644
index 0000000..2663693
--- /dev/null
+++ b/docs/configure.rst
@@ -0,0 +1,30 @@
+Package configuration
+=====================
+
+The ``zope.component`` package provides a ZCML file that configures some basic
+event handlers.
+
+.. doctest::
+
+ >>> from zope.configuration.xmlconfig import XMLConfig
+ >>> import zope.component
+ >>> from zope.component import event
+ >>> from zope.component import registry
+
+ >>> XMLConfig('configure.zcml', zope.component)()
+
+ >>> gsm = zope.component.getGlobalSiteManager()
+ >>> registered = list(gsm.registeredHandlers())
+ >>> len(registered)
+ 5
+ >>> handlers = [x.handler for x in registered]
+ >>> event.objectEventNotify in handlers
+ True
+ >>> registry.dispatchUtilityRegistrationEvent in handlers
+ True
+ >>> registry.dispatchAdapterRegistrationEvent in handlers
+ True
+ >>> registry.dispatchSubscriptionAdapterRegistrationEvent in handlers
+ True
+ >>> registry.dispatchHandlerRegistrationEvent in handlers
+ True
diff --git a/docs/event.rst b/docs/event.rst
new file mode 100644
index 0000000..bc10e8d
--- /dev/null
+++ b/docs/event.rst
@@ -0,0 +1,176 @@
+Events
+======
+
+The Component Architecture provides a way to dispatch events to event
+handlers. Event handlers are registered as *subscribers*
+a.k.a. *handlers*.
+
+Before we can start we need to import ``zope.component.event`` to make
+the dispatching effective:
+
+.. doctest::
+
+ >>> import zope.component.event
+
+Consider two event classes:
+
+.. doctest::
+
+ >>> class Event1(object):
+ ... pass
+
+ >>> class Event2(Event1):
+ ... pass
+
+Now consider two handlers for these event classes:
+
+.. doctest::
+
+ >>> called = []
+
+ >>> import zope.component
+ >>> @zope.component.adapter(Event1)
+ ... def handler1(event):
+ ... called.append(1)
+
+ >>> @zope.component.adapter(Event2)
+ ... def handler2(event):
+ ... called.append(2)
+
+We can register them with the Component Architecture:
+
+.. doctest::
+
+ >>> zope.component.provideHandler(handler1)
+ >>> zope.component.provideHandler(handler2)
+
+Now let's go through the events. We'll see that the handlers have been
+called accordingly:
+
+.. doctest::
+
+ >>> from zope.event import notify
+ >>> notify(Event1())
+ >>> called
+ [1]
+
+ >>> del called[:]
+ >>> notify(Event2())
+ >>> called.sort()
+ >>> called
+ [1, 2]
+
+
+
+Object events
+-------------
+
+
+The ``objectEventNotify`` function is a subscriber to dispatch
+ObjectEvents to interested adapters.
+
+First create an object class:
+
+.. doctest::
+
+ >>> class IUseless(zope.interface.Interface):
+ ... """Useless object"""
+
+ >>> class UselessObject(object):
+ ... """Useless object"""
+ ... zope.interface.implements(IUseless)
+
+Then create an event class:
+
+.. doctest::
+
+ >>> class IObjectThrownEvent(zope.component.interfaces.IObjectEvent):
+ ... """An object has been thrown away"""
+
+ >>> class ObjectThrownEvent(zope.component.interfaces.ObjectEvent):
+ ... """An object has been thrown away"""
+ ... zope.interface.implements(IObjectThrownEvent)
+
+Create an object and an event:
+
+.. doctest::
+
+ >>> hammer = UselessObject()
+ >>> event = ObjectThrownEvent(hammer)
+
+Then notify the event to the subscribers.
+Since the subscribers list is empty, nothing happens.
+
+.. doctest::
+
+ >>> zope.component.event.objectEventNotify(event)
+
+Now create an handler for the event:
+
+.. doctest::
+
+ >>> events = []
+ >>> def record(*args): #*
+ ... events.append(args)
+
+ >>> zope.component.provideHandler(record, [IUseless, IObjectThrownEvent])
+
+The event is notified to the subscriber:
+
+.. doctest::
+
+ >>> zope.component.event.objectEventNotify(event)
+ >>> events == [(hammer, event)]
+ True
+
+Following test demonstrates how a subscriber can raise an exception
+to prevent an action.
+
+.. doctest::
+
+ >>> zope.component.provideHandler(zope.component.event.objectEventNotify)
+
+Let's create a container:
+
+.. doctest::
+
+ >>> class ToolBox(dict):
+ ... def __delitem__(self, key):
+ ... notify(ObjectThrownEvent(self[key]))
+ ... return super(ToolBox,self).__delitem__(key)
+
+ >>> container = ToolBox()
+
+And put the object into the container:
+
+.. doctest::
+
+ >>> container['Red Hammer'] = hammer
+
+Create an handler function that will raise an error when called:
+
+.. doctest::
+
+ >>> class Veto(Exception):
+ ... pass
+
+ >>> def callback(item, event):
+ ... assert(item == event.object)
+ ... raise Veto
+
+Register the handler:
+
+.. doctest::
+
+ >>> zope.component.provideHandler(callback, [IUseless, IObjectThrownEvent])
+
+Then if we try to remove the object, an ObjectThrownEvent is fired:
+
+.. doctest::
+
+ >>> del container['Red Hammer']
+ ... # doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ raise Veto
+ Veto
diff --git a/docs/factory.rst b/docs/factory.rst
new file mode 100644
index 0000000..a8dff4f
--- /dev/null
+++ b/docs/factory.rst
@@ -0,0 +1,153 @@
+Factories
+=========
+
+
+The Factory Class
+-----------------
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> class IFunction(Interface):
+ ... pass
+
+ >>> class IKlass(Interface):
+ ... pass
+
+ >>> from zope.interface import implements
+ >>> class Klass(object):
+ ... implements(IKlass)
+ ...
+ ... def __init__(self, *args, **kw): #*
+ ... self.args = args
+ ... self.kw = kw
+
+ >>> from zope.component.factory import Factory
+ >>> factory = Factory(Klass, 'Klass', 'Klassier')
+ >>> factory2 = Factory(lambda x: x, 'Func', 'Function')
+ >>> factory3 = Factory(lambda x: x, 'Func', 'Function', (IFunction,))
+
+Calling a Factory
+~~~~~~~~~~~~~~~~~
+
+Here we test whether the factory correctly creates the objects and
+including the correct handling of constructor elements.
+
+First we create a factory that creates instanace of the `Klass` class:
+
+.. doctest::
+
+ >>> factory = Factory(Klass, 'Klass', 'Klassier')
+
+Now we use the factory to create the instance
+
+.. doctest::
+
+ >>> kl = factory(1, 2, foo=3, bar=4)
+
+and make sure that the correct class was used to create the object:
+
+.. doctest::
+
+ >>> kl.__class__
+ <class 'Klass'>
+
+Since we passed in a couple positional and keyword arguments
+
+.. doctest::
+
+ >>> kl.args
+ (1, 2)
+ >>> kl.kw
+ {'foo': 3, 'bar': 4}
+
+ >>> factory2(3)
+ 3
+ >>> factory3(3)
+ 3
+
+
+Title and Description
+~~~~~~~~~~~~~~~~~~~~~
+
+.. doctest::
+
+ >>> factory.title
+ 'Klass'
+ >>> factory.description
+ 'Klassier'
+ >>> factory2.title
+ 'Func'
+ >>> factory2.description
+ 'Function'
+ >>> factory3.title
+ 'Func'
+ >>> factory3.description
+ 'Function'
+
+
+Provided Interfaces
+~~~~~~~~~~~~~~~~~~~
+
+.. doctest::
+
+ >>> implemented = factory.getInterfaces()
+ >>> implemented.isOrExtends(IKlass)
+ True
+ >>> list(implemented)
+ [<InterfaceClass __builtin__.IKlass>]
+
+ >>> implemented2 = factory2.getInterfaces()
+ >>> list(implemented2)
+ []
+
+ >>> implemented3 = factory3.getInterfaces()
+ >>> list(implemented3)
+ [<InterfaceClass __builtin__.IFunction>]
+
+
+The Component Architecture Factory API
+--------------------------------------
+
+.. doctest::
+
+ >>> import zope.component
+ >>> factory = Factory(Klass, 'Klass', 'Klassier')
+ >>> gsm = zope.component.getGlobalSiteManager()
+
+ >>> from zope.component.interfaces import IFactory
+ >>> gsm.registerUtility(factory, IFactory, 'klass')
+
+Creating an Object
+~~~~~~~~~~~~~~~~~~
+
+.. doctest::
+
+ >>> kl = zope.component.createObject('klass', 1, 2, foo=3, bar=4)
+ >>> isinstance(kl, Klass)
+ True
+ >>> kl.args
+ (1, 2)
+ >>> kl.kw
+ {'foo': 3, 'bar': 4}
+
+Accessing Provided Interfaces
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. doctest::
+
+ >>> implemented = zope.component.getFactoryInterfaces('klass')
+ >>> implemented.isOrExtends(IKlass)
+ True
+ >>> [iface for iface in implemented]
+ [<InterfaceClass __builtin__.IKlass>]
+
+List of All Factories
+~~~~~~~~~~~~~~~~~~~~~
+
+.. doctest::
+
+ >>> [(name, fac.__class__) for name, fac in
+ ... zope.component.getFactoriesFor(IKlass)]
+ [(u'klass', <class 'zope.component.factory.Factory'>)]
+
diff --git a/docs/hooks.rst b/docs/hooks.rst
new file mode 100644
index 0000000..76fc6fd
--- /dev/null
+++ b/docs/hooks.rst
@@ -0,0 +1,112 @@
+==============================
+The current component registry
+==============================
+
+There can be any number of component registries in an application. One of them
+is the global component registry, and there is also the concept of a currently
+used component registry. Component registries other than the global one are
+associated with objects called sites. The ``zope.component.hooks`` module
+provides an API to set and access the current site as well as manipulate the
+adapter hook associated with it.
+
+As long as we haven't set a site, none is being considered current:
+
+.. doctest::
+
+ >>> from zope.component.hooks import getSite
+ >>> print getSite()
+ None
+
+We can also ask for the current component registry (aka site manager
+historically); it will return the global one if no current site is set:
+
+.. doctest::
+
+ >>> from zope.component.hooks import getSiteManager
+ >>> getSiteManager()
+ <BaseGlobalComponents base>
+
+Let's set a site now. A site has to be an object that provides the
+``getSiteManager`` method, which is specified by
+``zope.component.interfaces.IPossibleSite``:
+
+.. doctest::
+
+ >>> from zope.interface.registry import Components
+ >>> class Site(object):
+ ... def __init__(self):
+ ... self.registry = Components('components')
+ ... def getSiteManager(self):
+ ... return self.registry
+
+ >>> from zope.component.hooks import setSite
+ >>> site1 = Site()
+ >>> setSite(site1)
+
+After this, the newly set site is considered the currently active one:
+
+.. doctest::
+
+ >>> getSite() is site1
+ True
+ >>> getSiteManager() is site1.registry
+ True
+
+If we set another site, that one will be considered current:
+
+.. doctest::
+
+ >>> site2 = Site()
+ >>> site2.registry is not site1.registry
+ True
+ >>> setSite(site2)
+
+ >>> getSite() is site2
+ True
+ >>> getSiteManager() is site2.registry
+ True
+
+Finally we can unset the site and the global component registry is used again:
+
+.. doctest::
+
+ >>> setSite()
+ >>> print getSite()
+ None
+ >>> getSiteManager()
+ <BaseGlobalComponents base>
+
+
+Context manager
+===============
+
+There also is a context manager for setting the site, which is especially
+useful when writing tests:
+
+.. doctest::
+
+ >>> import zope.component.hooks
+ >>> print getSite()
+ None
+ >>> with zope.component.hooks.site(site2):
+ ... getSite() is site2
+ True
+ >>> print getSite()
+ None
+
+The site is properly restored even if the body of the with statement
+raises an exception:
+
+.. doctest::
+
+ >>> print getSite()
+ None
+ >>> with zope.component.hooks.site(site2):
+ ... getSite() is site2
+ ... raise ValueError('An error in the body')
+ Traceback (most recent call last):
+ ...
+ ValueError: An error in the body
+ >>> print getSite()
+ None
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..7524c56
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,26 @@
+:mod:`zope.component`
+=====================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ narr
+ socketexample
+ event
+ factory
+ persistentregistry
+ zcml
+ configure
+ hooks
+ testlayer
+
+ api
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..14b101e
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopeconfiguration.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopeconfiguration.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
diff --git a/docs/narr.rst b/docs/narr.rst
new file mode 100644
index 0000000..ddc1de9
--- /dev/null
+++ b/docs/narr.rst
@@ -0,0 +1,449 @@
+Zope Component Architecture
+===========================
+
+This package, together with `zope.interface`, provides facilities for
+defining, registering and looking up components. There are two basic
+kinds of components: adapters and utilities.
+
+Utilities
+---------
+
+Utilities are just components that provide an interface and that are
+looked up by an interface and a name. Let's look at a trivial utility
+definition:
+
+.. doctest::
+
+ >>> from zope import interface
+
+ >>> class IGreeter(interface.Interface):
+ ... def greet():
+ ... "say hello"
+
+ >>> class Greeter:
+ ... interface.implements(IGreeter)
+ ...
+ ... def __init__(self, other="world"):
+ ... self.other = other
+ ...
+ ... def greet(self):
+ ... print "Hello", self.other
+
+We can register an instance this class using `provideUtility` [1]_:
+
+.. doctest::
+
+ >>> from zope import component
+ >>> greet = Greeter('bob')
+ >>> component.provideUtility(greet, IGreeter, 'robert')
+
+In this example we registered the utility as providing the `IGreeter`
+interface with a name of 'bob'. We can look the interface up with
+either `queryUtility` or `getUtility`:
+
+.. doctest::
+
+ >>> component.queryUtility(IGreeter, 'robert').greet()
+ Hello bob
+
+ >>> component.getUtility(IGreeter, 'robert').greet()
+ Hello bob
+
+`queryUtility` and `getUtility` differ in how failed lookups are handled:
+
+.. doctest::
+
+ >>> component.queryUtility(IGreeter, 'ted')
+ >>> component.queryUtility(IGreeter, 'ted', 42)
+ 42
+ >>> component.getUtility(IGreeter, 'ted')
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass ...IGreeter>, 'ted')
+
+If a component provides only one interface, as in the example above,
+then we can omit the provided interface from the call to `provideUtility`:
+
+.. doctest::
+
+ >>> ted = Greeter('ted')
+ >>> component.provideUtility(ted, name='ted')
+ >>> component.queryUtility(IGreeter, 'ted').greet()
+ Hello ted
+
+The name defaults to an empty string:
+
+.. doctest::
+
+ >>> world = Greeter()
+ >>> component.provideUtility(world)
+ >>> component.queryUtility(IGreeter).greet()
+ Hello world
+
+Adapters
+--------
+
+Adapters are components that are computed from other components to
+adapt them to some interface. Because they are computed from other
+objects, they are provided as factories, usually classes. Here, we'll
+create a greeter for persons, so we can provide personalized greetings
+for different people:
+
+.. doctest::
+
+ >>> class IPerson(interface.Interface):
+ ... name = interface.Attribute("Name")
+
+ >>> class PersonGreeter:
+ ...
+ ... component.adapts(IPerson)
+ ... interface.implements(IGreeter)
+ ...
+ ... def __init__(self, person):
+ ... self.person = person
+ ...
+ ... def greet(self):
+ ... print "Hello", self.person.name
+
+The class defines a constructor that takes an argument for every
+object adapted.
+
+We used `component.adapts` to declare what we adapt. We can find
+out if an object declares that it adapts anything using adaptedBy:
+
+.. doctest::
+
+ >>> list(component.adaptedBy(PersonGreeter)) == [IPerson]
+ True
+
+If an object makes no declaration, then None is returned:
+
+.. doctest::
+
+ >>> component.adaptedBy(Greeter()) is None
+ True
+
+
+If we declare the interfaces adapted and if we provide only one
+interface, as in the example above, then we can provide the adapter
+very simply [1]_:
+
+.. doctest::
+
+ >>> component.provideAdapter(PersonGreeter)
+
+For adapters that adapt a single interface to a single interface
+without a name, we can get the adapter by simply calling the
+interface:
+
+.. doctest::
+
+ >>> class Person:
+ ... interface.implements(IPerson)
+ ...
+ ... def __init__(self, name):
+ ... self.name = name
+
+ >>> IGreeter(Person("Sally")).greet()
+ Hello Sally
+
+We can also provide arguments to be very specific about what
+how to register the adapter.
+
+.. doctest::
+
+ >>> class BobPersonGreeter(PersonGreeter):
+ ... name = 'Bob'
+ ... def greet(self):
+ ... print "Hello", self.person.name, "my name is", self.name
+
+ >>> component.provideAdapter(
+ ... BobPersonGreeter, [IPerson], IGreeter, 'bob')
+
+The arguments can also be provided as keyword arguments:
+
+.. doctest::
+
+ >>> class TedPersonGreeter(BobPersonGreeter):
+ ... name = "Ted"
+
+ >>> component.provideAdapter(
+ ... factory=TedPersonGreeter, adapts=[IPerson],
+ ... provides=IGreeter, name='ted')
+
+For named adapters, use `queryAdapter`, or `getAdapter`:
+
+.. doctest::
+
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet()
+ Hello Sally my name is Bob
+
+ >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet()
+ Hello Sally my name is Ted
+
+If an adapter can't be found, `queryAdapter` returns a default value
+and `getAdapter` raises an error:
+
+.. doctest::
+
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank')
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42)
+ 42
+ >>> component.getAdapter(Person("Sally"), IGreeter, 'frank')
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (...Person...>, <...IGreeter>, 'frank')
+
+Adapters can adapt multiple objects:
+
+.. doctest::
+
+ >>> class TwoPersonGreeter:
+ ...
+ ... component.adapts(IPerson, IPerson)
+ ... interface.implements(IGreeter)
+ ...
+ ... def __init__(self, person, greeter):
+ ... self.person = person
+ ... self.greeter = greeter
+ ...
+ ... def greet(self):
+ ... print "Hello", self.person.name
+ ... print "my name is", self.greeter.name
+
+ >>> component.provideAdapter(TwoPersonGreeter)
+
+Note that the declaration-order of the Interfaces beeing adapted to is
+important for adapter look up. It must be the the same as the order of
+parameters given to the adapter and used to query the adapter. This is
+especially the case when different Interfaces are adapt to (opposed to
+this example).
+
+To look up a multi-adapter, use either `queryMultiAdapter` or
+`getMultiAdapter`:
+
+.. doctest::
+
+ >>> component.queryMultiAdapter((Person("Sally"), Person("Bob")),
+ ... IGreeter).greet()
+ Hello Sally
+ my name is Bob
+
+Adapters need not be classes. Any callable will do. We use the
+adapter decorator to declare that a callable object adapts some interfaces
+(or classes):
+
+.. doctest::
+
+ >>> class IJob(interface.Interface):
+ ... "A job"
+
+ >>> @interface.implementer(IJob)
+ ... class Job:
+ ... pass
+ >>> @interface.implementer(IJob)
+ ... @component.adapter(IPerson)
+ ... def personJob(person):
+ ... return getattr(person, 'job', None)
+
+
+In this example, the personJob function simply returns the person's
+`job` attribute if present, or None if it's not present. An adapter
+factory can return None to indicate that adaptation wasn't possible.
+Let's register this adapter and try it out:
+
+.. doctest::
+
+ >>> component.provideAdapter(personJob)
+ >>> sally = Person("Sally")
+ >>> IJob(sally) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', ...
+
+The adaptation failed because sally didn't have a job. Let's give her
+one:
+
+.. doctest::
+
+ >>> job = Job()
+ >>> sally.job = job
+ >>> IJob(sally) is job
+ True
+
+Subscription Adapters
+---------------------
+
+Unlike regular adapters, subscription adapters are used when we want
+all of the adapters that adapt an object to a particular adapter.
+
+Consider a validation problem. We have objects and we want to assess
+whether they meet some sort of standards. We define a validation
+interface:
+
+.. doctest::
+
+ >>> class IValidate(interface.Interface):
+ ... def validate(ob):
+ ... """Determine whether the object is valid
+ ...
+ ... Return a string describing a validation problem.
+ ... An empty string is returned to indicate that the
+ ... object is valid.
+ ... """
+
+Perhaps we have documents:
+
+.. doctest::
+
+ >>> class IDocument(interface.Interface):
+ ... summary = interface.Attribute("Document summary")
+ ... body = interface.Attribute("Document text")
+
+ >>> class Document:
+ ... interface.implements(IDocument)
+ ... def __init__(self, summary, body):
+ ... self.summary, self.body = summary, body
+
+Now, we may want to specify various validation rules for
+documents. For example, we might require that the summary be a single
+line:
+
+.. doctest::
+
+ >>> class SingleLineSummary:
+ ... component.adapts(IDocument)
+ ... interface.implements(IValidate)
+ ...
+ ... def __init__(self, doc):
+ ... self.doc = doc
+ ...
+ ... def validate(self):
+ ... if '\n' in self.doc.summary:
+ ... return 'Summary should only have one line'
+ ... else:
+ ... return ''
+
+Or we might require the body to be at least 1000 characters in length:
+
+.. doctest::
+
+ >>> class AdequateLength:
+ ... component.adapts(IDocument)
+ ... interface.implements(IValidate)
+ ...
+ ... def __init__(self, doc):
+ ... self.doc = doc
+ ...
+ ... def validate(self):
+ ... if len(self.doc.body) < 1000:
+ ... return 'too short'
+ ... else:
+ ... return ''
+
+We can register these as subscription adapters [1]_:
+
+.. doctest::
+
+ >>> component.provideSubscriptionAdapter(SingleLineSummary)
+ >>> component.provideSubscriptionAdapter(AdequateLength)
+
+We can then use the subscribers to validate objects:
+
+.. doctest::
+
+ >>> doc = Document("A\nDocument", "blah")
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['Summary should only have one line', 'too short']
+
+ >>> doc = Document("A\nDocument", "blah" * 1000)
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['Summary should only have one line']
+
+ >>> doc = Document("A Document", "blah")
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['too short']
+
+Handlers
+--------
+
+Handlers are subscription adapter factories that don't produce
+anything. They do all of their work when called. Handlers
+are typically used to handle events.
+
+Event subscribers are different from other subscription adapters in
+that the caller of event subscribers doesn't expect to interact with
+them in any direct way. For example, an event publisher doesn't
+expect to get any return value. Because subscribers don't need to
+provide an API to their callers, it is more natural to define them
+with functions, rather than classes. For example, in a
+document-management system, we might want to record creation times for
+documents:
+
+.. doctest::
+
+ >>> import datetime
+
+ >>> def documentCreated(event):
+ ... event.doc.created = datetime.datetime.utcnow()
+
+In this example, we have a function that takes an event and performs
+some processing. It doesn't actually return anything. This is a
+special case of a subscription adapter that adapts an event to
+nothing. All of the work is done when the adapter "factory" is
+called. We call subscribers that don't actually create anything
+"handlers". There are special APIs for registering and calling
+them.
+
+To register the subscriber above, we define a document-created event:
+
+.. doctest::
+
+ >>> class IDocumentCreated(interface.Interface):
+ ... doc = interface.Attribute("The document that was created")
+
+ >>> class DocumentCreated:
+ ... interface.implements(IDocumentCreated)
+ ...
+ ... def __init__(self, doc):
+ ... self.doc = doc
+
+We'll also change our handler definition to:
+
+.. doctest::
+
+ >>> @component.adapter(IDocumentCreated)
+ ... def documentCreated(event):
+ ... event.doc.created = datetime.datetime.utcnow()
+
+This marks the handler as an adapter of `IDocumentCreated` events.
+
+Now we'll register the handler [1]_:
+
+.. doctest::
+
+ >>> component.provideHandler(documentCreated)
+
+Now, if we can create an event and use the `handle` function to call
+handlers registered for the event:
+
+.. doctest::
+
+ >>> component.handle(DocumentCreated(doc))
+ >>> doc.created.__class__.__name__
+ 'datetime'
+
+
+
+.. [1] CAUTION: This API should only be used from test or
+ application-setup code. This API shouldn't be used by regular
+ library modules, as component registration is a configuration
+ activity.
diff --git a/docs/persistentregistry.rst b/docs/persistentregistry.rst
new file mode 100644
index 0000000..e7f5f65
--- /dev/null
+++ b/docs/persistentregistry.rst
@@ -0,0 +1,6 @@
+Persistent Component Management
+===============================
+
+Persistent component management allows persistent management of
+components. From a usage point of view, there shouldn't be any new
+behavior.
diff --git a/docs/socketexample.rst b/docs/socketexample.rst
new file mode 100644
index 0000000..ed4edea
--- /dev/null
+++ b/docs/socketexample.rst
@@ -0,0 +1,705 @@
+The Zope 3 Component Architecture (Socket Example)
+==================================================
+
+The component architecture provides an application framework that provides its
+functionality through loosely-connected components. A *component* can be any
+Python object and has a particular purpose associated with it. Thus, in a
+component-based applications you have many small component in contrast to
+classical object-oriented development, where you have a few big objects.
+
+Components communicate via specific APIs, which are formally defined by
+interfaces, which are provided by the `zope.interface` package. *Interfaces*
+describe the methods and properties that a component is expected to
+provide. They are also used as a primary mean to provide developer-level
+documentation for the components. For more details about interfaces see
+`zope/interface/README.txt`.
+
+The two main types of components are *adapters* and *utilities*. They will be
+discussed in detail later in this document. Both component types are managed
+by the *site manager*, with which you can register and access these
+components. However, most of the site manager's functionality is hidden behind
+the component architecture's public API, which is documented in
+`IComponentArchitecture`.
+
+
+Adapters
+--------
+
+Adapters are a well-established pattern. An *adapter* uses an object providing
+one interface to produce an object that provides another interface. Here an
+example: Imagine that you purchased an electric shaver in the US, and thus
+you require the US socket type. You are now traveling in Germany, where another
+socket style is used. You will need a device, an adapter, that converts from
+the German to the US socket style.
+
+The functionality of adapters is actually natively provided by the
+`zope.interface` package and is thus well documented there. The `human.txt`
+file provides a gentle introduction to adapters, whereby `adapter.txt` is
+aimed at providing a comprehensive insight into adapters, but is too abstract
+for many as an initial read. Thus, we will only explain adapters in the context
+of the component architecture's API.
+
+So let's say that we have a German socket:
+
+.. doctest::
+
+ >>> from zope.interface import Interface, implements
+
+ >>> class IGermanSocket(Interface):
+ ... pass
+
+ >>> class Socket(object):
+ ... def __repr__(self):
+ ... return '<instance of %s>' %self.__class__.__name__
+
+ >>> class GermanSocket(Socket):
+ ... """German wall socket."""
+ ... implements(IGermanSocket)
+
+and we want to convert it to an US socket
+
+.. doctest::
+
+ >>> class IUSSocket(Interface):
+ ... pass
+
+so that our shaver can be used in Germany. So we go to a German electronics
+store to look for an adapter that we can plug in the wall:
+
+.. doctest::
+
+ >>> class GermanToUSSocketAdapter(Socket):
+ ... implements(IUSSocket)
+ ... __used_for__ = IGermanSocket
+ ...
+ ... def __init__(self, socket):
+ ... self.context = socket
+
+Note that I could have called the passed in socket any way I like, but
+`context` is the standard name accepted.
+
+
+Single Adapters
+~~~~~~~~~~~~~~~
+
+Before we can use the adapter, we have to buy it and make it part of our
+inventory. In the component architecture we do this by registering the adapter
+with the framework, more specifically with the global site manager:
+
+.. doctest::
+
+ >>> import zope.component
+ >>> gsm = zope.component.getGlobalSiteManager()
+ >>> gsm.registerAdapter(GermanToUSSocketAdapter, (IGermanSocket,), IUSSocket)
+
+`zope.component` is the component architecture API that is being
+presented by this file. You registered an adapter from `IGermanSocket`
+to `IUSSocket` having no name (thus the empty string).
+
+Anyways, you finally get back to your hotel room and shave, since you have not
+been able to shave in the plane. In the bathroom you discover a socket:
+
+.. doctest::
+
+ >>> bathroomDE = GermanSocket()
+ >>> IGermanSocket.providedBy(bathroomDE)
+ True
+
+You now insert the adapter in the German socket
+
+.. doctest::
+
+ >>> bathroomUS = zope.component.getAdapter(bathroomDE, IUSSocket, '')
+
+so that the socket now provides the US version:
+
+.. doctest::
+
+ >>> IUSSocket.providedBy(bathroomUS)
+ True
+
+Now you can insert your shaver and get on with your day.
+
+After a week you travel for a couple of days to the Prague and you notice that
+the Czech have yet another socket type:
+
+.. doctest::
+
+ >>> class ICzechSocket(Interface):
+ ... pass
+
+ >>> class CzechSocket(Socket):
+ ... implements(ICzechSocket)
+
+ >>> czech = CzechSocket()
+
+You try to find an adapter for your shaver in your bag, but you fail, since
+you do not have one:
+
+.. doctest::
+
+ >>> zope.component.getAdapter(czech, IUSSocket, '') \
+ ... #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance of CzechSocket>,
+ <InterfaceClass __builtin__.IUSSocket>,
+ '')
+
+or the more graceful way:
+
+.. doctest::
+
+ >>> marker = object()
+ >>> socket = zope.component.queryAdapter(czech, IUSSocket, '', marker)
+ >>> socket is marker
+ True
+
+In the component architecture API any `get*` method will fail with a specific
+exception, if a query failed, whereby methods starting with `query*` will
+always return a `default` value after a failure.
+
+
+Named Adapters
+~~~~~~~~~~~~~~
+
+You are finally back in Germany. You also brought your DVD player and a couple
+DVDs with you, which you would like to watch. Your shaver was able to convert
+automatically from 110 volts to 240 volts, but your DVD player cannot. So you
+have to buy another adapter that also handles converting the voltage and the
+frequency of the AC current:
+
+.. doctest::
+
+ >>> class GermanToUSSocketAdapterAndTransformer(object):
+ ... implements(IUSSocket)
+ ... __used_for__ = IGermanSocket
+ ...
+ ... def __init__(self, socket):
+ ... self.context = socket
+
+Now, we need a way to keep the two adapters apart. Thus we register them with
+a name:
+
+.. doctest::
+
+ >>> gsm.registerAdapter(GermanToUSSocketAdapter,
+ ... (IGermanSocket,), IUSSocket, 'shaver',)
+ >>> gsm.registerAdapter(GermanToUSSocketAdapterAndTransformer,
+ ... (IGermanSocket,), IUSSocket, 'dvd')
+
+Now we simply look up the adapters using their labels (called *name*):
+
+.. doctest::
+
+ >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'shaver')
+ >>> socket.__class__ is GermanToUSSocketAdapter
+ True
+
+ >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'dvd')
+ >>> socket.__class__ is GermanToUSSocketAdapterAndTransformer
+ True
+
+Clearly, we do not have an adapter for the MP3 player
+
+.. doctest::
+
+ >>> zope.component.getAdapter(bathroomDE, IUSSocket, 'mp3') \
+ ... #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance of GermanSocket>,
+ <InterfaceClass __builtin__.IUSSocket>,
+ 'mp3')
+
+but you could use the 'dvd' adapter in this case of course. ;)
+
+Sometimes you want to know all adapters that are available. Let's say you want
+to know about all the adapters that convert a German to a US socket type:
+
+.. doctest::
+
+ >>> sockets = list(zope.component.getAdapters((bathroomDE,), IUSSocket))
+ >>> len(sockets)
+ 3
+ >>> names = [name for name, socket in sockets]
+ >>> names.sort()
+ >>> names
+ [u'', u'dvd', u'shaver']
+
+`zope.component.getAdapters()` returns a list of tuples. The first
+entry of the tuple is the name of the adapter and the second is the
+adapter itself.
+
+
+Multi-Adapters
+~~~~~~~~~~~~~~
+
+After watching all the DVDs you brought at least twice, you get tired of them
+and you want to listen to some music using your MP3 player. But darn, the MP3
+player plug has a ground pin and all the adapters you have do not support
+that:
+
+.. doctest::
+
+ >>> class IUSGroundedSocket(IUSSocket):
+ ... pass
+
+So you go out another time to buy an adapter. This time, however, you do not
+buy yet another adapter, but a piece that provides the grounding plug:
+
+.. doctest::
+
+ >>> class IGrounder(Interface):
+ ... pass
+
+ >>> class Grounder(object):
+ ... implements(IGrounder)
+ ... def __repr__(self):
+ ... return '<instance of Grounder>'
+
+
+Then together they will provided a grounded us socket:
+
+.. doctest::
+
+ >>> class GroundedGermanToUSSocketAdapter(object):
+ ... implements(IUSGroundedSocket)
+ ... __used_for__ = (IGermanSocket, IGrounder)
+ ... def __init__(self, socket, grounder):
+ ... self.socket, self.grounder = socket, grounder
+
+You now register the combination, so that you know you can create a
+`IUSGroundedSocket`:
+
+.. doctest::
+
+ >>> gsm.registerAdapter(GroundedGermanToUSSocketAdapter,
+ ... (IGermanSocket, IGrounder), IUSGroundedSocket, 'mp3')
+
+Given the grounder
+
+.. doctest::
+
+ >>> grounder = Grounder()
+
+and a German socket
+
+.. doctest::
+
+ >>> livingroom = GermanSocket()
+
+we can now get a grounded US socket:
+
+.. doctest::
+
+ >>> socket = zope.component.getMultiAdapter((livingroom, grounder),
+ ... IUSGroundedSocket, 'mp3')
+
+.. doctest::
+
+ >>> socket.__class__ is GroundedGermanToUSSocketAdapter
+ True
+ >>> socket.socket is livingroom
+ True
+ >>> socket.grounder is grounder
+ True
+
+Of course, you do not have a 'dvd' grounded US socket available:
+
+.. doctest::
+
+ >>> zope.component.getMultiAdapter((livingroom, grounder),
+ ... IUSGroundedSocket, 'dvd') \
+ ... #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: ((<instance of GermanSocket>,
+ <instance of Grounder>),
+ <InterfaceClass __builtin__.IUSGroundedSocket>,
+ 'dvd')
+
+
+.. doctest::
+
+ >>> socket = zope.component.queryMultiAdapter(
+ ... (livingroom, grounder), IUSGroundedSocket, 'dvd', marker)
+ >>> socket is marker
+ True
+
+Again, you might want to read `adapter.txt` in `zope.interface` for a more
+comprehensive coverage of multi-adapters.
+
+Subscribers
+-----------
+
+While subscribers are directly supported by the adapter registry and are
+adapters for all theoretical purposes, practically it might be better to think
+of them as separate components. Subscribers are particularly useful for
+events.
+
+Let's say one of our adapters overheated and caused a small fire:
+
+.. doctest::
+
+ >>> class IFire(Interface):
+ ... pass
+
+ >>> class Fire(object):
+ ... implements(IFire)
+
+ >>> fire = Fire()
+
+We want to use all available objects to put out the fire:
+
+.. doctest::
+
+ >>> class IFireExtinguisher(Interface):
+ ... def extinguish():
+ ... pass
+
+ >>> class FireExtinguisher(object):
+ ... def __init__(self, fire):
+ ... pass
+ ... def extinguish(self):
+ ... "Place extinguish code here."
+ ... print 'Used ' + self.__class__.__name__ + '.'
+
+Here some specific methods to put out the fire:
+
+.. doctest::
+
+ >>> class PowderExtinguisher(FireExtinguisher):
+ ... pass
+ >>> gsm.registerSubscriptionAdapter(PowderExtinguisher,
+ ... (IFire,), IFireExtinguisher)
+
+ >>> class Blanket(FireExtinguisher):
+ ... pass
+ >>> gsm.registerSubscriptionAdapter(Blanket, (IFire,), IFireExtinguisher)
+
+ >>> class SprinklerSystem(FireExtinguisher):
+ ... pass
+ >>> gsm.registerSubscriptionAdapter(SprinklerSystem,
+ ... (IFire,), IFireExtinguisher)
+
+Now let use all these things to put out the fire:
+
+.. doctest::
+
+ >>> extinguishers = zope.component.subscribers((fire,), IFireExtinguisher)
+ >>> extinguishers.sort()
+ >>> for extinguisher in extinguishers:
+ ... extinguisher.extinguish()
+ Used Blanket.
+ Used PowderExtinguisher.
+ Used SprinklerSystem.
+
+If no subscribers are found for a particular object, then an empty list is
+returned:
+
+.. doctest::
+
+ >>> zope.component.subscribers((object(),), IFireExtinguisher)
+ []
+
+
+Utilities
+---------
+
+Utilities are the second type of component, the component architecture
+implements. *Utilities* are simply components that provide an interface. When
+you register an utility, you always register an instance (in contrast to a
+factory for adapters) since the initialization and setup process of a utility
+might be complex and is not well defined. In some ways a utility is much more
+fundamental than an adapter, because an adapter cannot be used without another
+component, but a utility is always self-contained. I like to think of
+utilities as the foundation of your application and adapters as components
+extending beyond this foundation.
+
+Back to our story...
+
+After your vacation is over you fly back home to Tampa, Florida. But it is
+August now, the middle of the Hurricane season. And, believe it or not, you are
+worried that you will not be able to shave when the power goes out for several
+days. (You just hate wet shavers.)
+
+So you decide to go to your favorite hardware store and by a Diesel-powered
+electric generator. The generator provides of course a US-style socket:
+
+.. doctest::
+
+ >>> class Generator(object):
+ ... implements(IUSSocket)
+ ... def __repr__(self):
+ ... return '<instance of Generator>'
+
+ >>> generator = Generator()
+
+Like for adapters, we now have to add the newly-acquired generator to our
+inventory by registering it as a utility:
+
+.. doctest::
+
+ >>> gsm.registerUtility(generator, IUSSocket)
+
+We can now get the utility using
+
+.. doctest::
+
+ >>> utility = zope.component.getUtility(IUSSocket)
+ >>> utility is generator
+ True
+
+As you can see, it is very simple to register and retrieve utilities. If a
+utility does not exist for a particular interface, such as the German socket,
+then the lookup fails
+
+.. doctest::
+
+ >>> zope.component.getUtility(IGermanSocket)
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass __builtin__.IGermanSocket>, '')
+
+or more gracefully when specifying a default value:
+
+.. doctest::
+
+ >>> default = object()
+ >>> utility = zope.component.queryUtility(IGermanSocket, default=default)
+ >>> utility is default
+ True
+
+Note: The only difference between `getUtility()` and `queryUtility()` is the
+fact that you can specify a default value for the latter function, so that it
+will never cause a `ComponentLookupError`.
+
+
+Named Utilities
+~~~~~~~~~~~~~~~
+
+It is often desirable to have several utilities providing the same interface
+per site. This way you can implement any sort of registry using utilities. For
+this reason, utilities -- like adapters -- can be named.
+
+In the context of our story, we might want to do the following: You really do
+not trust gas stations either. What if the roads are blocked after a hurricane
+and the gas stations run out of oil. So you look for another renewable power
+source. Then you think about solar panels! After a storm there is usually very
+nice weather, so why not? Via the Web you order a set of 110V/120W solar
+panels that provide a regular US-style socket as output:
+
+.. doctest::
+
+ >>> class SolarPanel(object):
+ ... implements(IUSSocket)
+ ... def __repr__(self):
+ ... return '<instance of Solar Panel>'
+
+ >>> panel = SolarPanel()
+
+Once it arrives, we add it to our inventory:
+
+.. doctest::
+
+ >>> gsm.registerUtility(panel, IUSSocket, 'Solar Panel')
+
+You can now access the solar panel using
+
+.. doctest::
+
+ >>> utility = zope.component.getUtility(IUSSocket, 'Solar Panel')
+ >>> utility is panel
+ True
+
+Of course, if a utility is not available, then the lookup will simply fail
+
+.. doctest::
+
+ >>> zope.component.getUtility(IUSSocket, 'Wind Mill')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass __builtin__.IUSSocket>, 'Wind Mill')
+
+or more gracefully when specifying a default value:
+
+.. doctest::
+
+ >>> default = object()
+ >>> utility = zope.component.queryUtility(IUSSocket, 'Wind Mill',
+ ... default=default)
+ >>> utility is default
+ True
+
+Now you want to look at all the utilities you have for a particular kind. The
+following API function will return a list of name/utility pairs:
+
+.. doctest::
+
+ >>> utils = list(zope.component.getUtilitiesFor(IUSSocket))
+ >>> utils.sort()
+ >>> utils #doctest: +NORMALIZE_WHITESPACE
+ [(u'', <instance of Generator>),
+ (u'Solar Panel', <instance of Solar Panel>)]
+
+Another method of looking up all utilities is by using
+`getAllUtilitiesRegisteredFor(iface)`. This function will return an iterable
+of utilities (without names); however, it will also return overridden
+utilities. If you are not using multiple site managers, you will not actually
+need this method.
+
+.. doctest::
+
+ >>> utils = list(zope.component.getAllUtilitiesRegisteredFor(IUSSocket))
+ >>> utils.sort()
+ >>> utils
+ [<instance of Generator>, <instance of Solar Panel>]
+
+
+Factories
+~~~~~~~~~
+
+A *factory* is a special kind of utility that exists to create other
+components. A factory is always identified by a name. It also provides a title
+and description and is able to tell the developer what interfaces the created
+object will provide. The advantage of using a factory to create an object
+instead of directly instantiating a class or executing any other callable is
+that we can refer to the factory by name. As long as the name stays fixed, the
+implementation of the callable can be renamed or moved without a breakage in
+code.
+
+Let's say that our solar panel comes in parts and they have to be
+assembled. This assembly would be done by a factory, so let's create one for
+the solar panel. To do this, we can use a standard implementation of the
+`IFactory` interface:
+
+.. doctest::
+
+ >>> from zope.component.factory import Factory
+ >>> factory = Factory(SolarPanel,
+ ... 'Solar Panel',
+ ... 'This factory creates a solar panel.')
+
+Optionally, I could have also specified the interfaces that the created object
+will provide, but the factory class is smart enough to determine the
+implemented interface from the class. We now register the factory:
+
+.. doctest::
+
+ >>> from zope.component.interfaces import IFactory
+ >>> gsm.registerUtility(factory, IFactory, 'SolarPanel')
+
+We can now get a list of interfaces the produced object will provide:
+
+.. doctest::
+
+ >>> ifaces = zope.component.getFactoryInterfaces('SolarPanel')
+ >>> IUSSocket in ifaces
+ True
+
+By the way, this is equivalent to
+
+.. doctest::
+
+ >>> ifaces2 = factory.getInterfaces()
+ >>> ifaces is ifaces2
+ True
+
+Of course you can also just create an object:
+
+.. doctest::
+
+ >>> panel = zope.component.createObject('SolarPanel')
+ >>> panel.__class__ is SolarPanel
+ True
+
+Note: Ignore the first argument (`None`) for now; it is the context of the
+utility lookup, which is usually an optional argument, but cannot be in this
+case, since all other arguments beside the `name` are passed in as arguments
+to the specified callable.
+
+Once you register several factories
+
+.. doctest::
+
+ >>> gsm.registerUtility(Factory(Generator), IFactory, 'Generator')
+
+you can also determine, which available factories will create objects
+providing a certain interface:
+
+.. doctest::
+
+ >>> factories = zope.component.getFactoriesFor(IUSSocket)
+ >>> factories = [(name, factory.__class__) for name, factory in factories]
+ >>> factories.sort()
+ >>> factories #doctest: +NORMALIZE_WHITESPACE
+ [(u'Generator', <class 'zope.component.factory.Factory'>),
+ (u'SolarPanel', <class 'zope.component.factory.Factory'>)]
+
+
+Site Managers
+-------------
+
+Why do we need site managers? Why is the component architecture API not
+sufficient? Some applications, including Zope 3, have a concept of
+locations. It is often desirable to have different configurations for these
+location; this can be done by overwriting existing or adding new component
+registrations. Site managers in locations below the root location, should be
+able to delegate requests to their parent locations. The root site manager is
+commonly known as *global site manager*, since it is always available. You can
+always get the global site manager using the API:
+
+.. doctest::
+
+ >>> gsm = zope.component.getGlobalSiteManager()
+
+ >>> from zope.component import globalSiteManager
+ >>> gsm is globalSiteManager
+ True
+ >>> from zope.component.interfaces import IComponentLookup
+ >>> IComponentLookup.providedBy(gsm)
+ True
+ >>> from zope.component.interfaces import IComponents
+ >>> IComponents.providedBy(gsm)
+ True
+
+You can also lookup at site manager in a given context. The only requirement
+is that the context can be adapted to a site manager. So let's create a
+special site manager:
+
+.. doctest::
+
+ >>> from zope.component.globalregistry import BaseGlobalComponents
+ >>> sm = BaseGlobalComponents()
+
+Now we create a context that adapts to the site manager via the `__conform__`
+method as specified in PEP 246.
+
+.. doctest::
+
+ >>> class Context(object):
+ ... def __init__(self, sm):
+ ... self.sm = sm
+ ... def __conform__(self, interface):
+ ... if interface.isOrExtends(IComponentLookup):
+ ... return self.sm
+
+We now instantiate the `Context` with our special site manager:
+
+.. doctest::
+
+ >>> context = Context(sm)
+ >>> context.sm is sm
+ True
+
+We can now ask for the site manager of this context:
+
+.. doctest::
+
+ >>> lsm = zope.component.getSiteManager(context)
+ >>> lsm is sm
+ True
+
+The site manager instance `lsm` is formally known as a *local site manager* of
+`context`.
diff --git a/docs/testlayer.rst b/docs/testlayer.rst
new file mode 100644
index 0000000..b74285e
--- /dev/null
+++ b/docs/testlayer.rst
@@ -0,0 +1,111 @@
+Layers
+======
+
+zope.component.testlayer defines two things:
+
+* a LayerBase that makes it easier and saner to use zope.testing's
+ test layers.
+
+* a ZCMLLayer which lets you implement a layer that loads up some
+ ZCML.
+
+LayerBase
+---------
+
+We check whether our LayerBase can be used to create layers of our
+own. We do this simply by subclassing:
+
+.. doctest::
+
+ >>> from zope.component.testlayer import LayerBase
+ >>> class OurLayer(LayerBase):
+ ... def setUp(self):
+ ... super(OurLayer, self).setUp()
+ ... print "setUp called"
+ ... def tearDown(self):
+ ... super(OurLayer, self).tearDown()
+ ... print "tearDown called"
+ ... def testSetUp(self):
+ ... super(OurLayer, self).testSetUp()
+ ... print "testSetUp called"
+ ... def testTearDown(self):
+ ... super(OurLayer, self).testTearDown()
+ ... print "testTearDown called"
+
+Note that if we wanted to ensure that the methods of the superclass
+were called we have to use super(). In this case we actually wouldn't
+need to, as these methods do nothing at all, but we just ensure that
+they are there in the first place.
+
+Let's instantiate our layer. We need to supply it with the package the
+layer is defined in:
+
+.. doctest::
+
+ >>> import zope.component
+ >>> layer = OurLayer(zope.component)
+
+Now we run some tests with this layer:
+
+.. doctest::
+
+ >>> import unittest
+ >>> class TestCase(unittest.TestCase):
+ ... layer = layer
+ ...
+ ... def testFoo(self):
+ ... print "testFoo"
+ >>> suite = unittest.TestSuite()
+ >>> suite.addTest(unittest.makeSuite(TestCase))
+ >>> from zope.testrunner.runner import Runner
+ >>> runner = Runner(args=[], found_suites=[suite])
+ >>> succeeded = runner.run()
+ Running zope.component.OurLayer tests:
+ Set up zope.component.OurLayer setUp called
+ in ... seconds.
+ testSetUp called
+ testFoo
+ testTearDown called
+ Ran 1 tests with 0 failures and 0 errors in ... seconds.
+ Tearing down left over layers:
+ Tear down zope.component.OurLayer tearDown called
+ in ... seconds.
+
+ZCMLLayer
+---------
+
+We now want a layer that loads up some ZCML from a file. The default
+is ``ftesting.zcml``, but here we'll load a test ``testlayer.zcml``.
+
+.. doctest::
+
+ >>> from zope.component.testlayer import ZCMLFileLayer
+ >>> import zope.component.testfiles
+ >>> zcml_file_layer = ZCMLFileLayer(
+ ... zope.component.testfiles,
+ ... 'testlayer.zcml')
+
+ >>> class TestCase(unittest.TestCase):
+ ... layer = zcml_file_layer
+ ...
+ ... def testFoo(self):
+ ... # we should now have the adapter registered
+ ... from zope import component
+ ... from zope.component.testfiles import components
+ ... self.assert_(isinstance(
+ ... components.IApp2(components.content), components.Comp2))
+
+Since the ZCML sets up an adapter, we expect the tests to pass:
+
+.. doctest::
+
+ >>> suite = unittest.TestSuite()
+ >>> suite.addTest(unittest.makeSuite(TestCase))
+ >>> runner = Runner(args=[], found_suites=[suite])
+ >>> succeeded = runner.run()
+ Running zope.component.testfiles.ZCMLFileLayer tests:
+ Set up zope.component.testfiles.ZCMLFileLayer in ... seconds.
+ Ran 1 tests with 0 failures and 0 errors in ... seconds.
+ Tearing down left over layers:
+ Tear down zope.component.testfiles.ZCMLFileLayer in ... seconds.
+
diff --git a/docs/zcml.rst b/docs/zcml.rst
new file mode 100644
index 0000000..330d353
--- /dev/null
+++ b/docs/zcml.rst
@@ -0,0 +1,1153 @@
+ZCML directives
+===============
+
+Components may be registered using the registration API exposed by
+``zope.component`` (provideAdapter, provideUtility, etc.). They may
+also be registered using configuration files. The common way to do
+that is by using ZCML (Zope Configuration Markup Language), an XML
+spelling of component registration.
+
+In ZCML, each XML element is a *directive*. There are different
+top-level directives that let us register components. We will
+introduce them one by one here.
+
+This helper will let us easily execute ZCML snippets:
+
+.. doctest::
+
+ >>> from cStringIO import StringIO
+ >>> from zope.configuration.xmlconfig import xmlconfig
+ >>> def runSnippet(snippet):
+ ... template = """\
+ ... <configure xmlns='http://namespaces.zope.org/zope'
+ ... i18n_domain="zope">
+ ... %s
+ ... </configure>"""
+ ... xmlconfig(StringIO(template % snippet))
+
+adapter
+-------
+
+Adapters play a key role in the Component Architecture. In ZCML, they
+are registered with the <adapter /> directive.
+
+.. doctest::
+
+ >>> from zope.component.testfiles.adapter import A1, A2, A3, Handler
+ >>> from zope.component.testfiles.adapter import I1, I2, I3, IS
+ >>> from zope.component.testfiles.components import IContent, Content, Comp, comp
+
+Before we register the first test adapter, we can verify that adapter
+lookup doesn't work yet:
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import clearZCML
+ >>> clearZCML()
+ >>> from zope.component.testfiles.components import IApp
+ >>> IApp(Content(), None) is None
+ True
+
+Then we register the adapter and see that the lookup works:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... />''')
+
+ >>> IApp(Content()).__class__
+ <class 'zope.component.testfiles.components.Comp'>
+
+It is also possible to give adapters names. Then the combination of
+required interface, provided interface and name makes the adapter
+lookup unique. The name is supplied using the ``name`` argument to
+the <adapter /> directive:
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import clearZCML
+ >>> clearZCML()
+ >>> import zope.component
+ >>> zope.component.queryAdapter(Content(), IApp, 'test') is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... name="test"
+ ... />''')
+
+ >>> zope.component.getAdapter(Content(), IApp, 'test').__class__
+ <class 'zope.component.testfiles.components.Comp'>
+
+Adapter factories
+~~~~~~~~~~~~~~~~~
+
+It is possible to supply more than one adapter factory. In this case,
+during adapter lookup each factory will be called and the return value
+will be given to the next factory. The return value of the last
+factory is returned as the result of the adapter lookup. For examle:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.adapter.A1
+ ... zope.component.testfiles.adapter.A2
+ ... zope.component.testfiles.adapter.A3"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... />''')
+
+The resulting adapter is an A3, around an A2, around an A1, around the
+adapted object:
+
+.. doctest::
+
+ >>> content = Content()
+ >>> a3 = IApp(content)
+ >>> a3.__class__ is A3
+ True
+
+ >>> a2 = a3.context[0]
+ >>> a2.__class__ is A2
+ True
+
+ >>> a1 = a2.context[0]
+ >>> a1.__class__ is A1
+ True
+
+ >>> a1.context[0] is content
+ True
+
+Of course, if no factory is provided at all, we will get an error:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory=""
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-8.8
+ ValueError: No factory specified
+
+
+Declaring ``for`` and ``provides`` in Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The <adapter /> directive can figure out from the in-line Python
+declaration (using ``zope.component.adapts()`` or
+``zope.component.adapter()`` as well as ``zope.interface.implements``)
+what the adapter should be registered for and what it provides:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter factory="zope.component.testfiles.components.Comp" />''')
+
+ >>> IApp(Content()).__class__
+ <class 'zope.component.testfiles.components.Comp'>
+
+Of course, if the adapter has no ``implements()`` declaration, ZCML
+can't figure out what it provides:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.adapter.A4"
+ ... for="zope.component.testfiles.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-7.8
+ TypeError: Missing 'provides' attribute
+
+On the other hand, if the factory implements more than one interface,
+ZCML can't figure out what it should provide either:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.adapter.A5"
+ ... for="zope.component.testfiles.components.IContent"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-7.8
+ TypeError: Missing 'provides' attribute
+
+A not so common edge case is registering adapters directly for
+classes, not for interfaces. For example:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.Content"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = zope.component.getAdapter(content, I1, '')
+ >>> isinstance(a1, A1)
+ True
+
+This time, any object providing ``IContent`` won't work if it's not an
+instance of the ``Content`` class:
+
+.. doctest::
+
+ >>> import zope.interface
+ >>> class MyContent:
+ ... zope.interface.implements(IContent)
+ >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: ...
+
+Multi-adapters
+~~~~~~~~~~~~~~
+
+Conventional adapters adapt one object to provide another interface.
+Multi-adapters adapt several objects at once:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1
+ ... zope.component.testfiles.adapter.I2"
+ ... provides="zope.component.testfiles.adapter.I3"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+ >>> a2 = A2()
+ >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+You can even adapt an empty list of objects (we call this a
+null-adapter):
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for=""
+ ... provides="zope.component.testfiles.adapter.I3"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... />''')
+
+ >>> a3 = zope.component.queryMultiAdapter((), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == ()
+ True
+
+Even with multi-adapters, ZCML can figure out the ``for`` and
+``provides`` parameters from the Python declarations:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter factory="zope.component.testfiles.adapter.A3" />''')
+
+ >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3)
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+Chained factories are not supported for multi-adapters, though:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1
+ ... zope.component.testfiles.adapter.I2"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... factory="zope.component.testfiles.adapter.A1
+ ... zope.component.testfiles.adapter.A2"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-11.8
+ ValueError: Can't use multiple factories and multiple for
+
+And neither for null-adapters:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for=""
+ ... provides="zope.component.testfiles.components.IApp"
+ ... factory="zope.component.testfiles.adapter.A1
+ ... zope.component.testfiles.adapter.A2"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-9.8
+ ValueError: Can't use multiple factories and multiple for
+
+Protected adapters
+~~~~~~~~~~~~~~~~~~
+
+Adapters can be protected with a permission. First we have to define
+a permission for which we'll have to register the <permission />
+directive:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> import zope.security
+ >>> from zope.configuration.xmlconfig import XMLConfig
+ >>> XMLConfig('meta.zcml', zope.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... permission="y.x"
+ ... />''')
+
+We see that the adapter is a location proxy now so that the
+appropriate permissions can be found from the context:
+
+.. doctest::
+
+ >>> IApp(Content()).__class__
+ <class 'zope.component.testfiles.components.Comp'>
+ >>> type(IApp(Content()))
+ <class 'zope.location.location.LocationProxy'>
+
+We can also go about it a different way. Let's make a public adapter
+and wrap the adapter in a security proxy. That often happens when
+an adapter is turned over to untrusted code:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> IApp(Content(), None) is None
+ True
+
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> from zope.security.checker import ProxyFactory
+ >>> adapter = ProxyFactory(IApp(Content()))
+ >>> from zope.security.proxy import getTestProxyItems
+ >>> items = [item[0] for item in getTestProxyItems(adapter)]
+ >>> items
+ ['a', 'f']
+
+ >>> from zope.security.proxy import removeSecurityProxy
+ >>> removeSecurityProxy(adapter).__class__ is Comp
+ True
+
+Of course, this still works when we let the ZCML directive handler
+figure out ``for`` and ``provides`` from the Python declarations:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> adapter = ProxyFactory(IApp(Content()))
+ >>> [item[0] for item in getTestProxyItems(adapter)]
+ ['a', 'f']
+ >>> removeSecurityProxy(adapter).__class__ is Comp
+ True
+
+It also works with multi adapters:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... provides="zope.component.testfiles.adapter.I3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1
+ ... zope.component.testfiles.adapter.I2"
+ ... permission="zope.Public"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+ >>> a2 = A2()
+ >>> a3 = ProxyFactory(zope.component.queryMultiAdapter((content, a1, a2), I3))
+ >>> a3.__class__ == A3
+ True
+ >>> [item[0] for item in getTestProxyItems(a3)]
+ ['f1', 'f2', 'f3']
+
+It's probably not worth mentioning, but when we try to protect an
+adapter with a permission that doesn't exist, we'll obviously get an
+error:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... for="zope.component.testfiles.components.IContent"
+ ... permission="zope.UndefinedPermission"
+ ... />''')
+ Traceback (most recent call last):
+ ...
+ ConfigurationExecutionError: exceptions.ValueError: ('Undefined permission id', 'zope.UndefinedPermission')
+ in:
+ File "<string>", line 4.2-9.8
+ Could not read source.
+
+Trusted adapters
+~~~~~~~~~~~~~~~~
+
+Trusted adapters are adapters that are trusted to do anything with the
+objects they are given so that these objects are not security-proxied.
+They are registered using the ``trusted`` argument to the <adapter />
+directive:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... trusted="yes"
+ ... />''')
+
+With an unproxied object, it's business as usual:
+
+.. doctest::
+
+ >>> ob = Content()
+ >>> type(I1(ob)) is A1
+ True
+
+With a security-proxied object, however, we get a security-proxied
+adapter:
+
+.. doctest::
+
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+While the adapter is security-proxied, the object it adapts is now
+proxy-free. The adapter has umlimited access to it:
+
+.. doctest::
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a) is A1
+ True
+ >>> a.context[0] is ob
+ True
+
+We can also protect the trusted adapter with a permission:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> XMLConfig('meta.zcml', zope.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... permission="y.x"
+ ... trusted="yes"
+ ... />''')
+
+Again, with an unproxied object, it's business as usual:
+
+.. doctest::
+
+ >>> ob = Content()
+ >>> type(I1(ob)) is A1
+ True
+
+With a security-proxied object, we again get a security-proxied
+adapter:
+
+.. doctest::
+
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+Since we protected the adapter with a permission, we now encounter a
+location proxy behind the security proxy:
+
+.. doctest::
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a)
+ <class 'zope.location.location.LocationProxy'>
+ >>> a.context[0] is ob
+ True
+
+There's one exception to all of this: When you use the public
+permission (``zope.Public``), there will be no location proxy:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... permission="zope.Public"
+ ... trusted="yes"
+ ... />''')
+
+ >>> ob = Content()
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a) is A1
+ True
+
+We can also explicitply pass the ``locate`` argument to make sure we
+get location proxies:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <adapter
+ ... for="zope.component.testfiles.components.IContent"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... trusted="yes"
+ ... locate="yes"
+ ... />''')
+
+ >>> ob = Content()
+ >>> p = ProxyFactory(ob)
+ >>> a = I1(p)
+ >>> type(a)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> a = removeSecurityProxy(a)
+ >>> type(a)
+ <class 'zope.location.location.LocationProxy'>
+
+
+subscriber
+----------
+
+With the <subscriber /> directive you can register subscription
+adapters or event subscribers with the adapter registry. Consider
+this very typical example of a <subscriber /> directive:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... />''')
+
+ >>> content = Content()
+ >>> a1 = A1()
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+
+Note how ZCML provides some additional information when registering
+components, such as the ZCML filename and line numbers:
+
+.. doctest::
+
+ >>> sm = zope.component.getSiteManager()
+ >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters()
+ ... if reg.provided is IS][0]
+ >>> print doc
+ File "<string>", line 4.2-9.8
+ Could not read source.
+
+The "fun" behind subscription adapters/subscribers is that when
+several ones are declared for the same for/provides, they are all
+found. With regular adapters, the most specific one (and in doubt the
+one registered last) wins. Consider these two subscribers:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... />
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A2"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... />''')
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> len(subscribers)
+ 2
+ >>> sorted([a.__class__.__name__ for a in subscribers])
+ ['A2', 'A3']
+
+Declaring ``for`` and ``provides`` in Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Like the <adapter /> directive, the <subscriber /> directive can
+figure out from the in-line Python declaration (using
+``zope.component.adapts()`` or ``zope.component.adapter()``) what the
+subscriber should be registered for:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... />''')
+
+ >>> content = Content()
+ >>> a2 = A2()
+ >>> subscribers = zope.component.subscribers((content, a1, a2), IS)
+
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1, a2)
+ True
+
+In the same way the directive can figure out what a subscriber
+provides:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber handler="zope.component.testfiles.adapter.A3" />''')
+
+ >>> sm = zope.component.getSiteManager()
+ >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0]
+ >>> a3 is A3
+ True
+
+A not so common edge case is declaring subscribers directly for
+classes, not for interfaces. For example:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... for="zope.component.testfiles.components.Content"
+ ... provides="zope.component.testfiles.adapter.I1"
+ ... factory="zope.component.testfiles.adapter.A1"
+ ... />''')
+
+ >>> subs = list(zope.component.subscribers((Content(),), I1))
+ >>> isinstance(subs[0], A1)
+ True
+
+This time, any object providing ``IContent`` won't work if it's not an
+instance of the ``Content`` class:
+
+.. doctest::
+
+ >>> list(zope.component.subscribers((MyContent(),), I1))
+ []
+
+Protected subscribers
+~~~~~~~~~~~~~~~~~~~~~
+
+Subscribers can also be protected with a permission. First we have to
+define a permission for which we'll have to register the <permission />
+directive:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> XMLConfig('meta.zcml', zope.security)()
+ >>> runSnippet('''
+ ... <permission
+ ... id="y.x"
+ ... title="XY"
+ ... description="Allow XY."
+ ... />
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... permission="y.x"
+ ... />''')
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> type(a3)
+ <class 'zope.location.location.LocationProxy'>
+ >>> a3.context == (content, a1)
+ True
+
+Trusted subscribers
+~~~~~~~~~~~~~~~~~~~
+
+Like trusted adapters, trusted subscribers are subscribers that are
+trusted to do anything with the objects they are given so that these
+objects are not security-proxied. In analogy to the <adapter />
+directive, they are registered using the ``trusted`` argument to the
+<subscriber /> directive:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... trusted="yes"
+ ... />''')
+
+With an unproxied object, it's business as usual:
+
+.. doctest::
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+ >>> type(a3) is A3
+ True
+
+Now with a proxied object. We will see that the subscriber has
+unproxied access to it, but the subscriber itself is proxied:
+
+.. doctest::
+
+ >>> p = ProxyFactory(content)
+ >>> a3 = zope.component.subscribers((p, a1), IS)[0]
+ >>> type(a3)
+ <type 'zope.security._proxy._Proxy'>
+
+There's no location proxy behind the security proxy:
+
+.. doctest::
+
+ >>> removeSecurityProxy(a3).context[0] is content
+ True
+ >>> type(removeSecurityProxy(a3)) is A3
+ True
+
+If you want the trusted subscriber to be located, you'll also have to
+use the ``locate`` argument:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... provides="zope.component.testfiles.adapter.IS"
+ ... factory="zope.component.testfiles.adapter.A3"
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... trusted="yes"
+ ... locate="yes"
+ ... />''')
+
+Again, it's business as usual with an unproxied object:
+
+.. doctest::
+
+ >>> subscribers = zope.component.subscribers((content, a1), IS)
+ >>> a3 = subscribers[0]
+ >>> a3.__class__ is A3
+ True
+ >>> a3.context == (content, a1)
+ True
+ >>> type(a3) is A3
+ True
+
+With a proxied object, we again get a security-proxied subscriber:
+
+.. doctest::
+
+ >>> p = ProxyFactory(content)
+ >>> a3 = zope.component.subscribers((p, a1), IS)[0]
+
+ >>> type(a3)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> removeSecurityProxy(a3).context[0] is content
+ True
+
+However, thanks to the ``locate`` argument, we now have a location
+proxy behind the security proxy:
+
+.. doctest::
+
+ >>> type(removeSecurityProxy(a3))
+ <class 'zope.location.location.LocationProxy'>
+
+Event subscriber (handlers)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, subscribers don't need to be adapters that actually provide
+anything. It's enough that a callable is called for a certain event.
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <subscriber
+ ... for="zope.component.testfiles.components.IContent
+ ... zope.component.testfiles.adapter.I1"
+ ... handler="zope.component.testfiles.adapter.Handler"
+ ... />''')
+
+In this case, simply getting the subscribers is enough to invoke them:
+
+.. doctest::
+
+ >>> list(zope.component.subscribers((content, a1), None))
+ []
+ >>> content.args == ((a1,),)
+ True
+
+
+utility
+-------
+
+Apart from adapters (and subscription adapters), the Component
+Architecture knows a second kind of component: utilities. They are
+registered using the <utility /> directive.
+
+Before we register the first test utility, we can verify that utility
+lookup doesn't work yet:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+Then we register the utility:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <utility
+ ... component="zope.component.testfiles.components.comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... />''')
+ >>> zope.component.getUtility(IApp) is comp
+ True
+
+Like adapters, utilities can also have names. There can be more than
+one utility registered for a certain interface, as long as they each
+have a different name.
+
+First, we make sure that there's no utility yet:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp, 'test') is None
+ True
+
+Then we register it:
+
+.. doctest::
+
+ >>> runSnippet('''
+ ... <utility
+ ... component="zope.component.testfiles.components.comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... name="test"
+ ... />''')
+ >>> zope.component.getUtility(IApp, 'test') is comp
+ True
+
+Utilities can also be registered from a factory. In this case, the
+ZCML handler calls the factory (without any arguments) and registers
+the returned value as a utility. Typically, you'd pass a class for
+the factory:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+ >>> runSnippet('''
+ ... <utility
+ ... factory="zope.component.testfiles.components.Comp"
+ ... provides="zope.component.testfiles.components.IApp"
+ ... />''')
+ >>> zope.component.getUtility(IApp).__class__ is Comp
+ True
+
+Declaring ``provides`` in Python
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Like other directives, <utility /> can also figure out which interface
+a utility provides from the Python declaration:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> zope.component.queryUtility(IApp) is None
+ True
+
+ >>> runSnippet('''
+ ... <utility component="zope.component.testfiles.components.comp" />''')
+ >>> zope.component.getUtility(IApp) is comp
+ True
+
+It won't work if the component that is to be registered doesn't
+provide anything:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.component.testfiles.adapter.a4" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+Or if more than one interface is provided (then the ZCML directive
+handler doesn't know under which the utility should be registered):
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility component="zope.component.testfiles.adapter.a5" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.61
+ TypeError: Missing 'provides' attribute
+
+We can repeat the same drill for utility factories:
+
+.. doctest::
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility factory="zope.component.testfiles.components.Comp" />''')
+ >>> zope.component.getUtility(IApp).__class__ is Comp
+ True
+
+ >>> runSnippet('''
+ ... <utility factory="zope.component.testfiles.adapter.A4" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.59
+ TypeError: Missing 'provides' attribute
+
+ >>> clearZCML()
+ >>> runSnippet('''
+ ... <utility factory="zope.component.testfiles.adapter.A5" />''')
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 4.2-4.59
+ TypeError: Missing 'provides' attribute
+
+Protected utilities
+~~~~~~~~~~~~~~~~~~~
+
+TODO::
+
+ def testProtectedUtility(self):
+ """Test that we can protect a utility.
+
+ Also:
+ Check that multiple configurations for the same utility and
+ don't interfere.
+ """
+ self.assertEqual(zope.component.queryUtility(IV), None)
+ xmlconfig(StringIO(template % (
+ '''
+ <permission id="tell.everyone" title="Yay" />
+ <utility
+ component="zope.component.testfiles.components.comp"
+ provides="zope.component.testfiles.components.IApp"
+ permission="tell.everyone"
+ />
+ <permission id="top.secret" title="shhhh" />
+ <utility
+ component="zope.component.testfiles.components.comp"
+ provides="zope.component.testfiles.components.IAppb"
+ permission="top.secret"
+ />
+ '''
+ )))
+
+ utility = ProxyFactory(zope.component.getUtility(IApp))
+ items = getTestProxyItems(utility)
+ self.assertEqual(items, [('a', 'tell.everyone'),
+ ('f', 'tell.everyone')
+ ])
+ self.assertEqual(removeSecurityProxy(utility), comp)
+
+ def testUtilityUndefinedPermission(self):
+ config = StringIO(template % (
+ '''
+ <utility
+ component="zope.component.testfiles.components.comp"
+ provides="zope.component.testfiles.components.IApp"
+ permission="zope.UndefinedPermission"
+ />
+ '''
+ ))
+ self.assertRaises(ValueError, xmlconfig, config,
+ testing=1)
+
+interface
+---------
+
+The <interface /> directive lets us register an interface. Interfaces
+are registered as named utilities. We therefore needn't go though all
+the lookup details again, it is sufficient to see whether the
+directive handler emits the right actions.
+
+First we provide a stub configuration context:
+
+.. doctest::
+
+ >>> import re, pprint
+ >>> atre = re.compile(' at [0-9a-fA-Fx]+')
+ >>> class Context(object):
+ ... actions = ()
+ ... def action(self, discriminator, callable, args):
+ ... self.actions += ((discriminator, callable, args), )
+ ... def __repr__(self):
+ ... stream = StringIO()
+ ... pprinter = pprint.PrettyPrinter(stream=stream, width=60)
+ ... pprinter.pprint(self.actions)
+ ... r = stream.getvalue()
+ ... return (''.join(atre.split(r))).strip()
+ >>> context = Context()
+
+Then we provide a test interface that we'd like to register:
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> class I(Interface):
+ ... pass
+
+It doesn't yet provide ``ITestType``:
+
+.. doctest::
+
+ >>> from zope.component.tests.examples import ITestType
+ >>> ITestType.providedBy(I)
+ False
+
+However, after calling the directive handler...
+
+.. doctest::
+
+ >>> from zope.component.zcml import interface
+ >>> interface(context, I, ITestType)
+ >>> context
+ ((None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass __builtin__.I>,
+ <InterfaceClass zope.component.tests.examples.ITestType>)),)
+
+...it does provide ``ITestType``:
+
+.. doctest::
+
+ >>> from zope.interface.interfaces import IInterface
+ >>> ITestType.extends(IInterface)
+ True
+ >>> IInterface.providedBy(I)
+ True