[Checkins] SVN: z3c.jbot/trunk/ Fixed issue with multiple layers; refactor, added tests.

Malthe Borch mborch at gmail.com
Sat Sep 26 11:27:32 EDT 2009


Log message for revision 104565:
  Fixed issue with multiple layers; refactor, added tests.

Changed:
  A   z3c.jbot/trunk/CHANGES.txt
  U   z3c.jbot/trunk/README.txt
  U   z3c.jbot/trunk/setup.py
  U   z3c.jbot/trunk/z3c/jbot/README.txt
  U   z3c.jbot/trunk/z3c/jbot/__init__.py
  U   z3c.jbot/trunk/z3c/jbot/manager.py
  U   z3c.jbot/trunk/z3c/jbot/metaconfigure.py
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/http/
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/http/z3c.jbot.tests.templates.example.pt
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/https/
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/https/z3c.jbot.tests.templates.example.pt
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/interface/
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/interface/z3c.jbot.tests.templates.example.pt
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/request/
  A   z3c.jbot/trunk/z3c/jbot/tests/overrides/request/z3c.jbot.tests.templates.example.pt
  D   z3c.jbot/trunk/z3c/jbot/tests/templates/z3c.jbot.tests.templates.example.pt
  U   z3c.jbot/trunk/z3c/jbot/utility.py

-=-
Added: z3c.jbot/trunk/CHANGES.txt
===================================================================
--- z3c.jbot/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.jbot/trunk/CHANGES.txt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -0,0 +1,20 @@
+Changes
+=======
+
+In next release...
+
+- Improved test coverage.
+
+- Refactored code, improving performance.
+
+- Fixed issue with multiple layers.
+
+0.2 (2008-07-14)
+----------------
+
+- Added layer support.
+
+0.1 (2007-11-27)
+----------------
+
+- Initial public release.

Modified: z3c.jbot/trunk/README.txt
===================================================================
--- z3c.jbot/trunk/README.txt	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/README.txt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -1,5 +1,5 @@
 Overview
---------
+========
 
 The z3c.jbot (or "Just a bunch of templates") package allows drop-in
 page template overrides.
@@ -11,7 +11,6 @@
 
 Overrides may be registered for a specific layer or any layer.
 
-
 Canonical filename
 ------------------
 
@@ -24,28 +23,24 @@
   Suppose you want to override: /plone/app/layout/viewlets/logo.pt
   You would use the filename:   plone.app.layout.viewlets.logo.pt
 
-
 Registering a on overrides directory
 ------------------------------------
 
 A Zope component configuration directive is available to configure
-overrides.
+overrides::
 
   <include package="z3c.jbot" file="meta.zcml" />
-  
+
   <browser:templateOverrides
       directory="<directory>"
       layer="<layer>" />
-  
 
 Performance considerations
 --------------------------
 
 The use of jbot adds to the general page load time. On a site with
-many templates this may be as much as 20 ms per request (a 7% increase
-on my machine).
+many templates this may be as much as 10 ms per request.
 
-      
 Author
 ------
 

Modified: z3c.jbot/trunk/setup.py
===================================================================
--- z3c.jbot/trunk/setup.py	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/setup.py	2009-09-26 15:27:31 UTC (rev 104565)
@@ -1,12 +1,12 @@
 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.2dev'
+version = '0.3dev'
 
 setup(name='z3c.jbot',
       version=version,
       description="Drop-in template overrides.",
-      long_description=open("README.txt").read(),
+      long_description=open("README.txt").read() + open("CHANGES.txt").read(),
       classifiers=[
         "Framework :: Zope2",
         "Framework :: Zope3",

Modified: z3c.jbot/trunk/z3c/jbot/README.txt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/README.txt	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/README.txt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -11,7 +11,7 @@
 
   >>> from zope.pagetemplate.pagetemplatefile import PageTemplateFile
   >>> template = PageTemplateFile("tests/templates/example.pt")
-  
+
 A call to the template will render it.
 
   >>> template()
@@ -30,41 +30,23 @@
   >>> import z3c.jbot.tests
   >>> directory = z3c.jbot.tests.__path__[0]
 
-Register template manager factory. We'll register it for
-``zope.interface.Interface`` which makes it available on all layers.
-  
-  >>> import z3c.jbot.manager
-  >>> import z3c.jbot.interfaces
-  >>> factory = z3c.jbot.manager.TemplateManagerFactory()
-  >>> component.provideAdapter(
-  ...     factory, (interface.Interface,),
-  ...     z3c.jbot.interfaces.ITemplateManager)
+Register overrides directory (by default for any request); we confirm
+that it's registered for the same template manager.
 
-Register overrides directory.
-  
-  >>> manager = factory.manager
-  >>> manager.registerDirectory("%s/templates" % directory)
+  >>> from z3c.jbot.metaconfigure import handler
+  >>> manager = handler("%s/overrides/interface" % directory, interface.Interface)
 
-Verify that we've registered the contents of the directory:
-
-  >>> manager.paths
-  {'z3c.jbot.tests.templates.example.pt': '.../z3c.jbot.tests.templates.example.pt',
-   'example.pt': '.../example.pt'}
-  
-Notice that the file "z3c.jbot.tests.templates.example.pt" is the
-dotted name for the original example page template file.
-
 We should now see that the new filename will be used for rendering:
 
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./interface.\n'
 
 Before we proceed we'll clean up.
 
-  >>> manager.unregisterDirectory("%s/templates" % directory)
+  >>> manager.unregisterAllDirectories()
 
 The template does indeed render the original template.
-  
+
   >>> template()
   u'This is an example page template.\n'
 
@@ -74,73 +56,112 @@
   >>> template.filename
   '.../z3c.jbot/z3c/jbot/tests/templates/example.pt'
 
-Overrides can be registered for a specific layer. Let's re-register an
-override template factory for the HTTP-request layer.
+Overrides can be registered for a specific layer. Let's register three
+more overrides, one for the general-purpose ``IRequest`` layer, one
+for the ``IHTTPRequest`` layer and one for a made-up ``IHTTPSRequest``
+layer.
 
-  >>> from zope.publisher.interfaces.browser import IHTTPRequest
-  >>> factory = z3c.jbot.manager.TemplateManagerFactory()
-  >>> component.provideAdapter(
-  ...     factory, (IHTTPRequest,),
-  ...     z3c.jbot.interfaces.ITemplateManager, name='http')
+  >>> from zope.publisher.interfaces import IRequest
+  >>> from zope.publisher.interfaces.http import IHTTPRequest
+  >>> class IHTTPSRequest(IRequest):
+  ...     """An HTTPS request."""
 
-Register overrides directory.
-  
-  >>> manager = factory.manager
-  >>> manager.registerDirectory("%s/templates" % directory)
+Next we register an overrides directory for the ``IRequest`` layer.
 
-Let's set up an interaction with a base request.
+  >>> general = handler("%s/overrides/request" % directory, IRequest)
 
+Let's set up an interaction with a trivial participation.
+
+  >>> class Participation:
+  ...     interaction = None
+
+  >>> participation = Participation()
   >>> import zope.security.management
-  >>> import zope.publisher.base
-  >>> request = zope.publisher.base.BaseRequest("", {})
-  >>> IHTTPRequest.providedBy(request)
+  >>> zope.security.management.newInteraction(participation)
+
+This participation does not provide even the basic request interface.
+
+  >>> IRequest.providedBy(participation)
   False
-  >>> zope.security.management.newInteraction(request)  
 
-Since this request is not an HTTP-request, we don't expect the
-override to be enabled.
+We don't expect the template to be overriden for this interaction.
 
   >>> template()
   u'This is an example page template.\n'
 
-Let's now engage in an interaction with an HTTP-request.
-  
-  >>> interface.alsoProvides(request, IHTTPRequest)
+Let's upgrade it.
+
+  >>> request = participation
+  >>> interface.alsoProvides(request, IRequest)
+
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./request.\n'
 
   >>> template._v_cooked
   1
-  
+
 Going back to a basic request.
 
-  >>> interface.noLongerProvides(request, IHTTPRequest)
-  >>> IHTTPRequest.providedBy(request)
-  False
-  
+  >>> interface.noLongerProvides(request, IRequest)
   >>> template()
   u'This is an example page template.\n'
 
 Let's verify that we only cook once per template source.
 
-  >>> import z3c.jbot.utility
   >>> output = template()
   >>> template._v_last_read and template._v_cooked
   1
 
-  >>> interface.alsoProvides(request, IHTTPRequest)
+  >>> interface.alsoProvides(request, IRequest)
   >>> output = template()
   >>> template._v_last_read and template._v_cooked
   1
 
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./request.\n'
 
-  >>> for manager in z3c.jbot.utility.getManagers():
-  ...     manager.unregisterDirectory("%s/templates" % directory)
-  
+Now, if we switch to the HTTP-layer.
+
+  >>> interface.noLongerProvides(request, IRequest)
+  >>> interface.alsoProvides(request, IHTTPRequest)
+
+  >>> template()
+  u'Override from ./request.\n'
+
+  >>> general.unregisterAllDirectories()
+
+  >>> template()
+  u'This is an example page template.\n'
+
+  >>> http = handler("%s/overrides/http" % directory, IHTTPRequest)
+  >>> https = handler("%s/overrides/https" % directory, IHTTPSRequest)
+
+  >>> template()
+  u'Override from ./http.\n'
+
+Switching to HTTPS.
+
   >>> interface.noLongerProvides(request, IHTTPRequest)
-  
+  >>> interface.alsoProvides(request, IHTTPSRequest)
+
+  >>> template()
+  u'Override from ./https.\n'
+
+  >>> interface.noLongerProvides(request, IHTTPSRequest)
+
+Unregister all directories (cleanup).
+
+  >>> for manager, layer in ((http, IHTTPRequest), (https, IHTTPSRequest)):
+  ...     interface.alsoProvides(request, layer)
+  ...     _ = template()
+  ...     manager.unregisterAllDirectories()
+  ...     interface.noLongerProvides(request, layer)
+
+The override is no longer in effect.
+
+  >>> template()
+  u'This is an example page template.\n'
+
 Configuring template override directories in ZCML
 -------------------------------------------------
 
@@ -155,42 +176,42 @@
 
   >>> xmlconfig.xmlconfig(StringIO("""
   ... <configure xmlns="http://namespaces.zope.org/browser">
-  ... <templateOverrides directory="%s/templates" />
+  ... <templateOverrides directory="%s/overrides/interface" />
   ... </configure>
   ... """ % directory))
 
 Once again, the override will be in effect.
-  
+
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./interface.\n'
 
 Providing the HTTP-request layer does not change this.
 
   >>> interface.alsoProvides(request, IHTTPRequest)
 
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./interface.\n'
 
 Unregister overrides.
-  
-  >>> manager = tuple(z3c.jbot.utility.getManagers())[0]
-  >>> manager.unregisterDirectory("%s/templates" % directory)
-  
+
+  >>> for manager in z3c.jbot.utility.getManagers():
+  ...     manager.unregisterAllDirectories()
+
   >>> template()
   u'This is an example page template.\n'
-    
+
 Let's register overrides for the HTTP-request layer.
 
   >>> xmlconfig.xmlconfig(StringIO("""
   ... <configure xmlns="http://namespaces.zope.org/browser">
   ... <templateOverrides
-  ...      directory="%s/templates"
+  ...      directory="%s/overrides/http"
   ...      layer="zope.publisher.interfaces.browser.IHTTPRequest" />
   ... </configure>
   ... """ % directory))
 
-If we now provide the HTTP-request layer, the override becomes active.
+Since we now provide the HTTP-request layer, the override is used.
 
   >>> template()
-  u'This template will override the example template.\n'
+  u'Override from ./http.\n'
 

Modified: z3c.jbot/trunk/z3c/jbot/__init__.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/__init__.py	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/__init__.py	2009-09-26 15:27:31 UTC (rev 104565)
@@ -1,4 +1,3 @@
-from zope import interface
 from zope.pagetemplate.pagetemplatefile import PageTemplateFile
 
 import utility
@@ -15,6 +14,8 @@
 except:
     pass
 
+_v_cache = threading.local()
+
 class LayerProperty(property):
     """Layer-specific property class.
 
@@ -30,46 +31,59 @@
     This pattern takes into account that attributes may be set before
     the property is defined on the class.
     """
-    
+
     def __init__(self, cls, name):
         self.name = name
         self.default = getattr(cls, name, None)
         property.__init__(self, self._get, self._set)
-        
+
     def _get(self, template):
         key = self.name
-        layer = getattr(template._v_cache, 'layer', None)
-        attrs = getattr(template, '_v_attrs', template.__dict__)
-        if (layer, key) in attrs:
+        __dict__ = template.__dict__
+
+        # note: exceptions should not arise during normal service
+        try:
+            layer = _v_cache.layer
+        except AttributeError:
+            layer = None
+
+        try:
+            # any of these could raise a key-error
+            attrs = __dict__['_v_attrs']
             return attrs[layer, key]
-        return attrs.get(key) or template.__dict__.get(key) or self.default
-    
+        except KeyError:
+            return __dict__.get(key) or self.default
+
     def _set(self, template, value):
         key = self.name
-        layer = getattr(template._v_cache, 'layer', None)
-        attrs = template.__dict__.get('_v_attrs')
-        if attrs is None:
-            attrs = template._v_attrs = {}
+        __dict__ = template.__dict__
 
-        # set value
+        # note: exceptions should not arise during normal service
+        try:
+            layer = _v_cache.layer
+        except AttributeError:
+            layer = None
+
+        try:
+            attrs = __dict__['_v_attrs']
+        except KeyError:
+            attrs = __dict__['_v_attrs'] = {}
+
         attrs[layer, key] = value
-            
-        # set default value
-        attrs.setdefault(key, value)
-            
+        __dict__.setdefault(key, value)
+
 # registration hook to template manager
 def jbot(func):
     def patch(self, *args, **kwargs):
-        # set layer
-        self._v_cache.layer = utility.getLayer()
+        _v_cache.layer = utility.getLayer()
 
         for manager in utility.getManagers():
             # register template; this call returns ``True`` if
             # template was invalidated
             if manager.registerTemplate(self):
                 break
-        
-        return func(self, *args, **kwargs)        
+
+        return func(self, *args, **kwargs)
     return patch
 
 logger.info("Patching page template classes for use with z3c.jbot...")
@@ -82,7 +96,5 @@
 for pt_class in PT_CLASSES:
     for name in ('_v_macros', '_v_program', '_v_cooked', '_v_errors',
                  '_v_last_read', '_v_warning', '_text_',
-                 'filename', 'content_type', 'is_html'):
+                 'filename', '_filename', 'content_type', 'is_html'):
         setattr(pt_class, name, LayerProperty(pt_class, name))
-
-    setattr(pt_class, '_v_cache', threading.local())

Modified: z3c.jbot/trunk/z3c/jbot/manager.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/manager.py	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/manager.py	2009-09-26 15:27:31 UTC (rev 104565)
@@ -7,6 +7,7 @@
 import interfaces
 
 IGNORE = object()
+DELETE = object()
 
 def root_length(a, b):
     if b.startswith(a):
@@ -21,7 +22,7 @@
 def find_zope2_product(path):
     """Check the Zope2 magic Products semi-namespace to see if the
     path is part of a Product."""
-    
+
     _syspaths = sort_by_path(path, sys.modules["Products"].__path__)
     syspath = _syspaths[0]
 
@@ -39,37 +40,41 @@
 
     _syspaths = sort_by_path(path, syspaths)
     syspath = _syspaths[0]
-    
+
     path = os.path.normpath(path)
     if not path.startswith(syspath):
         if utility.ZOPE_2:
             return find_zope2_product(path)
         return None
-    
+
     path = path[len(syspath):]
-    
+
     # convert path to dotted filename
     if path.startswith(os.path.sep):
         path = path[1:]
-        
+
     return path
 
 class TemplateManagerFactory(object):
-    def __init__(self):
-        self.manager = TemplateManager()
+    def __init__(self, name):
+        self.manager = TemplateManager(name)
 
     def __call__(self, layer):
         return self.manager
-    
+
 class TemplateManager(object):
     interface.implements(interfaces.ITemplateManager)
-    
-    def __init__(self):
+
+    def __init__(self, name):
         self.syspaths = tuple(sys.path)
         self.templates = {}
         self.paths = {}
+        self.directories = set()
+        self.name = name
 
     def registerDirectory(self, directory):
+        self.directories.add(directory)
+
         for filename in os.listdir(directory):
             if filename.endswith('.pt'):
                 self.paths[filename] = "%s/%s" % (directory, filename)
@@ -79,8 +84,10 @@
                 del self.templates[template]
 
     def unregisterDirectory(self, directory):
+        self.directories.remove(directory)
+
         templates = []
-        
+
         for template, filename in self.templates.items():
             if filename in self.paths:
                 templates.append(template)
@@ -92,12 +99,18 @@
         for template in templates:
             self.registerTemplate(template)
             del self.templates[template]
-            
+            template.filename = template._filename
+            template._v_last_read = False
+
+    def unregisterAllDirectories(self):
+        for directory in tuple(self.directories):
+            self.unregisterDirectory(directory)
+
     def registerTemplate(self, template):
         # only register templates that have a filename attribute
         if not hasattr(template, 'filename'):
             return
-        
+
         # assert that the template is not already registered
         filename = self.templates.get(template)
         if filename is IGNORE:
@@ -110,18 +123,16 @@
 
         # verify that override has not been unregistered
         if filename is not None and filename not in paths:
-            # restore original template
             template.filename = template._filename
-            delattr(template, '_filename')
             del self.templates[template]
-            
+
         # check if an override exists
         path = find_package(self.syspaths, template.filename)
         if path is None:
             # permanently ignore template
             self.templates[template] = IGNORE
             return
-        
+
         filename = path.replace(os.path.sep, '.')
         if filename in paths:
             path = paths[filename]

Modified: z3c.jbot/trunk/z3c/jbot/metaconfigure.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/metaconfigure.py	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/metaconfigure.py	2009-09-26 15:27:31 UTC (rev 104565)
@@ -5,10 +5,10 @@
 import interfaces
 
 def handler(directory, layer):
-    gsm = component.getGlobalSiteManager()
+    lookup_all = component.getGlobalSiteManager().adapters.lookupAll
 
     # check if a template manager already exists
-    factories = set(factory for name, factory in gsm.adapters.lookupAll(
+    factories = set(factory for name, factory in lookup_all(
         (layer,), interfaces.ITemplateManager))
 
     # if factory is available on the interface bases of the layer we
@@ -16,18 +16,23 @@
     if layer is interface.Interface:
         base_factories = set()
     else:
-        base_factories = set(factory for name, factory in gsm.adapters.lookupAll(
-            (interface.implementedBy(layer.__bases__),), interfaces.ITemplateManager))
+        base_factories = set()
+        for base in layer.__bases__:
+            for name, factory in lookup_all((base,), interfaces.ITemplateManager):
+                base_factories.add(factory)
 
     try:
         factory = factories.difference(base_factories).pop()
     except KeyError:
-        factory = manager.TemplateManagerFactory()
+        name = directory
+        factory = manager.TemplateManagerFactory(name)
         component.provideAdapter(
-            factory, (layer,), interfaces.ITemplateManager, name=directory)
+            factory, (layer,), interfaces.ITemplateManager, name=name)
 
     factory(layer).registerDirectory(directory)
-    
+
+    return factory(layer)
+
 def templateOverridesDirective(_context, directory, layer=interface.Interface):
     _context.action(
         discriminator = ('override', directory, layer),

Added: z3c.jbot/trunk/z3c/jbot/tests/overrides/http/z3c.jbot.tests.templates.example.pt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/overrides/http/z3c.jbot.tests.templates.example.pt	                        (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/tests/overrides/http/z3c.jbot.tests.templates.example.pt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -0,0 +1 @@
+Override from ./http.

Added: z3c.jbot/trunk/z3c/jbot/tests/overrides/https/z3c.jbot.tests.templates.example.pt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/overrides/https/z3c.jbot.tests.templates.example.pt	                        (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/tests/overrides/https/z3c.jbot.tests.templates.example.pt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -0,0 +1 @@
+Override from ./https.

Added: z3c.jbot/trunk/z3c/jbot/tests/overrides/interface/z3c.jbot.tests.templates.example.pt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/overrides/interface/z3c.jbot.tests.templates.example.pt	                        (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/tests/overrides/interface/z3c.jbot.tests.templates.example.pt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -0,0 +1 @@
+Override from ./interface.

Copied: z3c.jbot/trunk/z3c/jbot/tests/overrides/request/z3c.jbot.tests.templates.example.pt (from rev 104480, z3c.jbot/trunk/z3c/jbot/tests/templates/z3c.jbot.tests.templates.example.pt)
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/overrides/request/z3c.jbot.tests.templates.example.pt	                        (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/tests/overrides/request/z3c.jbot.tests.templates.example.pt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -0,0 +1 @@
+Override from ./request.

Deleted: z3c.jbot/trunk/z3c/jbot/tests/templates/z3c.jbot.tests.templates.example.pt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/templates/z3c.jbot.tests.templates.example.pt	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/tests/templates/z3c.jbot.tests.templates.example.pt	2009-09-26 15:27:31 UTC (rev 104565)
@@ -1 +0,0 @@
-This template will override the example template.

Modified: z3c.jbot/trunk/z3c/jbot/utility.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/utility.py	2009-09-26 06:24:52 UTC (rev 104564)
+++ z3c.jbot/trunk/z3c/jbot/utility.py	2009-09-26 15:27:31 UTC (rev 104565)
@@ -24,7 +24,7 @@
                 return site.request
             except AttributeError:
                 return site.REQUEST
-            
+
     try:
         i = zope.security.management.getInteraction()
     except zope.security.interfaces.NoInteraction:
@@ -45,6 +45,6 @@
 def getManagers():
     layer = getLayer()
     gsm = component.getGlobalSiteManager()
-        
+
     for name, factory in gsm.adapters.lookupAll((layer,), interfaces.ITemplateManager):
         yield factory(layer)



More information about the checkins mailing list