annotate genshi/template/eval.py @ 903:95d62e239f60 experimental-inline

inline branch: support for a couple more directives
author cmlenz
date Mon, 26 Apr 2010 17:09:08 +0000
parents 09cc3627654c
children
rev   line source
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
2 #
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
3 # Copyright (C) 2006-2010 Edgewall Software
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
5 #
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
9 #
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
13
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
14 """Support for "safe" evaluation of Python expressions."""
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
15
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
16 import __builtin__
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
17
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
18 from textwrap import dedent
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
19 from types import CodeType
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
20
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
21 from genshi.core import Markup
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
22 from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
23 _ast, parse
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
24 from genshi.template.base import TemplateRuntimeError
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
25 from genshi.util import flatten
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
26
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
27 __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup',
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
28 'Undefined', 'UndefinedError']
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
29 __docformat__ = 'restructuredtext en'
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
30
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
31
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
32 # Check for a Python 2.4 bug in the eval loop
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
33 has_star_import_bug = False
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
34 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
35 class _FakeMapping(object):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
36 __getitem__ = __setitem__ = lambda *a: None
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
37 exec 'from sys import *' in {}, _FakeMapping()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
38 except SystemError:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
39 has_star_import_bug = True
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
40 del _FakeMapping
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
41
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
42
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
43 def _star_import_patch(mapping, modname):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
44 """This function is used as helper if a Python version with a broken
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
45 star-import opcode is in use.
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
46 """
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
47 module = __import__(modname, None, None, ['__all__'])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
48 if hasattr(module, '__all__'):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
49 members = module.__all__
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
50 else:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
51 members = [x for x in module.__dict__ if not x.startswith('_')]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
52 mapping.update([(name, getattr(module, name)) for name in members])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
53
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
54
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
55 class Code(object):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
56 """Abstract base class for the `Expression` and `Suite` classes."""
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
57 __slots__ = ['source', 'code', 'ast', '_globals']
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
58
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
59 def __init__(self, source, filename=None, lineno=-1, lookup='strict',
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
60 xform=None):
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
61 """Create the code object, either from a string, or from an AST node.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
62
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
63 :param source: either a string containing the source code, or an AST
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
64 node
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
65 :param filename: the (preferably absolute) name of the file containing
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
66 the code
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
67 :param lineno: the number of the line on which the code was found
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
68 :param lookup: the lookup class that defines how variables are looked
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
69 up in the context; can be either "strict" (the default),
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
70 "lenient", or a custom lookup class
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
71 :param xform: the AST transformer that should be applied to the code;
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
72 if `None`, the appropriate transformation is chosen
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
73 depending on the mode
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
74 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
75 if isinstance(source, basestring):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
76 self.source = source
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
77 node = _parse(source, mode=self.mode)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
78 else:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
79 assert isinstance(source, _ast.AST), \
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
80 'Expected string or AST node, but got %r' % source
903
95d62e239f60 inline branch: support for a couple more directives
cmlenz
parents: 902
diff changeset
81 self.source = ASTCodeGenerator(source).code.strip()
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
82 if self.mode == 'eval':
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
83 node = _ast.Expression()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
84 node.body = source
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
85 else:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
86 node = _ast.Module()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
87 node.body = [source]
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
88
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
89 self.ast = node
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
90 self.code = _compile(node, self.source, mode=self.mode,
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
91 filename=filename, lineno=lineno, xform=xform)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
92 if lookup is None:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
93 lookup = LenientLookup
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
94 elif isinstance(lookup, basestring):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
95 lookup = {'lenient': LenientLookup, 'strict': StrictLookup}[lookup]
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
96 self._globals = lookup.globals
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
97
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
98 def __getstate__(self):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
99 state = {'source': self.source, 'ast': self.ast,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
100 'lookup': self._globals.im_self}
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
101 c = self.code
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
102 state['code'] = (c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
103 c.co_consts, c.co_names, c.co_varnames, c.co_filename,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
104 c.co_name, c.co_firstlineno, c.co_lnotab, (), ())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
105 return state
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
106
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
107 def __setstate__(self, state):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
108 self.source = state['source']
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
109 self.ast = state['ast']
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
110 self.code = CodeType(0, *state['code'])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
111 self._globals = state['lookup'].globals
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
112
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
113 def __eq__(self, other):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
114 return (type(other) == type(self)) and (self.code == other.code)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
115
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
116 def __hash__(self):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
117 return hash(self.code)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
118
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
119 def __ne__(self, other):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
120 return not self == other
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
121
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
122 def __repr__(self):
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
123 return '%s(%r)' % (type(self).__name__, self.source)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
124
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
125
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
126 class Expression(Code):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
127 """Evaluates Python expressions used in templates.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
128
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
129 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
130 >>> Expression('test').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
131 'Foo'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
132
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
133 >>> Expression('items[0]').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
134 1
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
135 >>> Expression('items[-1]').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
136 3
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
137 >>> Expression('dict["some"]').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
138 'thing'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
139
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
140 Similar to e.g. Javascript, expressions in templates can use the dot
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
141 notation for attribute access to access items in mappings:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
142
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
143 >>> Expression('dict.some').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
144 'thing'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
145
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
146 This also works the other way around: item access can be used to access
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
147 any object attribute:
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
148
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
149 >>> class MyClass(object):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
150 ... myattr = 'Bar'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
151 >>> data = dict(mine=MyClass(), key='myattr')
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
152 >>> Expression('mine.myattr').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
153 'Bar'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
154 >>> Expression('mine["myattr"]').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
155 'Bar'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
156 >>> Expression('mine[key]').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
157 'Bar'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
158
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
159 All of the standard Python operators are available to template expressions.
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
160 Built-in functions such as ``len()`` are also available in template
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
161 expressions:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
162
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
163 >>> data = dict(items=[1, 2, 3])
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
164 >>> Expression('len(items)').evaluate(data)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
165 3
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
166 """
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
167 __slots__ = []
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
168 mode = 'eval'
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
169
344
eb4ef44ea1ad inline branch: Merged [419:421/trunk].
cmlenz
parents: 341
diff changeset
170 def evaluate(self, data):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
171 """Evaluate the expression against the given data dictionary.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
172
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
173 :param data: a mapping containing the data to evaluate against
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
174 :return: the result of the evaluation
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
175 """
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
176 __traceback_hide__ = 'before_and_this'
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
177 _globals = self._globals(data)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
178 return eval(self.code, _globals, {'__data__': data})
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
179
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
180
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
181 class Suite(Code):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
182 """Executes Python statements used in templates.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
183
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
184 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
185 >>> Suite("foo = dict['some']").execute(data)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
186 >>> data['foo']
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
187 'thing'
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
188 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
189 __slots__ = []
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
190 mode = 'exec'
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
191
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
192 def execute(self, data):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
193 """Execute the suite in the given data dictionary.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
194
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
195 :param data: a mapping containing the data to execute in
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
196 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
197 __traceback_hide__ = 'before_and_this'
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
198 _globals = self._globals(data)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
199 exec self.code in _globals, data
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
200
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
201
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
202 UNDEFINED = object()
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
203
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
204
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
205 class UndefinedError(TemplateRuntimeError):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
206 """Exception thrown when a template expression attempts to access a variable
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
207 not defined in the context.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
208
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
209 :see: `LenientLookup`, `StrictLookup`
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
210 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
211 def __init__(self, name, owner=UNDEFINED):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
212 if owner is not UNDEFINED:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
213 message = '%s has no member named "%s"' % (repr(owner), name)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
214 else:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
215 message = '"%s" not defined' % name
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
216 TemplateRuntimeError.__init__(self, message)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
217
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
218
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
219 class Undefined(object):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
220 """Represents a reference to an undefined variable.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
221
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
222 Unlike the Python runtime, template expressions can refer to an undefined
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
223 variable without causing a `NameError` to be raised. The result will be an
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
224 instance of the `Undefined` class, which is treated the same as ``False`` in
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
225 conditions, but raise an exception on any other operation:
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
226
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
227 >>> foo = Undefined('foo')
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
228 >>> bool(foo)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
229 False
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
230 >>> list(foo)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
231 []
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
232 >>> print(foo)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
233 undefined
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
234
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
235 However, calling an undefined variable, or trying to access an attribute
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
236 of that variable, will raise an exception that includes the name used to
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
237 reference that undefined variable.
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
238
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
239 >>> foo('bar')
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
240 Traceback (most recent call last):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
241 ...
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
242 UndefinedError: "foo" not defined
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
243
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
244 >>> foo.bar
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
245 Traceback (most recent call last):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
246 ...
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
247 UndefinedError: "foo" not defined
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
248
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
249 :see: `LenientLookup`
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
250 """
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
251 __slots__ = ['_name', '_owner']
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
252
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
253 def __init__(self, name, owner=UNDEFINED):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
254 """Initialize the object.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
255
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
256 :param name: the name of the reference
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
257 :param owner: the owning object, if the variable is accessed as a member
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
258 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
259 self._name = name
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
260 self._owner = owner
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
261
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
262 def __iter__(self):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
263 return iter([])
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
264
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
265 def __nonzero__(self):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
266 return False
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
267
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
268 def __repr__(self):
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
269 return '<%s %r>' % (type(self).__name__, self._name)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
270
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
271 def __str__(self):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
272 return 'undefined'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
273
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
274 def _die(self, *args, **kwargs):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
275 """Raise an `UndefinedError`."""
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
276 __traceback_hide__ = True
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
277 raise UndefinedError(self._name, self._owner)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
278 __call__ = __getattr__ = __getitem__ = _die
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
279
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
280 # Hack around some behavior introduced in Python 2.6.2
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
281 # http://genshi.edgewall.org/ticket/324
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
282 __length_hint__ = None
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
283
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
284
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
285 class LookupBase(object):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
286 """Abstract base class for variable lookup implementations."""
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
287
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
288 @classmethod
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
289 def globals(cls, data):
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
290 """Construct the globals dictionary to use as the execution context for
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
291 the expression or suite.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
292 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
293 return {
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
294 '__data__': data,
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
295 '_lookup_name': cls.lookup_name,
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
296 '_lookup_attr': cls.lookup_attr,
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
297 '_lookup_item': cls.lookup_item,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
298 '_star_import_patch': _star_import_patch,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
299 'UndefinedError': UndefinedError,
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
300 }
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
301
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
302 @classmethod
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
303 def lookup_name(cls, data, name):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
304 __traceback_hide__ = True
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
305 val = data.get(name, UNDEFINED)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
306 if val is UNDEFINED:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
307 val = BUILTINS.get(name, val)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
308 if val is UNDEFINED:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
309 val = cls.undefined(name)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
310 return val
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
311
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
312 @classmethod
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
313 def lookup_attr(cls, obj, key):
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
314 __traceback_hide__ = True
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
315 try:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
316 val = getattr(obj, key)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
317 except AttributeError:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
318 if hasattr(obj.__class__, key):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
319 raise
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
320 else:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
321 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
322 val = obj[key]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
323 except (KeyError, TypeError):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
324 val = cls.undefined(key, owner=obj)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
325 return val
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
326
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
327 @classmethod
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
328 def lookup_item(cls, obj, key):
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
329 __traceback_hide__ = True
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
330 if len(key) == 1:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
331 key = key[0]
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
332 try:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
333 return obj[key]
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
334 except (AttributeError, KeyError, IndexError, TypeError), e:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
335 if isinstance(key, basestring):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
336 val = getattr(obj, key, UNDEFINED)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
337 if val is UNDEFINED:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
338 val = cls.undefined(key, owner=obj)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
339 return val
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
340 raise
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
341
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
342 @classmethod
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
343 def undefined(cls, key, owner=UNDEFINED):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
344 """Can be overridden by subclasses to specify behavior when undefined
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
345 variables are accessed.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
346
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
347 :param key: the name of the variable
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
348 :param owner: the owning object, if the variable is accessed as a member
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
349 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
350 raise NotImplementedError
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
351
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
352
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
353 class LenientLookup(LookupBase):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
354 """Default variable lookup mechanism for expressions.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
355
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
356 When an undefined variable is referenced using this lookup style, the
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
357 reference evaluates to an instance of the `Undefined` class:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
358
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
359 >>> expr = Expression('nothing', lookup='lenient')
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
360 >>> undef = expr.evaluate({})
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
361 >>> undef
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
362 <Undefined 'nothing'>
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
363
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
364 The same will happen when a non-existing attribute or item is accessed on
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
365 an existing object:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
366
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
367 >>> expr = Expression('something.nil', lookup='lenient')
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
368 >>> expr.evaluate({'something': dict()})
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
369 <Undefined 'nil'>
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
370
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
371 See the documentation of the `Undefined` class for details on the behavior
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
372 of such objects.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
373
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
374 :see: `StrictLookup`
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
375 """
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
376
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
377 @classmethod
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
378 def undefined(cls, key, owner=UNDEFINED):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
379 """Return an ``Undefined`` object."""
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
380 __traceback_hide__ = True
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
381 return Undefined(key, owner=owner)
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
382
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
383
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
384 class StrictLookup(LookupBase):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
385 """Strict variable lookup mechanism for expressions.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
386
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
387 Referencing an undefined variable using this lookup style will immediately
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
388 raise an ``UndefinedError``:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
389
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
390 >>> expr = Expression('nothing', lookup='strict')
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
391 >>> expr.evaluate({})
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
392 Traceback (most recent call last):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
393 ...
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
394 UndefinedError: "nothing" not defined
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
395
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
396 The same happens when a non-existing attribute or item is accessed on an
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
397 existing object:
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
398
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
399 >>> expr = Expression('something.nil', lookup='strict')
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
400 >>> expr.evaluate({'something': dict()})
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
401 Traceback (most recent call last):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
402 ...
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
403 UndefinedError: {} has no member named "nil"
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
404 """
830
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
405
de82830f8816 inline branch: synced with trunk@1038.
cmlenz
parents: 820
diff changeset
406 @classmethod
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
407 def undefined(cls, key, owner=UNDEFINED):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
408 """Raise an ``UndefinedError`` immediately."""
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
409 __traceback_hide__ = True
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
410 raise UndefinedError(key, owner=owner)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
411
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
412
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
413 def _parse(source, mode='eval'):
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
414 source = source.strip()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
415 if mode == 'exec':
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
416 lines = [line.expandtabs() for line in source.splitlines()]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
417 if lines:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
418 first = lines[0]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
419 rest = dedent('\n'.join(lines[1:])).rstrip()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
420 if first.rstrip().endswith(':') and not rest[0].isspace():
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
421 rest = '\n'.join([' %s' % line for line in rest.splitlines()])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
422 source = '\n'.join([first, rest])
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
423 if isinstance(source, unicode):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
424 source = '\xef\xbb\xbf' + source.encode('utf-8')
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
425 return parse(source, mode)
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
426
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
427
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
428 def _compile(node, source=None, mode='eval', filename=None, lineno=-1,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
429 xform=None):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
430 if isinstance(filename, unicode):
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
431 # unicode file names not allowed for code objects
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
432 filename = filename.encode('utf-8', 'replace')
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
433 elif not filename:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
434 filename = '<string>'
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
435 if lineno <= 0:
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
436 lineno = 1
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
437
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
438 if xform is None:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
439 xform = {
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
440 'eval': ExpressionASTTransformer
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
441 }.get(mode, TemplateASTTransformer)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
442 tree = xform().visit(node)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
443
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
444 if mode == 'eval':
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
445 name = '<Expression %r>' % (source or '?')
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
446 else:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
447 lines = source.splitlines()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
448 if not lines:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
449 extract = ''
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
450 else:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
451 extract = lines[0]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
452 if len(lines) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
453 extract += ' ...'
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
454 name = '<Suite %r>' % (extract)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
455 new_source = ASTCodeGenerator(tree).code
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
456 code = compile(new_source, filename, mode)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
457
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
458 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
459 # We'd like to just set co_firstlineno, but it's readonly. So we need
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
460 # to clone the code object while adjusting the line number
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
461 return CodeType(0, code.co_nlocals, code.co_stacksize,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
462 code.co_flags | 0x0040, code.co_code, code.co_consts,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
463 code.co_names, code.co_varnames, filename, name,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
464 lineno, code.co_lnotab, (), ())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
465 except RuntimeError:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
466 return code
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
467
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
468
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
469 def _new(class_, *args, **kwargs):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
470 ret = class_()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
471 for attr, value in zip(ret._fields, args):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
472 if attr in kwargs:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
473 raise ValueError('Field set both in args and kwargs')
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
474 setattr(ret, attr, value)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
475 for attr, value in kwargs:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
476 setattr(ret, attr, value)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
477 return ret
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
478
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
479
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
480 BUILTINS = __builtin__.__dict__.copy()
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
481 BUILTINS.update({'Markup': Markup, 'Undefined': Undefined})
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
482 CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
483
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
484
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
485 class TemplateASTTransformer(ASTTransformer):
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
486 """Concrete AST transformer that implements the AST transformations needed
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
487 for code embedded in templates.
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
488 """
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
489
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
490 def __init__(self):
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
491 self.locals = [CONSTANTS]
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
492
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
493 def _extract_names(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
494 names = set()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
495 def _process(node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
496 if isinstance(node, _ast.Name):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
497 names.add(node.id)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
498 elif isinstance(node, _ast.alias):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
499 names.add(node.asname or node.name)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
500 elif isinstance(node, _ast.Tuple):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
501 for elt in node.elts:
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
502 _process(elt)
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
503 if hasattr(node, 'args'):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
504 for arg in node.args:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
505 _process(arg)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
506 if hasattr(node, 'vararg'):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
507 names.add(node.vararg)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
508 if hasattr(node, 'kwarg'):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
509 names.add(node.kwarg)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
510 elif hasattr(node, 'names'):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
511 for elt in node.names:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
512 _process(elt)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
513 return names
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
514
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
515 def visit_Str(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
516 if isinstance(node.s, str):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
517 try: # If the string is ASCII, return a `str` object
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
518 node.s.decode('ascii')
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
519 except ValueError: # Otherwise return a `unicode` object
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
520 return _new(_ast.Str, node.s.decode('utf-8'))
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
521 return node
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
522
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
523 def visit_ClassDef(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
524 if len(self.locals) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
525 self.locals[-1].add(node.name)
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
526 self.locals.append(set())
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
527 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
528 return ASTTransformer.visit_ClassDef(self, node)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
529 finally:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
530 self.locals.pop()
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
531
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
532 def visit_Import(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
533 if len(self.locals) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
534 self.locals[-1].update(self._extract_names(node))
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
535 return ASTTransformer.visit_Import(self, node)
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
536
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
537 def visit_ImportFrom(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
538 if [a.name for a in node.names] == ['*']:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
539 if has_star_import_bug:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
540 # This is a Python 2.4 bug. Only if we have a broken Python
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
541 # version do we need to apply this hack
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
542 node = _new(_ast.Expr, _new(_ast.Call,
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
543 _new(_ast.Name, '_star_import_patch'), [
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
544 _new(_ast.Name, '__data__'),
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
545 _new(_ast.Str, node.module)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
546 ], (), ()))
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
547 return node
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
548 if len(self.locals) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
549 self.locals[-1].update(self._extract_names(node))
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
550 return ASTTransformer.visit_ImportFrom(self, node)
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
551
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
552 def visit_FunctionDef(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
553 if len(self.locals) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
554 self.locals[-1].add(node.name)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
555
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
556 self.locals.append(self._extract_names(node.args))
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
557 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
558 return ASTTransformer.visit_FunctionDef(self, node)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
559 finally:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
560 self.locals.pop()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
561
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
562 # GeneratorExp(expr elt, comprehension* generators)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
563 def visit_GeneratorExp(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
564 gens = []
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
565 for generator in node.generators:
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
566 # comprehension = (expr target, expr iter, expr* ifs)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
567 self.locals.append(set())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
568 gen = _new(_ast.comprehension, self.visit(generator.target),
902
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
569 self.visit(generator.iter),
09cc3627654c Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents: 830
diff changeset
570 [self.visit(if_) for if_ in generator.ifs])
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
571 gens.append(gen)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
572
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
573 # use node.__class__ to make it reusable as ListComp
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
574 ret = _new(node.__class__, self.visit(node.elt), gens)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
575 #delete inserted locals
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
576 del self.locals[-len(node.generators):]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
577 return ret
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
578
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
579 # ListComp(expr elt, comprehension* generators)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
580 visit_ListComp = visit_GeneratorExp
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
581
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
582 def visit_Lambda(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
583 self.locals.append(self._extract_names(node.args))
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
584 try:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
585 return ASTTransformer.visit_Lambda(self, node)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
586 finally:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
587 self.locals.pop()
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
588
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
589 def visit_Name(self, node):
358
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
590 # If the name refers to a local inside a lambda, list comprehension, or
028a9a50d95a inline branch: Merged [437].
cmlenz
parents: 344
diff changeset
591 # generator expression, leave it alone
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
592 if isinstance(node.ctx, _ast.Load) and \
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
593 node.id not in flatten(self.locals):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
594 # Otherwise, translate the name ref into a context lookup
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
595 name = _new(_ast.Name, '_lookup_name', _ast.Load())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
596 namearg = _new(_ast.Name, '__data__', _ast.Load())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
597 strarg = _new(_ast.Str, node.id)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
598 node = _new(_ast.Call, name, [namearg, strarg], [])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
599 elif isinstance(node.ctx, _ast.Store):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
600 if len(self.locals) > 1:
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
601 self.locals[-1].add(node.id)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
602
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
603 return node
336
5f2c7782cd8a Refactoring: `genshi.template` is now a package, it was getting way to crowded in that file.
cmlenz
parents:
diff changeset
604
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
605
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
606 class ExpressionASTTransformer(TemplateASTTransformer):
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
607 """Concrete AST transformer that implements the AST transformations needed
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
608 for code embedded in templates.
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
609 """
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
610
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
611 def visit_Attribute(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
612 if not isinstance(node.ctx, _ast.Load):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
613 return ASTTransformer.visit_Attribute(self, node)
500
0742f421caba Merged revisions 487-603 via svnmerge from
cmlenz
parents: 398
diff changeset
614
820
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
615 func = _new(_ast.Name, '_lookup_attr', _ast.Load())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
616 args = [self.visit(node.value), _new(_ast.Str, node.attr)]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
617 return _new(_ast.Call, func, args, [])
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
618
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
619 def visit_Subscript(self, node):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
620 if not isinstance(node.ctx, _ast.Load) or \
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
621 not isinstance(node.slice, _ast.Index):
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
622 return ASTTransformer.visit_Subscript(self, node)
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
623
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
624 func = _new(_ast.Name, '_lookup_item', _ast.Load())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
625 args = [
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
626 self.visit(node.value),
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
627 _new(_ast.Tuple, (self.visit(node.slice.value),), _ast.Load())
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
628 ]
1837f39efd6f Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents: 500
diff changeset
629 return _new(_ast.Call, func, args, [])
Copyright (C) 2012-2017 Edgewall Software