[Checkins] SVN: grok/trunk/src/grok/ Introduce ModuleGrokker and priority for grokkers. This allows us to

Martijn Faassen faassen at infrae.com
Tue Dec 12 15:08:47 EST 2006


Log message for revision 71541:
  Introduce ModuleGrokker and priority for grokkers. This allows us to
  get rid of the last bits in _grok that have to do with actual grokking
  and move it over to meta.py.
  
  ModuleGrokkers get executed once per module. This can then be used to
  register filesystem templates, or various decorators, or static files if
  the module happens to be a package.
  
  Grokkers get a priority now. The higher the priority, the earlier in the
  grokking process they're executed. We use this to make sure that some
  grokkers get executed before others.
  

Changed:
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/_grok.py
  U   grok/trunk/src/grok/components.py
  U   grok/trunk/src/grok/grokker.py
  U   grok/trunk/src/grok/interfaces.py
  U   grok/trunk/src/grok/meta.py

-=-
Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/__init__.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -29,7 +29,7 @@
     IObjectRemovedEvent, ObjectRemovedEvent,
     IContainerModifiedEvent, ContainerModifiedEvent)
 
-from grok.components import ClassGrokker, InstanceGrokker
+from grok.components import ClassGrokker, InstanceGrokker, ModuleGrokker
 from grok.components import Model, Adapter, MultiAdapter, View, XMLRPC
 from grok.components import PageTemplate, Utility, Container, Traverser, Site
 from grok.components import EditForm, DisplayForm, AddForm

Modified: grok/trunk/src/grok/_grok.py
===================================================================
--- grok/trunk/src/grok/_grok.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/_grok.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -18,10 +18,9 @@
 
 from zope import component
 from zope import interface
-import zope.component.interface
+
 from zope.component.interfaces import IDefaultViewName
-from zope.publisher.interfaces.browser import (IDefaultBrowserLayer,
-                                               IBrowserRequest)
+from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.app.component.site import LocalSiteManager
 
 import grok
@@ -59,6 +58,8 @@
 # the Component Architecture is torn down.
 def resetBootstrap():
     global _bootstrapped
+    # we need to make sure that the grokker registry is clean again
+    grokker.grokkerRegistry.clear()
     _bootstrapped = False
 from zope.testing.cleanup import addCleanUp
 addCleanUp(resetBootstrap)
@@ -75,47 +76,11 @@
 
 
 def grok_tree(module_info):
-    grok_module(module_info)
+    grokker.grokkerRegistry.grok(module_info)
 
-    if not module_info.isPackage():
-        return
-
-    resource_path = module_info.getResourcePath('static')
-    if os.path.isdir(resource_path):
-        static_module = module_info.getSubModuleInfo('static')
-        if static_module is not None:
-            if static_module.isPackage():
-                raise GrokError("The 'static' resource directory must not "
-                                "be a python package.", module_info.getModule())
-            else:
-                raise GrokError("A package can not contain both a 'static' "
-                                "resource directory and a module named "
-                                "'static.py'", module_info.getModule())
-
-        register_static_resources(module_info.dotted_name, resource_path)
-
     for sub_module_info in module_info.getSubModuleInfos():
         grok_tree(sub_module_info)
-
-def grok_module(module_info):
-    grokker.grokkerRegistry.grok(module_info)
-
-    # XXX we should ideally also make it pluggable to register decorators like
-    # the ones for subscribers.
-    register_subscribers(module_info.getAnnotation('grok.subscribers', []))    
-    
-def register_static_resources(dotted_name, resource_directory):
-    resource_factory = components.DirectoryResourceFactory(resource_directory,
-                                                    dotted_name)
-    component.provideAdapter(resource_factory, (IDefaultBrowserLayer,),
-                             interface.Interface, name=dotted_name)
-
-def register_subscribers(subscribers):
-    for factory, subscribed in subscribers:
-        component.provideHandler(factory, adapts=subscribed)
-        for iface in subscribed:
-            zope.component.interface.provideInterface('', iface)
-
+        
 # decorators
 class SubscribeDecorator:
     def __init__(self, *args):

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/components.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -47,7 +47,11 @@
 from grok import util, security, interfaces
 
 class ClassGrokker(object):
+    """Grokker for particular classes in a module.
+    """
     # subclasses should have a component_class class variable
+
+    priority = 0
     
     def match(self, obj):
         return util.check_subclass(obj, self.component_class)
@@ -57,7 +61,11 @@
 
 
 class InstanceGrokker(object):
+    """Grokker for particular instances in a module.
+    """
     # subclasses should have a component_class class variable
+
+    priority = 0
     
     def match(self, obj):
         return isinstance(obj, self.component_class)
@@ -66,6 +74,19 @@
         raise NotImplementedError
 
 
+class ModuleGrokker(object):
+    """Grokker that gets executed once for a module.
+    """
+    priority = 0
+
+    def match(self, obj):
+        # we never match with any object
+        return False
+
+    def register(self, context, module_info, templates):
+        raise NotImplementedError
+
+
 class Model(Contained, persistent.Persistent):
     # XXX Inheritance order is important here. If we reverse this,
     # then containers can't be models anymore because no unambigous MRO

Modified: grok/trunk/src/grok/grokker.py
===================================================================
--- grok/trunk/src/grok/grokker.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/grokker.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -3,14 +3,23 @@
 
 class GrokkerRegistry(object):
     def __init__(self):
-        self._grokkers = {}
+        self.clear()
+
+    def clear(self):
+        self._grokkers = []
+        # register the meta grokkers manually as we can't grok those
+        self.registerGrokker(ClassGrokkerGrokker())
+        self.registerGrokker(InstanceGrokkerGrokker())
+        self.registerGrokker(ModuleGrokkerGrokker())
         
     def registerGrokker(self, grokker):
-        self._grokkers[grokker.component_class] = grokker
+        self._grokkers.append(grokker)
 
     def scan(self, module_info):
         components = {}
-        for grokker in self._grokkers.values():
+        for grokker in self._grokkers:
+            if isinstance(grokker, grok.ModuleGrokker):
+                continue
             components[grokker.component_class] = []
     
         module = module_info.getModule()
@@ -22,7 +31,7 @@
                 continue
             # XXX find way to get rid of this inner loop by doing hash table
             # lookup?
-            for grokker in self._grokkers.values():
+            for grokker in self._grokkers:
                 if grokker.match(obj):
                     components[grokker.component_class].append((name, obj))
                     break
@@ -41,37 +50,33 @@
         
         templates = templatereg.TemplateRegistry()
 
-        # XXX because templates are instances and we need access to
-        # registered module-level templates during class grokking,
-        # we need to make sure we do PageTemplate grokking before any
-        # other grokking. We need to revise this as we work out
-        # extensible template grokking. Possibly we need to introduce
-        # a priority for grokkers so we can sort them.
-        page_templates = scanned_results.pop(grok.PageTemplate, None)
-        if page_templates is not None:
-            grokker = self._grokkers[grok.PageTemplate]
-            for name, component in page_templates:
+        # sort grokkers by priority
+        grokkers = sorted(self._grokkers, 
+                          key=lambda grokker: grokker.priority)
+        # we want to handle high priority first
+        grokkers.reverse()
+    
+        # run through all grokkers registering found components in order
+        for grokker in grokkers:
+            # if we run into a ModuleGrokker, just do simple registration.
+            # this allows us to hook up grokkers to the process that actually
+            # do not respond to anything in the module but for instance
+            # to the filesystem to find templates
+            if isinstance(grokker, grok.ModuleGrokker):
+                grokker.register(context, module_info, templates)
+                continue
+            
+            components = scanned_results.get(grokker.component_class, [])
+            for name, component in components:
                 grokker.register(context,
                                  name, component,
                                  module_info, templates)
 
-        # XXX filesystem level templates need to be scanned for after
-        # inline templates to produce the right errors
-        templates.findFilesystem(module_info)
-
-        # now grok the rest
-        for component_class, components in scanned_results.items():
-            grokker = self._grokkers[component_class]
-            for name, component in components:
-                grokker.register(context,
-                                 name, component,
-                                 module_info, templates)
-    
         templates.registerUnassociated(context, module_info)
 
 
-# deep meta mode here - we define grokkers for grok.ClassGrokker and
-# grok.InstanceGrokker.
+# deep meta mode here - we define grokkers for grok.ClassGrokker,
+# grok.InstanceGrokker, and grokker.ModuleGrokker.
 
 class MetaGrokker(grok.ClassGrokker):
     def register(self, context, name, factory, module_info, templates):
@@ -83,9 +88,9 @@
 class InstanceGrokkerGrokker(MetaGrokker):
     component_class = grok.InstanceGrokker
 
+class ModuleGrokkerGrokker(MetaGrokker):
+    component_class = grok.ModuleGrokker
+    
 # the global grokker registry
 grokkerRegistry = GrokkerRegistry()
 
-# register the meta grokkers manually as we can't grok those
-grokkerRegistry.registerGrokker(ClassGrokkerGrokker())
-grokkerRegistry.registerGrokker(InstanceGrokkerGrokker())

Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/interfaces.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -20,6 +20,8 @@
                                        "grokker.")
     InstanceGrokker = interface.Attribute("Base class to define an "
                                           "instance grokker.")
+    ModuleGrokker = interface.Attribute("Base class to define a "
+                                        "module grokker.")
     Model = interface.Attribute("Base class for persistent content objects "
                                 "(models).")
     Container = interface.Attribute("Base class for containers.")

Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py	2006-12-12 17:48:40 UTC (rev 71540)
+++ grok/trunk/src/grok/meta.py	2006-12-12 20:08:46 UTC (rev 71541)
@@ -1,5 +1,7 @@
+import os
 import inspect
 
+import zope.component.interface
 from zope import interface, component
 from zope.security.checker import (defineChecker, getCheckerForInstancesOf,
                                    NoProxy)
@@ -166,10 +168,62 @@
         component.provideAdapter(factory,
                                  adapts=(factory_context, IBrowserRequest),
                                  provides=IBrowserPublisher)
-
-class PageTemplateGrokker(grok.InstanceGrokker):
+    
+class ModulePageTemplateGrokker(grok.InstanceGrokker):
+    # this needs to happen before any other grokkers execute that actually
+    # use the templates
+    priority = 1000
+    
     component_class = grok.PageTemplate
 
     def register(self, context, name, instance, module_info, templates):
         templates.register(name, instance)
         instance._annotateGrokInfo(name, module_info.dotted_name)
+
+class FilesystemPageTemplateGrokker(grok.ModuleGrokker):
+    # do this early on, but after ModulePageTemplateGrokker, as
+    # findFilesystem depends on module-level templates to be
+    # already grokked for error reporting
+    priority = 999
+    
+    def register(self, context, module_info, templates):
+        templates.findFilesystem(module_info)
+
+class SubscriberGrokker(grok.ModuleGrokker):
+
+    def register(self, context, module_info, templates):
+        subscribers = module_info.getAnnotation('grok.subscribers', [])
+    
+        for factory, subscribed in subscribers:
+            component.provideHandler(factory, adapts=subscribed)
+            for iface in subscribed:
+                zope.component.interface.provideInterface('', iface)
+
+class StaticResourcesGrokker(grok.ModuleGrokker):
+
+    def register(self, context, module_info, templates):
+        # we're only interested in static resources if this module
+        # happens to be a package
+        if not module_info.isPackage():
+            return
+        
+        resource_path = module_info.getResourcePath('static')
+        if os.path.isdir(resource_path):
+            static_module = module_info.getSubModuleInfo('static')
+            if static_module is not None:
+                if static_module.isPackage():
+                    raise GrokError(
+                        "The 'static' resource directory must not "
+                        "be a python package.",
+                        module_info.getModule())
+                else:
+                    raise GrokError(
+                        "A package can not contain both a 'static' "
+                        "resource directory and a module named "
+                        "'static.py'", module_info.getModule())
+        
+        resource_factory = components.DirectoryResourceFactory(
+            resource_path, module_info.dotted_name)
+        component.provideAdapter(
+            resource_factory, (IDefaultBrowserLayer,),
+            interface.Interface, name=module_info.dotted_name)



More information about the Checkins mailing list