Opened 15 years ago
Closed 14 years ago
#388 closed enhancement (invalid)
Allow custom loaders to also plug in custom AstTransformers
Reported by: | carsten.klein@… | Owned by: | cmlenz |
---|---|---|---|
Priority: | trivial | Milestone: | 0.7 |
Component: | General | Version: | 0.6 |
Keywords: | Cc: |
Description
The current API does not allow templates to override the default AstTransformer? defined in astutil#_compile.
However, classes derived from Code do allow overriding the default transformer.
It would be nice to have a custom loader also incorporate a custom AstTransformer?.
The main concern here is security, effectfully limiting python API access by a custom AstTransformer?.
As I see it, the markup template's init method would require an additional named parameter that by default would be set to none, e.g. xform=None.
That way, upon instantiation of either Suite or Expression, the specified AstTransformer? could be passed as an alternative the the default transformer.
This would also be backwards compatible, so existing applications would not break.
Change History (3)
comment:1 follow-up: ↓ 2 Changed 15 years ago by carsten.klein@…
comment:2 in reply to: ↑ 1 Changed 15 years ago by Carsten Klein <carsten.klein@…>
Replying to carsten.klein@…:
Here is a revised version of the patch that allows passing in ast transformers for use with both expression transformations and embedded python code transformations. For this, it add the xform parameter to the interpolation routine, so that user defined AST transformers can be used with this as well. It also provides a revised documentation on the additional xform parameter in the template base class:
-
genshi/template/base.py
20 20 21 21 from genshi.core import Attrs, Stream, StreamEventKind, START, TEXT, _ensure 22 22 from genshi.input import ParseError 23 from genshi.template.astutil import ASTTransformer 23 24 24 25 __all__ = ['Context', 'DirectiveFactory', 'Template', 'TemplateError', 25 26 'TemplateRuntimeError', 'TemplateSyntaxError', 'BadDirectiveError'] … … 370 371 _number_conv = unicode # function used to convert numbers to event data 371 372 372 373 def __init__(self, source, filepath=None, filename=None, loader=None, 373 encoding=None, lookup='strict', allow_exec=True ):374 encoding=None, lookup='strict', allow_exec=True, xform=None): 374 375 """Initialize a template from either a string, a file-like object, or 375 376 an already parsed markup stream. 376 377 … … 386 387 default), "lenient", or a custom lookup class 387 388 :param allow_exec: whether Python code blocks in templates should be 388 389 allowed 390 :param xform: a dict in the form of 389 391 392 { 'eval' : <class>, 'exec' : <class> } 393 394 where <class> is the respective AST transformer class that 395 should be applied to the code representing either a python 396 expression or executable python code; if `None`, or either 397 of the two is omitted, then the default transformation is 398 chosen depending on the current execution mode, which is 399 either 'eval' or 'exec'. 400 390 401 :note: Changed in 0.5: Added the `allow_exec` argument 402 :note: Changed in 0.7: Added the `xform` argument 391 403 """ 392 404 self.filepath = filepath or filename 393 405 self.filename = filename 394 406 self.loader = loader 395 407 self.lookup = lookup 396 408 self.allow_exec = allow_exec 409 410 if xform is not None : 411 if 'eval' in xform and not ASTTransformer in xform['eval'].__mro__: 412 raise TemplateRuntimeError("xform['eval'] must inherit from " 413 "ASTTransformer.") 414 if 'exec' in xform and not ASTTransformer in xform['exec'].__mro__: 415 raise TemplateRuntimeError("xform['exec'] must inherit from " 416 "ASTTransformer.") 417 418 self.xform = xform 397 419 self._init_filters() 398 420 self._prepared = False 399 421 -
genshi/template/markup.py
61 61 _number_conv = Markup 62 62 63 63 def __init__(self, source, filepath=None, filename=None, loader=None, 64 encoding=None, lookup='strict', allow_exec=True ):64 encoding=None, lookup='strict', allow_exec=True, xform=None): 65 65 Template.__init__(self, source, filepath=filepath, filename=filename, 66 66 loader=loader, encoding=encoding, lookup=lookup, 67 allow_exec=allow_exec )67 allow_exec=allow_exec, xform=xform) 68 68 self.add_directives(self.DIRECTIVE_NAMESPACE, self) 69 69 70 70 def _init_filters(self): … … 86 86 87 87 if kind is TEXT: 88 88 for kind, data, pos in interpolate(data, self.filepath, pos[1], 89 pos[2], lookup=self.lookup): 89 pos[2], lookup=self.lookup, 90 xform=self.xform): 90 91 stream.append((kind, data, pos)) 91 92 92 93 elif kind is PI and data[0] == 'python': … … 95 96 self.filepath, *pos[1:]) 96 97 try: 97 98 suite = Suite(data[1], self.filepath, pos[1], 98 lookup=self.lookup )99 lookup=self.lookup, xform=self.xform) 99 100 except SyntaxError, err: 100 101 raise TemplateSyntaxError(err, self.filepath, 101 102 pos[1] + (err.lineno or 1) - 1, … … 278 279 for name, value in attrs: 279 280 if value: 280 281 value = list(interpolate(value, self.filepath, pos[1], 281 pos[2], lookup=self.lookup)) 282 pos[2], lookup=self.lookup, 283 xform=self.xform)) 282 284 if len(value) == 1 and value[0][0] is TEXT: 283 285 value = value[0][1] 284 286 new_attrs.append((name, value)) -
genshi/template/text.py
179 179 if start > offset: 180 180 text = _escape_sub(_escape_repl, source[offset:start]) 181 181 for kind, data, pos in interpolate(text, self.filepath, lineno, 182 lookup=self.lookup): 182 lookup=self.lookup, 183 xform=self.xform): 183 184 stream.append((kind, data, pos)) 184 185 lineno += len(text.splitlines()) 185 186 … … 189 190 if command == 'include': 190 191 pos = (self.filename, lineno, 0) 191 192 value = list(interpolate(value, self.filepath, lineno, 0, 192 lookup=self.lookup ))193 lookup=self.lookup, xform=self.xform)) 193 194 if len(value) == 1 and value[0][0] is TEXT: 194 195 value = value[0][1] 195 196 stream.append((INCLUDE, (value, None, []), pos)) … … 200 201 self.filepath, lineno) 201 202 try: 202 203 suite = Suite(value, self.filepath, lineno, 203 lookup=self.lookup )204 lookup=self.lookup, xform=self.xform) 204 205 except SyntaxError, err: 205 206 raise TemplateSyntaxError(err, self.filepath, 206 207 lineno + (err.lineno or 1) - 1) … … 228 229 if offset < len(source): 229 230 text = _escape_sub(_escape_repl, source[offset:]) 230 231 for kind, data, pos in interpolate(text, self.filepath, lineno, 231 lookup=self.lookup): 232 lookup=self.lookup, 233 xform=self.xform): 232 234 stream.append((kind, data, pos)) 233 235 234 236 return stream … … 289 291 if start > offset: 290 292 text = source[offset:start] 291 293 for kind, data, pos in interpolate(text, self.filepath, lineno, 292 lookup=self.lookup): 294 lookup=self.lookup, 295 xform=self.xform): 293 296 stream.append((kind, data, pos)) 294 297 lineno += len(text.splitlines()) 295 298 … … 324 327 if offset < len(source): 325 328 text = source[offset:].replace('\\#', '#') 326 329 for kind, data, pos in interpolate(text, self.filepath, lineno, 327 lookup=self.lookup): 330 lookup=self.lookup, 331 xform=self.xform): 328 332 stream.append((kind, data, pos)) 329 333 330 334 return stream -
genshi/template/interpolation.py
37 37 )) 38 38 39 39 40 def interpolate(text, filepath=None, lineno=-1, offset=0, lookup='strict'): 40 def interpolate(text, filepath=None, lineno=-1, offset=0, lookup='strict', 41 xform=None): 41 42 """Parse the given string and extract expressions. 42 43 43 44 This function is a generator that yields `TEXT` events for literal strings, … … 58 59 (optional) 59 60 :param lookup: the variable lookup mechanism; either "lenient" (the 60 61 default), "strict", or a custom lookup class 62 :param xform: a dict containing class references to AST transformers being 63 applied on evaluation of expressions. 61 64 :return: a list of `TEXT` and `EXPR` events 62 65 :raise TemplateSyntaxError: when a syntax error in an expression is 63 66 encountered … … 75 78 if chunk: 76 79 try: 77 80 expr = Expression(chunk.strip(), pos[0], pos[1], 78 lookup=lookup )81 lookup=lookup, xform=xform) 79 82 yield EXPR, expr, tuple(pos) 80 83 except SyntaxError, err: 81 84 raise TemplateSyntaxError(err, filepath, pos[1],
comment:3 Changed 14 years ago by Carsten Klein <carsten.klein@…>
- Resolution set to invalid
- Status changed from new to closed
I am closing this an reopening it. Inlining the patches was not a good idea.
Here is an untested patch, but it seems quite straight forward and should do the trick:
genshi/template/base.py
):genshi/template/markup.py
):))genshi/template/text.py
)