comparison genshi/template/eval.py @ 934:7c9ec79caedc

Merge r1142 from py3k: add support for python 3 to genshi.template expression evaluator: * add support for python 3 AST: * AST for raise has changed in Python 3. * Python 3 adds AST nodes for individual arguments and Bytes. * use genshi.compat functions for dealing with code objects. * do not coerce byte strings to unicode in Python 3 ASTTransformer. * replace doctests that reply on exception names with uglier but more compatible try:.. except:.. doctest * handle filename preferences of Python 2 and 3 (2 prefers bytes, 3 prefers unicode). * ifilter is gone from itertools in Python 3 so use repeat for tests instead.
author hodgestar
date Fri, 18 Mar 2011 09:15:29 +0000
parents 85e4678337cf
children
comparison
equal deleted inserted replaced
933:feba07fc925b 934:7c9ec79caedc
21 from genshi.core import Markup 21 from genshi.core import Markup
22 from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \ 22 from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \
23 _ast, parse 23 _ast, parse
24 from genshi.template.base import TemplateRuntimeError 24 from genshi.template.base import TemplateRuntimeError
25 from genshi.util import flatten 25 from genshi.util import flatten
26
27 from genshi.compat import get_code_params, build_code_chunk, IS_PYTHON2
26 28
27 __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup', 29 __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup',
28 'Undefined', 'UndefinedError'] 30 'Undefined', 'UndefinedError']
29 __docformat__ = 'restructuredtext en' 31 __docformat__ = 'restructuredtext en'
30 32
96 self._globals = lookup.globals 98 self._globals = lookup.globals
97 99
98 def __getstate__(self): 100 def __getstate__(self):
99 state = {'source': self.source, 'ast': self.ast, 101 state = {'source': self.source, 'ast': self.ast,
100 'lookup': self._globals.im_self} 102 'lookup': self._globals.im_self}
101 c = self.code 103 state['code'] = get_code_params(self.code)
102 state['code'] = (c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code,
103 c.co_consts, c.co_names, c.co_varnames, c.co_filename,
104 c.co_name, c.co_firstlineno, c.co_lnotab, (), ())
105 return state 104 return state
106 105
107 def __setstate__(self, state): 106 def __setstate__(self, state):
108 self.source = state['source'] 107 self.source = state['source']
109 self.ast = state['ast'] 108 self.ast = state['ast']
234 233
235 However, calling an undefined variable, or trying to access an attribute 234 However, calling an undefined variable, or trying to access an attribute
236 of that variable, will raise an exception that includes the name used to 235 of that variable, will raise an exception that includes the name used to
237 reference that undefined variable. 236 reference that undefined variable.
238 237
239 >>> foo('bar') 238 >>> try:
240 Traceback (most recent call last): 239 ... foo('bar')
241 ... 240 ... except UndefinedError, e:
242 UndefinedError: "foo" not defined 241 ... print e.msg
243 242 "foo" not defined
244 >>> foo.bar 243
245 Traceback (most recent call last): 244 >>> try:
246 ... 245 ... foo.bar
247 UndefinedError: "foo" not defined 246 ... except UndefinedError, e:
247 ... print e.msg
248 "foo" not defined
248 249
249 :see: `LenientLookup` 250 :see: `LenientLookup`
250 """ 251 """
251 __slots__ = ['_name', '_owner'] 252 __slots__ = ['_name', '_owner']
252 253
386 387
387 Referencing an undefined variable using this lookup style will immediately 388 Referencing an undefined variable using this lookup style will immediately
388 raise an ``UndefinedError``: 389 raise an ``UndefinedError``:
389 390
390 >>> expr = Expression('nothing', lookup='strict') 391 >>> expr = Expression('nothing', lookup='strict')
391 >>> expr.evaluate({}) 392 >>> try:
392 Traceback (most recent call last): 393 ... expr.evaluate({})
393 ... 394 ... except UndefinedError, e:
394 UndefinedError: "nothing" not defined 395 ... print e.msg
396 "nothing" not defined
395 397
396 The same happens when a non-existing attribute or item is accessed on an 398 The same happens when a non-existing attribute or item is accessed on an
397 existing object: 399 existing object:
398 400
399 >>> expr = Expression('something.nil', lookup='strict') 401 >>> expr = Expression('something.nil', lookup='strict')
400 >>> expr.evaluate({'something': dict()}) 402 >>> try:
401 Traceback (most recent call last): 403 ... expr.evaluate({'something': dict()})
402 ... 404 ... except UndefinedError, e:
403 UndefinedError: {} has no member named "nil" 405 ... print e.msg
406 {} has no member named "nil"
404 """ 407 """
405 408
406 @classmethod 409 @classmethod
407 def undefined(cls, key, owner=UNDEFINED): 410 def undefined(cls, key, owner=UNDEFINED):
408 """Raise an ``UndefinedError`` immediately.""" 411 """Raise an ``UndefinedError`` immediately."""
419 rest = dedent('\n'.join(lines[1:])).rstrip() 422 rest = dedent('\n'.join(lines[1:])).rstrip()
420 if first.rstrip().endswith(':') and not rest[0].isspace(): 423 if first.rstrip().endswith(':') and not rest[0].isspace():
421 rest = '\n'.join([' %s' % line for line in rest.splitlines()]) 424 rest = '\n'.join([' %s' % line for line in rest.splitlines()])
422 source = '\n'.join([first, rest]) 425 source = '\n'.join([first, rest])
423 if isinstance(source, unicode): 426 if isinstance(source, unicode):
424 source = '\xef\xbb\xbf' + source.encode('utf-8') 427 source = (u'\ufeff' + source).encode('utf-8')
425 return parse(source, mode) 428 return parse(source, mode)
426 429
427 430
428 def _compile(node, source=None, mode='eval', filename=None, lineno=-1, 431 def _compile(node, source=None, mode='eval', filename=None, lineno=-1,
429 xform=None): 432 xform=None):
430 if isinstance(filename, unicode): 433 if not filename:
431 # unicode file names not allowed for code objects
432 filename = filename.encode('utf-8', 'replace')
433 elif not filename:
434 filename = '<string>' 434 filename = '<string>'
435 if IS_PYTHON2:
436 # Python 2 requires non-unicode filenames
437 if isinstance(filename, unicode):
438 filename = filename.encode('utf-8', 'replace')
439 else:
440 # Python 3 requires unicode filenames
441 if not isinstance(filename, unicode):
442 filename = filename.decode('utf-8', 'replace')
435 if lineno <= 0: 443 if lineno <= 0:
436 lineno = 1 444 lineno = 1
437 445
438 if xform is None: 446 if xform is None:
439 xform = { 447 xform = {
456 code = compile(new_source, filename, mode) 464 code = compile(new_source, filename, mode)
457 465
458 try: 466 try:
459 # We'd like to just set co_firstlineno, but it's readonly. So we need 467 # We'd like to just set co_firstlineno, but it's readonly. So we need
460 # to clone the code object while adjusting the line number 468 # to clone the code object while adjusting the line number
461 return CodeType(0, code.co_nlocals, code.co_stacksize, 469 return build_code_chunk(code, filename, name, lineno)
462 code.co_flags | 0x0040, code.co_code, code.co_consts,
463 code.co_names, code.co_varnames, filename, name,
464 lineno, code.co_lnotab, (), ())
465 except RuntimeError: 470 except RuntimeError:
466 return code 471 return code
467 472
468 473
469 def _new(class_, *args, **kwargs): 474 def _new(class_, *args, **kwargs):
491 self.locals = [CONSTANTS] 496 self.locals = [CONSTANTS]
492 497
493 def _extract_names(self, node): 498 def _extract_names(self, node):
494 names = set() 499 names = set()
495 def _process(node): 500 def _process(node):
501 if not IS_PYTHON2 and isinstance(node, _ast.arg):
502 names.add(node.arg)
496 if isinstance(node, _ast.Name): 503 if isinstance(node, _ast.Name):
497 names.add(node.id) 504 names.add(node.id)
498 elif isinstance(node, _ast.alias): 505 elif isinstance(node, _ast.alias):
499 names.add(node.asname or node.name) 506 names.add(node.asname or node.name)
500 elif isinstance(node, _ast.Tuple): 507 elif isinstance(node, _ast.Tuple):
511 for elt in node.names: 518 for elt in node.names:
512 _process(elt) 519 _process(elt)
513 return names 520 return names
514 521
515 def visit_Str(self, node): 522 def visit_Str(self, node):
516 if isinstance(node.s, str): 523 if not isinstance(node.s, unicode):
517 try: # If the string is ASCII, return a `str` object 524 try: # If the string is ASCII, return a `str` object
518 node.s.decode('ascii') 525 node.s.decode('ascii')
519 except ValueError: # Otherwise return a `unicode` object 526 except ValueError: # Otherwise return a `unicode` object
520 return _new(_ast.Str, node.s.decode('utf-8')) 527 return _new(_ast.Str, node.s.decode('utf-8'))
521 return node 528 return node
Copyright (C) 2012-2017 Edgewall Software