[Zope-Checkins] CVS: Zope3/lib/python/Zope/PageTemplate - PageTemplate.py:1.1.2.12

Shane Hathaway shane@cvs.zope.org
Wed, 13 Mar 2002 22:55:36 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/PageTemplate
In directory cvs.zope.org:/tmp/cvs-serv11107

Modified Files:
      Tag: Zope-3x-branch
	PageTemplate.py 
Log Message:
- Made use of new EngineConfig.  You can use a specific expression engine
  by setting the _engine_name attribute.

- Improved reliability of delayed cooking.

- Removed dependency on ModuleImporter; the expression engine now provides
  a dictionary of base names and it should include the "module" name.

- Added __traceback_supplement__, which doesn't do anything yet, but will
  soon provide the detailed exception information that TALESErrors tried to
  provide.

- Removed a shadow of a dependency on implicit acquisition.


=== Zope3/lib/python/Zope/PageTemplate/PageTemplate.py 1.1.2.11 => 1.1.2.12 ===
 __version__ = '$Revision$'[11:-2]
 
-import os, sys, traceback, pprint
+import sys
 from Zope.TAL.TALParser import TALParser
 from Zope.TAL.HTMLTALParser import HTMLTALParser
 from Zope.TAL.TALGenerator import TALGenerator
 from Zope.TAL.TALInterpreter import TALInterpreter
-from Expressions import getEngine
+from EngineConfig import getEngine
 from cStringIO import StringIO
 from types import StringType
 
 
-Z_DEBUG_MODE = os.environ.get('Z_DEBUG_MODE') == '1'
-
 class MacroCollection(object):
     def __get__(self, parent, type=None):
         parent._cook_check()
         return parent._v_macros
 
+
 class PageTemplate(object):
     """Page Templates using TAL, TALES, and METAL.
 
@@ -60,7 +59,11 @@
     expand = 1
     _v_errors = ()
     _v_warnings = ()
+    _v_program = None
+    _v_macros = None
+    _v_cooked = 0
     _text = ''
+    _engine_name = 'default'
     _error_start = '<!-- Page Template Diagnostics'
 
     macros = MacroCollection()
@@ -73,35 +76,32 @@
         self.write(text)
 
     def pt_getContext(self, args=(), options={}, **ignored):
-        return {'template': self,
+        rval = {'template': self,
                 'options': options,
                 'args': args,
                 'nothing': None,
-                'modules': ModuleImporter,
                 }
+        rval.update(getEngine(self._engine_name).getBaseNames())
+        return rval
 
     def pt_render(self, namespace, source=0):
         """Render this Page Template"""
+        self._cook_check()
+        __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
         if self._v_errors:
-            raise PTRuntimeError(
-                '''Page Template %s has errors.
-                %s
-                ''' % ('', #XXX self.id
-                       self._v_errors))
+            raise PTRuntimeError(str(self._v_errors))
         output = StringIO()
-        if Z_DEBUG_MODE:
-            __traceback_info__ = pprint.pformat(namespace)
 
+        context = getEngine(self._engine_name).getContext(namespace)
         TALInterpreter(self._v_program, self._v_macros,
-                       getEngine().getContext(namespace),
-                       output,
-                       tal=not source, strictinsert=0)()
+                       context, output, tal=not source, strictinsert=0)()
         return output.getvalue()
 
     def __call__(self, *args, **kwargs):
         return self.pt_render(self.pt_getContext(*args, **kwargs))
 
     def pt_errors(self):
+        self._cook_check()
         err = self._v_errors
         if err:
             return err
@@ -109,8 +109,9 @@
             self.pt_render(source=1)
         except:
             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
-        
+
     def pt_warnings(self):
+        self._cook_check()
         return self._v_warnings
 
     def write(self, text):
@@ -124,6 +125,8 @@
         self._cook()
 
     def read(self):
+        """Gets the source, sometimes with macros expanded."""
+        self._cook_check()
         if not self._v_errors:
             if not self.expand:
                 return self._text
@@ -133,22 +136,31 @@
                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
                          self._text) )
-                                  
+
         return ('%s\n %s\n-->\n%s' % (self._error_start,
                                       '\n'.join(self._v_errors),
                                       self._text))
 
+    def pt_source_file(self):
+        """To be overridden."""
+        return None
+
+    def _cook_check(self):
+        if not self._v_cooked:
+            self._cook()
+
     def _cook(self):
         """Compile the TAL and METAL statments.
 
-        A Page Template must always be cooked, and cooking must not
-        fail due to user input.
+        Cooking must not fail due to compilation errors in templates.
         """
+        engine = getEngine(self._engine_name)
+        source_file = self.pt_source_file()
         if self.html():
-            gen = TALGenerator(getEngine(), xml=0)
+            gen = TALGenerator(engine, xml=0, source_file=source_file)
             parser = HTMLTALParser(gen)
         else:
-            gen = TALGenerator(getEngine())
+            gen = TALGenerator(engine, source_file=source_file)
             parser = TALParser(gen)
 
         self._v_errors = ()
@@ -160,24 +172,39 @@
             self._v_errors = ["Compilation failed",
                               "%s: %s" % sys.exc_info()[:2]]
         self._v_warnings = parser.getWarnings()
+        self._v_cooked = 1
 
     def html(self):
-        if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
+        if not hasattr(self, 'is_html'):
             return self.content_type == 'text/html'
         return self.is_html
 
 
-class _ModuleImporter:
-    def __getitem__(self, module):
-        mod = __import__(module)
-        path = module.split('.')
-        for name in path[1:]:
-            mod = getattr(mod, name)
-        return mod
-
-
-ModuleImporter = _ModuleImporter()
 
 class PTRuntimeError(RuntimeError):
     '''The Page Template has template errors that prevent it from rendering.'''
     pass
+
+
+class PageTemplateTracebackSupplement:
+    #__implements__ = ITracebackSupplement
+
+    def __init__(self, pt):
+        self.manageable_object = pt
+        try:
+            errors = pt.pt_errors()
+        except: # We're already trying to report an error, don't make another.
+            errors = None
+        self.errors = errors
+
+    def getInfo(self, as_html=0):
+        errors = self.errors
+        if not errors:
+            return None
+        if not as_html:
+            return '   - Errors:\n      %s' % '\n      '.join(errors)
+        else:
+            from cgi import escape
+            s = '<br />'.join(map(escape, errors))
+            return '<b>Errors:</b><br />%s' % s
+