[Checkins] SVN: grok/trunk/src/grok/ Implement non-grokking for base classes (base classes can still be a context,

Martijn Faassen faassen at infrae.com
Mon Jan 8 18:39:59 EST 2007


Log message for revision 71837:
  Implement non-grokking for base classes (base classes can still be a context,
  however). If your class ends with Base it won't be grokked. If your
  class has the grok.baseclass() directive it also won't be grokked.
  

Changed:
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/directive.py
  U   grok/trunk/src/grok/grokker.py
  U   grok/trunk/src/grok/interfaces.py
  A   grok/trunk/src/grok/tests/baseclass/
  A   grok/trunk/src/grok/tests/baseclass/__init__.py
  A   grok/trunk/src/grok/tests/baseclass/base.py
  A   grok/trunk/src/grok/tests/baseclass/basecontext.py
  A   grok/trunk/src/grok/tests/baseclass/basedirective.py
  U   grok/trunk/src/grok/tests/test_grok.py
  U   grok/trunk/src/grok/util.py

-=-
Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/__init__.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -35,8 +35,8 @@
 from grok.components import Site, GlobalUtility, LocalUtility
 from grok.components import EditForm, DisplayForm, AddForm
 from grok.directive import (context, name, template, templatedir, provides,
-                            global_utility, local_utility, define_permission,
-                            require)
+                            baseclass, global_utility, local_utility,
+                            define_permission, require)
 from grok._grok import do_grok as grok  # Avoid name clash within _grok
 from grok._grok import SubscribeDecorator as subscribe
 from grok.error import GrokError, GrokImportError

Modified: grok/trunk/src/grok/directive.py
===================================================================
--- grok/trunk/src/grok/directive.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/directive.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -127,6 +127,15 @@
         frame.f_locals[self.local_name] = value
 
 
+class MarkerDirective(OnceDirective):
+    """A directive without argument that places a marker.
+    """
+    def value_factory(self):
+        return True
+
+    def check_arguments(self):
+        pass
+
 class MultipleTimesDirective(Directive):
     def store(self, frame, value):
         values = frame.f_locals.get(self.local_name, [])
@@ -260,6 +269,7 @@
                                     ClassOrModuleDirectiveContext())
 templatedir = SingleTextDirective('grok.templatedir', ModuleDirectiveContext())
 provides = InterfaceDirective('grok.provides', ClassDirectiveContext())
+baseclass = MarkerDirective('grok.baseclass', ClassDirectiveContext())
 global_utility = GlobalUtilityDirective('grok.global_utility',
                                         ModuleDirectiveContext())
 local_utility = LocalUtilityDirective('grok.local_utility',

Modified: grok/trunk/src/grok/grokker.py
===================================================================
--- grok/trunk/src/grok/grokker.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/grokker.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -74,6 +74,13 @@
             
             components = scanned_results.get(grokker.component_class, [])
             for name, component in components:
+                # this is a base class as it ends with Base, skip
+                if type(component) is type:
+                    if name.endswith('Base'):
+                        continue
+                    elif util.class_annotation_nobase(component,
+                                                      'grok.baseclass', False):
+                        continue
                 grokker.register(context,
                                  name, component,
                                  module_info, templates)

Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/interfaces.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -84,6 +84,13 @@
         """Explicitly specify with which interface a component will be looked up.
         """
 
+    def baseclass():
+        """Mark this class as a base class.
+
+        This means it won't be grokked, though if it's a possible context,
+        it can still serve as a context.
+        """
+        
     def global_utility(factory, provides=None, name=u''):
         """Register a global utility.
 

Added: grok/trunk/src/grok/tests/baseclass/__init__.py
===================================================================
--- grok/trunk/src/grok/tests/baseclass/__init__.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/tests/baseclass/__init__.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -0,0 +1 @@
+#

Added: grok/trunk/src/grok/tests/baseclass/base.py
===================================================================
--- grok/trunk/src/grok/tests/baseclass/base.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/tests/baseclass/base.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -0,0 +1,44 @@
+"""
+Base classes shouldn't be grokked.
+
+One way to indicate that something is a base class is by postfixing the
+classname with 'Base'. Another way is to use the 'grok.baseclass' directive
+on the class itself.
+
+  >>> grok.grok(__name__)
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> model = ModelBase()
+  >>> view = component.getMultiAdapter((model, request), name='viewbase')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError: ((<grok.tests.baseclass.base.ModelBase object at 0x...>,
+  <zope.publisher.browser.TestRequest instance ...>),
+  <InterfaceClass zope.interface.Interface>,
+  'viewbase')
+
+  >>> view = component.getMultiAdapter((model, request), name='anotherview')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError: ((<grok.tests.baseclass.base.ModelBase object at 0x...>,
+  <zope.publisher.browser.TestRequest instance ...>),
+  <InterfaceClass zope.interface.Interface>,
+  'anotherview')
+  
+"""
+import grok
+
+class ModelBase(grok.Model):
+    pass
+
+class ViewBase(grok.View):
+    def render(self):
+        return "hello world"
+
+class AnotherView(grok.View):
+    grok.baseclass()
+    
+    def render(self):
+        return "hello world"

Added: grok/trunk/src/grok/tests/baseclass/basecontext.py
===================================================================
--- grok/trunk/src/grok/tests/baseclass/basecontext.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/tests/baseclass/basecontext.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -0,0 +1,34 @@
+"""
+A base class of something that can be a context (such as a model) can
+function as a module-level context, and thus can have views associated
+with it.
+
+  >>> grok.grok(__name__)
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> model = ModelBase()
+  >>> view = component.getMultiAdapter((model, request), name='viewbase')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError: ((<grok.tests.baseclass.basecontext.ModelBase object at 0x...>,
+  <zope.publisher.browser.TestRequest instance ...>),
+  <InterfaceClass zope.interface.Interface>,
+  'viewbase')
+  >>> view = component.getMultiAdapter((model, request), name='realview')
+  >>> view.render()
+  'hello world'
+"""
+
+import grok
+
+class ModelBase(grok.Model):
+    pass
+
+class ViewBase(grok.View):    
+    def render(self):
+        return "hello world"
+
+class RealView(ViewBase):
+    pass

Added: grok/trunk/src/grok/tests/baseclass/basedirective.py
===================================================================
--- grok/trunk/src/grok/tests/baseclass/basedirective.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/tests/baseclass/basedirective.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -0,0 +1,42 @@
+"""
+The baseclass directive can be used to mark something a base class. Of course
+subclasses shouldn't inherit this otherwise there is no way to turn them
+into non-base classes.
+
+  >>> grok.grok(__name__)
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> from zope import component
+  >>> model = Model()
+
+We can't look up SomeView as a view, as it's a base class:
+
+  >>> view = component.getMultiAdapter((model, request), name='someview')
+  Traceback (most recent call last):
+    ...
+  ComponentLookupError: ((<grok.tests.baseclass.basedirective.Model object at 0x...>,
+  <zope.publisher.browser.TestRequest instance ...>),
+  <InterfaceClass zope.interface.Interface>,
+  'someview')
+
+We can however get a subclass of SomeView:
+
+  >>> view = component.getMultiAdapter((model, request), name='anotherview')
+  >>> view.render()
+  'hello world'
+"""
+import grok
+
+class Model(grok.Model):
+    pass
+
+class SomeView(grok.View):
+    grok.baseclass()
+    
+    def render(self):
+        return "hello world"
+
+class AnotherView(SomeView):
+    pass
+

Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/tests/test_grok.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -34,7 +34,8 @@
     suite = unittest.TestSuite()
     for name in ['adapter', 'error', 'view', 'scan', 'event', 'security',
                  'zcml', 'static', 'utility', 'xmlrpc', 'container',
-                 'traversal', 'form', 'site', 'grokker', 'directive', 'util']:
+                 'traversal', 'form', 'site', 'grokker', 'directive', 'util',
+                 'baseclass']:
         suite.addTest(suiteFromPackage(name))
     return suite
 

Modified: grok/trunk/src/grok/util.py
===================================================================
--- grok/trunk/src/grok/util.py	2007-01-08 23:37:29 UTC (rev 71836)
+++ grok/trunk/src/grok/util.py	2007-01-08 23:39:58 UTC (rev 71837)
@@ -53,6 +53,13 @@
 def class_annotation(obj, name, default):
     return getattr(obj, '__%s__' % name.replace('.', '_'), default)
 
+def class_annotation_nobase(obj, name, default):
+    """This will only look in the given class obj for the annotation.
+
+    It will not look in the inheritance chain.
+    """
+    return obj.__dict__.get('__%s__' % name.replace('.', '_'), default)
+    
 def class_annotation_list(obj, name, default):
     """This will process annotations that are lists correctly in the face of
     inheritance.



More information about the Checkins mailing list