[Checkins] SVN: grokcore.component/trunk/ change determine_module_component so it takes an interface instead of

Martijn Faassen faassen at infrae.com
Sat May 3 11:12:04 EDT 2008


Log message for revision 86233:
  change determine_module_component so it takes an interface instead of
  a list of base classes.
  
  Also make Context implement IContext, so we can hook up things to that
  instead of to the general base class.
  

Changed:
  U   grokcore.component/trunk/CHANGES.txt
  U   grokcore.component/trunk/src/grokcore/component/components.py
  A   grokcore.component/trunk/src/grokcore/component/interfaces.py
  U   grokcore.component/trunk/src/grokcore/component/meta.py
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py
  A   grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py
  U   grokcore.component/trunk/src/grokcore/component/tests/test_grok.py
  U   grokcore.component/trunk/src/grokcore/component/util.py

-=-
Modified: grokcore.component/trunk/CHANGES.txt
===================================================================
--- grokcore.component/trunk/CHANGES.txt	2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/CHANGES.txt	2008-05-03 15:12:03 UTC (rev 86233)
@@ -4,7 +4,11 @@
 1.1 (unreleased)
 ----------------
 
-* ...
+* ``determine_module_component`` now looks for classes that implement
+  a certain interface (such as ``IContext``), instead of taking a list
+  of classes.  If looking for ``IContext``, it still will find
+  ``Context`` subclasses, as these were also made to implement
+  ``IContext``.
 
 1.0.1 (2008-05-02)
 ------------------

Modified: grokcore.component/trunk/src/grokcore/component/components.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/components.py	2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/components.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -13,6 +13,10 @@
 ##############################################################################
 """Grok components"""
 
+from zope.interface import implements
+
+from grokcore.component.interfaces import IContext
+
 class Adapter(object):
     def __init__(self, context):
         self.context = context
@@ -24,4 +28,4 @@
     pass
 
 class Context(object):
-    pass
+    implements(IContext)

Added: grokcore.component/trunk/src/grokcore/component/interfaces.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/interfaces.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/interfaces.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,18 @@
+from zope.interface import Interface
+
+class IContext(Interface):
+    """Marker interface for auto-association of context.
+
+    The ``grok.context()`` directive is used to associate adapters with the
+    class or interface they adapt. If there is only a single possible context
+    object to adapt to in a module, you can leave out this directive and
+    let the adapter associate automatically.
+
+    If you want to make an object to be a candidate for this automatic
+    association, you can subclass from ``grokcore.component.Context``.
+    This implements this ``IContext`` directive.
+
+    In some cases, you don't want to mix in a base class. You can instead
+    mark up your class with ``zope.interface.implements(IContext)`` to make
+    it a candidate for auto-association.
+    """

Modified: grokcore.component/trunk/src/grokcore/component/meta.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/meta.py	2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/meta.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -25,6 +25,7 @@
 from grokcore.component.util import determine_module_component
 from grokcore.component.util import determine_class_component
 from grokcore.component.util import check_provides_one
+from grokcore.component.interfaces import IContext
 
 def get_context(module_info, factory):
     return determine_class_component(module_info, factory,
@@ -55,7 +56,7 @@
 
     def grok(self, name, module, module_info, config, **kw):
         context = determine_module_component(module_info, 'grok.context',
-                                             [grokcore.component.Context])
+                                             IContext)
         module.__grok_context__ = context
         return True
 

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,32 @@
+Scanning for the context object
+-------------------------------
+
+Let's import a module that contains no ``Context`` subclass, nor classes
+that implement ``IContext``::
+
+  >>> import grokcore.component.tests.scan_for_module_components_fixture as tests
+  >>> from grokcore.component.interfaces import IContext
+
+We shouldn't see any classes that are contexts::
+
+  >>> from grokcore.component.util import scan_for_module_components
+  >>> scan_for_module_components(tests.test1, IContext)
+  []
+
+Now we look at a module with a single ``Context`` subclass::
+
+  >>> scan_for_module_components(tests.test2, IContext)
+  [<class 'grokcore.component.tests.scan_for_module_components_fixture.test2.MyContext'>]
+
+Now we'll look at a module with a single class that implements ``IContext``::
+
+  >>> scan_for_module_components(tests.test3, IContext)
+  [<class 'grokcore.component.tests.scan_for_module_components_fixture.test3.MyContext'>]
+
+Let's finish by looking at a module which defines multiple contexts::
+
+  >>> len(scan_for_module_components(tests.test4, IContext))
+  4
+
+
+

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,4 @@
+from grokcore.component.tests.scan_for_module_components_fixture import test1
+from grokcore.component.tests.scan_for_module_components_fixture import test2
+from grokcore.component.tests.scan_for_module_components_fixture import test3
+from grokcore.component.tests.scan_for_module_components_fixture import test4

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,16 @@
+# this is a module that has deliberately NO Context subclass, nor a class
+# that implements IContext class. It is used to write a scan_for_context.txt
+# test.
+
+import os
+
+foo = "Bar"
+
+class Qux(object):
+    pass
+
+class Hallo:
+    pass
+
+qux = Qux()
+hallo = Hallo()

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,20 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component import Context
+
+foo = "Bar"
+
+class Qux(object):
+    pass
+
+class Hallo:
+    pass
+
+class MyContext(Context):
+    pass
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,21 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component.interfaces import IContext
+from zope.interface import implements
+
+foo = "Bar"
+
+class Qux(object):
+    pass
+
+class Hallo:
+    pass
+
+class MyContext(object):
+    implements(IContext)
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()

Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,31 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component import Context
+from grokcore.component.interfaces import IContext
+from zope.interface import implements
+
+foo = "Bar"
+
+class Qux(object):
+    pass
+
+class Hallo:
+    pass
+
+class MyContext(object):
+    implements(IContext)
+
+class MyContext2(Context):
+    pass
+
+class MyContext3(MyContext):
+    pass
+
+class MyContext4(MyContext2):
+    pass
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()

Modified: grokcore.component/trunk/src/grokcore/component/tests/test_grok.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/test_grok.py	2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/tests/test_grok.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -48,6 +48,10 @@
     for name in ['adapter', 'directive', 'grokker', 'order', 'testsetup',
                  'util', 'utility', 'view', 'event']:
         suite.addTest(suiteFromPackage(name))
+
+    suite.addTest(doctest.DocFileSuite(
+            'scan_for_module_components.txt',
+            optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE))
     return suite
 
 if __name__ == '__main__':

Modified: grokcore.component/trunk/src/grokcore/component/util.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/util.py	2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/util.py	2008-05-03 15:12:03 UTC (rev 86233)
@@ -17,8 +17,10 @@
 from zope import component, interface
 
 from martian.error import GrokError
-from martian.util import class_annotation, scan_for_classes
+from martian.util import class_annotation, defined_locally, isclass
 
+from grokcore.component.interfaces import IContext
+
 def check_adapts(class_):
     if component.adaptedBy(class_) is None:
         raise GrokError("%r must specify which contexts it adapts "
@@ -66,16 +68,31 @@
         raise GrokError("Multiple possible %ss for %r, please use "
                         "%s." % (component_name, factory, component_directive),
                         factory)
+
+def scan_for_module_components(module, iface):
+    """Given a module, scan for classes that that implements an interface.
+    """
+    result = set()
+    for name in dir(module):
+        if name.startswith('__grok_'):
+            continue
+        obj = getattr(module, name)
+        if not defined_locally(obj, module.__name__):
+            continue
+        
+        if isclass(obj) and iface.implementedBy(obj):
+            result.add(obj)
+    return list(result)
     
-def determine_module_component(module_info, annotation, classes):
+def determine_module_component(module_info, annotation, iface):
     """Determine module-level component.
 
     The module-level component can be set explicitly using the
     annotation (such as grok.context).
 
     If there is no annotation, the module-level component is determined
-    by scanning for subclasses of any in the list of classes.
-
+    by scanning for classes that implement an interface.
+    
     If there is no module-level component, the module-level component is
     None.
 
@@ -84,7 +101,7 @@
     If there are more than one module-level component, AMBIGUOUS_COMPONENT
     is returned.
     """
-    components = scan_for_classes(module_info.getModule(), classes)
+    components = scan_for_module_components(module_info.getModule(), iface)
     if len(components) == 0:
         component = None
     elif len(components) == 1:



More information about the Checkins mailing list