Mercurial > genshi > genshi-test
diff 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 |
line wrap: on
line diff
--- a/genshi/template/eval.py +++ b/genshi/template/eval.py @@ -24,6 +24,8 @@ from genshi.template.base import TemplateRuntimeError from genshi.util import flatten +from genshi.compat import get_code_params, build_code_chunk, IS_PYTHON2 + __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup', 'Undefined', 'UndefinedError'] __docformat__ = 'restructuredtext en' @@ -98,10 +100,7 @@ def __getstate__(self): state = {'source': self.source, 'ast': self.ast, 'lookup': self._globals.im_self} - c = self.code - state['code'] = (c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code, - c.co_consts, c.co_names, c.co_varnames, c.co_filename, - c.co_name, c.co_firstlineno, c.co_lnotab, (), ()) + state['code'] = get_code_params(self.code) return state def __setstate__(self, state): @@ -236,15 +235,17 @@ of that variable, will raise an exception that includes the name used to reference that undefined variable. - >>> foo('bar') - Traceback (most recent call last): - ... - UndefinedError: "foo" not defined + >>> try: + ... foo('bar') + ... except UndefinedError, e: + ... print e.msg + "foo" not defined - >>> foo.bar - Traceback (most recent call last): - ... - UndefinedError: "foo" not defined + >>> try: + ... foo.bar + ... except UndefinedError, e: + ... print e.msg + "foo" not defined :see: `LenientLookup` """ @@ -388,19 +389,21 @@ raise an ``UndefinedError``: >>> expr = Expression('nothing', lookup='strict') - >>> expr.evaluate({}) - Traceback (most recent call last): - ... - UndefinedError: "nothing" not defined + >>> try: + ... expr.evaluate({}) + ... except UndefinedError, e: + ... print e.msg + "nothing" not defined The same happens when a non-existing attribute or item is accessed on an existing object: >>> expr = Expression('something.nil', lookup='strict') - >>> expr.evaluate({'something': dict()}) - Traceback (most recent call last): - ... - UndefinedError: {} has no member named "nil" + >>> try: + ... expr.evaluate({'something': dict()}) + ... except UndefinedError, e: + ... print e.msg + {} has no member named "nil" """ @classmethod @@ -421,17 +424,22 @@ rest = '\n'.join([' %s' % line for line in rest.splitlines()]) source = '\n'.join([first, rest]) if isinstance(source, unicode): - source = '\xef\xbb\xbf' + source.encode('utf-8') + source = (u'\ufeff' + source).encode('utf-8') return parse(source, mode) def _compile(node, source=None, mode='eval', filename=None, lineno=-1, xform=None): - if isinstance(filename, unicode): - # unicode file names not allowed for code objects - filename = filename.encode('utf-8', 'replace') - elif not filename: + if not filename: filename = '<string>' + if IS_PYTHON2: + # Python 2 requires non-unicode filenames + if isinstance(filename, unicode): + filename = filename.encode('utf-8', 'replace') + else: + # Python 3 requires unicode filenames + if not isinstance(filename, unicode): + filename = filename.decode('utf-8', 'replace') if lineno <= 0: lineno = 1 @@ -458,10 +466,7 @@ try: # We'd like to just set co_firstlineno, but it's readonly. So we need # to clone the code object while adjusting the line number - return CodeType(0, code.co_nlocals, code.co_stacksize, - code.co_flags | 0x0040, code.co_code, code.co_consts, - code.co_names, code.co_varnames, filename, name, - lineno, code.co_lnotab, (), ()) + return build_code_chunk(code, filename, name, lineno) except RuntimeError: return code @@ -493,6 +498,8 @@ def _extract_names(self, node): names = set() def _process(node): + if not IS_PYTHON2 and isinstance(node, _ast.arg): + names.add(node.arg) if isinstance(node, _ast.Name): names.add(node.id) elif isinstance(node, _ast.alias): @@ -513,7 +520,7 @@ return names def visit_Str(self, node): - if isinstance(node.s, str): + if not isinstance(node.s, unicode): try: # If the string is ASCII, return a `str` object node.s.decode('ascii') except ValueError: # Otherwise return a `unicode` object