[Checkins] SVN: z3c.discriminator/trunk/ Generalized discriminator machinery to all adapters (see docs/HISTORY.txt). Bumped version to 0.2.

Malthe Borch mborch at gmail.com
Mon Nov 26 11:53:18 EST 2007


Log message for revision 81988:
  Generalized discriminator machinery to all adapters (see docs/HISTORY.txt). Bumped version to 0.2.

Changed:
  A   z3c.discriminator/trunk/docs/
  A   z3c.discriminator/trunk/docs/HISTORY.txt
  U   z3c.discriminator/trunk/setup.py
  U   z3c.discriminator/trunk/z3c/discriminator/README.txt
  U   z3c.discriminator/trunk/z3c/discriminator/__init__.py
  A   z3c.discriminator/trunk/z3c/discriminator/discriminator.py
  D   z3c.discriminator/trunk/z3c/discriminator/meta.zcml
  A   z3c.discriminator/trunk/z3c/discriminator/patches.py
  U   z3c.discriminator/trunk/z3c/discriminator/tests.py
  D   z3c.discriminator/trunk/z3c/discriminator/zcml.py

-=-
Added: z3c.discriminator/trunk/docs/HISTORY.txt
===================================================================
--- z3c.discriminator/trunk/docs/HISTORY.txt	                        (rev 0)
+++ z3c.discriminator/trunk/docs/HISTORY.txt	2007-11-26 16:53:17 UTC (rev 81988)
@@ -0,0 +1,23 @@
+Changelog
+---------
+
+Version 0.2  - November 26, 2007
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Discriminator machinery is now introduced globally by patching the global
+  adapter registry and the GlobalObject configuration field.
+
+  The ``discriminator`` method now returns an interface that is in all
+  aspects equal to the discriminated interface except it's marked as a
+  discriminator.
+  
+  Test suite have been expanded by including the entire test suite from
+  ``zope.component``.
+  [malthe]
+
+
+Version 0.1  - November 23, 2007
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- First public release
+  [malthe]

Modified: z3c.discriminator/trunk/setup.py
===================================================================
--- z3c.discriminator/trunk/setup.py	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/setup.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -1,16 +1,12 @@
 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.1'
+version = '0.2'
 
 setup(name='z3c.discriminator',
       version=version,
-      description="Provides a formalism for using adapters with discriminators.",
-      long_description="""\
-This package provides a formalism for designating adapter arguments as
-discriminators in the sense that they will be used only for adapter lookup,
-not instantiation.""",
-      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      description="Provides a formalism for marking adapter specifications as discriminators.",
+      long_description=open("README.txt").read() + open("docs/HISTORY.txt").read(),
       classifiers=[
         "Framework :: Zope2",
         "Framework :: Zope3",

Modified: z3c.discriminator/trunk/z3c/discriminator/README.txt
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/README.txt	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/z3c/discriminator/README.txt	2007-11-26 16:53:17 UTC (rev 81988)
@@ -1,14 +1,14 @@
 z3c.discriminator
 =================
-
-This package provides a formalism for designating adapter arguments as
+  
+This package provides a formalism for marking adapter specifications as
 discriminators in the sense that they will be used only for adapter lookup,
 not instantiation.
 
-  >>> from zope import interface
-
 First a set of interfaces and their implementations.
   
+  >>> from zope import interface
+  
   >>> class IFoo(interface.Interface):
   ...   pass
 
@@ -20,70 +20,68 @@
 
   >>> class Bar(object):
   ...   interface.implements(IBar)
-
+  
   >>> foo = Foo()
   >>> bar = Bar()
 
 Let's say we want to register an adapter for IFoo that also discriminates
-on IBar.
+on IBar. That is, the adapter itself takes only one argument (providing IFoo).
 
   >>> def give_me_foo(foo):
   ...   return foo
 
-We can use the ``discriminator`` method the decorate the interface as a
-discriminator. To register the adapter we use a custom ``provideAdapter``
-method that is basically a wrapper around the actual implementation from
-``zope.component``.
+We can use the ``discriminator`` method the mark the interface as a
+discriminator. Let's look at its properties:
 
   >>> from z3c.discriminator import discriminator
-  >>> from z3c.discriminator import provideAdapter
-
-Let's look at the properties of a discriminator.
-
   >>> discriminator(IFoo).providedBy(foo)
   True
 
-We designate that IBar is a discriminator by wrapping it using the
-``discriminator`` method:
-  
-  >>> provideAdapter(give_me_foo, (IFoo, discriminator(IBar)), IFoo)
+To register the adapter we use the standard ``provideAdapter`` method.
 
-Let's look up the adapter with the proper arguments.
+  >>> from zope import component
+  >>> component.provideAdapter(give_me_foo, (IFoo, discriminator(IBar)), IFoo)
 
+Let's look up the adapter providing both ``foo`` and ``bar``:
+
   >>> from zope import component
   >>> component.getMultiAdapter((foo, bar), IFoo)
   <Foo object at ...>
 
-Extended adapter directive
---------------------------
+Adapter registration using ZCML
+-------------------------------
 
-The discriminator extension is also available from ZCML. The convention
-is that if a dotted interface specification is prefaced by a minus
-sign, it's interpreted as a discriminator, e.g.
+Directives that use ``zope.configuration.fields.GlobalObject`` as the value
+type for the global object parameters are automatically equipped to use
+discriminators.
 
+The convention is that if a dotted interface specification is prefaced by a dash,
+it's interpreted as a discriminator, e.g.
+
   for="-some.package.ISomeInterface"
   
-The ``clearZCML`` method sets up the extended adapter directive.
+Let's try with the ``adapter`` directive. We'll register an adapter for IBar that
+also discriminates on IFoo.
 
-  >>> from z3c.discriminator.tests import clearZCML
-  >>> clearZCML()
-
-Let's register an adapter for IBar that also discriminates on IFoo.
-
   >>> def give_me_bar(bar):
   ...   return bar
 
-We need to patch our definitions onto the tests module to target
-them from the configuration string.
-  
+To make our symbols available from the configuration machine we patch it onto
+the tests module.
+
   >>> import z3c.discriminator.tests
   >>> z3c.discriminator.tests.IBar = IBar
   >>> z3c.discriminator.tests.IFoo = IFoo
   >>> z3c.discriminator.tests.give_me_bar = give_me_bar
 
+We must first load the meta directives from ``zope.component``.
+  
   >>> from cStringIO import StringIO
   >>> from zope.configuration import xmlconfig
+  >>> xmlconfig.XMLConfig('meta.zcml', component)()
 
+Now we can Register the adapter.
+  
   >>> xmlconfig.xmlconfig(StringIO("""
   ... <configure xmlns="http://namespaces.zope.org/zope">
   ... <adapter for="-z3c.discriminator.tests.IFoo
@@ -92,6 +90,8 @@
   ...          factory="z3c.discriminator.tests.give_me_bar" />
   ... </configure>
   ... """))
+
+Let's verify the adapter lookup:
   
   >>> component.getMultiAdapter((foo, bar), IBar)
   <Bar object at ...>

Modified: z3c.discriminator/trunk/z3c/discriminator/__init__.py
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/__init__.py	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/z3c/discriminator/__init__.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -1,22 +1,2 @@
-from zope import interface
-from zope import component
-
-def discriminator(iface):
-    class _(iface):
-        pass
-
-    _.__discriminated__ = iface
-    _.providedBy = iface.providedBy
-        
-    return _
-        
-def provideAdapter(factory, adapts=None, provides=None, name=''):
-    def _factory(*args):
-        _ = [provided for (provided, implemented) in zip(args, adapts)
-             if not hasattr(implemented, '__discriminated__')]
-        return factory(*_)
-
-    # unwrap discriminators
-    _adapts = [getattr(a, '__discriminated__', a) for a in adapts]
-
-    component.provideAdapter(_factory, _adapts, provides, name)
+from discriminator import discriminator
+import patches

Added: z3c.discriminator/trunk/z3c/discriminator/discriminator.py
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/discriminator.py	                        (rev 0)
+++ z3c.discriminator/trunk/z3c/discriminator/discriminator.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -0,0 +1,23 @@
+def discriminator(iface):
+    """This method creates an interface class derived from ``iface`` that behaves
+    and identifies exactly like ``iface`` except it is marked as a discriminator
+    by providing ``__discriminated__``."""
+
+    class meta(type(iface)):
+        def __init__(self, name, bases=(), attrs=None, **kwargs):
+            del attrs['__metaclass__']
+            super(meta, self).__init__(name, bases=bases, attrs=attrs, **kwargs)
+            
+        def __eq__(self, other):
+            if other is iface or other is self:
+                return True
+
+        def __hash__(self):
+            return hash(iface)
+            
+    class _(iface):
+        __metaclass__ = meta
+
+    _.__discriminated__ = iface
+    
+    return _

Deleted: z3c.discriminator/trunk/z3c/discriminator/meta.zcml
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/meta.zcml	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/z3c/discriminator/meta.zcml	2007-11-26 16:53:17 UTC (rev 81988)
@@ -1,15 +0,0 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope"
-    xmlns:meta="http://namespaces.zope.org/meta">
-
-  <meta:directives namespace="http://namespaces.zope.org/zope">
-
-    <meta:directive
-       name="adapter"
-       schema="zope.component.zcml.IAdapterDirective"
-       handler=".zcml.adapter"
-       />
-    
-  </meta:directives>
-
-</configure>

Added: z3c.discriminator/trunk/z3c/discriminator/patches.py
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/patches.py	                        (rev 0)
+++ z3c.discriminator/trunk/z3c/discriminator/patches.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -0,0 +1,40 @@
+import zope.interface.adapter
+import zope.configuration.fields
+
+from z3c.discriminator import discriminator
+
+_register = zope.interface.adapter.BaseAdapterRegistry.register
+def register(self, required, provided, name, factory):
+    """This method wraps ``factory`` so it's discriminator-aware
+    if one or more ``required`` interfaces are designated as
+    discriminators."""
+    
+    drequired = [hasattr(r, '__discriminated__') for r in required]
+
+    if factory is None or len(drequired) == 0:
+        return _register(self, required, provided, name, factory)
+
+    def _factory(*args):
+        _ = [provided for (provided, implemented) in 
+             zip(args, tuple(required) + (None,) * (len(args) - len(required))) \
+             if not hasattr(implemented, '__discriminated__')]
+
+        return factory(*_)
+
+    _register(self, required, provided, name, _factory)
+
+# monkey-patch ``register`` method on zope.interface.adapter.BaseAdapterRegistry
+zope.interface.adapter.BaseAdapterRegistry.register = register
+
+_fromUnicode = zope.configuration.fields.GlobalObject.fromUnicode
+def fromUnicode(self, u):
+    """This method wraps ``fromUnicode`` so strings that begin with a
+    dash are wrapped as a discriminator."""
+    
+    if u.startswith('-'):
+        return discriminator(self.fromUnicode(u[1:]))
+
+    return _fromUnicode(self, u)
+
+# monkey-patch ``fromUnicode`` on zope.configuration.fields.GlobalObject.fromUnicode
+zope.configuration.fields.GlobalObject.fromUnicode = fromUnicode

Modified: z3c.discriminator/trunk/z3c/discriminator/tests.py
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/tests.py	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/z3c/discriminator/tests.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -5,52 +5,22 @@
                doctest.ELLIPSIS |
                doctest.NORMALIZE_WHITESPACE)
 
-import zope.component
 import zope.component.testing
 import zope.component.tests
 
-from zope.configuration import xmlconfig
-
-import z3c.discriminator
-
-"""
-Note:
-
-To make sure the new adapter directive is correctly implemented,
-we run the corresponding test suite from zope.component against
-our implementation.
-"""
-
-def clearZCML(test=None):
-    zope.component.testing.tearDown()
-    zope.component.testing.setUp()
-
-    xmlconfig.XMLConfig('meta.zcml', zope.component)()
-    xmlconfig.XMLConfig('meta.zcml', z3c.discriminator)()
-
-clearZCML_save = zope.component.tests.clearZCML
-
-def setUp(test):
-    zope.component.testing.setUp()
-    zope.component.tests.clearZCML = clearZCML
-    
-def tearDown(test):
-    zope.component.testing.tearDown()
-    zope.component.tests.clearZCML = clearZCML_save
-    
 def test_suite():
     return unittest.TestSuite((
         doctest.DocFileSuite('README.txt',
                              optionflags=OPTIONFLAGS,
                              setUp=zope.component.testing.setUp,
                              tearDown=zope.component.testing.tearDown,
-                             package="z3c.discriminator"),
-        doctest.DocFileSuite('zcml.txt',
-                             optionflags=OPTIONFLAGS,
-                             setUp=setUp,
-                             tearDown=tearDown,
-                             package="zope.component"),
-        ))
+                             package="z3c.discriminator"),) + 
 
+        # run test suite from zope.component to make sure
+        # our patches are correct
+ 
+        tuple(suite for suite in zope.component.tests.test_suite()),
+    )
+
 if __name__ == '__main__':
     unittest.main(defaultTest='test_suite')

Deleted: z3c.discriminator/trunk/z3c/discriminator/zcml.py
===================================================================
--- z3c.discriminator/trunk/z3c/discriminator/zcml.py	2007-11-25 22:23:21 UTC (rev 81987)
+++ z3c.discriminator/trunk/z3c/discriminator/zcml.py	2007-11-26 16:53:17 UTC (rev 81988)
@@ -1,42 +0,0 @@
-import zope.interface
-import zope.component.zcml
-import zope.configuration.fields
-
-from z3c.discriminator import discriminator
-
-class DiscriminatorAwareGlobalObject(zope.configuration.fields.GlobalObject):
-    def fromUnicode(self, u):
-        if u.startswith('-'):
-            return discriminator(self.fromUnicode(u[1:]))
-
-        return super(DiscriminatorAwareGlobalObject, self).fromUnicode(u)
-
-# monkey-patch value type on for_-handler
-zope.component.zcml.IAdapterDirective['for_'].value_type = \
-    DiscriminatorAwareGlobalObject(missing_value=object())
-
-def adapter(_context, factory, provides=None, for_=None, **kwargs):
-    if len(factory) != 1:
-        return zope.component.zcml.adapter(_context, factory, provides, for_, **kwargs)
-
-    factory = factory[0]
-
-    if for_ is None:
-        for_ = zope.component.adaptedBy(factory)
-
-        if for_ is None:
-            raise TypeError("No for attribute was provided and can't "
-                            "determine what the factory adapts.")
-
-    for_ = tuple(for_)
-
-    @zope.interface.implementer(zope.interface.implementedBy(factory))
-    def _factory(*args):
-        _ = [provided for (provided, implemented) in zip(args, for_)
-             if not hasattr(implemented, '__discriminated__')]
-        return factory(*_)
-
-    # unwrap discriminators
-    adapts = [getattr(a, '__discriminated__', a) for a in for_]
-    
-    zope.component.zcml.adapter(_context, [_factory], provides=provides, for_=adapts, **kwargs)



More information about the Checkins mailing list