[Checkins] SVN: grokcore.component/trunk/ Upgrade to use the new Martian 0.12. This allowed us to

Martijn Faassen faassen at startifact.com
Tue Sep 15 12:30:25 EDT 2009


Log message for revision 104104:
  Upgrade to use the new Martian 0.12. This allowed us to 
  significantly simplify the way the context directive worked. It
  also made us realize it's bad form to use directives in the
  implementation of a directive - directive.bind.get() should only
  be used during Grok time, never during import time.
  

Changed:
  U   grokcore.component/trunk/CHANGES.txt
  U   grokcore.component/trunk/buildout.cfg
  U   grokcore.component/trunk/src/grokcore/component/directive.py
  U   grokcore.component/trunk/src/grokcore/component/meta.py
  D   grokcore.component/trunk/src/grokcore/component/scan.py
  U   grokcore.component/trunk/src/grokcore/component/tests/directive/multipletimes.py
  A   grokcore.component/trunk/src/grokcore/component/tests/inherit/
  A   grokcore.component/trunk/src/grokcore/component/tests/inherit/__init__.py
  A   grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit.py
  A   grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit_fixture.py
  U   grokcore.component/trunk/src/grokcore/component/tests/test_grok.py

-=-
Modified: grokcore.component/trunk/CHANGES.txt
===================================================================
--- grokcore.component/trunk/CHANGES.txt	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/CHANGES.txt	2009-09-15 16:30:24 UTC (rev 104104)
@@ -4,11 +4,22 @@
 1.8 (unreleased)
 ----------------
 
+* Use a newer version of Martian that has better support for
+  inheritance.  This is demonstrated in ``tests/inherit``.
+
+* The ``ContextGrokker`` and the ``scan.py`` module have gone away
+  thanks the newer Martian.
+
+* Directive implementations (in their factory method) should *not*
+  bind directives. Directive binding cannot take place at import time,
+  but only at grok time. Binding directives during import time (when
+  directives are executed) can lead to change problems. (we noticed
+  this during our refactoring to use the new Martian).
+
 * Use 1.0b1 versions.cfg in Grok's release info instead of a local
   copy; a local copy for all grokcore.* packages is just too hard to
   maintain.
 
-
 1.7 (2009-06-01)
 ----------------
 

Modified: grokcore.component/trunk/buildout.cfg
===================================================================
--- grokcore.component/trunk/buildout.cfg	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/buildout.cfg	2009-09-15 16:30:24 UTC (rev 104104)
@@ -4,6 +4,10 @@
 extends = http://grok.zope.org/releaseinfo/grok-1.0b1.cfg
 versions = versions
 
+[versions]
+martian = 0.12
+grokcore.component = 
+
 [interpreter]
 recipe = zc.recipe.egg
 eggs = grokcore.component

Modified: grokcore.component/trunk/src/grokcore/component/directive.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/directive.py	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/src/grokcore/component/directive.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -17,7 +17,6 @@
 import grokcore.component
 from zope.interface.interfaces import IInterface
 from martian.error import GrokImportError
-from grokcore.component.scan import UnambiguousComponentScope
 
 class global_utility(martian.MultipleTimesDirective):
     scope = martian.MODULE
@@ -27,13 +26,6 @@
             raise GrokImportError(
                 "You can only pass an interface to the "
                 "provides argument of %s." % self.name)
-
-        if provides is None:
-            provides = grokcore.component.provides.bind().get(factory)
-        if direct is None:
-            direct = grokcore.component.direct.bind().get(factory)
-        if not name:
-            name = grokcore.component.name.bind().get(factory)
         return (factory, provides, name, direct)
 
 class global_adapter(martian.MultipleTimesDirective):
@@ -44,23 +36,11 @@
             raise GrokImportError(
                 "You can only pass an interface to the "
                 "provides argument of %s." % self.name)
-
-        if provides is None:
-            provides = grokcore.component.provides.bind().get(factory)
-        
-        if adapts is None:
-            adapts = getattr(factory, '__component_adapts__', None)
-            if adapts is None:
-                adapts = grokcore.component.context.bind().get(factory)
-        
         if not isinstance(adapts, (list, tuple,)):
             adapts = (adapts,)
         elif isinstance(adapts, list):
             adapts = tuple(adapts)
-        
-        if not name:
-            name = grokcore.component.name.bind().get(factory)
-        
+
         return (factory, adapts, provides, name)
 
 class name(martian.Directive):
@@ -70,7 +50,7 @@
     validate = martian.validateText
 
 class context(martian.Directive):
-    scope = UnambiguousComponentScope('context')
+    scope = martian.CLASS_OR_MODULE
     store = martian.ONCE
     validate = martian.validateInterfaceOrClass
 
@@ -88,4 +68,4 @@
 class provides(martian.Directive):
     scope = martian.CLASS
     store = martian.ONCE
-    validate = martian.validateInterface
\ No newline at end of file
+    validate = martian.validateInterface

Modified: grokcore.component/trunk/src/grokcore/component/meta.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/meta.py	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/src/grokcore/component/meta.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -20,8 +20,8 @@
 
 from zope import component, interface
 from martian.error import GrokError
-from grokcore.component.scan import check_module_component
-from grokcore.component.scan import determine_module_component
+from martian.util import scan_for_classes
+
 from grokcore.component.interfaces import IContext
 
 
@@ -35,23 +35,26 @@
         return list(interface.providedBy(factory))[0]
     return default_provides(factory)
 
+def default_context(factory, module, **data):
+    components = list(scan_for_classes(module, IContext))
+    if len(components) == 0:
+        raise GrokError(
+            "No module-level context for %r, please use the 'context' "
+            "directive." % (factory), factory)
+    elif len(components) == 1:
+        component = components[0]
+    else:
+        raise GrokError(
+            "Multiple possible contexts for %r, please use the 'context' "
+            "directive."
+            % (factory), factory)
+    return component
 
-class ContextGrokker(martian.GlobalGrokker):
-
-    martian.priority(1001)
-
-    def grok(self, name, module, module_info, config, **kw):
-        context = determine_module_component(module_info,
-                                             grokcore.component.context,
-                                             IContext)
-        grokcore.component.context.set(module, context)
-        return True
-
-
 class AdapterGrokker(martian.ClassGrokker):
     martian.component(grokcore.component.Adapter)
 
-    martian.directive(grokcore.component.context)
+    martian.directive(grokcore.component.context,
+                      get_default=default_context)
     martian.directive(grokcore.component.provides,
                       get_default=default_provides)
     martian.directive(grokcore.component.name)
@@ -113,15 +116,12 @@
 class AdapterDecoratorGrokker(martian.GlobalGrokker):
 
     def grok(self, name, module, module_info, config, **kw):
-        context = grokcore.component.context.bind().get(module=module)
         adapters = module_info.getAnnotation('grok.adapters', [])
         for function in adapters:
             interfaces = getattr(function, '__component_adapts__', None)
             if interfaces is None:
-                # There's no explicit interfaces defined, so we assume the
-                # module context to be the thing adapted.
-                check_module_component(function, context, 'context',
-                                       grokcore.component.context)
+                context = grokcore.component.context.bind(
+                    get_default=default_context).get(module)
                 interfaces = (context, )
             name = getattr(function, '__component_name__', u"")
             config.action(
@@ -135,9 +135,16 @@
 class GlobalUtilityDirectiveGrokker(martian.GlobalGrokker):
 
     def grok(self, name, module, module_info, config, **kw):
-        infos = grokcore.component.global_utility.bind().get(module=module)
+        infos = grokcore.component.global_utility.bind().get(module)
 
         for factory, provides, name, direct in infos:
+            if direct is None:
+                direct = grokcore.component.direct.bind().get(factory)
+            if provides is None:
+                provides = grokcore.component.provides.bind().get(factory)
+            if not name:
+                name = grokcore.component.name.bind().get(factory)
+
             if direct:
                 obj = factory
                 if provides is None:
@@ -159,9 +166,18 @@
 class GlobalAdapterDirectiveGrokker(martian.GlobalGrokker):
 
     def grok(self, name, module, module_info, config, **kw):
-        infos = grokcore.component.global_adapter.bind().get(module=module)
-
+        infos = grokcore.component.global_adapter.bind().get(module)
         for factory, adapts, provides, name in infos:
+            if provides is None:
+                provides = grokcore.component.provides.bind().get(factory)
+            if adapts is None:
+                adapts = getattr(factory, '__component_adapts__', None)
+                if adapts is None:
+                    adapts = grokcore.component.context.bind(
+                        get_default=default_context).get(factory)
+            if not name:
+                name = grokcore.component.name.bind().get(factory)
+            
             config.action(
                 discriminator=('adapter', adapts, provides, name),
                 callable=component.provideAdapter,

Deleted: grokcore.component/trunk/src/grokcore/component/scan.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/scan.py	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/src/grokcore/component/scan.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -1,94 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006-2007 Zope Corporation 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.
-#
-##############################################################################
-"""Grok utility functions.
-"""
-
-from martian.error import GrokError
-from martian.util import scan_for_classes
-from martian.directive import ClassOrModuleScope
-
-AMBIGUOUS_COMPONENT = object()
-def check_module_component(factory, component, component_name, directive):
-    """Raise error if module-level component cannot be determined.
-
-    If the module-level component is None, it's never been specified;
-    raise error telling developer to specify.
-
-    if the module-level component is AMBIGUOUS_COMPONENT, raise
-    an error telling developer to specify which one to use.
-    """
-    if component is None:
-        raise GrokError("No module-level %s for %r, please use the '%s' "
-                        "directive."
-                        % (component_name, factory, directive.__name__),
-                        factory)
-    elif component is AMBIGUOUS_COMPONENT:
-        raise GrokError("Multiple possible %ss for %r, please use the '%s' "
-                        "directive."
-                        % (component_name, factory, directive.__name__),
-                        factory)
-    
-def determine_module_component(module_info, directive, 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 classes that implement an interface.
-    
-    If there is no module-level component, the module-level component is
-    None.
-
-    If there is one module-level component, it is returned.
-
-    If there are more than one module-level component, AMBIGUOUS_COMPONENT
-    is returned.
-    """
-    module = module_info.getModule()
-    components = list(scan_for_classes(module, iface))
-    if len(components) == 0:
-        component = None
-    elif len(components) == 1:
-        component = components[0]
-    else:
-        component= AMBIGUOUS_COMPONENT
-
-    module_component = directive.bind().get(module=module)
-    if module_component is not None:
-        component = module_component
-    return component
-
-
-class UnambiguousComponentScope(ClassOrModuleScope):
-
-    def __init__(self, name):
-        self.name = name
-
-    def get(self, directive, component, module, default):
-        value = default
-        if component is not None:
-            value = directive.store.get(directive, component, default)
-        if value is default and module is not None:
-            value = directive.store.get(directive, module, default)
-
-        # When both 'component' and 'module' where passed in, perform
-        # a check for ambiguous components.
-        if None not in (component, module):
-            value_to_check = value
-            if value_to_check is default:
-                value_to_check = None
-            check_module_component(component, value_to_check, self.name,
-                                   directive)
-        return value

Modified: grokcore.component/trunk/src/grokcore/component/tests/directive/multipletimes.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/directive/multipletimes.py	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/src/grokcore/component/tests/directive/multipletimes.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -6,7 +6,7 @@
   >>> from martian import scan
   >>> import grokcore.component as grok
   >>> from grokcore.component.tests.directive import multipletimes
-  >>> guis = grok.global_utility.bind().get(module=multipletimes)
+  >>> guis = grok.global_utility.bind().get(multipletimes)
   >>> len(guis)
   2
 

Added: grokcore.component/trunk/src/grokcore/component/tests/inherit/__init__.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/inherit/__init__.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/inherit/__init__.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -0,0 +1 @@
+#

Added: grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -0,0 +1,22 @@
+"""
+We expect the module-level grok.context to be inherited by subclasses of
+an adapter that is associated with this directive. FooAdapter is such
+an adapter, defined in inherit_fixture. In this module we've inherited
+from it.
+
+Explicit module-level context for an imported model:
+
+  >>> grok.testing.grok(__name__)
+
+  >>> from zope import component
+  >>> o = component.getAdapter(inherit_fixture.Foo(), inherit_fixture.IAnder,
+  ...   name='bar')
+  >>> isinstance(o, BarAdapter)
+  True
+"""
+import grokcore.component as grok
+from grokcore.component.tests.inherit import inherit_fixture
+
+# FooAdapter has a module-level grok.context to associate it
+class BarAdapter(inherit_fixture.FooAdapter):
+    grok.name('bar')

Added: grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit_fixture.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit_fixture.py	                        (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/inherit/inherit_fixture.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -0,0 +1,14 @@
+import grokcore.component
+from zope.interface import Interface
+
+class Foo(grokcore.component.Context):
+    pass
+
+grokcore.component.context(Foo)
+
+class IAnder(Interface):
+    pass
+
+class FooAdapter(grokcore.component.Adapter):
+    grokcore.component.provides(IAnder)
+

Modified: grokcore.component/trunk/src/grokcore/component/tests/test_grok.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/test_grok.py	2009-09-15 16:29:18 UTC (rev 104103)
+++ grokcore.component/trunk/src/grokcore/component/tests/test_grok.py	2009-09-15 16:30:24 UTC (rev 104104)
@@ -46,7 +46,7 @@
 def test_suite():
     suite = unittest.TestSuite()
     for name in ['adapter', 'directive', 'grokker', 'utility', 'view',
-                 'event']:
+                 'event', 'inherit']:
         suite.addTest(suiteFromPackage(name))
 
     api = doctest.DocFileSuite('api.txt')



More information about the checkins mailing list