[Checkins] SVN: five.pt/trunk/src/five/pt/ Avoid unnecessary recompilations and fix security for the TALES 'repeat' object

Leonardo Rochael Almeida leorochael at gmail.com
Thu Feb 24 06:27:29 EST 2011


Log message for revision 120560:
  Avoid unnecessary recompilations and fix security for the TALES 'repeat' object

Changed:
  U   five.pt/trunk/src/five/pt/pagetemplate.py
  U   five.pt/trunk/src/five/pt/patches.py
  U   five.pt/trunk/src/five/pt/tests/test_persistenttemplate.py

-=-
Modified: five.pt/trunk/src/five/pt/pagetemplate.py
===================================================================
--- five.pt/trunk/src/five/pt/pagetemplate.py	2011-02-24 08:28:59 UTC (rev 120559)
+++ five.pt/trunk/src/five/pt/pagetemplate.py	2011-02-24 11:27:29 UTC (rev 120560)
@@ -49,8 +49,8 @@
     return b
 
 
-class BaseTemplate(pagetemplate.BaseTemplate):
-    """Zope 2-compatible page template class."""
+class BaseTemplateBase(pagetemplate.BaseTemplate):
+    """Base for Zope 2-compatible page template classes."""
 
     utility_builtins = {}
     encoding = 'utf-8'
@@ -101,8 +101,15 @@
 
         return namespace
 
+class BaseTemplate(BaseTemplateBase):
+    """Zope 2-compatible page template class."""
 
-class BaseTemplateFile(BaseTemplate, pagetemplate.BaseTemplateFile):
+    def __init__(self, body, *args, **kw):
+        super(BaseTemplate, self).__init__(body, *args, **kw)
+        # keep the body for comparison and caching purposes
+        self.body = body
+
+class BaseTemplateFile(BaseTemplateBase, pagetemplate.BaseTemplateFile):
     """Zope 2-compatible page template file class."""
 
 

Modified: five.pt/trunk/src/five/pt/patches.py
===================================================================
--- five.pt/trunk/src/five/pt/patches.py	2011-02-24 08:28:59 UTC (rev 120559)
+++ five.pt/trunk/src/five/pt/patches.py	2011-02-24 11:27:29 UTC (rev 120560)
@@ -28,7 +28,18 @@
 from Acquisition import ImplicitAcquisitionWrapper
 
 from ComputedAttribute import ComputedAttribute
+from AccessControl.SecurityInfo import ClassSecurityInfo
+from App.class_init import InitializeClass
 
+# declare Chameleon's repeatdict public
+from chameleon.tal import RepeatDict
+
+RepeatDict.security = ClassSecurityInfo()
+RepeatDict.security.declareObjectPublic()
+RepeatDict.__allow_access_to_unprotected_subobjects__ = True
+
+InitializeClass(RepeatDict)
+
 try:
     from Products.Five.browser.pagetemplatefile import BoundPageTemplate
 except ImportError:
@@ -72,7 +83,7 @@
 
 def _get_five_pt_template(self):
     template = getattr(self, '_v_template', _marker)
-    if template is _marker or self._text != template.source:
+    if template is _marker or self._text != template.body:
         self._v_template = template = BaseTemplate(self._text, keep_source=True)
 
     return template
@@ -88,7 +99,7 @@
     return template
 
 def call_template(self, *args, **kw):
-    # avoid accidental exposure of the extra context
+    # avoid accidental exposure of the extra context parameter
     kw.pop(EXTRA_CONTEXT_KEY, None)
     template = self._get_five_pt_template()
     return template(self, *args, **kw)

Modified: five.pt/trunk/src/five/pt/tests/test_persistenttemplate.py
===================================================================
--- five.pt/trunk/src/five/pt/tests/test_persistenttemplate.py	2011-02-24 08:28:59 UTC (rev 120559)
+++ five.pt/trunk/src/five/pt/tests/test_persistenttemplate.py	2011-02-24 11:27:29 UTC (rev 120560)
@@ -37,6 +37,11 @@
 </tal:block>
 """.strip()
 
+repeat_object = """
+<tal:loop repeat="counter python: range(3)" 
+          content="python: repeat['counter'].index" />
+""".strip()
+
 options_capture_update_base = """
 <metal:use use-macro="context/macro_outer/macros/master">
   <metal:fills fill-slot="main_slot">
@@ -50,6 +55,8 @@
                        for name in names)
     return options_capture_update_base % (params,)
 
+_marker = object()
+
 class TestPersistent(ZopeTestCase):
     def afterSetUp(self):
         from Products.Five import zcml
@@ -97,3 +104,27 @@
         template.pt_render(extra_context=extra_context)
         del extra_context['capture']
         self.assertEquals(extra_context, capture)
+
+    def test_avoid_recompilation(self):
+        template = self._makeOne('foo', simple_i18n)
+        macro_outer = self._makeOne('macro_outer', simple_i18n)
+        # templates are only compiled after the first call
+        self.assertEqual(getattr(template, '_v_template', _marker), _marker)
+        template()
+        # or the first fetching of macros
+        self.assertEqual(getattr(macro_outer, '_v_template', _marker), _marker)
+        macro_outer.macros
+
+        template_compiled = template._v_template
+        macro_outer_compiled = macro_outer._v_template
+
+        # but they should not be recompiled afterwards
+        template()
+        macro_outer.macros
+        self.assertTrue(template_compiled is template._v_template)
+        self.assertTrue(macro_outer_compiled is macro_outer._v_template)
+
+    def test_repeat_object_security(self):
+        template = self._makeOne('foo', repeat_object)
+        # this should not raise an Unauthorized error
+        self.assertEquals(template().strip().split(), u'0 1 2'.split())



More information about the checkins mailing list