[Checkins] SVN: z3c.jbot/trunk/ Reimplemented override logic.

Malthe Borch mborch at gmail.com
Thu Oct 15 08:42:24 EDT 2009


Log message for revision 105083:
  Reimplemented override logic.

Changed:
  U   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

-=-
Modified: z3c.jbot/trunk/CHANGES.txt
===================================================================
--- z3c.jbot/trunk/CHANGES.txt	2009-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/CHANGES.txt	2009-10-15 12:42:24 UTC (rev 105083)
@@ -1,6 +1,13 @@
 Changes
 =======
 
+In next release...
+
+- Reimplemented override logic; the total usage cost is now reduced to
+  an insignificant amount. Meanwhile, only templates that are defined
+  as class-attributes (e.g. on views, viewlets and portlets) can be
+  overriden.
+
 0.3 (2009-09-26)
 ----------------
 

Modified: z3c.jbot/trunk/README.txt
===================================================================
--- z3c.jbot/trunk/README.txt	2009-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/README.txt	2009-10-15 12:42:24 UTC (rev 105083)
@@ -2,46 +2,36 @@
 ========
 
 The z3c.jbot (or "Just a bunch of templates") package allows drop-in
-page template overrides.
+page template overrides. It works on Zope 2 and Zope 3.
 
-It works by giving page templates a canonical filename which you can
-use to provide a replacement in your own package. Simply register a
-template overrides directory and give your new template the canonical
-filename.
+Any template that is defined as a class-attribute can be overriden
+using jbot, e.g. those used in views, viewlets and portlets. The
+template overrides may be registered for any request layer or only a
+specific layer.
 
-Overrides may be registered for a specific layer or any layer.
+To override a particular template, first determine its *canonical
+filename*. It's defined as the path relative to the package within
+which the template is located; directory separators are replaced with
+dots.
 
-Canonical filename
-------------------
-
-The canonical filename is defined as the path relative to the package
-within which the template is located with directory separators
-replaced with dots.
-
 Example:
 
   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
-------------------------------------
+Simply drop the file in a directory and register that directory for
+use with jbot using a ZCML-directive::
 
-A Zope component configuration directive is available to configure
-overrides::
-
   <include package="z3c.jbot" file="meta.zcml" />
 
   <browser:templateOverrides
-      directory="<directory>"
+      directory="<path>"
       layer="<layer>" />
 
-Performance considerations
---------------------------
+Use of this package adds a small (2-3 ms per request on Plone) to the
+total application response time.
 
-The use of jbot adds to the general page load time. On a site with
-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-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/setup.py	2009-10-15 12:42:24 UTC (rev 105083)
@@ -1,7 +1,7 @@
 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.3'
+version = '0.3dev'
 
 setup(name='z3c.jbot',
       version=version,

Modified: z3c.jbot/trunk/z3c/jbot/README.txt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/README.txt	2009-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/z3c/jbot/README.txt	2009-10-15 12:42:24 UTC (rev 105083)
@@ -4,17 +4,17 @@
 The z3c.jbot (or "Just a bunch of templates") package allows drop-in
 page template overrides.
 
-Registration
-------------
+It works with templates which are defined as an attribute on a view.
 
-Let's instantiate a page template
-
   >>> from zope.pagetemplate.pagetemplatefile import PageTemplateFile
-  >>> template = PageTemplateFile("tests/templates/example.pt")
+  >>> class View(object):
+  ...     template = PageTemplateFile("tests/templates/example.pt")
+  >>> view = View()
 
-A call to the template will render it.
+To render the template, we instantiate the view and call the
+``template`` attribute.
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Providing a template override
@@ -38,7 +38,7 @@
 
 We should now see that the new filename will be used for rendering:
 
-  >>> template()
+  >>> view.template()
   u'Override from ./interface.\n'
 
 Before we proceed we'll clean up.
@@ -47,13 +47,13 @@
 
 The template does indeed render the original template.
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Upon rendering, the global template manager will have reverted the
 template filename to the original.
 
-  >>> template.filename
+  >>> view.template.filename
   '.../z3c.jbot/z3c/jbot/tests/templates/example.pt'
 
 Overrides can be registered for a specific layer. Let's register three
@@ -86,7 +86,7 @@
 
 We don't expect the template to be overriden for this interaction.
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Let's upgrade it.
@@ -94,30 +94,30 @@
   >>> request = participation
   >>> interface.alsoProvides(request, IRequest)
 
-  >>> template()
+  >>> view.template()
   u'Override from ./request.\n'
 
-  >>> template._v_cooked
+  >>> view.template._v_cooked
   1
 
 Going back to a basic request.
 
   >>> interface.noLongerProvides(request, IRequest)
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Let's verify that we only cook once per template source.
 
-  >>> output = template()
-  >>> template._v_last_read and template._v_cooked
+  >>> output = view.template()
+  >>> view.template._v_last_read and view.template._v_cooked
   1
 
   >>> interface.alsoProvides(request, IRequest)
-  >>> output = template()
-  >>> template._v_last_read and template._v_cooked
+  >>> output = view.template()
+  >>> view.template._v_last_read and view.template._v_cooked
   1
 
-  >>> template()
+  >>> view.template()
   u'Override from ./request.\n'
 
 Now, if we switch to the HTTP-layer.
@@ -125,18 +125,18 @@
   >>> interface.noLongerProvides(request, IRequest)
   >>> interface.alsoProvides(request, IHTTPRequest)
 
-  >>> template()
+  >>> view.template()
   u'Override from ./request.\n'
 
   >>> general.unregisterAllDirectories()
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
   >>> http = handler("%s/overrides/http" % directory, IHTTPRequest)
   >>> https = handler("%s/overrides/https" % directory, IHTTPSRequest)
 
-  >>> template()
+  >>> view.template()
   u'Override from ./http.\n'
 
 Switching to HTTPS.
@@ -144,7 +144,7 @@
   >>> interface.noLongerProvides(request, IHTTPRequest)
   >>> interface.alsoProvides(request, IHTTPSRequest)
 
-  >>> template()
+  >>> view.template()
   u'Override from ./https.\n'
 
   >>> interface.noLongerProvides(request, IHTTPSRequest)
@@ -153,13 +153,13 @@
 
   >>> for manager, layer in ((http, IHTTPRequest), (https, IHTTPSRequest)):
   ...     interface.alsoProvides(request, layer)
-  ...     _ = template()
+  ...     _ = view.template()
   ...     manager.unregisterAllDirectories()
   ...     interface.noLongerProvides(request, layer)
 
 The override is no longer in effect.
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Configuring template override directories in ZCML
@@ -182,14 +182,14 @@
 
 Once again, the override will be in effect.
 
-  >>> template()
+  >>> view.template()
   u'Override from ./interface.\n'
 
 Providing the HTTP-request layer does not change this.
 
   >>> interface.alsoProvides(request, IHTTPRequest)
 
-  >>> template()
+  >>> view.template()
   u'Override from ./interface.\n'
 
 Unregister overrides.
@@ -197,7 +197,7 @@
   >>> for manager in z3c.jbot.utility.getManagers():
   ...     manager.unregisterAllDirectories()
 
-  >>> template()
+  >>> view.template()
   u'This is an example page template.\n'
 
 Let's register overrides for the HTTP-request layer.
@@ -212,6 +212,6 @@
 
 Since we now provide the HTTP-request layer, the override is used.
 
-  >>> template()
+  >>> view.template()
   u'Override from ./http.\n'
 

Modified: z3c.jbot/trunk/z3c/jbot/__init__.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/__init__.py	2009-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/z3c/jbot/__init__.py	2009-10-15 12:42:24 UTC (rev 105083)
@@ -2,7 +2,6 @@
 
 import utility
 import logging
-import threading
 
 logger = logging.getLogger('jbot')
 
@@ -14,87 +13,26 @@
 except:
     pass
 
-_v_cache = threading.local()
+logger.info("Patching page template classes for use with z3c.jbot...")
 
-class LayerProperty(property):
-    """Layer-specific property class.
+registry = {}
 
-    Instance attributes are instance *and* layer-specific when defined
-    using this property class.
+for pt_class in PT_CLASSES:
+    def get(template, view=None, cls=None):
+        layer = utility.getLayer()
+        key = layer, template
+        inst = registry.get(key)
+        if inst is None:
+            cls = type(template)
+            inst = registry[key] = cls.__new__(cls)
+            inst.__dict__ = template.__dict__.copy()
 
-    Lookup order:
-
-      1. By layer
-      2. By instance
-      3. By class
-
-    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
-        __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]
-        except KeyError:
-            return __dict__.get(key) or self.default
-
-    def _set(self, template, value):
-        key = self.name
-        __dict__ = template.__dict__
-
-        # 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
-        __dict__.setdefault(key, value)
-
-# registration hook to template manager
-def jbot(func):
-    def patch(self, *args, **kwargs):
-        _v_cache.layer = utility.getLayer()
-
         for manager in utility.getManagers():
             # register template; this call returns ``True`` if
             # template was invalidated
-            if manager.registerTemplate(self):
+            if manager.registerTemplate(inst, template):
                 break
 
-        return func(self, *args, **kwargs)
-    return patch
+        return inst
 
-logger.info("Patching page template classes for use with z3c.jbot...")
-
-# patch ``_cook_check``-method to insert jbot-logic
-for pt_class in PT_CLASSES:
-    pt_class._cook_check = jbot(pt_class._cook_check)
-
-# munge per-layer attribute descriptors on class
-for pt_class in PT_CLASSES:
-    for name in ('_v_macros', '_v_program', '_v_cooked', '_v_errors',
-                 '_v_last_read', '_v_warning', '_text_',
-                 'filename', '_filename', 'content_type', 'is_html'):
-        setattr(pt_class, name, LayerProperty(pt_class, name))
+    pt_class.__get__ = get

Modified: z3c.jbot/trunk/z3c/jbot/manager.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/manager.py	2009-10-15 12:33:30 UTC (rev 105082)
+++ z3c.jbot/trunk/z3c/jbot/manager.py	2009-10-15 12:42:24 UTC (rev 105083)
@@ -97,22 +97,19 @@
                 del self.paths[filename]
 
         for template in templates:
-            self.registerTemplate(template)
+            inst = template.__get__(self)
+            self.registerTemplate(inst, template)
             del self.templates[template]
-            template.filename = template._filename
-            template._v_last_read = False
+            inst.filename = inst._filename
+            inst._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
-
+    def registerTemplate(self, template, token):
         # assert that the template is not already registered
-        filename = self.templates.get(template)
+        filename = self.templates.get(token)
         if filename is IGNORE:
             return
 
@@ -124,13 +121,13 @@
         # verify that override has not been unregistered
         if filename is not None and filename not in paths:
             template.filename = template._filename
-            del self.templates[template]
+            del self.templates[token]
 
         # check if an override exists
         path = find_package(self.syspaths, template.filename)
         if path is None:
             # permanently ignore template
-            self.templates[template] = IGNORE
+            self.templates[token] = IGNORE
             return
 
         filename = path.replace(os.path.sep, '.')
@@ -142,9 +139,9 @@
 
             # save template and registry and assign path
             template.filename = path
-            self.templates[template] = filename
+            self.templates[token] = filename
         else:
-            self.templates[template] = IGNORE
+            self.templates[token] = IGNORE
 
         # force cook
         template._v_last_read = False



More information about the checkins mailing list