[Checkins] SVN: grok/trunk/ Refactored Directive base class to
prepare for directives that can be called multiple times
and/or with multiple values
Wolfgang Schnerring
wosc at wosc.de
Sat Jan 6 11:15:19 EST 2007
Log message for revision 71742:
Refactored Directive base class to prepare for directives that can be called multiple times and/or with multiple values
Changed:
U grok/trunk/doc/design/utility.py
U grok/trunk/src/grok/directive.py
A grok/trunk/src/grok/tests/directive/
A grok/trunk/src/grok/tests/directive/__init__.py
A grok/trunk/src/grok/tests/directive/argumenterror.py
A grok/trunk/src/grok/tests/directive/argumenterror_fixture.py
U grok/trunk/src/grok/tests/test_grok.py
-=-
Modified: grok/trunk/doc/design/utility.py
===================================================================
--- grok/trunk/doc/design/utility.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/doc/design/utility.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -3,9 +3,9 @@
class Calculator(grok.GlobalUtility):
grok.implements(ICalculator) # if this is not specified, it breaks
grok.name('') # this is actually the default
- grok.utility_provides(ICalculator) # this is actually the default
+ grok.provides(ICalculator) # this is actually the default
-grok.global_utility(factory, provides=IFace, name=u'', setup=None)
+grok.global_utility(factory, provides=IFace, name=u'')
class Calculator(grok.LocalUtility):
grok.utility_provides(ICalculator)
Modified: grok/trunk/src/grok/directive.py
===================================================================
--- grok/trunk/src/grok/directive.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/src/grok/directive.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -15,6 +15,7 @@
"""
import sys
+import inspect
from zope import interface
from zope.interface.interfaces import IInterface
@@ -66,66 +67,106 @@
"""
Directive sets a value into the context's locals as __<name>__
('.' in the name are replaced with '_').
- A directive can be called only once.
"""
- def __init__(self, name, directive_context):
+ def __init__(self, name, directive_context, value_factory):
self.name = name
+ self.local_name = '__%s__' % name.replace('.', '_')
self.directive_context = directive_context
+ self.value_factory = value_factory
- def __call__(self, value):
- self.check(value)
+ def __call__(self, *args, **kw):
+ self.check_argument_signature(*args, **kw)
+ self.check_arguments(*args, **kw)
frame = sys._getframe(1)
+ self.check_directive_context(frame)
+
+ value = self.value_factory(*args, **kw)
+ self.store(frame, value)
+
+ def check_arguments(self, *args, **kw):
+ raise NotImplementedError
+
+ # to get a correct error message, we construct a function that has the same
+ # signature as check_arguments(), but without "self".
+ def check_argument_signature(self, *arguments, **kw):
+ args, varargs, varkw, defaults = inspect.getargspec(self.check_arguments)
+ argspec = inspect.formatargspec(args[1:], varargs, varkw, defaults)
+ exec("def signature_checker" + argspec + ": pass")
+ try:
+ signature_checker(*arguments, **kw)
+ except TypeError, e:
+ message = e.args[0]
+ message = message.replace("signature_checker()", self.name)
+ raise TypeError(message)
+
+ def check_directive_context(self, frame):
if not self.directive_context.matches(frame):
raise GrokImportError("%s can only be used on %s level."
% (self.name,
self.directive_context.description))
- local_name = '__%s__' % self.name.replace('.', '_')
- if local_name in frame.f_locals:
+ def store(self, frame, value):
+ raise NotImplementedError
+
+class OnceDirective(Directive):
+ def store(self, frame, value):
+ if self.local_name in frame.f_locals:
raise GrokImportError("%s can only be called once per %s."
% (self.name,
self.directive_context.description))
- frame.f_locals[local_name] = value
+ frame.f_locals[self.local_name] = value
- def check(self, value):
- pass
-
-class TextDirective(Directive):
+class TextDirective(OnceDirective):
"""
Directive that only accepts unicode/ASCII values.
"""
- def check(self, value):
+ def check_arguments(self, value):
if util.not_unicode_or_ascii(value):
raise GrokImportError("You can only pass unicode or ASCII to "
"%s." % self.name)
-class InterfaceOrClassDirective(Directive):
+class InterfaceOrClassDirective(OnceDirective):
"""
Directive that only accepts classes or interface values.
"""
- def check(self, value):
+ def check_arguments(self, value):
if not (IInterface.providedBy(value) or util.isclass(value)):
raise GrokImportError("You can only pass classes or interfaces to "
"%s." % self.name)
-class InterfaceDirective(Directive):
+class InterfaceDirective(OnceDirective):
"""
Directive that only accepts interface values.
"""
- def check(self, value):
+ def check_arguments(self, value):
if not (IInterface.providedBy(value)):
raise GrokImportError("You can only pass interfaces to "
"%s." % self.name)
+
+# Even though the value_factory is called with (*args, **kw), we're safe since
+# check_arguments would have bailed out with a TypeError if the number arguments
+# we were called with was not what we expect here.
+def single_value_factory(value):
+ return value
# Define grok directives
-name = TextDirective('grok.name', ClassDirectiveContext())
-template = TextDirective('grok.template', ClassDirectiveContext())
+name = TextDirective('grok.name',
+ ClassDirectiveContext(),
+ single_value_factory)
+template = TextDirective('grok.template',
+ ClassDirectiveContext(),
+ single_value_factory)
context = InterfaceOrClassDirective('grok.context',
- ClassOrModuleDirectiveContext())
-templatedir = TextDirective('grok.templatedir', ModuleDirectiveContext())
-provides = InterfaceDirective('grok.provides', ClassDirectiveContext())
+ ClassOrModuleDirectiveContext(),
+ single_value_factory)
+templatedir = TextDirective('grok.templatedir',
+ ModuleDirectiveContext(),
+ single_value_factory)
+provides = InterfaceDirective('grok.provides',
+ ClassDirectiveContext(),
+ single_value_factory)
Added: grok/trunk/src/grok/tests/directive/__init__.py
===================================================================
--- grok/trunk/src/grok/tests/directive/__init__.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/src/grok/tests/directive/__init__.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -0,0 +1 @@
+# this is a package
Added: grok/trunk/src/grok/tests/directive/argumenterror.py
===================================================================
--- grok/trunk/src/grok/tests/directive/argumenterror.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/src/grok/tests/directive/argumenterror.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -0,0 +1,6 @@
+"""
+ >>> import grok.tests.directive.argumenterror_fixture
+ Traceback (most recent call last):
+ ...
+ TypeError: grok.templatedir takes exactly 1 argument (3 given)
+"""
Added: grok/trunk/src/grok/tests/directive/argumenterror_fixture.py
===================================================================
--- grok/trunk/src/grok/tests/directive/argumenterror_fixture.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/src/grok/tests/directive/argumenterror_fixture.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -0,0 +1,3 @@
+import grok
+
+grok.templatedir('too', 'many', 'arguments')
Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py 2007-01-06 16:07:59 UTC (rev 71741)
+++ grok/trunk/src/grok/tests/test_grok.py 2007-01-06 16:15:18 UTC (rev 71742)
@@ -34,7 +34,7 @@
suite = unittest.TestSuite()
for name in ['adapter', 'error', 'view', 'scan', 'event',
'zcml', 'static', 'utility', 'xmlrpc', 'container',
- 'traversal', 'form', 'site', 'grokker']:
+ 'traversal', 'form', 'site', 'grokker', 'directive']:
suite.addTest(suiteFromPackage(name))
return suite
More information about the Checkins
mailing list