[Checkins] SVN: Sandbox/jbohman/zope.registry/ Initial commit of zope.registry

Joel Bohmann joelboh at gmail.com
Mon Jul 18 17:19:30 EDT 2011


Log message for revision 122285:
  Initial commit of zope.registry

Changed:
  A   Sandbox/jbohman/zope.registry/
  A   Sandbox/jbohman/zope.registry/branches/
  A   Sandbox/jbohman/zope.registry/tags/
  A   Sandbox/jbohman/zope.registry/trunk/
  A   Sandbox/jbohman/zope.registry/trunk/.gitignore
  A   Sandbox/jbohman/zope.registry/trunk/CHANGES.txt
  A   Sandbox/jbohman/zope.registry/trunk/README.txt
  A   Sandbox/jbohman/zope.registry/trunk/REGISTRY.txt
  A   Sandbox/jbohman/zope.registry/trunk/bootstrap.py
  A   Sandbox/jbohman/zope.registry/trunk/buildout.cfg
  A   Sandbox/jbohman/zope.registry/trunk/setup.py
  A   Sandbox/jbohman/zope.registry/trunk/src/
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/__init__.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/__init__.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/_declaration.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/interfaces.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/__init__.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_adapter.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_extending.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_handler.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_subscriber.py
  A   Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_utility.py
  A   Sandbox/jbohman/zope.registry/trunk/tox.ini

-=-
Added: Sandbox/jbohman/zope.registry/trunk/.gitignore
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/.gitignore	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/.gitignore	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,9 @@
+.installed.cfg
+bin/*
+eggs/*
+develop-eggs/*
+docs/*
+parts/*
+*.egg-info
+*.pyc
+coverage/*

Added: Sandbox/jbohman/zope.registry/trunk/CHANGES.txt
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/CHANGES.txt	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/CHANGES.txt	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,2 @@
+CHANGES
+*******

Added: Sandbox/jbohman/zope.registry/trunk/README.txt
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/README.txt	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/README.txt	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,12 @@
+*****************************
+zope.registry Package Readme
+*****************************
+
+*This package is intended to be independently reusable in any Python
+project. It is maintained by the* `Zope Toolkit project <http://docs.zope.org/zopetoolkit/>`_.
+
+This package represents the core of the Zope Component Architecture.
+Together with the 'zope.interface' package, it provides facilities for
+defining, registering and looking up components.
+
+.. contents::

Added: Sandbox/jbohman/zope.registry/trunk/REGISTRY.txt
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/REGISTRY.txt	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/REGISTRY.txt	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,989 @@
+Component-Management objects
+============================
+
+Component-management objects provide a higher-level
+component-management API over the basic adapter-registration API
+provided by the zope.interface package.  In particular, it provides:
+
+- utilities
+
+- support for computing adapters, rather than just looking up adapter
+  factories.
+
+- management of registration comments
+
+The zope.registry.Components class provides an
+implementation of zope.registry.interfaces.IComponents that provides
+these features.
+
+    >>> from zope import registry
+    >>> from zope.registry import tests
+    >>> components = registry.Components('comps')
+
+As components are registered, events are generated.  Let's register
+an event subscriber, so we can see the events generated:
+
+    >>> import zope.event
+    >>> def logevent(event):
+    ...     print event
+    >>> zope.event.subscribers.append(logevent)
+    
+Utilities
+---------
+
+You can register Utilities using registerUtility:
+
+    >>> components.registerUtility(tests.U1(1))
+    Registered event:
+    UtilityRegistration(<Components comps>, I1, u'', 1, None, u'')
+
+Here we didn't specify an interface or name.  An unnamed utility was
+registered for interface I1, since that is only interface implemented
+by the U1 class:
+
+    >>> components.getUtility(tests.I1)
+    U1(1)
+
+You can also register a utility using a factory instead of a utility instance:
+
+    >>> def factory():
+    ...    return tests.U1(1)
+    >>> components.registerUtility(factory=factory)
+    Unregistered event:
+    UtilityRegistration(<Components comps>, I1, u'', 1, None, u'')
+    Registered event:
+    UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at 0x...>, u'')
+
+
+If a component implements other than one interface or no interface,
+then an error will be raised:
+
+    >>> components.registerUtility(tests.U12(2))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The utility doesn't provide a single interface and
+    no provided interface was specified.
+
+    >>> components.registerUtility(tests.A)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The utility doesn't provide a single interface and
+    no provided interface was specified.
+ 
+    
+We can provide an interface if desired:
+
+    >>> components.registerUtility(tests.U12(2), tests.I2)
+    Registered event:
+    UtilityRegistration(<Components comps>, I2, u'', 2, None, u'')
+
+and we can specify a name:
+
+    >>> components.registerUtility(tests.U12(3), tests.I2, u'three')
+    Registered event:
+    UtilityRegistration(<Components comps>, I2, u'three', 3, None, u'')
+
+    >>> components.getUtility(tests.I2)
+    U12(2)
+
+    >>> components.getUtility(tests.I2, 'three')
+    U12(3)
+
+If you try to get a utility that doesn't exist, you'll get a component
+lookup error:
+
+    >>> components.getUtility(tests.I3)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    ComponentLookupError: 
+    (<InterfaceClass zope.registry.tests.I3>, u'')
+
+Unless you use queryUtility:
+
+    >>> components.queryUtility(tests.I3)
+    >>> components.queryUtility(tests.I3, default=42)
+    42
+
+You can get information about registered utilities with the
+registeredUtilities method:
+
+    >>> for registration in sorted(components.registeredUtilities()):
+    ...     print registration.provided, registration.name
+    ...     print registration.component, registration.info
+    <InterfaceClass zope.registry.tests.I1> 
+    U1(1) 
+    <InterfaceClass zope.registry.tests.I2> 
+    U12(2) 
+    <InterfaceClass zope.registry.tests.I2> three
+    U12(3) 
+
+Duplicate registrations replace existing ones:
+
+    >>> components.registerUtility(tests.U1(4), info=u'use 4 now')
+    Unregistered event:
+    UtilityRegistration(<Components comps>, I1, u'', 1, <function factory at 0x...>, u'')
+    Registered event:
+    UtilityRegistration(<Components comps>, I1, u'', 4, None, u'use 4 now')
+    >>> components.getUtility(tests.I1)
+    U1(4)
+
+    >>> for registration in sorted(components.registeredUtilities()):
+    ...     print registration.provided, registration.name
+    ...     print registration.component, registration.info
+    <InterfaceClass zope.registry.tests.I1> 
+    U1(4) use 4 now
+    <InterfaceClass zope.registry.tests.I2> 
+    U12(2) 
+    <InterfaceClass zope.registry.tests.I2> three
+    U12(3) 
+
+As shown in the this example, you can provide an "info" argumemnt when
+registering utilities.  This provides extra documentation about the
+registration itself that is shown when listing registrations.
+
+You can also unregister utilities:
+
+    >>> components.unregisterUtility(provided=tests.I1)
+    Unregistered event:
+    UtilityRegistration(<Components comps>, I1, u'', 4, None, u'use 4 now')
+    True
+
+A boolean is returned indicating whether anything changed:
+
+    >>> components.queryUtility(tests.I1)
+    >>> for registration in sorted(components.registeredUtilities()):
+    ...     print registration.provided, registration.name
+    ...     print registration.component, registration.info
+    <InterfaceClass zope.registry.tests.I2> 
+    U12(2) 
+    <InterfaceClass zope.registry.tests.I2> three
+    U12(3) 
+
+When you unregister, you can specify a component.  If the component
+doesn't match the one registered, then nothing happens:
+
+    >>> u5 = tests.U1(5)
+    >>> components.registerUtility(u5)
+    Registered event:
+    UtilityRegistration(<Components comps>, I1, u'', 5, None, u'')
+    >>> components.unregisterUtility(tests.U1(6))
+    False
+    >>> components.queryUtility(tests.I1)
+    U1(5)
+    >>> components.unregisterUtility(u5)
+    Unregistered event:
+    UtilityRegistration(<Components comps>, I1, u'', 5, None, u'')
+    True
+    >>> components.queryUtility(tests.I1)
+
+You can get the name and utility for all of the utilities that provide
+an interface using getUtilitiesFor:
+
+    >>> sorted(components.getUtilitiesFor(tests.I2))
+    [(u'', U12(2)), (u'three', U12(3))]
+
+getAllUtilitiesRegisteredFor is similar to getUtilitiesFor except that
+it includes utilities that are overridden.  For example, we'll
+register a utility that for an extending interface of I2:
+
+    >>> util = tests.U('ext')
+    >>> components.registerUtility(util, tests.I2e)
+    Registered event:
+    UtilityRegistration(<Components comps>, I2e, u'', ext, None, u'')
+
+We don't get the new utility for getUtilitiesFor:
+
+    >>> sorted(components.getUtilitiesFor(tests.I2))
+    [(u'', U12(2)), (u'three', U12(3))]
+
+but we do get it from getAllUtilitiesRegisteredFor:
+
+    >>> sorted(map(str, components.getAllUtilitiesRegisteredFor(tests.I2)))
+    ['U(ext)', 'U12(2)', 'U12(3)']
+
+Removing a utility also makes it disappear from getUtilitiesFor:
+
+    >>> components.unregisterUtility(util, tests.I2e)
+    Unregistered event:
+    UtilityRegistration(<Components comps>, I2e, u'', ext, None, u'')
+    True
+    >>> list(components.getAllUtilitiesRegisteredFor(tests.I2e))
+    []
+
+Adapters
+--------
+
+You can register adapters with registerAdapter:
+
+    >>> components.registerAdapter(tests.A12_1)
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'')
+
+Here, we didn't specify required interfaces, a provided interface, or
+a name.  The required interfaces were determined from the factory
+s __component_adapts__ attribute and the provided interface was
+determined by introspecting what the factory implements.
+
+    >>> components.getMultiAdapter((tests.U1(6), tests.U12(7)), tests.IA1)
+    A12_1(U1(6), U12(7))
+
+If a factory implements more than one interface, an exception will be
+raised:
+
+    >>> components.registerAdapter(tests.A1_12)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single
+    interface and no provided interface was specified.
+
+Unless the provided interface is specified:
+
+    >>> components.registerAdapter(tests.A1_12, provided=tests.IA2)
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+
+If a factory doesn't declare an implemented interface, an exception will be
+raised:
+
+    >>> components.registerAdapter(tests.A12_)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single
+    interface and no provided interface was specified. 
+
+Unless the provided interface is specified:
+
+    >>> components.registerAdapter(tests.A12_, provided=tests.IA2)
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1, I2], IA2, u'', A12_, u'')
+
+The required interface needs to be specified in the registration if
+the factory doesn't have a __component_adapts__ attribute: 
+
+    >>> components.registerAdapter(tests.A_2)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't have a __component_adapts__
+    attribute and no required specifications were specified 
+
+Unless the required specifications specified:
+
+    >>> components.registerAdapter(tests.A_2, required=[tests.I3])
+    Registered event:
+    AdapterRegistration(<Components comps>, [I3], IA2, u'', A_2, u'')
+
+Classes can be specified in place of specifications, in which case the
+implementedBy specification for the class is used:
+
+    >>> components.registerAdapter(tests.A_3, required=[tests.U],
+    ...                            info="Really class specific")
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    AdapterRegistration(<Components comps>, [zope.registry.tests.U], IA3, u'',
+                        A_3, 'Really class specific')
+
+We can see the adapters that have been registered using the
+registeredAdapters method:
+
+    >>> for registration in sorted(components.registeredAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<InterfaceClass zope.registry.tests.I1>, 
+     <InterfaceClass zope.registry.tests.I2>)
+    <InterfaceClass zope.registry.tests.IA1> 
+    zope.registry.tests.A12_1 
+    (<InterfaceClass zope.registry.tests.I1>, 
+     <InterfaceClass zope.registry.tests.I2>)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A12_ 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_12 
+    (<InterfaceClass zope.registry.tests.I3>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A_2 
+    (<implementedBy zope.registry.tests.U>,)
+    <InterfaceClass zope.registry.tests.IA3> 
+    zope.registry.tests.A_3 Really class specific
+
+As with utilities, we can provide registration information when
+registering adapters.
+
+If you try to fetch an adapter that isn't registered, you'll get a
+component-lookup error:
+
+    >>> components.getMultiAdapter((tests.U(8), ), tests.IA1)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    ComponentLookupError: ((U(8),), 
+                          <InterfaceClass zope.registry.tests.IA1>, u'')
+
+unless you use queryAdapter:
+
+    >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1)
+    >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1, default=42)
+    42
+
+When looking up an adapter for a single object, you can use the
+slightly simpler getAdapter and queryAdapter calls:
+
+    >>> components.getAdapter(tests.U1(9), tests.IA2)
+    A1_12(U1(9))
+
+    >>> components.queryAdapter(tests.U1(9), tests.IA2)
+    A1_12(U1(9))
+
+    >>> components.getAdapter(tests.U(8), tests.IA1)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    ComponentLookupError: (U(8), 
+                           <InterfaceClass zope.registry.tests.IA1>, u'')
+
+    >>> components.queryAdapter(tests.U(8), tests.IA2)
+    >>> components.queryAdapter(tests.U(8), tests.IA2, default=42)
+    42
+
+You can unregister an adapter.  If a factory is provided and if the
+rewuired and provided interfaces, can be infered, then they need not
+be provided:
+
+    >>> components.unregisterAdapter(tests.A12_1)
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I1, I2], IA1, u'', A12_1, u'')
+    True
+
+    >>> for registration in sorted(components.registeredAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<InterfaceClass zope.registry.tests.I1>, 
+     <InterfaceClass zope.registry.tests.I2>)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A12_ 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_12 
+    (<InterfaceClass zope.registry.tests.I3>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A_2 
+    (<implementedBy zope.registry.tests.U>,)
+    <InterfaceClass zope.registry.tests.IA3> 
+    zope.registry.tests.A_3 Really class specific
+
+A boolean is returned indicating whether a change was made.
+
+If a factory implements more than one interface, an exception will be
+raised:
+
+    >>> components.unregisterAdapter(tests.A1_12)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single
+    interface and no provided interface was specified.
+
+Unless the provided interface is specified:
+
+    >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2)
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+    True
+
+If a factory doesn't declare an implemented interface, an exception will be
+raised:
+
+    >>> components.unregisterAdapter(tests.A12_)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single
+    interface and no provided interface was specified. 
+
+Unless the provided interface is specified:
+
+    >>> components.unregisterAdapter(tests.A12_, provided=tests.IA2)
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I1, I2], IA2, u'', A12_, u'')
+    True
+
+The required interface needs to be specified if the factory doesn't
+have a __component_adapts__ attribute:
+
+    >>> components.unregisterAdapter(tests.A_2)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't have a __component_adapts__
+    attribute and no required specifications were specified 
+
+    >>> components.unregisterAdapter(tests.A_2, required=[tests.I3])
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I3], IA2, u'', A_2, u'')
+    True
+
+    >>> for registration in sorted(components.registeredAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<implementedBy zope.registry.tests.U>,)
+    <InterfaceClass zope.registry.tests.IA3> 
+    zope.registry.tests.A_3 Really class specific
+
+If a factory is unregistered that is not registered, False is
+returned:
+
+
+    >>> components.unregisterAdapter(tests.A_2, required=[tests.I3])
+    False
+    >>> components.unregisterAdapter(tests.A12_1, required=[tests.U])
+    False
+
+The factory can be omitted, to unregister *any* factory that matches
+specified required and provided interfaces:
+
+    >>> components.unregisterAdapter(required=[tests.U], provided=tests.IA3)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [zope.registry.tests.U], 
+                        IA3, u'', A_3, 'Really class specific')
+    True
+
+    >>> for registration in sorted(components.registeredAdapters()):
+    ...     print registration
+
+Adapters can be named:
+
+    >>> components.registerAdapter(tests.A1_12, provided=tests.IA2, 
+    ...                            name=u'test')
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'test', A1_12, u'')
+
+    >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2)
+    >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2, name=u'test')
+    A1_12(U1(9))
+
+    >>> components.queryAdapter(tests.U1(9), tests.IA2)
+    >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test')
+    A1_12(U1(9))
+    >>> components.getAdapter(tests.U1(9), tests.IA2, name=u'test')
+    A1_12(U1(9))
+
+It is possible to look up all of the adapters that provide an
+interface:
+
+    >>> components.registerAdapter(tests.A1_23, provided=tests.IA2, 
+    ...                            name=u'test 2')
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'test 2', A1_23, u'')
+
+    >>> components.registerAdapter(tests.A1_12, provided=tests.IA2)
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+
+    >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), 
+    ...                                                    tests.IA2)):
+    ...     print name, adapter
+     A1_12(U1(9))
+    test A1_12(U1(9))
+    test 2 A1_23(U1(9))
+
+
+getAdapters is most commonly used as the basis of menu systems.
+
+If an adapter factory returns None, it is equivalent to there being no
+factory:
+
+    >>> components.registerAdapter(tests.noop, 
+    ...                            required=[tests.IA1], provided=tests.IA2, 
+    ...                            name=u'test noop')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    AdapterRegistration(<Components comps>, [IA1], IA2, u'test noop', 
+                        noop, u'')
+    >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test noop')
+
+    >>> components.registerAdapter(tests.A1_12, provided=tests.IA2)
+    Registered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+
+    >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), 
+    ...                                                    tests.IA2)):
+    ...     print name, adapter
+     A1_12(U1(9))
+    test A1_12(U1(9))
+    test 2 A1_23(U1(9))
+
+
+    >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2, 
+    ...                              name=u'test')
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'test', A1_12, u'')
+    True
+    >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2)
+    Unregistered event:
+    AdapterRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+    True
+    >>> for registration in sorted(components.registeredAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> test 2
+    zope.registry.tests.A1_23 
+    (<InterfaceClass zope.registry.tests.IA1>,)
+    <InterfaceClass zope.registry.tests.IA2> test noop
+    <function noop at 0x...>
+
+
+Subscribers
+-----------
+
+Subscribers provide a way to get multiple adapters of a given type.
+In this regard, subscribers are like named adapters, except that there
+isn't any concept of the most specific adapter for a given name.
+
+Subscribers are registered by calling registerSubscriptionAdapter:
+
+    >>> components.registerSubscriptionAdapter(tests.A1_2)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, u'')
+
+    >>> components.registerSubscriptionAdapter(
+    ...     tests.A1_12, provided=tests.IA2)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_12, u'')
+
+    >>> components.registerSubscriptionAdapter(
+    ...     tests.A, [tests.I1], tests.IA2,
+    ...     info='a sample comment')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', 
+                             A, 'a sample comment')
+
+The same rules, with regard to when required and provided interfaces
+have to be specified apply as with adapters:
+
+    >>> components.registerSubscriptionAdapter(tests.A1_12)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single 
+    interface and no provided interface was specified.
+
+    >>> components.registerSubscriptionAdapter(tests.A)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single interface and
+     no provided interface was specified.
+
+    >>> components.registerSubscriptionAdapter(tests.A, required=[tests.IA1])
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single interface
+    and no provided interface was specified.
+
+Note that we provided the info argument as a keyword argument above.
+That's because there is a name argument that's reserved for future
+use. We can give a name, as long as it is an empty string:
+
+    >>> components.registerSubscriptionAdapter(
+    ...     tests.A, [tests.I1], tests.IA2, u'', 'a sample comment')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', 
+                             A, 'a sample comment')
+
+    >>> components.registerSubscriptionAdapter(
+    ...     tests.A, [tests.I1], tests.IA2, u'oops', 'a sample comment')
+    Traceback (most recent call last):
+    ...
+    TypeError: Named subscribers are not yet supported
+
+Subscribers are looked up using the subscribers method:
+
+    >>> for s in components.subscribers((tests.U1(1), ), tests.IA2):
+    ...    print s
+    A1_2(U1(1))
+    A1_12(U1(1))
+    A(U1(1),)
+    A(U1(1),)
+
+Note that, because we created multiple subscriptions for A, we got multiple
+subscriber instances.
+
+As with normal adapters, if a factory returns None, the result is skipped:
+
+    >>> components.registerSubscriptionAdapter(
+    ...     tests.noop, [tests.I1], tests.IA2)
+    Registered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', noop, u'')
+
+    >>> for s in components.subscribers((tests.U1(1), ), tests.IA2):
+    ...    print s
+    A1_2(U1(1))
+    A1_12(U1(1))
+    A(U1(1),)
+    A(U1(1),)
+
+We can get registration information for subscriptions:
+
+    >>> for registration in sorted(
+    ...     components.registeredSubscriptionAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A a sample comment
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A a sample comment
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_12 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_2 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    <function noop at 0x...>
+
+We can also unregister subscriptions in much the same way we can for adapters:
+
+    >>> components.unregisterSubscriptionAdapter(tests.A1_2)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Unregistered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A1_2, '')
+    True
+
+    >>> for s in components.subscribers((tests.U1(1), ), tests.IA2):
+    ...    print s
+    A1_12(U1(1))
+    A(U1(1),)
+    A(U1(1),)
+
+    >>> for registration in sorted(
+    ...     components.registeredSubscriptionAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A a sample comment
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A a sample comment
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_12 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    <function noop at 0x...>
+
+    >>> components.unregisterSubscriptionAdapter(
+    ...     tests.A, [tests.I1], tests.IA2)
+    Unregistered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', A, '')
+    True
+
+    >>> for s in components.subscribers((tests.U1(1), ), tests.IA2):
+    ...    print s
+    A1_12(U1(1))
+
+    >>> for registration in sorted(
+    ...     components.registeredSubscriptionAdapters()):
+    ...     print registration.required
+    ...     print registration.provided, registration.name
+    ...     print registration.factory, registration.info
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    zope.registry.tests.A1_12 
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <InterfaceClass zope.registry.tests.IA2> 
+    <function noop at 0x...>
+
+Note here that both registrations for A were removed.
+
+If we omit the factory, we must specify the required and provided interfaces:
+
+    >>> components.unregisterSubscriptionAdapter(required=[tests.I1])
+    Traceback (most recent call last):
+    ...
+    TypeError: Must specify one of factory and provided
+
+    >>> components.unregisterSubscriptionAdapter(provided=tests.IA2)
+    Traceback (most recent call last):
+    ...
+    TypeError: Must specify one of factory and required
+
+    >>> components.unregisterSubscriptionAdapter(
+    ...     required=[tests.I1], provided=tests.IA2)
+    Unregistered event:
+    SubscriptionRegistration(<Components comps>, [I1], IA2, u'', None, '')
+    True
+
+    >>> for s in components.subscribers((tests.U1(1), ), tests.IA2):
+    ...    print s
+
+    >>> for registration in sorted(
+    ...     components.registeredSubscriptionAdapters()):
+    ...     print registration.factory
+
+As when registering, an error is raised if the registration
+information can't be determined from the factory and isn't specified:
+
+    >>> components.unregisterSubscriptionAdapter(tests.A1_12)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single 
+    interface and no provided interface was specified.
+
+    >>> components.unregisterSubscriptionAdapter(tests.A)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single interface and
+     no provided interface was specified.
+
+    >>> components.unregisterSubscriptionAdapter(tests.A, required=[tests.IA1])
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't implement a single interface
+    and no provided interface was specified.
+
+If you unregister something that's not registered, nothing will be
+changed and False will be returned:
+
+
+    >>> components.unregisterSubscriptionAdapter(
+    ...     required=[tests.I1], provided=tests.IA2)
+    False
+
+Handlers
+--------
+
+Handlers are used when you want to perform some function in response
+to an event.  Handlers aren't expected to return anything when called
+and are not registered to provide any interface.
+
+    >>> from zope import registry
+    >>> @registry.adapter(tests.I1)
+    ... def handle1(x):
+    ...     print 'handle1', x
+
+    >>> components.registerHandler(handle1, info="First handler")
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    HandlerRegistration(<Components comps>, [I1], u'', 
+                        handle1, 'First handler')
+    >>> components.handle(tests.U1(1))
+    handle1 U1(1)
+
+    >>> @registry.adapter(tests.I1, tests.I2)
+    ... def handle12(x, y):
+    ...     print 'handle12', x, y
+
+    >>> components.registerHandler(handle12)
+    Registered event:
+    HandlerRegistration(<Components comps>, [I1, I2], u'', handle12, u'')
+    >>> components.handle(tests.U1(1), tests.U12(2))
+    handle12 U1(1) U12(2)
+
+If a handler doesn't document interfaces it handles, then 
+the required interfaces must be specified:
+
+    >>> def handle(*objects):
+    ...     print 'handle', objects
+
+    >>> components.registerHandler(handle)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't have a __component_adapts__ 
+    attribute and no required specifications were specified
+
+    >>> components.registerHandler(handle, required=[tests.I1], 
+    ...                            info="a comment")
+    Registered event:
+    HandlerRegistration(<Components comps>, [I1], u'', handle, 'a comment')
+
+Handlers can also be registered for classes:
+
+    >>> components.registerHandler(handle, required=[tests.U], 
+    ...                            info="handle a class")
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    HandlerRegistration(<Components comps>, [zope.registry.tests.U], u'', 
+                        handle, 'handle a class')
+
+
+    >>> components.handle(tests.U1(1))
+    handle (U1(1),)
+    handle1 U1(1)
+    handle (U1(1),)
+
+We can list the handler registrations:
+
+    >>> for registration in components.registeredHandlers():
+    ...     print registration.required
+    ...     print registration.handler, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle1 at 0x...> First handler
+    (<InterfaceClass zope.registry.tests.I1>,
+     <InterfaceClass zope.registry.tests.I2>)
+    <function handle12 at 0x...>
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle at 0x...> a comment
+    (<implementedBy zope.registry.tests.U>,)
+    <function handle at 0x...> handle a class
+
+and we can unregister handlers:
+
+    >>> components.unregisterHandler(required=[tests.U])
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Unregistered event:
+    HandlerRegistration(<Components comps>, [zope.registry.tests.U], u'', 
+                        None, '')
+    True
+
+    >>> for registration in components.registeredHandlers():
+    ...     print registration.required
+    ...     print registration.handler, registration.info
+    ... # doctest: +NORMALIZE_WHITESPACE
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle1 at 0x...> First handler
+    (<InterfaceClass zope.registry.tests.I1>,
+     <InterfaceClass zope.registry.tests.I2>)
+    <function handle12 at 0x...>
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle at 0x...> a comment
+
+    >>> components.unregisterHandler(handle12)
+    Unregistered event:
+    HandlerRegistration(<Components comps>, [I1, I2], u'', handle12, '')
+    True
+
+    >>> for registration in components.registeredHandlers():
+    ...     print registration.required
+    ...     print registration.handler, registration.info
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle1 at 0x...> First handler
+    (<InterfaceClass zope.registry.tests.I1>,)
+    <function handle at 0x...> a comment
+
+    >>> components.unregisterHandler(handle12)
+    False
+
+    >>> components.unregisterHandler()
+    Traceback (most recent call last):
+    ...
+    TypeError: Must specify one of factory and required
+
+    >>> components.registerHandler(handle)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TypeError: The adapter factory doesn't have a __component_adapts__ 
+    attribute and no required specifications were specified
+
+Extending
+---------
+
+Component-management objects can extend other component-management
+objects. 
+
+    >>> c1 = registry.Components('1')
+    >>> c1.__bases__
+    ()
+
+    >>> c2 = registry.Components('2', (c1, ))
+    >>> c2.__bases__ == (c1, )
+    True
+
+    >>> c1.registerUtility(tests.U1(1))
+    Registered event:
+    UtilityRegistration(<Components 1>, I1, u'', 1, None, u'')
+
+    >>> c1.queryUtility(tests.I1)
+    U1(1)
+    >>> c2.queryUtility(tests.I1)
+    U1(1)
+    >>> c1.registerUtility(tests.U1(2))
+    Unregistered event:
+    UtilityRegistration(<Components 1>, I1, u'', 1, None, u'')
+    Registered event:
+    UtilityRegistration(<Components 1>, I1, u'', 2, None, u'')
+
+    >>> c2.queryUtility(tests.I1)
+    U1(2)
+
+We can use multiple inheritence:
+
+    >>> c3 = registry.Components('3', (c1, ))
+    >>> c4 = registry.Components('4', (c2, c3))
+    >>> c4.queryUtility(tests.I1)
+    U1(2)
+
+    >>> c1.registerUtility(tests.U12(1), tests.I2)
+    Registered event:
+    UtilityRegistration(<Components 1>, I2, u'', 1, None, u'')
+
+    >>> c4.queryUtility(tests.I2)
+    U12(1)
+
+    >>> c3.registerUtility(tests.U12(3), tests.I2)
+    Registered event:
+    UtilityRegistration(<Components 3>, I2, u'', 3, None, u'')
+    >>> c4.queryUtility(tests.I2)
+    U12(3)
+
+    >>> c1.registerHandler(handle1, info="First handler")
+    Registered event:
+    HandlerRegistration(<Components 1>, [I1], u'', handle1, 'First handler')
+
+    >>> c2.registerHandler(handle, required=[tests.U])
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Registered event:
+    HandlerRegistration(<Components 2>, [zope.registry.tests.U], u'', 
+                        handle, u'')
+    
+    >>> @registry.adapter(tests.I1)
+    ... def handle3(x):
+    ...     print 'handle3', x
+    >>> c3.registerHandler(handle3)
+    Registered event:
+    HandlerRegistration(<Components 3>, [I1], u'', handle3, u'')
+    
+    >>> @registry.adapter(tests.I1)
+    ... def handle4(x):
+    ...     print 'handle4', x
+    >>> c4.registerHandler(handle4)
+    Registered event:
+    HandlerRegistration(<Components 4>, [I1], u'', handle4, u'')
+
+    >>> c4.handle(tests.U1(1))
+    handle1 U1(1)
+    handle3 U1(1)
+    handle (U1(1),)
+    handle4 U1(1)

Added: Sandbox/jbohman/zope.registry/trunk/bootstrap.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/bootstrap.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/bootstrap.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,119 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+"""
+
+import os, shutil, sys, tempfile, urllib2
+from optparse import OptionParser
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+# parsing arguments
+parser = OptionParser()
+parser.add_option("-v", "--version", dest="version",
+                          help="use a specific zc.buildout version")
+parser.add_option("-d", "--distribute",
+                   action="store_true", dest="distribute", default=False,
+                   help="Use Disribute rather than Setuptools.")
+
+parser.add_option("-c", None, action="store", dest="config_file",
+                   help=("Specify the path to the buildout configuration "
+                         "file to be used."))
+
+options, args = parser.parse_args()
+
+# if -c was provided, we push it back into args for buildout' main function
+if options.config_file is not None:
+    args += ['-c', options.config_file]
+
+if options.version is not None:
+    VERSION = '==%s' % options.version
+else:
+    VERSION = ''
+
+USE_DISTRIBUTE = options.distribute
+args = args + ['bootstrap']
+
+to_reload = False
+try:
+    import pkg_resources
+    if not hasattr(pkg_resources, '_distribute'):
+        to_reload = True
+        raise ImportError
+except ImportError:
+    ez = {}
+    if USE_DISTRIBUTE:
+        exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
+                         ).read() in ez
+        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
+    else:
+        exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                             ).read() in ez
+        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    if to_reload:
+        reload(pkg_resources)
+    else:
+        import pkg_resources
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+
+if USE_DISTRIBUTE:
+    requirement = 'distribute'
+else:
+    requirement = 'setuptools'
+
+if is_jython:
+    import subprocess
+
+    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+           quote(tmpeggs), 'zc.buildout' + VERSION],
+           env=dict(os.environ,
+               PYTHONPATH=
+               ws.find(pkg_resources.Requirement.parse(requirement)).location
+               ),
+           ).wait() == 0
+
+else:
+    assert os.spawnle(
+        os.P_WAIT, sys.executable, quote (sys.executable),
+        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
+        dict(os.environ,
+            PYTHONPATH=
+            ws.find(pkg_resources.Requirement.parse(requirement)).location
+            ),
+        ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout' + VERSION)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)

Added: Sandbox/jbohman/zope.registry/trunk/buildout.cfg
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/buildout.cfg	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/buildout.cfg	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,30 @@
+[buildout]
+develop = .
+parts = test python sphinx coverage-test coverage-report
+unzip = true
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zope.registry
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = ${test:eggs}
+defaults = ['--coverage', 'coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
+
+[python]
+recipe = zc.recipe.egg
+interpreter = python
+eggs = ${test:eggs}
+
+[sphinx]
+recipe = zc.recipe.egg
+eggs =
+    Sphinx
+    zope.registry

Added: Sandbox/jbohman/zope.registry/trunk/setup.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/setup.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/setup.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+# This package is developed by the Zope Toolkit project, documented here:
+# http://docs.zope.org/zopetoolkit
+# When developing and releasing this package, please follow the documented
+# Zope Toolkit policies as described by this documentation.
+##############################################################################
+"""Setup for zope.registry package
+"""
+import sys
+import os
+from setuptools import setup
+
+extra = {}
+if sys.version_info >= (3,):
+    extra['setup_requires'] = ['zope.fixers']
+    extra['use_2to3'] = True
+    extra['use_2to3_fixers'] = ['zope.fixers']
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(
+    name='zope.registry',
+    version='0.1dev',
+    url='http://pypi.python.org/pypi/zope.registry',
+    license='ZPL 2.1',
+    description='Zope Component Architecture',
+    author='Zope Foundation and Contributors',
+    author_email='zope-dev at zope.org',
+    long_description=(
+        read('README.txt')
+        + '\n' +
+        'Detailed Documentation\n'
+        '**********************\n'
+        + '\n' +
+        read('REGISTRY.txt')
+        + '\n' +
+        read('CHANGES.txt')
+        + '\n' +
+        'Download\n'
+        '********\n'
+        ),
+    packages=['zope', 'zope.registry', 'zope.registry.tests'],
+    package_dir={'': 'src'},
+    namespace_packages=['zope'],
+    install_requires=['setuptools', 'zope.interface', 'zope.event'],
+    tests_require=['setuptools', 'zope.interface', 'zope.event'],
+    test_suite='zope.registry.tests',
+    zip_safe=False,
+    **extra
+    )

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/__init__.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/__init__.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/__init__.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/__init__.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/__init__.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/__init__.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,545 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Basic components support
+"""
+import sys
+import types
+
+from zope.event import notify
+
+from zope.interface import Interface
+from zope.interface import implementedBy
+from zope.interface import implements
+from zope.interface import implementsOnly
+from zope.interface import providedBy
+from zope.interface.adapter import AdapterRegistry
+from zope.interface.interfaces import ISpecification
+
+from zope.registry.interfaces import ComponentLookupError
+from zope.registry.interfaces import IAdapterRegistration
+from zope.registry.interfaces import IComponents
+from zope.registry.interfaces import IHandlerRegistration
+from zope.registry.interfaces import IRegistrationEvent
+from zope.registry.interfaces import ISubscriptionAdapterRegistration
+from zope.registry.interfaces import IUtilityRegistration
+from zope.registry.interfaces import Registered
+from zope.registry.interfaces import Unregistered
+
+from zope.registry._declaration import adaptedBy
+from zope.registry._declaration import adapter
+from zope.registry._declaration import adapts
+
+if sys.version_info[0] == 3:
+    def _u(s):
+        return s
+    class_types = type
+    string_types = (str,)
+else:
+    def _u(s):
+        return unicode(s, 'unicode_escape')
+    class_types = (type, types.ClassType)
+    string_types = (basestring,)
+
+class Components(object):
+
+    implements(IComponents)
+
+    def __init__(self, name='', bases=()):
+        assert isinstance(name, string_types)
+        self.__name__ = name
+        self._init_registries()
+        self._init_registrations()
+        self.__bases__ = tuple(bases)
+
+    def __repr__(self):
+        return "<%s %s>" % (self.__class__.__name__, self.__name__)
+
+    def _init_registries(self):
+        self.adapters = AdapterRegistry()
+        self.utilities = AdapterRegistry()
+
+    def _init_registrations(self):
+        self._utility_registrations = {}
+        self._adapter_registrations = {}
+        self._subscription_registrations = []
+        self._handler_registrations = []
+
+    def _getBases(self):
+        # Subclasses might override
+        return self.__dict__.get('__bases__', ())
+
+    def _setBases(self, bases):
+        # Subclasses might override
+        self.adapters.__bases__ = tuple([
+            base.adapters for base in bases])
+        self.utilities.__bases__ = tuple([
+            base.utilities for base in bases])
+        self.__dict__['__bases__'] = tuple(bases)
+
+    __bases__ = property(
+        lambda self: self._getBases(),
+        lambda self, bases: self._setBases(bases),
+        )
+
+    def registerUtility(self, component=None, provided=None, name=_u(''), 
+                        info=_u(''), event=True, factory=None):
+        if factory:
+            if component:
+                raise TypeError("Can't specify factory and component.")
+            component = factory()
+
+        if provided is None:
+            provided = _getUtilityProvided(component)
+
+        reg = self._utility_registrations.get((provided, name))
+        if reg is not None:
+            if reg[:2] == (component, info):
+                # already registered
+                return
+            self.unregisterUtility(reg[0], provided, name)
+
+        subscribed = False
+        for ((p, _), data) in iter(self._utility_registrations.items()):
+            if p == provided and data[0] == component:
+                subscribed = True
+                break
+
+        self._utility_registrations[(provided, name)] = component, info, factory
+        self.utilities.register((), provided, name, component)
+
+        if not subscribed:
+            self.utilities.subscribe((), provided, component)
+
+        if event:
+            notify(Registered(
+                UtilityRegistration(self, provided, name, component, info,
+                                    factory)
+                ))
+
+    def unregisterUtility(self, component=None, provided=None, name=_u(''),
+                          factory=None):
+        if factory:
+            if component:
+                raise TypeError("Can't specify factory and component.")
+            component = factory()
+
+        if provided is None:
+            if component is None:
+                raise TypeError("Must specify one of component, factory and "
+                                "provided")
+            provided = _getUtilityProvided(component)
+
+        old = self._utility_registrations.get((provided, name))
+        if (old is None) or ((component is not None) and
+                             (component != old[0])):
+            return False
+
+        if component is None:
+            component = old[0]
+
+        # Note that component is now the old thing registered
+
+        del self._utility_registrations[(provided, name)]
+        self.utilities.unregister((), provided, name)
+
+        subscribed = False
+        for ((p, _), data) in iter(self._utility_registrations.items()):
+            if p == provided and data[0] == component:
+                subscribed = True
+                break
+
+        if not subscribed:
+            self.utilities.unsubscribe((), provided, component)
+
+        notify(Unregistered(
+            UtilityRegistration(self, provided, name, component, *old[1:])
+            ))
+
+        return True
+
+    def registeredUtilities(self):
+        for ((provided, name), data
+             ) in iter(self._utility_registrations.items()):
+            yield UtilityRegistration(self, provided, name, *data)
+
+    def queryUtility(self, provided, name=_u(''), default=None):
+        return self.utilities.lookup((), provided, name, default)
+
+    def getUtility(self, provided, name=_u('')):
+        utility = self.utilities.lookup((), provided, name)
+        if utility is None:
+            raise ComponentLookupError(provided, name)
+        return utility
+
+    def getUtilitiesFor(self, interface):
+        for name, utility in self.utilities.lookupAll((), interface):
+            yield name, utility
+
+    def getAllUtilitiesRegisteredFor(self, interface):
+        return self.utilities.subscriptions((), interface)
+
+    def registerAdapter(self, factory, required=None, provided=None, 
+                        name=_u(''), info=_u(''), event=True):
+        if provided is None:
+            provided = _getAdapterProvided(factory)
+        required = _getAdapterRequired(factory, required)
+        self._adapter_registrations[(required, provided, name)
+                                    ] = factory, info
+        self.adapters.register(required, provided, name, factory)
+
+        if event:
+            notify(Registered(
+                AdapterRegistration(self, required, provided, name,
+                                    factory, info)
+                ))
+
+
+    def unregisterAdapter(self, factory=None,
+                          required=None, provided=None, name=_u(''),
+                          ):
+        if provided is None:
+            if factory is None:
+                raise TypeError("Must specify one of factory and provided")
+            provided = _getAdapterProvided(factory)
+
+        if (required is None) and (factory is None):
+            raise TypeError("Must specify one of factory and required")
+
+        required = _getAdapterRequired(factory, required)
+        old = self._adapter_registrations.get((required, provided, name))
+        if (old is None) or ((factory is not None) and
+                             (factory != old[0])):
+            return False
+
+        del self._adapter_registrations[(required, provided, name)]
+        self.adapters.unregister(required, provided, name)
+
+        notify(Unregistered(
+            AdapterRegistration(self, required, provided, name,
+                                *old)
+            ))
+
+        return True
+
+    def registeredAdapters(self):
+        for ((required, provided, name), (component, info)
+             ) in iter(self._adapter_registrations.items()):
+            yield AdapterRegistration(self, required, provided, name,
+                                      component, info)
+
+    def queryAdapter(self, object, interface, name=_u(''), default=None):
+        return self.adapters.queryAdapter(object, interface, name, default)
+
+    def getAdapter(self, object, interface, name=_u('')):
+        adapter = self.adapters.queryAdapter(object, interface, name)
+        if adapter is None:
+            raise ComponentLookupError(object, interface, name)
+        return adapter
+
+    def queryMultiAdapter(self, objects, interface, name=_u(''), 
+                          default=None):
+        return self.adapters.queryMultiAdapter(
+            objects, interface, name, default)
+
+    def getMultiAdapter(self, objects, interface, name=_u('')):
+        adapter = self.adapters.queryMultiAdapter(objects, interface, name)
+        if adapter is None:
+            raise ComponentLookupError(objects, interface, name)
+        return adapter
+
+    def getAdapters(self, objects, provided):
+        for name, factory in self.adapters.lookupAll(
+            list(map(providedBy, objects)),
+            provided):
+            adapter = factory(*objects)
+            if adapter is not None:
+                yield name, adapter
+
+    def registerSubscriptionAdapter(self,
+                                    factory, required=None, provided=None,
+                                    name=_u(''), info=_u(''),
+                                    event=True):
+        if name:
+            raise TypeError("Named subscribers are not yet supported")
+        if provided is None:
+            provided = _getAdapterProvided(factory)
+        required = _getAdapterRequired(factory, required)
+        self._subscription_registrations.append(
+            (required, provided, name, factory, info)
+            )
+        self.adapters.subscribe(required, provided, factory)
+
+        if event:
+            notify(Registered(
+                SubscriptionRegistration(self, required, provided, name,
+                                         factory, info)
+                ))
+
+    def registeredSubscriptionAdapters(self):
+        for data in self._subscription_registrations:
+            yield SubscriptionRegistration(self, *data)
+
+    def unregisterSubscriptionAdapter(self, factory=None,
+                          required=None, provided=None, name=_u(''),
+                          ):
+        if name:
+            raise TypeError("Named subscribers are not yet supported")
+        if provided is None:
+            if factory is None:
+                raise TypeError("Must specify one of factory and provided")
+            provided = _getAdapterProvided(factory)
+
+        if (required is None) and (factory is None):
+            raise TypeError("Must specify one of factory and required")
+
+        required = _getAdapterRequired(factory, required)
+
+        if factory is None:
+            new = [(r, p, n, f, i)
+                   for (r, p, n, f, i)
+                   in self._subscription_registrations
+                   if not (r == required and p == provided)
+                   ]
+        else:
+            new = [(r, p, n, f, i)
+                   for (r, p, n, f, i)
+                   in self._subscription_registrations
+                   if not (r == required and p == provided and f == factory)
+                   ]
+
+        if len(new) == len(self._subscription_registrations):
+            return False
+
+
+        self._subscription_registrations[:] = new
+        self.adapters.unsubscribe(required, provided, factory)
+
+        notify(Unregistered(
+            SubscriptionRegistration(self, required, provided, name,
+                                     factory, '')
+            ))
+
+        return True
+
+    def subscribers(self, objects, provided):
+        return self.adapters.subscribers(objects, provided)
+
+    def registerHandler(self,
+                        factory, required=None,
+                        name=_u(''), info=_u(''),
+                        event=True):
+        if name:
+            raise TypeError("Named handlers are not yet supported")
+        required = _getAdapterRequired(factory, required)
+        self._handler_registrations.append(
+            (required, name, factory, info)
+            )
+        self.adapters.subscribe(required, None, factory)
+
+        if event:
+            notify(Registered(
+                HandlerRegistration(self, required, name, factory, info)
+                ))
+
+    def registeredHandlers(self):
+        for data in self._handler_registrations:
+            yield HandlerRegistration(self, *data)
+
+    def unregisterHandler(self, factory=None, required=None, name=_u('')):
+        if name:
+            raise TypeError("Named subscribers are not yet supported")
+
+        if (required is None) and (factory is None):
+            raise TypeError("Must specify one of factory and required")
+
+        required = _getAdapterRequired(factory, required)
+
+        if factory is None:
+            new = [(r, n, f, i)
+                   for (r, n, f, i)
+                   in self._handler_registrations
+                   if r != required
+                   ]
+        else:
+            new = [(r, n, f, i)
+                   for (r, n, f, i)
+                   in self._handler_registrations
+                   if not (r == required and f == factory)
+                   ]
+
+        if len(new) == len(self._handler_registrations):
+            return False
+
+        self._handler_registrations[:] = new
+        self.adapters.unsubscribe(required, None, factory)
+
+        notify(Unregistered(
+            HandlerRegistration(self, required, name, factory, '')
+            ))
+
+        return True
+
+    def handle(self, *objects):
+        self.adapters.subscribers(objects, None)
+
+
+def _getUtilityProvided(component):
+    provided = list(providedBy(component))
+    if len(provided) == 1:
+        return provided[0]
+    raise TypeError(
+        "The utility doesn't provide a single interface "
+        "and no provided interface was specified.")
+
+def _getAdapterProvided(factory):
+    provided = list(implementedBy(factory))
+    if len(provided) == 1:
+        return provided[0]
+    raise TypeError(
+        "The adapter factory doesn't implement a single interface "
+        "and no provided interface was specified.")
+
+def _getAdapterRequired(factory, required):
+    if required is None:
+        try:
+            required = factory.__component_adapts__
+        except AttributeError:
+            raise TypeError(
+                "The adapter factory doesn't have a __component_adapts__ "
+                "attribute and no required specifications were specified"
+                )
+    elif ISpecification.providedBy(required):
+        raise TypeError("the required argument should be a list of "
+                        "interfaces, not a single interface")
+
+    result = []
+    for r in required:
+        if r is None:
+            r = Interface
+        elif not ISpecification.providedBy(r):
+            if isinstance(r, class_types):
+                r = implementedBy(r)
+            else:
+                raise TypeError("Required specification must be a "
+                                "specification or class."
+                                )
+        result.append(r)
+    return tuple(result)
+
+
+class UtilityRegistration(object):
+
+    implements(IUtilityRegistration)
+
+    def __init__(self, registry, provided, name, component, doc, factory=None):
+        (self.registry, self.provided, self.name, self.component, self.info,
+         self.factory
+         ) = registry, provided, name, component, doc, factory
+
+    def __repr__(self):
+        return '%s(%r, %s, %r, %s, %r, %r)' % (
+                self.__class__.__name__,
+                self.registry,
+                getattr(self.provided, '__name__', None), self.name,
+                getattr(self.component, '__name__', repr(self.component)),
+                self.factory, self.info,
+                )
+
+    def __hash__(self):
+        return id(self)
+
+    def __eq__(self, other):
+        return repr(self) == repr(other)
+
+    def __ne__(self, other):
+        return repr(self) != repr(other)
+
+    def __lt__(self, other):
+        return repr(self) < repr(other)
+
+    def __le__(self, other):
+        return repr(self) <= repr(other)
+
+    def __gt__(self, other):
+        return repr(self) > repr(other)
+
+    def __ge__(self, other):
+        return repr(self) >= repr(other)
+
+class AdapterRegistration(object):
+
+    implements(IAdapterRegistration)
+
+    def __init__(self, registry, required, provided, name, component, doc):
+        (self.registry, self.required, self.provided, self.name,
+         self.factory, self.info
+         ) = registry, required, provided, name, component, doc
+
+    def __repr__(self):
+        return '%s(%r, %s, %s, %r, %s, %r)' % (
+            self.__class__.__name__,
+            self.registry,
+            '[' + ", ".join([r.__name__ for r in self.required]) + ']',
+            getattr(self.provided, '__name__', None), self.name,
+            getattr(self.factory, '__name__', repr(self.factory)), self.info,
+            )
+
+    def __hash__(self):
+        return id(self)
+
+    def __eq__(self, other):
+        return repr(self) == repr(other)
+
+    def __ne__(self, other):
+        return repr(self) != repr(other)
+
+    def __lt__(self, other):
+        return repr(self) < repr(other)
+
+    def __le__(self, other):
+        return repr(self) <= repr(other)
+
+    def __gt__(self, other):
+        return repr(self) > repr(other)
+
+    def __ge__(self, other):
+        return repr(self) >= repr(other)
+
+class SubscriptionRegistration(AdapterRegistration):
+
+    implementsOnly(ISubscriptionAdapterRegistration)
+
+class HandlerRegistration(AdapterRegistration):
+
+    implementsOnly(IHandlerRegistration)
+
+    def __init__(self, registry, required, name, handler, doc):
+        (self.registry, self.required, self.name, self.handler, self.info
+         ) = registry, required, name, handler, doc
+
+    @property
+    def factory(self):
+        return self.handler
+
+    provided = None
+
+    def __repr__(self):
+        return '%s(%r, %s, %r, %s, %r)' % (
+            self.__class__.__name__,
+            self.registry,
+            '[' + ", ".join([r.__name__ for r in self.required]) + ']',
+            self.name,
+            getattr(self.factory, '__name__', repr(self.factory)), self.info,
+            )
+

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/_declaration.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/_declaration.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/_declaration.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Adapter declarations"""
+import types
+import sys
+
+if sys.version_info[0] == 3:
+    _class_types = type
+else:
+    _class_types = (type, types.ClassType)
+
+class adapter:
+
+    def __init__(self, *interfaces):
+        self.interfaces = interfaces
+
+    def __call__(self, ob):
+        if isinstance(ob, _class_types):
+            ob.__component_adapts__ = _adapts_descr(self.interfaces)
+        else:
+            ob.__component_adapts__ = self.interfaces
+
+        return ob
+
+def adapts(*interfaces):
+    frame = sys._getframe(1)
+    locals = frame.f_locals
+
+    # Try to make sure we were called from a class def. In 2.2.0 we can't
+    # check for __module__ since it doesn't seem to be added to the locals
+    # until later on.
+    if (locals is frame.f_globals) or (
+        ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)):
+        raise TypeError("adapts can be used only from a class definition.")
+
+    if '__component_adapts__' in locals:
+        raise TypeError("adapts can be used only once in a class definition.")
+
+    locals['__component_adapts__'] = _adapts_descr(interfaces)
+
+def adaptedBy(ob):
+    return getattr(ob, '__component_adapts__', None)
+
+class _adapts_descr(object):
+    def __init__(self, interfaces):
+        self.interfaces = interfaces
+
+    def __get__(self, inst, cls):
+        if inst is None:
+            return self.interfaces
+        raise AttributeError('__component_adapts__')

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/interfaces.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/interfaces.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/interfaces.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,950 @@
+############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+############################################################################
+"""Component and Component Architecture Interfaces
+"""
+__docformat__ = "reStructuredText"
+import sys
+
+from zope.interface import Attribute
+from zope.interface import Interface
+from zope.interface import implements
+
+if sys.version_info[0] == 3:
+    def _u(s):
+        return s
+else:
+    def _u(s):
+        return unicode(s, 'unicode_escape')
+
+class ComponentLookupError(LookupError):
+    """A component could not be found."""
+
+class Invalid(Exception):
+    """A component doesn't satisfy a promise."""
+
+class Misused(Exception):
+    """A component is being used (registered) for the wrong interface."""
+
+
+class IObjectEvent(Interface):
+    """An event related to an object.
+
+    The object that generated this event is not necessarily the object
+    refered to by location.
+    """
+
+    object = Attribute("The subject of the event.")
+
+
+class ObjectEvent(object):
+    implements(IObjectEvent)
+
+    def __init__(self, object):
+        self.object = object
+
+class IComponentArchitecture(Interface):
+    """The Component Architecture is defined by two key components: Adapters
+    and Utiltities. Both are managed by site managers. All other components
+    build on top of them.
+    """
+    # Site Manager API
+
+    def getGlobalSiteManager():
+        """Return the global site manager.
+
+        This function should never fail and always return an object that
+        provides `IGlobalSiteManager`.
+        """
+
+    def getSiteManager(context=None):
+        """Get the nearest site manager in the given context.
+
+        If `context` is `None`, return the global site manager.
+
+        If the `context` is not `None`, it is expected that an adapter
+        from the `context` to `IComponentLookup` can be found. If no
+        adapter is found, a `ComponentLookupError` is raised.
+
+        """
+
+    # Utility API
+
+    def getUtility(interface, name='', context=None):
+        """Get the utility that provides interface
+
+        Returns the nearest utility to the context that implements the
+        specified interface.  If one is not found, raises
+        ComponentLookupError.
+        """
+
+    def queryUtility(interface, name='', default=None, context=None):
+        """Look for the utility that provides interface
+
+        Returns the nearest utility to the context that implements
+        the specified interface.  If one is not found, returns default.
+        """
+
+    def queryNextUtility(context, interface, name='', default=None):
+        """Query for the next available utility.
+    
+        Find the next available utility providing `interface` and having the
+        specified name. If no utility was found, return the specified `default`
+        value.
+        """
+    
+    def getNextUtility(context, interface, name=''):
+        """Get the next available utility.
+    
+        If no utility was found, a `ComponentLookupError` is raised.
+        """
+
+    def getUtilitiesFor(interface, context=None):
+        """Return the utilities that provide an interface
+
+        An iterable of utility name-value pairs is returned.
+        """
+
+    def getAllUtilitiesRegisteredFor(interface, context=None):
+        """Return all registered utilities for an interface
+
+        This includes overridden utilities.
+
+        An iterable of utility instances is returned.  No names are
+        returned.
+        """
+
+    # Adapter API
+
+    def getAdapter(object,
+                   interface=Interface, name=_u(''),
+                   context=None):
+        """Get a named adapter to an interface for an object
+
+        Returns an adapter that can adapt object to interface.  If a matching
+        adapter cannot be found, raises ComponentLookupError.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters' service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+        """
+
+    def getAdapterInContext(object, interface, context):
+        """Get a special adapter to an interface for an object
+
+        NOTE: This method should only be used if a custom context
+        needs to be provided to provide custom component
+        lookup. Otherwise, call the interface, as in::
+
+           interface(object)
+
+        Returns an adapter that can adapt object to interface.  If a matching
+        adapter cannot be found, raises ComponentLookupError.
+
+        Context is adapted to IServiceService, and this adapter's
+        'Adapters' service is used.
+
+        If the object has a __conform__ method, this method will be
+        called with the requested interface.  If the method returns a
+        non-None value, that value will be returned. Otherwise, if the
+        object already implements the interface, the object will be
+        returned.
+        """
+
+    def getMultiAdapter(objects,
+                        interface=Interface, name='',
+                        context=None):
+        """Look for a multi-adapter to an interface for an objects
+
+        Returns a multi-adapter that can adapt objects to interface.  If a
+        matching adapter cannot be found, raises ComponentLookupError.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters' service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+
+        The name consisting of an empty string is reserved for unnamed
+        adapters. The unnamed adapter methods will often call the
+        named adapter methods with an empty string for a name.
+        """
+
+    def queryAdapter(object, interface=Interface, name=_u(''),
+                     default=None, context=None):
+        """Look for a named adapter to an interface for an object
+
+        Returns an adapter that can adapt object to interface.  If a matching
+        adapter cannot be found, returns the default.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters' service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+        """
+
+    def queryAdapterInContext(object, interface, context, default=None):
+        """Look for a special adapter to an interface for an object
+
+        NOTE: This method should only be used if a custom context
+        needs to be provided to provide custom component
+        lookup. Otherwise, call the interface, as in::
+
+           interface(object, default)
+
+        Returns an adapter that can adapt object to interface.  If a matching
+        adapter cannot be found, returns the default.
+
+        Context is adapted to IServiceService, and this adapter's
+        'Adapters' service is used.
+
+        If the object has a __conform__ method, this method will be
+        called with the requested interface.  If the method returns a
+        non-None value, that value will be returned. Otherwise, if the
+        object already implements the interface, the object will be
+        returned.
+        """
+
+    def queryMultiAdapter(objects,
+                          interface=Interface, name=_u(''),
+                          default=None,
+                          context=None):
+        """Look for a multi-adapter to an interface for objects
+
+        Returns a multi-adapter that can adapt objects to interface.  If a
+        matching adapter cannot be found, returns the default.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters' service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+
+        The name consisting of an empty string is reserved for unnamed
+        adapters. The unnamed adapter methods will often call the
+        named adapter methods with an empty string for a name.
+        """
+
+    def getAdapters(objects, provided, context=None):
+        """Look for all matching adapters to a provided interface for objects
+
+        Return a list of adapters that match. If an adapter is named, only the
+        most specific adapter of a given name is returned.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters'
+        service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+        """
+
+    def subscribers(required, provided, context=None):
+        """Get subscribers
+
+        Subscribers are returned that provide the provided interface
+        and that depend on and are computed from the sequence of
+        required objects.
+
+        If context is None, an application-defined policy is used to choose
+        an appropriate service manager from which to get an 'Adapters'
+        service.
+
+        If 'context' is not None, context is adapted to IServiceService,
+        and this adapter's 'Adapters' service is used.
+        """
+
+    def handle(*objects):
+        """Call all of the handlers for the given objects
+
+        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.
+
+        """
+
+
+    def adapts(*interfaces):
+        """Declare that a class adapts the given interfaces.
+
+        This function can only be used in a class definition.
+
+        (TODO, allow classes to be passed as well as interfaces.)
+        """
+
+    # Factory service
+
+    def createObject(factory_name, *args, **kwargs):
+        """Create an object using a factory
+
+        Finds the named factory in the current site and calls it with
+        the given arguments.  If a matching factory cannot be found
+        raises ComponentLookupError.  Returns the created object.
+
+        A context keyword argument can be provided to cause the
+        factory to be looked up in a location other than the current
+        site.  (Of course, this means that it is impossible to pass a
+        keyword argument named "context" to the factory.
+        """
+
+    def getFactoryInterfaces(name, context=None):
+        """Get interfaces implemented by a factory
+
+        Finds the factory of the given name that is nearest to the
+        context, and returns the interface or interface tuple that
+        object instances created by the named factory will implement.
+        """
+
+    def getFactoriesFor(interface, context=None):
+        """Return a tuple (name, factory) of registered factories that
+        create objects which implement the given interface.
+        """
+
+class IComponentLookup(Interface):
+    """Component Manager for a Site
+
+    This object manages the components registered at a particular site. The
+    definition of a site is intentionally vague.
+    """
+
+    adapters = Attribute(
+        "Adapter Registry to manage all registered adapters.")
+
+    utilities = Attribute(
+        "Adapter Registry to manage all registered utilities.")
+
+    def queryAdapter(object, interface, name=_u(''), default=None):
+        """Look for a named adapter to an interface for an object
+
+        If a matching adapter cannot be found, returns the default.
+        """
+
+    def getAdapter(object, interface, name=_u('')):
+        """Look for a named adapter to an interface for an object
+
+        If a matching adapter cannot be found, a ComponentLookupError
+        is raised.
+        """
+
+    def queryMultiAdapter(objects, interface, name=_u(''), default=None):
+        """Look for a multi-adapter to an interface for multiple objects
+
+        If a matching adapter cannot be found, returns the default.
+        """
+
+    def getMultiAdapter(objects, interface, name=_u('')):
+        """Look for a multi-adapter to an interface for multiple objects
+
+        If a matching adapter cannot be found, a ComponentLookupError
+        is raised.
+        """
+
+    def getAdapters(objects, provided):
+        """Look for all matching adapters to a provided interface for objects
+
+        Return an iterable of name-adapter pairs for adapters that
+        provide the given interface.
+        """
+
+    def subscribers(objects, provided):
+        """Get subscribers
+
+        Subscribers are returned that provide the provided interface
+        and that depend on and are comuted from the sequence of
+        required objects.
+        """
+
+    def handle(*objects):
+        """Call handlers for the given objects
+
+        Handlers registered for the given objects are called.
+        """
+
+    def queryUtility(interface, name='', default=None):
+        """Look up a utility that provides an interface.
+
+        If one is not found, returns default.
+        """
+
+    def getUtilitiesFor(interface):
+        """Look up the registered utilities that provide an interface.
+
+        Returns an iterable of name-utility pairs.
+        """
+
+    def getAllUtilitiesRegisteredFor(interface):
+        """Return all registered utilities for an interface
+
+        This includes overridden utilities.
+
+        An iterable of utility instances is returned.  No names are
+        returned.
+        """
+
+class IComponentRegistrationConvenience(Interface):
+    """API for registering components.
+
+    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.
+    """
+
+    def provideUtility(component, provides=None, name=_u('')):
+        """Register a utility globally
+
+        A utility is registered to provide an interface with a
+        name. If a component provides only one interface, then the
+        provides argument can be omitted and the provided interface
+        will be used. (In this case, provides argument can still be
+        provided to provide a less specific interface.)
+
+        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.
+
+        """
+
+    def provideAdapter(factory, adapts=None, provides=None, name=_u('')):
+        """Register an adapter globally
+
+        An adapter is registered to provide an interface with a name
+        for some number of object types. If a factory implements only
+        one interface, then the provides argument can be omitted and
+        the provided interface will be used. (In this case, a provides
+        argument can still be provided to provide a less specific
+        interface.)
+
+        If the factory has an adapts declaration, then the adapts
+        argument can be omitted and the declaration will be used.  (An
+        adapts argument can be provided to override the declaration.)
+
+        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.
+        """
+
+    def provideSubscriptionAdapter(factory, adapts=None, provides=None):
+        """Register a subscription adapter
+
+        A subscription adapter is registered to provide an interface
+        for some number of object types. If a factory implements only
+        one interface, then the provides argument can be omitted and
+        the provided interface will be used. (In this case, a provides
+        argument can still be provided to provide a less specific
+        interface.)
+
+        If the factory has an adapts declaration, then the adapts
+        argument can be omitted and the declaration will be used.  (An
+        adapts argument can be provided to override the declaration.)
+
+        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.
+        """
+
+    def provideHandler(handler, adapts=None):
+        """Register a handler
+
+        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.
+
+        If the handler has an adapts declaration, then the adapts
+        argument can be omitted and the declaration will be used.  (An
+        adapts argument can be provided to override the declaration.)
+
+        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.
+        """
+
+class IRegistry(Interface):
+    """Object that supports component registry
+    """
+
+    def registrations():
+        """Return an iterable of component registrations
+        """
+
+class IFactory(Interface):
+    """A factory is responsible for creating other components."""
+
+    title = Attribute("The factory title.")
+
+    description = Attribute("A brief description of the factory.")
+
+    def __call__(*args, **kw):
+        """Return an instance of the objects we're a factory for."""
+
+
+    def getInterfaces():
+        """Get the interfaces implemented by the factory
+
+        Return the interface(s), as an instance of Implements, that objects
+        created by this factory will implement. If the callable's Implements
+        instance cannot be created, an empty Implements instance is returned.
+        """
+
+class IRegistration(Interface):
+    """A registration-information object
+    """
+
+    registry = Attribute("The registry having the registration")
+
+    name = Attribute("The registration name")
+
+    info = Attribute("""Information about the registration
+
+    This is information deemed useful to people browsing the
+    configuration of a system. It could, for example, include
+    commentary or information about the source of the configuration.
+    """)
+
+class IUtilityRegistration(IRegistration):
+    """Information about the registration of a utility
+    """
+
+    factory = Attribute("The factory used to create the utility. Optional.")
+    component = Attribute("The object registered")
+    provided = Attribute("The interface provided by the component")
+
+class _IBaseAdapterRegistration(IRegistration):
+    """Information about the registration of an adapter
+    """
+
+    factory = Attribute("The factory used to create adapters")
+
+    required = Attribute("""The adapted interfaces
+
+    This is a sequence of interfaces adapters by the registered
+    factory.  The factory will be caled with a sequence of objects, as
+    positional arguments, that provide these interfaces.
+    """)
+
+    provided = Attribute("""The interface provided by the adapters.
+
+    This interface is implemented by the factory
+    """)
+
+class IAdapterRegistration(_IBaseAdapterRegistration):
+    """Information about the registration of an adapter
+    """
+
+class ISubscriptionAdapterRegistration(_IBaseAdapterRegistration):
+    """Information about the registration of a subscription adapter
+    """
+
+class IHandlerRegistration(IRegistration):
+
+    handler = Attribute("An object called used to handle an event")
+
+    required = Attribute("""The handled interfaces
+
+    This is a sequence of interfaces handled by the registered
+    handler.  The handler will be caled with a sequence of objects, as
+    positional arguments, that provide these interfaces.
+    """)
+
+class IRegistrationEvent(IObjectEvent):
+    """An event that involves a registration"""
+
+class RegistrationEvent(ObjectEvent):
+    """There has been a change in a registration
+    """
+    implements(IRegistrationEvent)
+
+    def __repr__(self):
+        return "%s event:\n%r" % (self.__class__.__name__, self.object)
+
+class IRegistered(IRegistrationEvent):
+    """A component or factory was registered
+    """
+
+class Registered(RegistrationEvent):
+    implements(IRegistered)
+
+class IUnregistered(IRegistrationEvent):
+    """A component or factory was unregistered
+    """
+
+class Unregistered(RegistrationEvent):
+    """A component or factory was unregistered
+    """
+    implements(IUnregistered)
+
+class IComponentRegistry(Interface):
+    """Register components
+    """
+
+    def registerUtility(component=None, provided=None, name=_u(''), info=_u(''), factory=None):
+        """Register a utility
+
+        factory
+           Factory for the component to be registerd.
+
+        component
+           The registered component
+
+        provided
+           This is the interface provided by the utility.  If the
+           component provides a single interface, then this
+           argument is optional and the component-implemented
+           interface will be used.
+
+        name
+           The utility name.
+
+        info
+           An object that can be converted to a string to provide
+           information about the registration.
+
+        Only one of component and factory can be used.
+        A Registered event is generated with an IUtilityRegistration.
+        """
+
+    def unregisterUtility(component=None, provided=None, name=_u(''), factory=None):
+        """Unregister a utility
+
+        A boolean is returned indicating whether the registry was
+        changed.  If the given component is None and there is no
+        component registered, or if the given component is not
+        None and is not registered, then the function returns
+        False, otherwise it returns True.
+
+        factory
+           Factory for the component to be unregisterd.
+
+        component
+           The registered component The given component can be
+           None, in which case any component registered to provide
+           the given provided interface with the given name is
+           unregistered.
+
+        provided
+           This is the interface provided by the utility.  If the
+           component is not None and provides a single interface,
+           then this argument is optional and the
+           component-implemented interface will be used.
+
+        name
+           The utility name.
+
+        Only one of component and factory can be used.
+        An UnRegistered event is generated with an IUtilityRegistration.
+        """
+
+    def registeredUtilities():
+        """Return an iterable of IUtilityRegistration instances.
+
+        These registrations describe the current utility registrations
+        in the object.
+        """
+
+    def registerAdapter(factory, required=None, provided=None, name=_u(''),
+                       info=_u('')):
+        """Register an adapter factory
+
+        Parameters:
+
+        factory
+            The object used to compute the adapter
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute is usually attribute is
+            normally set in class definitions using adapts
+            function, or for callables using the adapter
+            decorator.  If the factory doesn't have a
+            __component_adapts__ adapts attribute, then this
+            argument is required.
+
+        provided
+            This is the interface provided by the adapter and
+            implemented by the factory.  If the factory
+            implements a single interface, then this argument is
+            optional and the factory-implemented interface will be
+            used.
+
+        name
+            The adapter name.
+
+        info
+           An object that can be converted to a string to provide
+           information about the registration.
+
+        A Registered event is generated with an IAdapterRegistration.
+        """
+
+    def unregisterAdapter(factory=None, required=None,
+                          provided=None, name=_u('')):
+        """Register an adapter factory
+
+        A boolean is returned indicating whether the registry was
+        changed.  If the given component is None and there is no
+        component registered, or if the given component is not
+        None and is not registered, then the function returns
+        False, otherwise it returns True.
+
+        Parameters:
+
+        factory
+            This is the object used to compute the adapter. The
+            factory can be None, in which case any factory
+            registered to implement the given provided interface
+            for the given required specifications with the given
+            name is unregistered.
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If the factory is not None and the required
+            arguments is omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute attribute is normally
+            set in class definitions using adapts function, or for
+            callables using the adapter decorator.  If the factory
+            is None or doesn't have a __component_adapts__ adapts
+            attribute, then this argument is required.
+
+        provided
+            This is the interface provided by the adapter and
+            implemented by the factory.  If the factory is not
+            None and implements a single interface, then this
+            argument is optional and the factory-implemented
+            interface will be used.
+
+        name
+            The adapter name.
+
+        An Unregistered event is generated with an IAdapterRegistration.
+        """
+
+    def registeredAdapters():
+        """Return an iterable of IAdapterRegistration instances.
+
+        These registrations describe the current adapter registrations
+        in the object.
+        """
+
+    def registerSubscriptionAdapter(factory, required=None, provides=None,
+                                    name=_u(''), info=''):
+        """Register a subscriber factory
+
+        Parameters:
+
+        factory
+            The object used to compute the adapter
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute is usually attribute is
+            normally set in class definitions using adapts
+            function, or for callables using the adapter
+            decorator.  If the factory doesn't have a
+            __component_adapts__ adapts attribute, then this
+            argument is required.
+
+        provided
+            This is the interface provided by the adapter and
+            implemented by the factory.  If the factory implements
+            a single interface, then this argument is optional and
+            the factory-implemented interface will be used.
+
+        name
+            The adapter name.
+
+            Currently, only the empty string is accepted.  Other
+            strings will be accepted in the future when support for
+            named subscribers is added.
+
+        info
+           An object that can be converted to a string to provide
+           information about the registration.
+
+        A Registered event is generated with an
+        ISubscriptionAdapterRegistration.
+        """
+
+    def unregisterSubscriptionAdapter(factory=None, required=None,
+                                      provides=None, name=_u('')):
+        """Unregister a subscriber factory.
+
+        A boolean is returned indicating whether the registry was
+        changed.  If the given component is None and there is no
+        component registered, or if the given component is not
+        None and is not registered, then the function returns
+        False, otherwise it returns True.
+
+        Parameters:
+
+        factory
+            This is the object used to compute the adapter. The
+            factory can be None, in which case any factories
+            registered to implement the given provided interface
+            for the given required specifications with the given
+            name are unregistered.
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If the factory is not None and the required
+            arguments is omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute attribute is normally
+            set in class definitions using adapts function, or for
+            callables using the adapter decorator.  If the factory
+            is None or doesn't have a __component_adapts__ adapts
+            attribute, then this argument is required.
+
+        provided
+            This is the interface provided by the adapter and
+            implemented by the factory.  If the factory is not
+            None implements a single interface, then this argument
+            is optional and the factory-implemented interface will
+            be used.
+
+        name
+            The adapter name.
+
+            Currently, only the empty string is accepted.  Other
+            strings will be accepted in the future when support for
+            named subscribers is added.
+
+        An Unregistered event is generated with an
+        ISubscriptionAdapterRegistration.
+        """
+
+    def registeredSubscriptionAdapters():
+        """Return an iterable of ISubscriptionAdapterRegistration instances.
+
+        These registrations describe the current subscription adapter
+        registrations in the object.
+        """
+
+    def registerHandler(handler, required=None, name=_u(''), info=''):
+        """Register a handler.
+
+        A handler is a subscriber that doesn't compute an adapter
+        but performs some function when called.
+
+        Parameters:
+
+        handler
+            The object used to handle some event represented by
+            the objects passed to it.
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute is usually attribute is
+            normally set in class definitions using adapts
+            function, or for callables using the adapter
+            decorator.  If the factory doesn't have a
+            __component_adapts__ adapts attribute, then this
+            argument is required.
+
+        name
+            The handler name.
+
+            Currently, only the empty string is accepted.  Other
+            strings will be accepted in the future when support for
+            named handlers is added.
+
+        info
+           An object that can be converted to a string to provide
+           information about the registration.
+
+
+        A Registered event is generated with an IHandlerRegistration.
+        """
+
+    def unregisterHandler(handler=None, required=None, name=_u('')):
+        """Unregister a handler.
+
+        A handler is a subscriber that doesn't compute an adapter
+        but performs some function when called.
+
+        A boolean is returned indicating whether the registry was
+        changed.
+
+        Parameters:
+
+        handler
+            This is the object used to handle some event
+            represented by the objects passed to it. The handler
+            can be None, in which case any handlers registered for
+            the given required specifications with the given are
+            unregistered.
+
+        required
+            This is a sequence of specifications for objects to be
+            adapted.  If omitted, then the value of the factory's
+            __component_adapts__ attribute will be used.  The
+            __component_adapts__ attribute is usually attribute is
+            normally set in class definitions using adapts
+            function, or for callables using the adapter
+            decorator.  If the factory doesn't have a
+            __component_adapts__ adapts attribute, then this
+            argument is required.
+
+        name
+            The handler name.
+
+            Currently, only the empty string is accepted.  Other
+            strings will be accepted in the future when support for
+            named handlers is added.
+
+        An Unregistered event is generated with an IHandlerRegistration.
+        """
+
+    def registeredHandlers():
+        """Return an iterable of IHandlerRegistration instances.
+
+        These registrations describe the current handler registrations
+        in the object.
+        """
+
+
+class IComponents(IComponentLookup, IComponentRegistry):
+    """Component registration and access
+    """
+
+
+class IPossibleSite(Interface):
+    """An object that could be a site.
+    """
+
+    def setSiteManager(sitemanager):
+        """Sets the site manager for this object.
+        """
+
+    def getSiteManager():
+        """Returns the site manager contained in this object.
+
+        If there isn't a site manager, raise a component lookup.
+        """
+
+
+class ISite(IPossibleSite):
+    """Marker interface to indicate that we have a site"""

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/__init__.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/__init__.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/__init__.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,112 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002, 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Registry Tests"""
+
+import doctest
+import re
+import unittest
+
+from zope import interface
+from zope import registry
+
+from zope.registry.tests import test_utility
+from zope.registry.tests import test_adapter
+from zope.registry.tests import test_subscriber
+from zope.registry.tests import test_handler
+from zope.registry.tests import test_extending
+
+class I1(interface.Interface):
+    pass
+class I2(interface.Interface):
+    pass
+class I2e(I2):
+    pass
+class I3(interface.Interface):
+    pass
+class IC(interface.Interface):
+    pass
+
+class ITestType(interface.interfaces.IInterface):
+    pass
+
+class U:
+
+    def __init__(self, name):
+        self.__name__ = name
+
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, self.__name__)
+
+class U1(U):
+    interface.implements(I1)
+
+class U12(U):
+    interface.implements(I1, I2)
+
+class IA1(interface.Interface):
+    pass
+
+class IA2(interface.Interface):
+    pass
+
+class IA3(interface.Interface):
+    pass
+
+class A:
+
+    def __init__(self, *context):
+        self.context = context
+
+    def __repr__(self):
+        return "%s%r" % (self.__class__.__name__, self.context)
+
+class A12_1(A):
+    registry.adapts(I1, I2)
+    interface.implements(IA1)
+
+class A12_(A):
+    registry.adapts(I1, I2)
+
+class A_2(A):
+    interface.implements(IA2)
+
+class A_3(A):
+    interface.implements(IA3)
+
+class A1_12(U):
+    registry.adapts(I1)
+    interface.implements(IA1, IA2)
+
+class A1_2(U):
+    registry.adapts(I1)
+    interface.implements(IA2)
+
+class A1_23(U):
+    registry.adapts(I1)
+    interface.implements(IA1, IA3)
+
+def noop(*args):
+    pass
+
+def test_suite():
+    return unittest.TestSuite((
+            unittest.makeSuite(test_utility.Test),
+            unittest.makeSuite(test_adapter.Test),
+            unittest.makeSuite(test_subscriber.Test),
+            unittest.makeSuite(test_handler.Test),
+            unittest.makeSuite(test_extending.Test)
+        ))
+
+if __name__ == "__main__":
+    unittest.main(defaultTest='test_suite')

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_adapter.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_adapter.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_adapter.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,175 @@
+import unittest
+
+from zope.interface import implementedBy
+from zope.registry import ComponentLookupError
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        from zope import registry
+        self.components = registry.Components('comps')
+        self.tests = registry.tests
+
+    def test_register_and_unregister_adapter(self):
+        self.components.registerAdapter(self.tests.A12_1)
+
+        multi_adapter = self.components.getMultiAdapter((self.tests.U1(1), self.tests.U12(2)), self.tests.IA1)
+        self.assertEqual(multi_adapter.__class__, self.tests.A12_1)
+        self.assertEqual(repr(multi_adapter), 'A12_1(U1(1), U12(2))')
+
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A12_1))
+        self.assertRaises(ComponentLookupError, self.components.getMultiAdapter, (self.tests.U1(1), self.tests.U12(2)), self.tests.IA1)
+
+    def test_register_and_unregister_adapter_with_two_interfaces(self):
+        self.assertRaises(TypeError, self.components.registerAdapter, self.tests.A1_12)
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+
+        test_object = self.tests.U1(1)
+        multi_adapter = self.components.getMultiAdapter((self.tests.U1(1),), self.tests.IA2)
+        self.assertEqual(multi_adapter.__class__, self.tests.A1_12)
+        self.assertEqual(repr(multi_adapter), 'A1_12(U1(1))')
+
+        self.assertRaises(TypeError, self.components.unregisterAdapter, self.tests.A1_12)
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A1_12, provided=self.tests.IA2))
+        self.assertRaises(ComponentLookupError, self.components.getMultiAdapter, (self.tests.U1(1),), self.tests.IA2)
+
+    def test_register_and_unregister_adapter_with_no_interfaces(self):
+        self.assertRaises(TypeError, self.components.registerAdapter, self.tests.A12_)
+
+        self.components.registerAdapter(self.tests.A12_, provided=self.tests.IA2)
+        multi_adapter = self.components.getMultiAdapter((self.tests.U1(1), self.tests.U12(2)), self.tests.IA2)
+        self.assertEqual(multi_adapter.__class__, self.tests.A12_)
+        self.assertEqual(repr(multi_adapter), 'A12_(U1(1), U12(2))')
+
+        self.assertRaises(TypeError, self.components.unregisterAdapter, self.tests.A12_)
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A12_, provided=self.tests.IA2))
+        self.assertRaises(ComponentLookupError, self.components.getMultiAdapter, (self.tests.U1(1), self.tests.U12(2)), self.tests.IA2)
+
+    def test_register_and_unregister_adapter_with_no___component_adapts___attribute(self):
+        self.assertRaises(TypeError, self.components.registerAdapter, self.tests.A_2)
+        self.components.registerAdapter(self.tests.A_2, required=[self.tests.I3])
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A_2, required=[self.tests.I3]))
+
+    def test_register_and_unregister_class_specific(self):
+        self.components.registerAdapter(self.tests.A_3, required=[self.tests.U], info=u'Really class specific')
+        self.assertTrue(self.components.unregisterAdapter(required=[self.tests.U], provided=self.tests.IA3))
+      
+    def test_registered_adapters_and_sorting(self):
+        self.components.registerAdapter(self.tests.A12_1)
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.A12_, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.A_2, required=[self.tests.I3])
+        self.components.registerAdapter(self.tests.A_3, required=[self.tests.U], info=u'Really class specific')
+
+        sorted_adapters = sorted(self.components.registeredAdapters())
+        sorted_adapters_name = map(lambda x: getattr(x, 'name'), sorted_adapters)
+        sorted_adapters_provided = map(lambda x: getattr(x, 'provided'), sorted_adapters) 
+        sorted_adapters_required = map(lambda x: getattr(x, 'required'), sorted_adapters)
+        sorted_adapters_info = map(lambda x: getattr(x, 'info'), sorted_adapters)
+
+        self.assertEqual(len(sorted_adapters), 5)
+        self.assertEqual(sorted_adapters_name, [u'', u'', u'', u'', u''])
+        self.assertEqual(sorted_adapters_provided, [self.tests.IA1,
+                                                    self.tests.IA2,
+                                                    self.tests.IA2,
+                                                    self.tests.IA2,
+                                                    self.tests.IA3])
+
+        self.assertEqual(sorted_adapters_required, [(self.tests.I1, self.tests.I2),
+                                                    (self.tests.I1, self.tests.I2),
+                                                    (self.tests.I1,),
+                                                    (self.tests.I3,),
+                                                    (implementedBy(self.tests.U),)])
+        self.assertEqual(sorted_adapters_info, [u'', u'', u'', u'', u'Really class specific'])
+
+    def test_get_none_existing_adapter(self):
+        self.assertRaises(ComponentLookupError, self.components.getMultiAdapter, (self.tests.U(1),), self.tests.IA1)
+
+    def test_query_none_existing_adapter(self):
+        self.assertTrue(self.components.queryMultiAdapter((self.tests.U(1),), self.tests.IA1) is None)
+        self.assertEqual(self.components.queryMultiAdapter((self.tests.U(1),), self.tests.IA1, default=42), 42)
+
+    def test_unregister_none_existing_adapter(self):
+        self.assertFalse(self.components.unregisterAdapter(self.tests.A_2, required=[self.tests.I3]))
+        self.assertFalse(self.components.unregisterAdapter(self.tests.A12_1, required=[self.tests.U]))
+
+    def test_unregister_adapter(self):
+        self.components.registerAdapter(self.tests.A12_1)
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.A12_, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.A_2, required=[self.tests.I3])
+        self.components.registerAdapter(self.tests.A_3, required=[self.tests.U], info=u'Really class specific')
+
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A12_1))
+        self.assertTrue(self.components.unregisterAdapter(required=[self.tests.U], provided=self.tests.IA3))
+
+        sorted_adapters = sorted(self.components.registeredAdapters())
+        sorted_adapters_name = map(lambda x: getattr(x, 'name'), sorted_adapters)
+        sorted_adapters_provided = map(lambda x: getattr(x, 'provided'), sorted_adapters) 
+        sorted_adapters_required = map(lambda x: getattr(x, 'required'), sorted_adapters)
+        sorted_adapters_info = map(lambda x: getattr(x, 'info'), sorted_adapters)
+
+        self.assertEqual(len(sorted_adapters), 3)
+        self.assertEqual(sorted_adapters_name, [u'', u'', u''])
+        self.assertEqual(sorted_adapters_provided, [self.tests.IA2,
+                                                    self.tests.IA2,
+                                                    self.tests.IA2])
+        self.assertEqual(sorted_adapters_required, [(self.tests.I1, self.tests.I2),
+                                                    (self.tests.I1,),
+                                                    (self.tests.I3,)])
+        self.assertEqual(sorted_adapters_info, [u'', u'', u''])
+
+    def test_register_named_adapter(self):
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2, name=u'test')
+        self.assertTrue(self.components.queryMultiAdapter((self.tests.U1(1),), self.tests.IA2) is None)
+        self.assertEqual(repr(self.components.queryMultiAdapter((self.tests.U1(1),), self.tests.IA2, name=u'test')), 'A1_12(U1(1))')
+
+        self.assertTrue(self.components.queryAdapter(self.tests.U1(1), self.tests.IA2) is None)
+        self.assertEqual(repr(self.components.queryAdapter(self.tests.U1(1), self.tests.IA2, name=u'test')), 'A1_12(U1(1))')
+        self.assertEqual(repr(self.components.getAdapter(self.tests.U1(1), self.tests.IA2, name=u'test')), 'A1_12(U1(1))')
+
+    def test_get_adapters(self):
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA1, name=u'test 1')
+        self.components.registerAdapter(self.tests.A1_23, provided=self.tests.IA2, name=u'test 2')
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+
+        adapters = list(self.components.getAdapters((self.tests.U1(1),), self.tests.IA2))
+        self.assertEqual(len(adapters), 2)
+        self.assertEqual(adapters[0][0], u'test 2')
+        self.assertEqual(adapters[1][0], u'')
+        self.assertEqual(repr(adapters[0][1]), 'A1_23(U1(1))')
+        self.assertEqual(repr(adapters[1][1]), 'A1_12(U1(1))')
+
+    def test_register_no_factory(self):
+        self.components.registerAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerAdapter(self.tests.noop, 
+                                        required=[self.tests.IA1], provided=self.tests.IA2, 
+                                        name=u'test noop')
+
+        self.assertTrue(self.components.queryAdapter(self.tests.U1(9), self.tests.IA2, name=u'test noop') is None)
+        adapters = list(self.components.getAdapters((self.tests.U1(1),), self.tests.IA2))
+        self.assertEqual(len(adapters), 1)
+        self.assertEqual(adapters[0][0], u'')
+        self.assertEqual(repr(adapters[0][1]), 'A1_12(U1(1))')
+
+        self.assertTrue(self.components.unregisterAdapter(self.tests.A1_12, provided=self.tests.IA2))
+
+        sorted_adapters = sorted(self.components.registeredAdapters())
+        sorted_adapters_name = map(lambda x: getattr(x, 'name'), sorted_adapters)
+        sorted_adapters_provided = map(lambda x: getattr(x, 'provided'), sorted_adapters) 
+        sorted_adapters_required = map(lambda x: getattr(x, 'required'), sorted_adapters)
+        sorted_adapters_info = map(lambda x: getattr(x, 'info'), sorted_adapters)
+
+        self.assertEqual(len(sorted_adapters), 1)
+        self.assertEqual(sorted_adapters_name, [u'test noop'])
+        self.assertEqual(sorted_adapters_provided, [self.tests.IA2])
+        self.assertEqual(sorted_adapters_required, [(self.tests.IA1,)])
+        self.assertEqual(sorted_adapters_info, [u''])
+
+
+def test_suite():
+    return unittest.TestSuite((unittest.makeSuite(Test),))
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_extending.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_extending.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_extending.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,76 @@
+import unittest
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        from zope import registry
+        self.tests = registry.tests
+
+    def test_extendning(self):
+        from zope import registry
+
+        c1 = registry.Components('1')
+        self.assertEqual(c1.__bases__, ())
+
+        c2 = registry.Components('2', (c1, ))
+        self.assertTrue(c2.__bases__ == (c1, ))
+
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U1(2)
+        test_object3 = self.tests.U12(1)
+        test_object4 = self.tests.U12(3)
+
+        self.assertEqual(len(list(c1.registeredUtilities())), 0)
+        self.assertEqual(len(list(c2.registeredUtilities())), 0)
+
+        c1.registerUtility(test_object1)
+        self.assertEqual(len(list(c1.registeredUtilities())), 1)
+        self.assertEqual(len(list(c2.registeredUtilities())), 0)
+        self.assertEqual(c1.queryUtility(self.tests.I1), test_object1)
+        self.assertEqual(c2.queryUtility(self.tests.I1), test_object1)
+
+        c1.registerUtility(test_object2)
+        self.assertEqual(len(list(c1.registeredUtilities())), 1)
+        self.assertEqual(len(list(c2.registeredUtilities())), 0)
+        self.assertEqual(c1.queryUtility(self.tests.I1), test_object2)
+        self.assertEqual(c2.queryUtility(self.tests.I1), test_object2)
+
+
+        c3 = registry.Components('3', (c1, ))
+        c4 = registry.Components('4', (c2, c3))
+        self.assertEqual(c4.queryUtility(self.tests.I1), test_object2)
+    
+        c1.registerUtility(test_object3, self.tests.I2)
+        self.assertEqual(c4.queryUtility(self.tests.I2), test_object3)
+
+        c3.registerUtility(test_object4, self.tests.I2)
+        self.assertEqual(c4.queryUtility(self.tests.I2), test_object4)
+
+        @registry.adapter(self.tests.I1)
+        def handle1(x):
+            self.assertEqual(x, test_object1)
+
+        def handle(*objects):
+            self.assertEqual(objects, (test_object1,))
+
+        @registry.adapter(self.tests.I1)
+        def handle3(x):
+            self.assertEqual(x, test_object1)
+
+        @registry.adapter(self.tests.I1)
+        def handle4(x):
+            self.assertEqual(x, test_object1)
+
+        c1.registerHandler(handle1, info=u'First handler')
+        c2.registerHandler(handle, required=[self.tests.U])
+        c3.registerHandler(handle3)
+        c4.registerHandler(handle4)
+
+        c4.handle(test_object1)
+
+
+def test_suite():
+    return unittest.TestSuite((unittest.makeSuite(Test),))
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')    

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_handler.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_handler.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_handler.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,154 @@
+import unittest
+
+from zope.interface import implementedBy
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        from zope import registry
+        self.components = registry.Components('comps')
+        self.tests = registry.tests
+
+    def test_register_handler(self):
+        from zope import registry
+
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+
+        @registry.adapter(self.tests.I1)
+        def handle1(x):
+            self.assertEqual(x, test_object1)
+
+        self.components.registerHandler(handle1, info=u'First handler')
+        self.components.handle(test_object1)
+
+        @registry.adapter(self.tests.I1, self.tests.I2)
+        def handle12(x, y):
+            self.assertEqual(x, test_object1)
+            self.assertEqual(y, test_object2)
+
+        self.components.registerHandler(handle12)
+        self.components.handle(test_object1, test_object2)
+
+    def test_register_noncompliant_handler(self):
+        handle_calls = []
+        def handle(*objects):
+            handle_calls.append(objects)
+
+        self.assertRaises(TypeError, self.components.registerHandler, handle)
+        self.components.registerHandler(handle, required=[self.tests.I1], info=u'a comment')
+        self.components.registerHandler(handle, required=[self.tests.U], info=u'handle a class')
+
+        test_object = self.tests.U1(1)
+        self.components.handle(test_object)
+        self.assertEqual(len(handle_calls), 2)
+        map(self.assertEqual, handle_calls, [(test_object,), (test_object,)])
+
+    def test_list_handlers(self):
+        from zope import registry
+
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+
+        @registry.adapter(self.tests.I1)
+        def handle1(x):
+            self.assertEqual(x, test_object1)
+
+        @registry.adapter(self.tests.I1, self.tests.I2)
+        def handle12(x, y):
+            self.assertEqual(x, test_object1)
+            self.assertEqual(y, test_object2)
+
+        handle_calls = []
+        def handle(*objects):
+            handle_calls.append(objects)
+
+        self.components.registerHandler(handle1, info=u'First handler')
+        self.components.registerHandler(handle12)
+        self.components.registerHandler(handle, required=[self.tests.I1], info=u'a comment')
+        self.components.registerHandler(handle, required=[self.tests.U], info=u'handle a class')
+
+        handlers = list(self.components.registeredHandlers())
+        handlers_required = map(lambda x: getattr(x, 'required'), handlers)
+        handlers_handler = map(lambda x: getattr(x, 'handler'), handlers)
+        handlers_info = map(lambda x: getattr(x, 'info'), handlers)
+
+        self.assertEqual(len(handlers), 4)
+        self.assertEqual(handlers_required, [(self.tests.I1,), (self.tests.I1, self.tests.I2), (self.tests.I1,), (implementedBy(self.tests.U),)])
+        self.assertEqual(handlers_handler, [handle1, handle12, handle, handle])
+        self.assertEqual(handlers_info, [u'First handler', u'', u'a comment', u'handle a class'])
+
+    def test_unregister_handler(self):
+        from zope import registry
+
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+
+        @registry.adapter(self.tests.I1)
+        def handle1(x):
+            self.assertEqual(x, test_object1)
+
+        @registry.adapter(self.tests.I1, self.tests.I2)
+        def handle12(x, y):
+            self.assertEqual(x, test_object1)
+            self.assertEqual(y, test_object2)
+
+        handle_calls = []
+        def handle(*objects):
+            handle_calls.append(objects)
+
+        self.components.registerHandler(handle1, info=u'First handler')
+        self.components.registerHandler(handle12)
+        self.components.registerHandler(handle, required=[self.tests.I1], info=u'a comment')
+        self.components.registerHandler(handle, required=[self.tests.U], info=u'handle a class')
+
+        self.assertEqual(len(list(self.components.registeredHandlers())), 4)
+        self.assertTrue(self.components.unregisterHandler(handle12))
+        self.assertEqual(len(list(self.components.registeredHandlers())), 3)
+        self.assertFalse(self.components.unregisterHandler(handle12))
+        self.assertEqual(len(list(self.components.registeredHandlers())), 3)
+        self.assertRaises(TypeError, self.components.unregisterHandler)
+        self.assertEqual(len(list(self.components.registeredHandlers())), 3)
+        self.assertTrue(self.components.unregisterHandler(handle, required=[self.tests.I1]))
+        self.assertEqual(len(list(self.components.registeredHandlers())), 2)
+        self.assertTrue(self.components.unregisterHandler(handle, required=[self.tests.U]))
+        self.assertEqual(len(list(self.components.registeredHandlers())), 1)
+
+    def test_multi_handler_unregistration(self):
+        """
+        There was a bug where multiple handlers for the same required
+        specification would all be removed when one of them was
+        unregistered.
+
+        """
+        from zope import interface
+
+        calls = []
+
+        class I(interface.Interface):
+            pass
+
+        def factory1(event):
+            calls.append(2)
+
+        def factory2(event):
+            calls.append(3)
+
+        class Event(object):
+            interface.implements(I)
+
+        self.components.registerHandler(factory1, [I,])
+        self.components.registerHandler(factory2, [I,])
+        self.components.handle(Event())
+        self.assertEqual(sum(calls), 5)
+        self.assertTrue(self.components.unregisterHandler(factory1, [I,]))
+        calls = []
+        self.components.handle(Event())
+        self.assertEqual(sum(calls), 3)
+
+
+def test_suite():
+    return unittest.TestSuite((unittest.makeSuite(Test),))
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')    

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_subscriber.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_subscriber.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_subscriber.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,104 @@
+import unittest
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        from zope import registry
+        self.components = registry.Components('comps')
+        self.tests = registry.tests
+
+    def test_register_subscriber(self):
+        self.components.registerSubscriptionAdapter(self.tests.A1_2)
+        self.components.registerSubscriptionAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, info='a sample comment')
+        subscribers = self.components.subscribers((self.tests.U1(1),), self.tests.IA2)
+        self.assertEqual(len(subscribers), 3)
+        self.assertEqual(repr(subscribers[0]), 'A1_2(U1(1))')
+        self.assertEqual(repr(subscribers[1]), 'A1_12(U1(1))')
+        self.assertEqual(repr(subscribers[2]), 'A(U1(1),)') 
+
+    def test_register_noncompliant_subscriber(self):
+        self.assertRaises(TypeError, self.components.registerSubscriptionAdapter, self.tests.A1_12)
+        self.assertRaises(TypeError, self.components.registerSubscriptionAdapter, self.tests.A)
+        self.assertRaises(TypeError, self.components.registerSubscriptionAdapter, self.tests.A, required=[self.tests.IA1])
+
+    def test_register_named_subscriber(self):
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, u'', u'a sample comment')
+        self.assertRaises(TypeError, self.components.registerSubscriptionAdapter, 
+                          self.tests.A, [self.tests.I1], self.tests.IA2, u'oops', u'a sample comment')
+        subscribers = self.components.subscribers((self.tests.U1(1),), self.tests.IA2)
+        self.assertEqual(len(subscribers), 1)
+        self.assertEqual(repr(subscribers[0]), 'A(U1(1),)')
+
+    def test_register_no_factory(self):
+        self.components.registerSubscriptionAdapter(self.tests.noop, [self.tests.I1], self.tests.IA2)
+        subscribers = self.components.subscribers((self.tests.U1(1),), self.tests.IA2)
+        self.assertEqual(len(subscribers), 0)
+
+    def test_sorting_registered_subscription_adapters(self):
+        self.components.registerSubscriptionAdapter(self.tests.A1_2)
+        self.components.registerSubscriptionAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, info=u'a sample comment')
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, u'', u'a sample comment')
+        self.components.registerSubscriptionAdapter(self.tests.noop, [self.tests.I1], self.tests.IA2)
+
+        sorted_subscribers = sorted(self.components.registeredSubscriptionAdapters())
+        sorted_subscribers_name = map(lambda x: getattr(x, 'name'), sorted_subscribers)
+        sorted_subscribers_provided = map(lambda x: getattr(x, 'provided'), sorted_subscribers) 
+        sorted_subscribers_required = map(lambda x: getattr(x, 'required'), sorted_subscribers)
+        sorted_subscribers_factory = map(lambda x: getattr(x, 'factory'), sorted_subscribers)
+        sorted_subscribers_info = map(lambda x: getattr(x, 'info'), sorted_subscribers)
+
+        self.assertEqual(len(sorted_subscribers), 5)
+        self.assertEqual(sorted_subscribers_name, [u'', u'', u'', u'', u''])
+        self.assertEqual(sorted_subscribers_provided, [self.tests.IA2, self.tests.IA2, self.tests.IA2, self.tests.IA2, self.tests.IA2])
+        self.assertEqual(sorted_subscribers_required, [(self.tests.I1,), (self.tests.I1,), (self.tests.I1,),(self.tests.I1,), (self.tests.I1,)])
+        self.assertEqual(sorted_subscribers_factory, [self.tests.A, self.tests.A, self.tests.A1_12, self.tests.A1_2, self.tests.noop])
+        self.assertEqual(sorted_subscribers_info, [u'a sample comment', u'a sample comment', u'', u'', u''])
+
+    def test_unregister(self):
+        self.components.registerSubscriptionAdapter(self.tests.A1_2)
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 1)
+        self.assertTrue(self.components.unregisterSubscriptionAdapter(self.tests.A1_2))
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 0)
+
+    def test_unregister_multiple(self):
+        self.components.registerSubscriptionAdapter(self.tests.A1_2)
+        self.components.registerSubscriptionAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, info=u'a sample comment')
+        self.components.registerSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2, u'', u'a sample comment')
+        self.components.registerSubscriptionAdapter(self.tests.noop, [self.tests.I1], self.tests.IA2)
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 4)
+        self.assertEqual(len(list(self.components.registeredSubscriptionAdapters())), 5)
+
+        self.assertTrue(self.components.unregisterSubscriptionAdapter(self.tests.A, [self.tests.I1], self.tests.IA2))
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 2)
+        self.assertEqual(len(list(self.components.registeredSubscriptionAdapters())), 3)
+
+    def test_unregister_no_factory(self):
+        self.components.registerSubscriptionAdapter(self.tests.A1_2)
+        self.components.registerSubscriptionAdapter(self.tests.A1_12, provided=self.tests.IA2)
+        self.components.registerSubscriptionAdapter(self.tests.noop, [self.tests.I1], self.tests.IA2)
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 2)
+        self.assertEqual(len(list(self.components.registeredSubscriptionAdapters())), 3)
+
+        self.assertRaises(TypeError, self.components.unregisterSubscriptionAdapter, required=[self.tests.I1])
+        self.assertRaises(TypeError, self.components.unregisterSubscriptionAdapter, provided=self.tests.IA2)
+        self.assertTrue(self.components.unregisterSubscriptionAdapter(required=[self.tests.I1], provided=self.tests.IA2))
+        self.assertEqual(len(self.components.subscribers((self.tests.U1(1),), self.tests.IA2)), 0)
+        self.assertEqual(len(list(self.components.registeredSubscriptionAdapters())), 0)
+
+    def test_unregister_noncompliant_subscriber(self):
+        self.assertRaises(TypeError, self.components.unregisterSubscriptionAdapter, self.tests.A1_12)
+        self.assertRaises(TypeError, self.components.unregisterSubscriptionAdapter, self.tests.A)
+        self.assertRaises(TypeError, self.components.unregisterSubscriptionAdapter, self.tests.A, required=[self.tests.IA1])
+
+    def test_unregister_nonexistent_subscriber(self):
+        self.assertFalse(self.components.unregisterSubscriptionAdapter(required=[self.tests.I1], provided=self.tests.IA2))
+
+
+def test_suite():
+    return unittest.TestSuite((unittest.makeSuite(Test),))
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')       

Added: Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_utility.py
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_utility.py	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/src/zope/registry/tests/test_utility.py	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,189 @@
+import unittest
+
+class Test(unittest.TestCase):
+
+    def setUp(self):
+        from zope import registry
+        self.components = registry.Components('comps')
+        self.tests = registry.tests
+
+    def test_register_utility(self):
+        test_object = self.tests.U1(1)
+        self.components.registerUtility(test_object)
+        self.assertEqual(self.components.getUtility(self.tests.I1), test_object)
+
+    def test_register_utility_with_factory(self):
+        test_object = self.tests.U1(1)
+        def factory():
+           return test_object
+        self.components.registerUtility(factory=factory)
+        self.assertEqual(self.components.getUtility(self.tests.I1), test_object)
+        self.assertTrue(self.components.unregisterUtility(factory=factory))
+
+    def test_register_utility_with_component_and_factory(self):
+        def factory():
+            return self.tests.U1(1)
+        self.assertRaises(TypeError, self.components.registerUtility, self.tests.U1(1), factory=factory)
+
+    def test_unregister_utility_with_and_without_component_and_factory(self):
+        def factory():
+            return self.tests.U1(1)
+        self.assertRaises(TypeError, self.components.unregisterUtility, self.tests.U1(1), factory=factory)
+        self.assertRaises(TypeError, self.components.unregisterUtility)
+
+    def test_register_utility_with_no_interfaces(self):
+        self.assertRaises(TypeError, self.components.registerUtility, self.tests.A)
+
+    def test_register_utility_with_two_interfaces(self):
+        self.assertRaises(TypeError, self.components.registerUtility, self.tests.U12(1))
+
+    def test_register_utility_with_arguments(self):
+        test_object1 = self.tests.U12(1)
+        test_object2 = self.tests.U12(2)
+        self.components.registerUtility(test_object1, self.tests.I2)
+        self.components.registerUtility(test_object2, self.tests.I2, 'name')
+        self.assertEqual(self.components.getUtility(self.tests.I2), test_object1)
+        self.assertEqual(self.components.getUtility(self.tests.I2, 'name'), test_object2)
+
+    def test_get_none_existing_utility(self):
+        from zope.registry import ComponentLookupError
+        self.assertRaises(ComponentLookupError, self.components.getUtility, self.tests.I3)
+
+    def test_query_none_existing_utility(self):
+        self.assertTrue(self.components.queryUtility(self.tests.I3) is None)
+        self.assertEqual(self.components.queryUtility(self.tests.I3, default=42), 42)
+
+    def test_registered_utilities_and_sorting(self):
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+        test_object3 = self.tests.U12(3)
+        self.components.registerUtility(test_object1)
+        self.components.registerUtility(test_object3, self.tests.I2, u'name')
+        self.components.registerUtility(test_object2, self.tests.I2)
+
+        sorted_utilities = sorted(self.components.registeredUtilities())
+        sorted_utilities_name = map(lambda x: getattr(x, 'name'), sorted_utilities)
+        sorted_utilities_component = map(lambda x: getattr(x, 'component'), sorted_utilities)
+        sorted_utilities_provided = map(lambda x: getattr(x, 'provided'), sorted_utilities)
+
+        self.assertEqual(len(sorted_utilities), 3)
+        self.assertEqual(sorted_utilities_name, [u'', u'', u'name'])
+        self.assertEqual(sorted_utilities_component, [test_object1, test_object2, test_object3])
+        self.assertEqual(sorted_utilities_provided, [self.tests.I1, self.tests.I2, self.tests.I2])
+
+    def test_duplicate_utility(self):
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+        test_object3 = self.tests.U12(3)
+        test_object4 = self.tests.U1(4)
+        self.components.registerUtility(test_object1)
+        self.components.registerUtility(test_object2, self.tests.I2)
+        self.components.registerUtility(test_object3, self.tests.I2, u'name')
+        self.assertEqual(self.components.getUtility(self.tests.I1), test_object1)
+
+        self.components.registerUtility(test_object4, info=u'use 4 now')
+        self.assertEqual(self.components.getUtility(self.tests.I1), test_object4)
+
+    def test_unregister_utility(self):
+        test_object = self.tests.U1(1)
+        self.components.registerUtility(test_object)
+        self.assertEqual(self.components.getUtility(self.tests.I1), test_object)
+        self.assertTrue(self.components.unregisterUtility(provided=self.tests.I1))
+        self.assertFalse(self.components.unregisterUtility(provided=self.tests.I1))
+
+    def test_unregister_utility_extended(self):
+        test_object = self.tests.U1(1)
+        self.components.registerUtility(test_object)
+        self.assertFalse(self.components.unregisterUtility(self.tests.U1(1)))
+        self.assertEqual(self.components.queryUtility(self.tests.I1), test_object)
+        self.assertTrue(self.components.unregisterUtility(test_object))
+        self.assertTrue(self.components.queryUtility(self.tests.I1) is None)
+
+    def test_get_utilities_for(self):
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+        test_object3 = self.tests.U12(3)
+        self.components.registerUtility(test_object1)
+        self.components.registerUtility(test_object2, self.tests.I2)
+        self.components.registerUtility(test_object3, self.tests.I2, u'name')
+
+        sorted_utilities = sorted(self.components.getUtilitiesFor(self.tests.I2))
+        self.assertEqual(len(sorted_utilities), 2)
+        self.assertEqual(sorted_utilities[0], (u'', test_object2))
+        self.assertEqual(sorted_utilities[1], (u'name', test_object3))
+
+    def test_get_all_utilities_registered_for(self):
+        test_object1 = self.tests.U1(1)
+        test_object2 = self.tests.U12(2)
+        test_object3 = self.tests.U12(3)
+        test_object4 = self.tests.U('ext')
+        self.components.registerUtility(test_object1)
+        self.components.registerUtility(test_object2, self.tests.I2)
+        self.components.registerUtility(test_object3, self.tests.I2, u'name')
+        self.components.registerUtility(test_object4, self.tests.I2e)
+
+        sorted_utilities = sorted(self.components.getUtilitiesFor(self.tests.I2))
+        self.assertEqual(len(sorted_utilities), 2)
+        self.assertEqual(sorted_utilities[0], (u'', test_object2))
+        self.assertEqual(sorted_utilities[1], (u'name', test_object3))
+
+        all_utilities = self.components.getAllUtilitiesRegisteredFor(self.tests.I2)
+        self.assertEqual(len(all_utilities), 3)
+        self.assertTrue(test_object2 in all_utilities)
+        self.assertTrue(test_object3 in all_utilities)
+        self.assertTrue(test_object4 in all_utilities)
+
+        self.assertTrue(self.components.unregisterUtility(test_object4, self.tests.I2e))
+        self.assertEqual(self.components.getAllUtilitiesRegisteredFor(self.tests.I2e), [])
+
+    def test_utility_events(self):
+        from zope.event import subscribers
+        old_subscribers = subscribers[:]
+        subscribers[:] = []
+
+        test_object = self.tests.U1(1)
+        def log_event(event):
+            self.assertEqual(event.object.component, test_object)
+        subscribers.append(log_event)
+        self.components.registerUtility(test_object)
+
+        subscribers[:] = old_subscribers
+
+    def test_dont_leak_utility_registrations_in__subscribers(self):
+        """
+        We've observed utilities getting left in _subscribers when they
+        get unregistered.
+
+        """
+        class C:
+            def __init__(self, name):
+                self.name = name
+            def __repr__(self):
+                return "C(%s)" % self.name
+
+        c1 = C(1)
+        c2 = C(2)
+
+        self.components.registerUtility(c1, self.tests.I1)
+        self.components.registerUtility(c1, self.tests.I1)
+        utilities = list(self.components.getAllUtilitiesRegisteredFor(self.tests.I1))
+        self.assertEqual(len(utilities), 1)
+        self.assertEqual(utilities[0], c1)
+
+        self.assertTrue(self.components.unregisterUtility(provided=self.tests.I1))
+        utilities = list(self.components.getAllUtilitiesRegisteredFor(self.tests.I1))
+        self.assertEqual(len(utilities), 0)
+
+        self.components.registerUtility(c1, self.tests.I1)
+        self.components.registerUtility(c2, self.tests.I1)
+
+        utilities = list(self.components.getAllUtilitiesRegisteredFor(self.tests.I1))
+        self.assertEqual(len(utilities), 1)
+        self.assertEqual(utilities[0], c2)
+
+
+def test_suite():
+    return unittest.TestSuite((unittest.makeSuite(Test),))
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')

Added: Sandbox/jbohman/zope.registry/trunk/tox.ini
===================================================================
--- Sandbox/jbohman/zope.registry/trunk/tox.ini	                        (rev 0)
+++ Sandbox/jbohman/zope.registry/trunk/tox.ini	2011-07-18 21:19:27 UTC (rev 122285)
@@ -0,0 +1,14 @@
+[tox]
+envlist = 
+    py25,py26,py27,py32,jython,pypy
+
+[testenv]
+commands = 
+    python setup.py test -q
+deps =
+    virtualenv
+
+# we separate coverage into its own testenv because a) "last run wins" wrt
+# cobertura jenkins reporting and b) pypy and jython can't handle any
+# combination of versions of coverage and nosexcover that i can find.
+



More information about the checkins mailing list