[Checkins] SVN: grok/trunk/src/grok/ * refactored grok.grok() into
parts
Wolfgang Schnerring
wosc at wosc.de
Tue Oct 17 09:25:47 EDT 2006
Log message for revision 70743:
* refactored grok.grok() into parts
* implemented @grok.subscribe
Changed:
U grok/trunk/src/grok/__init__.py
U grok/trunk/src/grok/_grok.py
A grok/trunk/src/grok/tests/event/
A grok/trunk/src/grok/tests/event/__init__.py
A grok/trunk/src/grok/tests/event/errorconditions.py
A grok/trunk/src/grok/tests/event/errorconditions_fixture.py
A grok/trunk/src/grok/tests/event/subscriber.py
U grok/trunk/src/grok/tests/test_grok.py
-=-
Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/__init__.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -16,7 +16,10 @@
from zope.interface import implements
from zope.component import adapts
+from zope.lifecycleevent import IObjectCreatedEvent, ObjectCreatedEvent
+from zope.event import notify
from grok._grok import (Model, Adapter, MultiAdapter, View, PageTemplate,
grok, context, name, template, resources, )
+from grok._grok import SubscribeDecorator as subscribe
from grok.error import GrokError, GrokImportError
Modified: grok/trunk/src/grok/_grok.py
===================================================================
--- grok/trunk/src/grok/_grok.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/_grok.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -20,6 +20,7 @@
from zope.dottedname.resolve import resolve
from zope import component
from zope import interface
+import zope.component.interface
from zope.component.interfaces import IDefaultViewName
from zope.security.checker import defineChecker, getCheckerForInstancesOf, NoProxy
from zope.publisher.browser import BrowserPage
@@ -31,8 +32,9 @@
from grok.error import GrokError, GrokImportError
from grok.directive import (ClassDirectiveContext, ModuleDirectiveContext,
ClassOrModuleDirectiveContext,
- TextDirective, InterfaceOrClassDirective)
+ TextDirective, InterfaceOrClassDirective, frame_is_module)
+AMBIGUOUS_CONTEXT = object()
class Model(persistent.Persistent):
pass
@@ -80,9 +82,6 @@
def __repr__(self):
return '<%s template in %s>' % (self.__grok_name__, self.__grok_location__)
-
-AMBIGUOUS_CONTEXT = object()
-
def grok(dotted_name):
# register the name 'index' as the default view name
# TODO this needs to be moved to grok startup time (similar to ZCML-time)
@@ -97,12 +96,27 @@
def grok_module(dotted_name):
module = resolve(dotted_name)
- context = None
+ models, adapters, multiadapters, views, templates, subscribers = scan_module(dotted_name, module)
+
+ find_filesystem_templates(dotted_name, module, templates)
+
+ context = determine_module_context(module, models)
+
+ register_models(models)
+ register_adapters(context, adapters)
+ register_multiadapters(multiadapters)
+ register_views(context, views, templates)
+ register_unassociated_templates(context, templates)
+ register_subscribers(subscribers)
+
+def scan_module(dotted_name, module):
models = []
adapters = []
multiadapters = []
views = []
templates = TemplateRegistry()
+ subscribers = getattr(module, '__grok_subscribers__', [])
+
for name in dir(module):
obj = getattr(module, name)
@@ -122,7 +136,9 @@
obj.__grok_name__ = name
obj.__grok_location__ = dotted_name
- # find filesystem resources
+ return models, adapters, multiadapters, views, templates, subscribers
+
+def find_filesystem_templates(dotted_name, module, templates):
module_name = dotted_name.split('.')[-1]
directory_name = directive_annotation(module, 'grok.resources', module_name)
if resource_exists(dotted_name, directory_name):
@@ -149,33 +165,27 @@
inline_template)
templates.register(template_name, template)
- if len(models) == 0:
- context = None
- elif len(models) == 1:
- context = models[0]
- else:
- context = AMBIGUOUS_CONTEXT
- module_context = directive_annotation(module, 'grok.context', None)
- if module_context:
- context = module_context
-
+def register_models(models):
for model in models:
# TODO minimal security here (read: everything is public)
if not getCheckerForInstancesOf(model):
defineChecker(model, NoProxy)
+def register_adapters(context, adapters):
for factory in adapters:
- adapter_context = determine_context(factory, context)
+ adapter_context = determine_class_context(factory, context)
name = directive_annotation(factory, 'grok.name', '')
component.provideAdapter(factory, adapts=(adapter_context,), name=name)
+def register_multiadapters(multiadapters):
for factory in multiadapters:
name = directive_annotation(factory, 'grok.name', '')
component.provideAdapter(factory, name=name)
+def register_views(context, views, templates):
for factory in views:
- view_context = determine_context(factory, context)
+ view_context = determine_class_context(factory, context)
factory_name = factory.__name__.lower()
# find inline templates
@@ -216,6 +226,7 @@
# TODO minimal security here (read: everything is public)
defineChecker(factory, NoProxy)
+def register_unassociated_templates(context, templates):
for name, unassociated in templates.listUnassociatedTemplates():
check_context(unassociated, context)
@@ -232,6 +243,12 @@
# TODO minimal security here (read: everything is public)
defineChecker(TemplateView, NoProxy)
+def register_subscribers(subscribers):
+ for factory, subscribed in subscribers:
+ component.provideHandler(factory, adapts=subscribed)
+ for iface in subscribed:
+ zope.component.interface.provideInterface('', iface)
+
class TemplateRegistry(object):
def __init__(self):
@@ -268,10 +285,24 @@
raise GrokError("Multiple possible contexts for %r, please use "
"grok.context." % component, component)
-def determine_context(factory, module_context):
- context = directive_annotation(factory, 'grok.context', module_context)
- check_context(factory, context)
+def determine_module_context(module, models):
+ if len(models) == 0:
+ context = None
+ elif len(models) == 1:
+ context = models[0]
+ else:
+ context = AMBIGUOUS_CONTEXT
+
+ module_context = directive_annotation(module, 'grok.context', None)
+ if module_context:
+ context = module_context
+
return context
+
+def determine_class_context(class_, module_context):
+ context = directive_annotation(class_, 'grok.context', module_context)
+ check_context(class_, context)
+ return context
def directive_annotation(obj, name, default):
return getattr(obj, '__%s__' % name.replace('.', '_'), default)
@@ -285,3 +316,21 @@
context = InterfaceOrClassDirective('grok.context',
ClassOrModuleDirectiveContext())
resources = TextDirective('grok.resources', ModuleDirectiveContext())
+
+# decorators
+class SubscribeDecorator:
+ def __init__(self, *args):
+ self.subscribed = args
+
+ def __call__(self, function):
+ frame = sys._getframe(1)
+ if not frame_is_module(frame):
+ raise GrokImportError("@grok.subscribe can only be used on module level.")
+
+ if not self.subscribed:
+ raise GrokImportError("@grok.subscribe requires at least one argument.")
+
+ subscribers = frame.f_locals.get('__grok_subscribers__', None)
+ if subscribers is None:
+ frame.f_locals['__grok_subscribers__'] = subscribers = []
+ subscribers.append((function, self.subscribed))
Added: grok/trunk/src/grok/tests/event/__init__.py
===================================================================
--- grok/trunk/src/grok/tests/event/__init__.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/tests/event/__init__.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -0,0 +1 @@
+# this is a package
Added: grok/trunk/src/grok/tests/event/errorconditions.py
===================================================================
--- grok/trunk/src/grok/tests/event/errorconditions.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/tests/event/errorconditions.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -0,0 +1,34 @@
+"""
+ at grok.subscribe can only be used on module level:
+
+ >>> function_context()
+ Traceback (most recent call last):
+ ...
+ GrokImportError: @grok.subscribe can only be used on module level.
+
+ >>> class_context()
+ Traceback (most recent call last):
+ ...
+ GrokImportError: @grok.subscribe can only be used on module level.
+
+
+ at grok.subscribe can not be called without arguments:
+
+ >>> import grok.tests.event.errorconditions_fixture
+ Traceback (most recent call last):
+ ...
+ GrokImportError: @grok.subscribe requires at least one argument.
+
+"""
+import grok
+
+def function_context():
+ @grok.subscribe(grok.Model, grok.IObjectCreatedEvent)
+ def subscriber():
+ pass
+
+def class_context():
+ class Wrapper:
+ @grok.subscribe(grok.Model, grok.IObjectCreatedEvent)
+ def subscriber(self):
+ pass
Added: grok/trunk/src/grok/tests/event/errorconditions_fixture.py
===================================================================
--- grok/trunk/src/grok/tests/event/errorconditions_fixture.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/tests/event/errorconditions_fixture.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -0,0 +1,5 @@
+import grok
+
+ at grok.subscribe()
+def subscriber():
+ pass
Added: grok/trunk/src/grok/tests/event/subscriber.py
===================================================================
--- grok/trunk/src/grok/tests/event/subscriber.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/tests/event/subscriber.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -0,0 +1,21 @@
+"""
+You can subscribe to events using the @grok.subscribe decorator:
+
+ >>> grok.grok(__name__)
+ >>> manfred = Mammoth('Manfred')
+ >>> grok.notify(grok.ObjectCreatedEvent(manfred))
+ >>> mammoths
+ ['Manfred']
+
+"""
+import grok
+
+class Mammoth(object):
+ def __init__(self, name):
+ self.name = name
+
+mammoths = []
+
+ at grok.subscribe(Mammoth, grok.IObjectCreatedEvent)
+def mammothAdded(mammoth, event):
+ mammoths.append(mammoth.name)
Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py 2006-10-17 11:33:46 UTC (rev 70742)
+++ grok/trunk/src/grok/tests/test_grok.py 2006-10-17 13:25:47 UTC (rev 70743)
@@ -1,7 +1,11 @@
import unittest
from pkg_resources import resource_listdir
from zope.testing import doctest, cleanup
+import zope.component.eventtesting
+def setUpZope(test):
+ zope.component.eventtesting.setUp(test)
+
def cleanUpZope(test):
cleanup.cleanUp()
@@ -17,7 +21,9 @@
continue
dottedname = 'grok.tests.%s.%s' % (name, filename[:-3])
- test = doctest.DocTestSuite(dottedname, tearDown=cleanUpZope,
+ test = doctest.DocTestSuite(dottedname,
+ setUp=setUpZope,
+ tearDown=cleanUpZope,
optionflags=doctest.ELLIPSIS+
doctest.NORMALIZE_WHITESPACE)
@@ -26,7 +32,7 @@
def test_suite():
suite = unittest.TestSuite()
- for name in ['adapter', 'error', 'view', 'security', 'scan']:
+ for name in ['adapter', 'error', 'view', 'security', 'scan', 'event']:
suite.addTest(suiteFromPackage(name))
return suite
More information about the Checkins
mailing list