[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