[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