Premiere version : mise en route du suivi.
[auf_roundup.git] / roundup / cgi / PageTemplates / .svn / text-base / TALES.py.svn-base
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 # Modified for Roundup:
14
15 # 1. changed imports to import from roundup.cgi
16 # 2. implemented ustr as str (removes import from DocumentTemplate)
17 # 3. removed import and use of Unauthorized from zExceptions
18 """TALES
19
20 An implementation of a generic TALES engine
21 """
22
23 __version__='$Revision: 1.9 $'[11:-2]
24
25 import re, sys
26 from roundup.cgi import ZTUtils
27 from weakref import ref
28 from MultiMapping import MultiMapping
29 from GlobalTranslationService import getGlobalTranslationService
30
31 ustr = str
32
33 StringType = type('')
34
35 NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
36 _parse_expr = re.compile(r"(%s):" % NAME_RE).match
37 _valid_name = re.compile('%s$' % NAME_RE).match
38
39 class TALESError(Exception):
40     """Error during TALES expression evaluation"""
41
42 class Undefined(TALESError):
43     '''Exception raised on traversal of an undefined path'''
44
45 class RegistrationError(Exception):
46     '''TALES Type Registration Error'''
47
48 class CompilerError(Exception):
49     '''TALES Compiler Error'''
50
51 class Default:
52     '''Retain Default'''
53 Default = Default()
54
55 class SafeMapping(MultiMapping):
56     '''Mapping with security declarations and limited method exposure.
57
58     Since it subclasses MultiMapping, this class can be used to wrap
59     one or more mapping objects.  Restricted Python code will not be
60     able to mutate the SafeMapping or the wrapped mappings, but will be
61     able to read any value.
62     '''
63     __allow_access_to_unprotected_subobjects__ = 1
64     push = pop = None
65
66     _push = MultiMapping.push
67     _pop = MultiMapping.pop
68
69
70 class Iterator(ZTUtils.Iterator):
71     def __init__(self, name, seq, context):
72         ZTUtils.Iterator.__init__(self, seq)
73         self.name = name
74         self._context_ref = ref(context)
75
76     def next(self):
77         if ZTUtils.Iterator.next(self):
78             context = self._context_ref()
79             if context is not None:
80                 context.setLocal(self.name, self.item)
81             return 1
82         return 0
83
84
85 class ErrorInfo:
86     """Information about an exception passed to an on-error handler."""
87     __allow_access_to_unprotected_subobjects__ = 1
88
89     def __init__(self, err, position=(None, None)):
90         if isinstance(err, Exception):
91             self.type = err.__class__
92             self.value = err
93         else:
94             self.type = err
95             self.value = None
96         self.lineno = position[0]
97         self.offset = position[1]
98
99
100 class Engine:
101     '''Expression Engine
102
103     An instance of this class keeps a mutable collection of expression
104     type handlers.  It can compile expression strings by delegating to
105     these handlers.  It can provide an expression Context, which is
106     capable of holding state and evaluating compiled expressions.
107     '''
108     Iterator = Iterator
109
110     def __init__(self, Iterator=None):
111         self.types = {}
112         if Iterator is not None:
113             self.Iterator = Iterator
114
115     def registerType(self, name, handler):
116         if not _valid_name(name):
117             raise RegistrationError, 'Invalid Expression type "%s".' % name
118         types = self.types
119         if types.has_key(name):
120             raise RegistrationError, (
121                 'Multiple registrations for Expression type "%s".' %
122                 name)
123         types[name] = handler
124
125     def getTypes(self):
126         return self.types
127
128     def compile(self, expression):
129         m = _parse_expr(expression)
130         if m:
131             type = m.group(1)
132             expr = expression[m.end():]
133         else:
134             type = "standard"
135             expr = expression
136         try:
137             handler = self.types[type]
138         except KeyError:
139             raise CompilerError, (
140                 'Unrecognized expression type "%s".' % type)
141         return handler(type, expr, self)
142
143     def getContext(self, contexts=None, **kwcontexts):
144         if contexts is not None:
145             if kwcontexts:
146                 kwcontexts.update(contexts)
147             else:
148                 kwcontexts = contexts
149         return Context(self, kwcontexts)
150
151     def getCompilerError(self):
152         return CompilerError
153
154 class Context:
155     '''Expression Context
156
157     An instance of this class holds context information that it can
158     use to evaluate compiled expressions.
159     '''
160
161     _context_class = SafeMapping
162     position = (None, None)
163     source_file = None
164
165     def __init__(self, compiler, contexts):
166         self._compiler = compiler
167         self.contexts = contexts
168         contexts['nothing'] = None
169         contexts['default'] = Default
170
171         self.repeat_vars = rv = {}
172         # Wrap this, as it is visible to restricted code
173         contexts['repeat'] = rep =  self._context_class(rv)
174         contexts['loop'] = rep # alias
175
176         self.global_vars = gv = contexts.copy()
177         self.local_vars = lv = {}
178         self.vars = self._context_class(gv, lv)
179
180         # Keep track of what needs to be popped as each scope ends.
181         self._scope_stack = []
182
183     def getCompiler(self):
184         return self._compiler
185
186     def beginScope(self):
187         self._scope_stack.append([self.local_vars.copy()])
188
189     def endScope(self):
190         scope = self._scope_stack.pop()
191         self.local_vars = lv = scope[0]
192         v = self.vars
193         v._pop()
194         v._push(lv)
195         # Pop repeat variables, if any
196         i = len(scope) - 1
197         while i:
198             name, value = scope[i]
199             if value is None:
200                 del self.repeat_vars[name]
201             else:
202                 self.repeat_vars[name] = value
203             i = i - 1
204
205     def setLocal(self, name, value):
206         self.local_vars[name] = value
207
208     def setGlobal(self, name, value):
209         self.global_vars[name] = value
210
211     def setRepeat(self, name, expr):
212         expr = self.evaluate(expr)
213         if not expr:
214             return self._compiler.Iterator(name, (), self)
215         it = self._compiler.Iterator(name, expr, self)
216         old_value = self.repeat_vars.get(name)
217         self._scope_stack[-1].append((name, old_value))
218         self.repeat_vars[name] = it
219         return it
220
221     def evaluate(self, expression,
222                  isinstance=isinstance, StringType=StringType):
223         if isinstance(expression, StringType):
224             expression = self._compiler.compile(expression)
225         __traceback_supplement__ = (
226             TALESTracebackSupplement, self, expression)
227         return expression(self)
228
229     evaluateValue = evaluate
230     evaluateBoolean = evaluate
231
232     def evaluateText(self, expr):
233         text = self.evaluate(expr)
234         if text is Default or text is None:
235             return text
236         if isinstance(text, unicode):
237             return text
238         else:
239             return ustr(text)
240
241     def evaluateStructure(self, expr):
242         return self.evaluate(expr)
243     evaluateStructure = evaluate
244
245     def evaluateMacro(self, expr):
246         # XXX Should return None or a macro definition
247         return self.evaluate(expr)
248     evaluateMacro = evaluate
249
250     def createErrorInfo(self, err, position):
251         return ErrorInfo(err, position)
252
253     def getDefault(self):
254         return Default
255
256     def setSourceFile(self, source_file):
257         self.source_file = source_file
258
259     def setPosition(self, position):
260         self.position = position
261
262     def translate(self, domain, msgid, mapping=None,
263                   context=None, target_language=None, default=None):
264         if context is None:
265             context = self.contexts.get('here')
266         return getGlobalTranslationService().translate(
267             domain, msgid, mapping=mapping,
268             context=context,
269             default=default,
270             target_language=target_language)
271
272 class TALESTracebackSupplement:
273     """Implementation of ITracebackSupplement"""
274     def __init__(self, context, expression):
275         self.context = context
276         self.source_url = context.source_file
277         self.line = context.position[0]
278         self.column = context.position[1]
279         self.expression = repr(expression)
280
281     def getInfo(self, as_html=0):
282         import pprint
283         data = self.context.contexts.copy()
284         s = pprint.pformat(data)
285         if not as_html:
286             return '   - Names:\n      %s' % s.replace('\n', '\n      ')
287         else:
288             from cgi import escape
289             return '<b>Names:</b><pre>%s</pre>' % (escape(s))
290
291
292 class SimpleExpr:
293     '''Simple example of an expression type handler'''
294     def __init__(self, name, expr, engine):
295         self._name = name
296         self._expr = expr
297     def __call__(self, econtext):
298         return self._name, self._expr
299     def __repr__(self):
300         return '<SimpleExpr %s %s>' % (self._name, `self._expr`)