Code

missed one
[roundup.git] / PageTemplates / PageTemplate.py
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE
11
12 ##############################################################################
13 """Page Template module
15 HTML- and XML-based template objects using TAL, TALES, and METAL.
16 """
18 __version__='$Revision: 1.1 $'[11:-2]
20 import sys
22 from TAL.TALParser import TALParser
23 from TAL.HTMLTALParser import HTMLTALParser
24 from TAL.TALGenerator import TALGenerator
25 from TAL.TALInterpreter import TALInterpreter
26 from Expressions import getEngine
27 from string import join, strip, rstrip, split, replace, lower, find
28 from cStringIO import StringIO
29 from ExtensionClass import Base
30 from ComputedAttribute import ComputedAttribute
33 class PageTemplate(Base):
34     "Page Templates using TAL, TALES, and METAL"
35      
36     content_type = 'text/html'
37     expand = 0
38     _v_errors = ()
39     _v_warnings = ()
40     _v_program = None
41     _v_macros = None
42     _v_cooked = 0
43     id = '(unknown)'
44     _text = ''
45     _error_start = '<!-- Page Template Diagnostics'
47     def macros(self):
48         return self.pt_macros()
49     macros = ComputedAttribute(macros, 1)
51     def pt_edit(self, text, content_type):
52         if content_type:
53             self.content_type = str(content_type)
54         if hasattr(text, 'read'):
55             text = text.read()
56         self.write(text)
58     def pt_getContext(self):
59         c = {'template': self,
60              'options': {},
61              'nothing': None,
62              'request': None,
63              'modules': ModuleImporter,
64              }
65         parent = getattr(self, 'aq_parent', None)
66         if parent is not None:
67             c['here'] = parent
68             c['container'] = self.aq_inner.aq_parent
69             while parent is not None:
70                 self = parent
71                 parent = getattr(self, 'aq_parent', None)
72             c['root'] = self
73         return c
74     
75     def pt_render(self, source=0, extra_context={}):
76         """Render this Page Template"""
77         if not self._v_cooked:
78             self._cook()
80         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
82         if self._v_errors:
83             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
84         output = StringIO()
85         c = self.pt_getContext()
86         c.update(extra_context)
88         TALInterpreter(self._v_program, self._v_macros,
89                        getEngine().getContext(c),
90                        output,
91                        tal=not source, strictinsert=0)()
92         return output.getvalue()
94     def __call__(self, *args, **kwargs):
95         if not kwargs.has_key('args'):
96             kwargs['args'] = args
97         return self.pt_render(extra_context={'options': kwargs})
99     def pt_errors(self):
100         if not self._v_cooked:
101             self._cook()
102         err = self._v_errors
103         if err:
104             return err
105         if not self.expand: return
106         try:
107             self.pt_render(source=1)
108         except:
109             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
110         
111     def pt_warnings(self):
112         if not self._v_cooked:
113             self._cook()
114         return self._v_warnings
116     def pt_macros(self):
117         if not self._v_cooked:
118             self._cook()
119         if self._v_errors:
120             __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
121             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
122         return self._v_macros
124     def pt_source_file(self):
125         return None  # Unknown.
127     def write(self, text):
128         assert type(text) is type('')
129         if text[:len(self._error_start)] == self._error_start:
130             errend = find(text, '-->')
131             if errend >= 0:
132                 text = text[errend + 4:]
133         if self._text != text:
134             self._text = text
135         self._cook()
137     def read(self):
138         if not self._v_cooked:
139             self._cook()
140         if not self._v_errors:
141             if not self.expand:
142                 return self._text
143             try:
144                 return self.pt_render(source=1)
145             except:
146                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
147                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
148                          self._text) )
149                                   
150         return ('%s\n %s\n-->\n%s' % (self._error_start,
151                                       join(self._v_errors, '\n '),
152                                       self._text))
154     def _cook(self):
155         """Compile the TAL and METAL statments.
157         Cooking must not fail due to compilation errors in templates.
158         """
159         source_file = self.pt_source_file()
160         if self.html():
161             gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
162             parser = HTMLTALParser(gen)
163         else:
164             gen = TALGenerator(getEngine(), source_file=source_file)
165             parser = TALParser(gen)
167         self._v_errors = ()
168         try:
169             parser.parseString(self._text)
170             self._v_program, self._v_macros = parser.getCode()
171         except:
172             self._v_errors = ["Compilation failed",
173                               "%s: %s" % sys.exc_info()[:2]]
174         self._v_warnings = parser.getWarnings()
175         self._v_cooked = 1
177     def html(self):
178         if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
179             return self.content_type == 'text/html'
180         return self.is_html
182 class _ModuleImporter:
183     def __getitem__(self, module):
184         mod = __import__(module)
185         path = split(module, '.')
186         for name in path[1:]:
187             mod = getattr(mod, name)
188         return mod
190 ModuleImporter = _ModuleImporter()
192 class PTRuntimeError(RuntimeError):
193     '''The Page Template has template errors that prevent it from rendering.'''
194     pass
197 class PageTemplateTracebackSupplement:
198     #__implements__ = ITracebackSupplement
200     def __init__(self, pt):
201         self.object = pt
202         w = pt.pt_warnings()
203         e = pt.pt_errors()
204         if e:
205             w = list(w) + list(e)
206         self.warnings = w