Hide keyboard shortcuts

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 

5 

6import inspect 

7import textwrap 

8import types 

9import copy 

10 

11try: 

12 import __builtin__ as builtins 

13except ImportError: 

14 import builtins 

15 

16reverse_builtin_map = {} 

17for name, value in builtins.__dict__.items(): 

18 try: 

19 hash(value) 

20 except TypeError: 

21 continue 

22 

23 reverse_builtin_map[value] = name 

24 

25try: 

26 basestring 

27except NameError: 

28 basestring = str 

29 

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 

37 

38from .exc import CompilationError 

39 

40 

41try: 

42 NATIVE_NUMBERS = int, float, long, bool 

43except NameError: 

44 NATIVE_NUMBERS = int, float, bool 

45 

46 

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) 

51 

52 class Visitor(ast.NodeVisitor): 

53 def visit_FunctionDef(self, node): 

54 self.generic_visit(node) 

55 

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 ) 

64 

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) 

76 

77 assert node not in node_annotations 

78 assert hasattr(value, '_fields') 

79 node_annotations[node] = value 

80 

81 expr = parse(textwrap.dedent(source), mode=mode) 

82 

83 Visitor().visit(expr) 

84 return expr.body 

85 

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) 

93 

94 

95class TemplateCodeGenerator(ASTCodeGenerator): 

96 """Extends the standard Python code generator class with handlers 

97 for the helper node classes: 

98 

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) 

103 

104 """ 

105 

106 names = () 

107 

108 def __init__(self, tree, source=None): 

109 self.imports = {} 

110 self.defines = {} 

111 self.markers = {} 

112 self.source = source 

113 self.tokens = [] 

114 

115 # Generate code 

116 super(TemplateCodeGenerator, self).__init__(tree) 

117 

118 def visit_Module(self, node): 

119 super(TemplateCodeGenerator, self).visit_Module(node) 

120 

121 # Make sure we terminate the line printer 

122 self.flush() 

123 

124 # Clear lines array for import visits 

125 body = self.lines 

126 self.lines = [] 

127 

128 while self.defines: 

129 name, node = self.defines.popitem() 

130 assignment = ast.Assign(targets=[store(name)], value=node) 

131 self.visit(assignment) 

132 

133 # Make sure we terminate the line printer 

134 self.flush() 

135 

136 # Clear lines array for import visits 

137 defines = self.lines 

138 self.lines = [] 

139 

140 while self.imports: 

141 value, node = self.imports.popitem() 

142 

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) 

158 

159 self.visit(stmt) 

160 

161 # Clear last import 

162 self.flush() 

163 

164 # Stich together lines 

165 self.lines += defines + body 

166 

167 def define(self, name, node): 

168 assert node is not None 

169 value = self.defines.get(name) 

170 

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) 

178 

179 return load(name) 

180 

181 def require(self, value): 

182 if value is None: 

183 return load("None") 

184 

185 if isinstance(value, NATIVE_NUMBERS): 

186 return ast.Num(value) 

187 

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) 

195 

196 return node 

197 

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) 

204 

205 def visit_Comment(self, node): 

206 if node.stmt is None: 

207 self._new_line() 

208 else: 

209 self.visit(node.stmt) 

210 

211 for line in node.text.replace('\r', '\n').split('\n'): 

212 self._new_line() 

213 self._write("%s#%s" % (node.space, line)) 

214 

215 def visit_Builtin(self, node): 

216 name = load(node.id) 

217 self.visit(name) 

218 

219 def visit_Symbol(self, node): 

220 node = self.require(node.value) 

221 self.visit(node) 

222 

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 

228 

229 node = self.define(name, node.value) 

230 self.visit(node) 

231 

232 def visit_TokenRef(self, node): 

233 self.tokens.append((node.pos, node.length)) 

234 super(TemplateCodeGenerator, self).visit(ast.Num(n=node.pos))