Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/chameleon/codegen.py : 41%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1try:
2 import ast
3except ImportError:
4 from chameleon import ast25 as ast
6import inspect
7import textwrap
8import types
9import copy
11try:
12 import __builtin__ as builtins
13except ImportError:
14 import builtins
16reverse_builtin_map = {}
17for name, value in builtins.__dict__.items():
18 try:
19 hash(value)
20 except TypeError:
21 continue
23 reverse_builtin_map[value] = name
25try:
26 basestring
27except NameError:
28 basestring = str
30from .astutil import ASTCodeGenerator
31from .astutil import load
32from .astutil import store
33from .astutil import parse
34from .astutil import Builtin
35from .astutil import Symbol
36from .astutil import node_annotations
38from .exc import CompilationError
41try:
42 NATIVE_NUMBERS = int, float, long, bool
43except NameError:
44 NATIVE_NUMBERS = int, float, bool
47def template(source, mode='exec', is_func=False, func_args=(), func_defaults=(), **kw):
48 def wrapper(*vargs, **kwargs):
49 symbols = dict(zip(args, vargs + defaults))
50 symbols.update(kwargs)
52 class Visitor(ast.NodeVisitor):
53 def visit_FunctionDef(self, node):
54 self.generic_visit(node)
56 name = symbols.get(node.name, self)
57 if name is not self:
58 node_annotations[node] = ast.FunctionDef(
59 name=name,
60 args=node.args,
61 body=node.body,
62 decorator_list=getattr(node, "decorator_list", []),
63 )
65 def visit_Name(self, node):
66 value = symbols.get(node.id, self)
67 if value is not self:
68 if isinstance(value, basestring):
69 value = load(value)
70 if isinstance(value, type) or value in reverse_builtin_map:
71 name = reverse_builtin_map.get(value)
72 if name is not None:
73 value = Builtin(name)
74 else:
75 value = Symbol(value)
77 assert node not in node_annotations
78 assert hasattr(value, '_fields')
79 node_annotations[node] = value
81 expr = parse(textwrap.dedent(source), mode=mode)
83 Visitor().visit(expr)
84 return expr.body
86 assert isinstance(source, basestring)
87 defaults = func_defaults
88 args = func_args
89 if is_func:
90 return wrapper
91 else:
92 return wrapper(**kw)
95class TemplateCodeGenerator(ASTCodeGenerator):
96 """Extends the standard Python code generator class with handlers
97 for the helper node classes:
99 - Symbol (an importable value)
100 - Static (value that can be made global)
101 - Builtin (from the builtins module)
102 - Marker (short-hand for a unique static object)
104 """
106 names = ()
108 def __init__(self, tree, source=None):
109 self.imports = {}
110 self.defines = {}
111 self.markers = {}
112 self.source = source
113 self.tokens = []
115 # Generate code
116 super(TemplateCodeGenerator, self).__init__(tree)
118 def visit_Module(self, node):
119 super(TemplateCodeGenerator, self).visit_Module(node)
121 # Make sure we terminate the line printer
122 self.flush()
124 # Clear lines array for import visits
125 body = self.lines
126 self.lines = []
128 while self.defines:
129 name, node = self.defines.popitem()
130 assignment = ast.Assign(targets=[store(name)], value=node)
131 self.visit(assignment)
133 # Make sure we terminate the line printer
134 self.flush()
136 # Clear lines array for import visits
137 defines = self.lines
138 self.lines = []
140 while self.imports:
141 value, node = self.imports.popitem()
143 if isinstance(value, types.ModuleType):
144 stmt = ast.Import(
145 names=[ast.alias(name=value.__name__, asname=node.id)])
146 elif hasattr(value, '__name__'):
147 path = reverse_builtin_map.get(value)
148 if path is None:
149 path = value.__module__
150 name = value.__name__
151 stmt = ast.ImportFrom(
152 module=path,
153 names=[ast.alias(name=name, asname=node.id)],
154 level=0,
155 )
156 else:
157 raise TypeError(value)
159 self.visit(stmt)
161 # Clear last import
162 self.flush()
164 # Stich together lines
165 self.lines += defines + body
167 def define(self, name, node):
168 assert node is not None
169 value = self.defines.get(name)
171 if value is node:
172 pass
173 elif value is None:
174 self.defines[name] = node
175 else:
176 raise CompilationError(
177 "Duplicate symbol name for define.", name)
179 return load(name)
181 def require(self, value):
182 if value is None:
183 return load("None")
185 if isinstance(value, NATIVE_NUMBERS):
186 return ast.Num(value)
188 node = self.imports.get(value)
189 if node is None:
190 # we come up with a unique symbol based on the class name
191 name = "_%s" % getattr(value, '__name__', str(value)).\
192 rsplit('.', 1)[-1]
193 node = load(name)
194 self.imports[value] = store(node.id)
196 return node
198 def visit(self, node):
199 annotation = node_annotations.get(node)
200 if annotation is None:
201 super(TemplateCodeGenerator, self).visit(node)
202 else:
203 self.visit(annotation)
205 def visit_Comment(self, node):
206 if node.stmt is None:
207 self._new_line()
208 else:
209 self.visit(node.stmt)
211 for line in node.text.replace('\r', '\n').split('\n'):
212 self._new_line()
213 self._write("%s#%s" % (node.space, line))
215 def visit_Builtin(self, node):
216 name = load(node.id)
217 self.visit(name)
219 def visit_Symbol(self, node):
220 node = self.require(node.value)
221 self.visit(node)
223 def visit_Static(self, node):
224 if node.name is None:
225 name = "_static_%s" % str(id(node.value)).replace('-', '_')
226 else:
227 name = node.name
229 node = self.define(name, node.value)
230 self.visit(node)
232 def visit_TokenRef(self, node):
233 self.tokens.append((node.pos, node.length))
234 super(TemplateCodeGenerator, self).visit(ast.Num(n=node.pos))