Coverage for /pythoncovmergedfiles/medio/medio/src/underscore/underscore/codegen.py: 80%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

451 statements  

1""" 

2 codegen 

3 ~~~~~~~ 

4 

5 Extension to ast that allow ast -> python code generation. 

6 

7 :copyright: Copyright 2008 by Armin Ronacher. 

8 :license: BSD. 

9""" 

10from ast import * 

11 

12BOOLOP_SYMBOLS = { 

13 And: 'and', 

14 Or: 'or' 

15} 

16 

17BINOP_SYMBOLS = { 

18 Add: '+', 

19 Sub: '-', 

20 Mult: '*', 

21 Div: '/', 

22 FloorDiv: '//', 

23 Mod: '%', 

24 LShift: '<<', 

25 RShift: '>>', 

26 BitOr: '|', 

27 BitAnd: '&', 

28 BitXor: '^', 

29 Pow: '**' 

30} 

31 

32CMPOP_SYMBOLS = { 

33 Eq: '==', 

34 Gt: '>', 

35 GtE: '>=', 

36 In: 'in', 

37 Is: 'is', 

38 IsNot: 'is not', 

39 Lt: '<', 

40 LtE: '<=', 

41 NotEq: '!=', 

42 NotIn: 'not in' 

43} 

44 

45UNARYOP_SYMBOLS = { 

46 Invert: '~', 

47 Not: 'not', 

48 UAdd: '+', 

49 USub: '-' 

50} 

51 

52 

53def to_source(node, indent_with=' ' * 4, add_line_information=False): 

54 """This function can convert a node tree back into python sourcecode. 

55 This is useful for debugging purposes, especially if you're dealing with 

56 custom asts not generated by python itself. 

57 

58 It could be that the sourcecode is evaluable when the AST itself is not 

59 compilable / evaluable. The reason for this is that the AST contains some 

60 more data than regular sourcecode does, which is dropped during 

61 conversion. 

62 

63 Each level of indentation is replaced with `indent_with`. Per default this 

64 parameter is equal to four spaces as suggested by PEP 8, but it might be 

65 adjusted to match the application's styleguide. 

66 

67 If `add_line_information` is set to `True` comments for the line numbers 

68 of the nodes are added to the output. This can be used to spot wrong line 

69 number information of statement nodes. 

70 """ 

71 generator = SourceGenerator(indent_with, add_line_information) 

72 generator.visit(node) 

73 

74 return ''.join(generator.result) 

75 

76 

77class SourceGenerator(NodeVisitor): 

78 """This visitor is able to transform a well formed syntax tree into python 

79 sourcecode. For more details have a look at the docstring of the 

80 `node_to_source` function. 

81 """ 

82 

83 def __init__(self, indent_with, add_line_information=False): 

84 self.result = [] 

85 self.indent_with = indent_with 

86 self.add_line_information = add_line_information 

87 self.indentation = 0 

88 self.new_lines = 0 

89 

90 def write(self, x): 

91 if self.new_lines: 

92 if self.result: 

93 self.result.append('\n' * self.new_lines) 

94 self.result.append(self.indent_with * self.indentation) 

95 self.new_lines = 0 

96 self.result.append(x) 

97 

98 def newline(self, node=None, extra=0): 

99 self.new_lines = max(self.new_lines, 1 + extra) 

100 if node is not None and self.add_line_information: 

101 self.write('# line: %s' % node.lineno) 

102 self.new_lines = 1 

103 

104 def body(self, statements): 

105 self.new_line = True 

106 self.indentation += 1 

107 for stmt in statements: 

108 self.visit(stmt) 

109 self.indentation -= 1 

110 

111 def body_or_else(self, node): 

112 self.body(node.body) 

113 if node.orelse: 

114 self.newline() 

115 self.write('else:') 

116 self.body(node.orelse) 

117 

118 def signature(self, node): 

119 want_comma = [] 

120 def write_comma(): 

121 if want_comma: 

122 self.write(', ') 

123 else: 

124 want_comma.append(True) 

125 

126 padding = [None] * (len(node.args) - len(node.defaults)) 

127 for arg, default in zip(node.args, padding + node.defaults): 

128 write_comma() 

129 self.visit(arg) 

130 if default is not None: 

131 self.write('=') 

132 self.visit(default) 

133 if node.vararg is not None: 

134 write_comma() 

135 self.write('*' + node.vararg) 

136 if node.kwarg is not None: 

137 write_comma() 

138 self.write('**' + node.kwarg) 

139 

140 def decorators(self, node): 

141 for decorator in node.decorator_list: 

142 self.newline(decorator) 

143 self.write('@') 

144 self.visit(decorator) 

145 

146 # Module 

147 def visit_Module(self, node): 

148 NodeVisitor.generic_visit(self, node) 

149 self.write('\n') 

150 

151 # Statements 

152 

153 def visit_Assert(self, node): 

154 self.newline(node) 

155 self.write('assert ') 

156 self.visit(node.test) 

157 if node.msg: 

158 self.write(', ') 

159 self.visit(node.msg) 

160 

161 def visit_Assign(self, node): 

162 self.newline(node) 

163 for idx, target in enumerate(node.targets): 

164 self.visit(target) 

165 self.write(' = ') 

166 self.visit(node.value) 

167 

168 def visit_AugAssign(self, node): 

169 self.newline(node) 

170 self.visit(node.target) 

171 self.write(BINOP_SYMBOLS[type(node.op)] + '=') 

172 self.visit(node.value) 

173 

174 def visit_ImportFrom(self, node): 

175 self.newline(node) 

176 for item in node.names: 

177 self.write('from %s%s import ' % ('.' * node.level, node.module)) 

178 self.visit(item) 

179 self.newline(node) 

180 

181 def visit_Import(self, node): 

182 self.newline(node) 

183 for item in node.names: 

184 self.write('import ') 

185 self.visit(item) 

186 self.newline(node) 

187 

188 def visit_Expr(self, node): 

189 self.newline(node) 

190 self.generic_visit(node) 

191 

192 def visit_FunctionDef(self, node): 

193 self.newline(extra=1) 

194 self.decorators(node) 

195 self.newline(node) 

196 self.write('def %s(' % node.name) 

197 self.signature(node.args) 

198 self.write('):') 

199 self.body(node.body) 

200 

201 def visit_ClassDef(self, node): 

202 have_args = [] 

203 def paren_or_comma(): 

204 if have_args: 

205 self.write(', ') 

206 else: 

207 have_args.append(True) 

208 self.write('(') 

209 

210 self.newline(extra=2) 

211 self.decorators(node) 

212 self.newline(node) 

213 self.write('class %s' % node.name) 

214 for base in node.bases: 

215 paren_or_comma() 

216 self.visit(base) 

217 # XXX: python >= 3.0 only 

218 if hasattr(node, 'keywords'): 

219 for keyword in node.keywords: 

220 paren_or_comma() 

221 self.write(keyword.arg + '=') 

222 self.visit(keyword.value) 

223 if node.starargs is not None: 

224 paren_or_comma() 

225 self.write('*') 

226 self.visit(node.starargs) 

227 if node.kwargs is not None: 

228 paren_or_comma() 

229 self.write('**') 

230 self.visit(node.kwargs) 

231 self.write(have_args and '):' or ':') 

232 self.body(node.body) 

233 

234 def visit_If(self, node): 

235 self.newline(node) 

236 self.write('if ') 

237 self.visit(node.test) 

238 self.write(':') 

239 self.body(node.body) 

240 while True: 

241 else_ = node.orelse 

242 if len(else_) == 1 and isinstance(else_[0], If): 

243 node = else_[0] 

244 self.newline() 

245 self.write('elif ') 

246 self.visit(node.test) 

247 self.write(':') 

248 self.body(node.body) 

249 else: 

250 if else_: 

251 self.newline() 

252 self.write('else:') 

253 self.body(else_) 

254 break 

255 

256 def visit_For(self, node): 

257 self.newline(node) 

258 self.write('for ') 

259 self.visit(node.target) 

260 self.write(' in ') 

261 self.visit(node.iter) 

262 self.write(':') 

263 self.body_or_else(node) 

264 

265 def visit_While(self, node): 

266 self.newline(node) 

267 self.write('while ') 

268 self.visit(node.test) 

269 self.write(':') 

270 self.body_or_else(node) 

271 

272 def visit_With(self, node): 

273 self.newline(node) 

274 self.write('with ') 

275 

276 # Python >= 3.0 

277 if hasattr(node, 'items'): 

278 for idx, with_item in enumerate(node.items): 

279 if idx: 

280 self.write(', ') 

281 self.visit(with_item.context_expr) 

282 if with_item.optional_vars is not None: 

283 self.write(' as ') 

284 self.visit(with_item.optional_vars) 

285 

286 

287 if hasattr(node, 'context_expr'): 

288 self.visit(node.context_expr) 

289 if node.optional_vars is not None: 

290 self.write(' as ') 

291 self.visit(node.optional_vars) 

292 

293 self.write(':') 

294 self.body(node.body) 

295 

296 def visit_Pass(self, node): 

297 self.newline(node) 

298 self.write('pass') 

299 

300 def visit_Print(self, node): 

301 # XXX: python 2.6 only 

302 self.newline(node) 

303 self.write('print ') 

304 want_comma = False 

305 if node.dest is not None: 

306 self.write(' >> ') 

307 self.visit(node.dest) 

308 want_comma = True 

309 for value in node.values: 

310 if want_comma: 

311 self.write(', ') 

312 self.visit(value) 

313 want_comma = True 

314 if not node.nl: 

315 self.write(',') 

316 

317 def visit_Delete(self, node): 

318 self.newline(node) 

319 self.write('del ') 

320 for idx, target in enumerate(node.targets): 

321 if idx: 

322 self.write(', ') 

323 self.visit(target) 

324 

325 def visit_TryExcept(self, node): 

326 self.newline(node) 

327 self.write('try:') 

328 self.body(node.body) 

329 for handler in node.handlers: 

330 self.visit(handler) 

331 if node.orelse: 

332 self.newline(node) 

333 self.write('else:') 

334 self.body(node.orelse) 

335 # XXX: python >= 3.0 

336 if getattr(node, 'finalbody', []): 

337 self.newline(node) 

338 self.write('finally:') 

339 self.body(node.finalbody) 

340 

341 visit_Try = visit_TryExcept 

342 

343 def visit_ExceptHandler(self, node): 

344 self.newline(node) 

345 self.write('except') 

346 if node.type: 

347 self.write(' ') 

348 self.visit(node.type) 

349 if node.name: 

350 self.write(' as ') 

351 # XXX: Python >= 3.0 

352 if isinstance(node.name, str): 

353 self.write(node.name) 

354 else: 

355 self.visit(node.name) 

356 self.write(':') 

357 self.body(node.body) 

358 

359 def visit_TryFinally(self, node): 

360 self.newline(node) 

361 self.write('try:') 

362 self.body(node.body) 

363 self.newline(node) 

364 self.write('finally:') 

365 self.body(node.finalbody) 

366 

367 def visit_Global(self, node): 

368 self.newline(node) 

369 self.write('global ' + ', '.join(node.names)) 

370 

371 # XXX: Python >= 3.0 

372 def visit_Nonlocal(self, node): 

373 self.newline(node) 

374 self.write('nonlocal ' + ', '.join(node.names)) 

375 

376 def visit_Return(self, node): 

377 self.newline(node) 

378 self.write('return') 

379 if node.value: 

380 self.write(' ') 

381 self.visit(node.value) 

382 

383 def visit_Break(self, node): 

384 self.newline(node) 

385 self.write('break') 

386 

387 def visit_Continue(self, node): 

388 self.newline(node) 

389 self.write('continue') 

390 

391 def visit_Raise(self, node): 

392 self.newline(node) 

393 self.write('raise') 

394 # XXX: Python <= 2.6 only 

395 if hasattr(node, 'exc') and node.exc is not None: 

396 self.write(' ') 

397 self.visit(node.exc) 

398 if node.cause is not None: 

399 self.write(' from ') 

400 self.visit(node.cause) 

401 # XXX: Python <= 2.7 

402 elif hasattr(node, 'type') and node.type is not None: 

403 self.visit(node.type) 

404 if node.inst is not None: 

405 self.write(', ') 

406 self.visit(node.inst) 

407 if node.tback is not None: 

408 self.write(', ') 

409 self.visit(node.tback) 

410 

411 # Expressions 

412 

413 def visit_Attribute(self, node): 

414 self.visit(node.value) 

415 self.write('.' + node.attr) 

416 

417 def visit_Call(self, node): 

418 want_comma = [] 

419 def write_comma(): 

420 if want_comma: 

421 self.write(', ') 

422 else: 

423 want_comma.append(True) 

424 

425 self.visit(node.func) 

426 self.write('(') 

427 for arg in node.args: 

428 write_comma() 

429 self.visit(arg) 

430 for keyword in node.keywords: 

431 write_comma() 

432 self.write(keyword.arg + '=') 

433 self.visit(keyword.value) 

434 if node.starargs is not None: 

435 write_comma() 

436 self.write('*') 

437 self.visit(node.starargs) 

438 if node.kwargs is not None: 

439 write_comma() 

440 self.write('**') 

441 self.visit(node.kwargs) 

442 self.write(')') 

443 

444 # XXX: Python >= 3.0 only 

445 def visit_arg(self, node): 

446 self.write(node.arg) 

447 

448 def visit_Name(self, node): 

449 self.write(node.id) 

450 

451 def visit_Str(self, node): 

452 self.write(repr(node.s)) 

453 

454 def visit_Bytes(self, node): 

455 self.write(repr(node.s)) 

456 

457 def visit_Num(self, node): 

458 self.write(repr(node.n)) 

459 

460 def visit_Tuple(self, node): 

461 self.write('(') 

462 idx = -1 

463 for idx, item in enumerate(node.elts): 

464 if idx: 

465 self.write(', ') 

466 self.visit(item) 

467 self.write(idx and ')' or ',)') 

468 

469 def _sequence_visit(left, right): # pylint: disable=E0213 

470 def visit(self, node): 

471 self.write(left) 

472 for idx, item in enumerate(node.elts): 

473 if idx: 

474 self.write(', ') 

475 self.visit(item) 

476 self.write(right) 

477 return visit 

478 

479 visit_List = _sequence_visit('[', ']') 

480 visit_Set = _sequence_visit('{', '}') 

481 

482 def visit_Dict(self, node): 

483 self.write('{') 

484 for idx, (key, value) in enumerate(zip(node.keys, node.values)): 

485 if idx: 

486 self.write(', ') 

487 self.visit(key) 

488 self.write(': ') 

489 self.visit(value) 

490 self.write('}') 

491 

492 def visit_BinOp(self, node): 

493 self.visit(node.left) 

494 self.write(' %s ' % BINOP_SYMBOLS[type(node.op)]) 

495 self.visit(node.right) 

496 

497 def visit_BoolOp(self, node): 

498 self.write('(') 

499 for idx, value in enumerate(node.values): 

500 if idx: 

501 self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)]) 

502 self.visit(value) 

503 self.write(')') 

504 

505 def visit_Compare(self, node): 

506 self.write('(') 

507 self.visit(node.left) 

508 for op, right in zip(node.ops, node.comparators): 

509 self.write(' %s ' % CMPOP_SYMBOLS[type(op)]) 

510 self.visit(right) 

511 self.write(')') 

512 

513 def visit_UnaryOp(self, node): 

514 self.write('(') 

515 op = UNARYOP_SYMBOLS[type(node.op)] 

516 self.write(op) 

517 if op == 'not': 

518 self.write(' ') 

519 self.visit(node.operand) 

520 self.write(')') 

521 

522 def visit_Subscript(self, node): 

523 self.visit(node.value) 

524 self.write('[') 

525 self.visit(node.slice) 

526 self.write(']') 

527 

528 def visit_Slice(self, node): 

529 if node.lower is not None: 

530 self.visit(node.lower) 

531 self.write(':') 

532 if node.upper is not None: 

533 self.visit(node.upper) 

534 if node.step is not None: 

535 self.write(':') 

536 if not (isinstance(node.step, Name) and node.step.id == 'None'): 

537 self.visit(node.step) 

538 

539 def visit_ExtSlice(self, node): 

540 for idx, item in enumerate(node.dims): 

541 if idx: 

542 self.write(', ') 

543 self.visit(item) 

544 

545 def visit_Yield(self, node): 

546 self.write('yield ') 

547 self.visit(node.value) 

548 

549 def visit_Lambda(self, node): 

550 self.write('lambda ') 

551 self.signature(node.args) 

552 self.write(': ') 

553 self.visit(node.body) 

554 

555 def visit_Ellipsis(self, node): 

556 self.write('Ellipsis') 

557 

558 def _generator_visit(left, right): # pylint: disable=E0213 

559 def visit(self, node): 

560 self.write(left) 

561 self.visit(node.elt) 

562 for comprehension in node.generators: 

563 self.visit(comprehension) 

564 self.write(right) 

565 return visit 

566 

567 visit_ListComp = _generator_visit('[', ']') 

568 visit_GeneratorExp = _generator_visit('(', ')') 

569 visit_SetComp = _generator_visit('{', '}') 

570 

571 def visit_DictComp(self, node): 

572 self.write('{') 

573 self.visit(node.key) 

574 self.write(': ') 

575 self.visit(node.value) 

576 for comprehension in node.generators: 

577 self.visit(comprehension) 

578 self.write('}') 

579 

580 def visit_IfExp(self, node): 

581 self.visit(node.body) 

582 self.write(' if ') 

583 self.visit(node.test) 

584 self.write(' else ') 

585 self.visit(node.orelse) 

586 

587 # XXX: Python >= 3.0 only 

588 def visit_Starred(self, node): 

589 self.write('*') 

590 self.visit(node.value) 

591 

592 def visit_Repr(self, node): 

593 # XXX: python 2.6 only 

594 self.write('`') 

595 self.visit(node.value) 

596 self.write('`') 

597 

598 # Helper Nodes 

599 

600 def visit_alias(self, node): 

601 self.write(node.name) 

602 if node.asname is not None: 

603 self.write(' as ' + node.asname) 

604 

605 def visit_comprehension(self, node): 

606 self.write(' for ') 

607 self.visit(node.target) 

608 self.write(' in ') 

609 self.visit(node.iter) 

610 if node.ifs: 

611 for if_ in node.ifs: 

612 self.write(' if ') 

613 self.visit(if_)