1 #!/usr/bin/env python 2 3 """ 4 View annotated sources. 5 6 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk> 7 8 This software is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of 11 the License, or (at your option) any later version. 12 13 This software is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public 19 License along with this library; see the file LICENCE.txt 20 If not, write to the Free Software Foundation, Inc., 21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 22 """ 23 24 from compiler.visitor import ASTVisitor 25 from simplified import * 26 import sys 27 import textwrap 28 29 # Exceptions. 30 31 class ViewerError(SimplifiedError): 32 33 "An error in viewing." 34 35 pass 36 37 # Classes. 38 39 class Viewer(ASTVisitor): 40 41 """ 42 A viewing visitor for AST nodes. 43 """ 44 45 def __init__(self, stream): 46 ASTVisitor.__init__(self) 47 self.cached_files = {} 48 self.printed_lines = {} 49 self.visitor = self 50 self.stream = stream 51 52 def process(self, module): 53 self.dispatch(module) 54 55 def dispatch(self, node): 56 self.dispatch_only(node) 57 ASTVisitor.dispatch(self, node) 58 59 def dispatch_only(self, node, every_time=0): 60 self.print_line(getattr(node, "filename", None), getattr(node, "lineno", None), every_time) 61 62 def print_line(self, filename, lineno, every_time): 63 last_printed = self.printed_lines.get(filename, 0) 64 if lineno > last_printed or every_time: 65 self.stream.write(self.get_line(filename, lineno)) 66 self.printed_lines[filename] = lineno 67 68 def get_line(self, filename, lineno): 69 if filename is None or lineno is None: 70 return "" 71 72 if self.cached_files.has_key(filename): 73 lines = self.cached_files[filename] 74 else: 75 f = open(filename) 76 try: 77 self.cached_files[filename] = lines = f.readlines() 78 finally: 79 f.close() 80 81 try: 82 return lines[lineno - 1] 83 except IndexError: 84 return "" 85 86 def report(self, exc): 87 self.stream.write("Exception was:\n\n" + str(exc.exc) + "\n\n") 88 self.stream.write("Nodes:\n\n") 89 for node in exc.nodes: 90 self.stream.write(repr(node) + "\n") 91 if node is not None: 92 self.dispatch_only(node.original, every_time=1) 93 self.stream.write("\nOriginal node was:\n\n" + repr(exc.nodes[0].original) + "\n") 94 self.stream.write("\nSimplified node was:\n\n") 95 exc.nodes[0].pprint(stream=self.stream) 96 97 # HTML-related output production. 98 99 html_header = """<?xml version="1.0" encoding="iso-8859-15"?> 100 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 101 <html xmlns="http://www.w3.org/1999/xhtml"> 102 <head> 103 <title>Module</title> 104 <style type="text/css"> 105 body { 106 padding-top: 4em; padding-bottom: 4em; 107 font-size: 14pt; font-family: monospace; 108 background-color: black; color: white; 109 } 110 111 .class { margin-bottom: 1em; } 112 .function { margin-bottom: 1em; } 113 .body { padding-left: 2em; } 114 .keyword { color: yellow; } 115 .comment { color: blue; } 116 .str { color: #FF00FF; } 117 .doc { color: #FF00FF; margin-bottom: 1em; } 118 .invocation a { color: white; text-decoration: none; } 119 120 .popup { 121 display: none; z-index: 2; 122 position: absolute; top: 1em; left: 0.5em; 123 padding: 0.2em; background-color: #000000; 124 } 125 126 .invocations { 127 padding: 0.5em; background-color: #770000; 128 clear: all; 129 } 130 131 .types { 132 padding: 0.5em; background-color: #0000FF; 133 float: right; 134 } 135 136 .raises { 137 padding: 0.5em; background-color: #7700FF; 138 float: right; 139 } 140 141 .scopes { 142 padding: 0.5em; background-color: #007700; 143 float: left; 144 } 145 146 .non-writes, .non-accesses { 147 padding: 0.5em; background-color: #FF0000; 148 float: right; 149 } 150 151 .op, 152 .name, 153 .attr, 154 .conditional, 155 .operator, 156 .iterator, 157 .returns 158 { 159 position: relative; 160 } 161 162 .op:hover > .popup, 163 .name:hover > .popup, 164 .attr:hover > .popup, 165 .conditional:hover > .popup, 166 .operator:hover > .popup, 167 .iterator:hover > .popup, 168 .returns:hover > .popup 169 { 170 display: block; 171 } 172 173 </style> 174 </head> 175 <body> 176 """ 177 178 html_footer = """</body> 179 </html> 180 """ 181 182 # Browser classes. 183 184 class Browser(ASTVisitor): 185 186 """ 187 A browsing visitor for AST nodes. 188 189 Covered: Add, And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign, 190 Break, CallFunc, Class, Compare, Const, Continue, Dict, Discard, 191 For, Function, Getattr, If, Keyword, Lambda, List, Module, Name, 192 Not, Or, Pass, Raise, Return, Slice, Stmt, Subscript, TryExcept, 193 TryFinally, Tuple, While. 194 195 Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Div, 196 Ellipsis, Exec, FloorDiv, From, Global, Import, Invert, LeftShift, 197 ListComp, ListCompFor, ListCompIf, Mod, Mul, Not, Or, Power, Print, 198 Printnl, RightShift, Sliceobj, Sub, UnaryAdd, UnarySub, Yield. 199 """ 200 201 def __init__(self, stream): 202 ASTVisitor.__init__(self) 203 self.visitor = self 204 self.stream = stream 205 206 def process(self, module): 207 self.stream.write(html_header) 208 self.dispatch(module) 209 self.stream.write(html_footer) 210 211 def dispatch(self, node): 212 try: 213 ASTVisitor.dispatch(self, node) 214 except ViewerError, exc: 215 exc.add(node) 216 raise 217 except Exception, exc: 218 raise ViewerError(exc, node) 219 220 def visitModule(self, node): 221 self.default(node) 222 223 # Statements. 224 225 def visitAssign(self, node): 226 self.stream.write("<div class='assign'>\n") 227 for lvalue in node.nodes: 228 self.dispatch(lvalue) 229 self.stream.write("=\n") 230 self.dispatch(node.expr) 231 self.stream.write("</div>\n") 232 233 def visitAugAssign(self, node): 234 self.stream.write("<div class='augassign'>\n") 235 self.dispatch(node.node) 236 self.stream.write("%s\n" % node.op) 237 self.dispatch(node.expr) 238 self.stream.write("</div>\n") 239 240 def visitBreak(self, node): 241 self.stream.write("<div class='break'>\n") 242 self._keyword("break") 243 self.stream.write("</div>\n") 244 245 def visitClass(self, node): 246 definition = node._node 247 structure = definition.expr.ref 248 self.stream.write("<div class='class' id='%s'>\n" % self._url(structure.full_name())) 249 self.stream.write("<div>\n") 250 self._keyword("class") 251 self._name_start(structure.name) 252 self._popup_start() 253 self._scopes(definition) 254 self._popup_end() 255 self._name_end() 256 bases = structure.bases 257 258 # Suppress the "object" class appearing alone. 259 260 if bases and not (len(bases) == 1 and bases[0].name == "object"): 261 self.stream.write("(") 262 first = 1 263 for base in bases: 264 if not first: 265 self.stream.write(",\n") 266 self._name_start(base.name) 267 self._popup_start() 268 self._types(base) 269 self._scopes(base) 270 self._popup_end() 271 self._name_end() 272 first = 0 273 self.stream.write(")") 274 275 self.stream.write(":\n") 276 self._comment(self._text(structure.full_name())) 277 self.stream.write("</div>\n") 278 279 self.stream.write("<div class='body'>\n") 280 self._doc(node) 281 self.dispatch(node.code) 282 self.stream.write("</div>\n") 283 self.stream.write("</div>\n") 284 285 def visitContinue(self, node): 286 self.stream.write("<div class='continue'>\n") 287 self._keyword("continue") 288 self.stream.write("</div>\n") 289 290 def visitDiscard(self, node): 291 self.stream.write("<div class='discard'>\n") 292 self.default(node) 293 self.stream.write("</div>\n") 294 295 def visitFor(self, node): 296 self.stream.write("<div class='if'>\n") 297 self.stream.write("<div>\n") 298 self.stream.write("<span class='iterator'>\n") 299 self._keyword("for") 300 self._popup_start() 301 self._invocations(node._node.code[1].expr.ref.code[0].body[0].code[0].expr) # Link to next call in subprogram. 302 self._popup_end() 303 self.stream.write("</span>\n") 304 self.dispatch(node.assign) 305 self.stream.write("<span class='iterator'>\n") 306 self._keyword("in") 307 self._popup_start() 308 self._invocations(node._node.code[0].expr) # Link to __iter__ call. 309 self._popup_end() 310 self.stream.write("</span>\n") 311 self.dispatch(node.list) 312 self.stream.write(":\n") 313 self.stream.write("</div>\n") 314 self.stream.write("<div class='body'>\n") 315 self.dispatch(node.body) 316 self.stream.write("</div>\n") 317 if node.else_ is not None: 318 self.stream.write("<div>\n") 319 self._keyword("else") 320 self.stream.write(":\n") 321 self.stream.write("</div>\n") 322 self.stream.write("<div class='body'>\n") 323 self.dispatch(node.else_) 324 self.stream.write("</div>\n") 325 self.stream.write("</div>\n") 326 327 def visitFunction(self, node): 328 definition = node._node 329 subprogram = definition.expr.ref 330 self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name())) 331 self.stream.write("<div>\n") 332 self._keyword("def") 333 self._name_start(subprogram.name) 334 self._popup_start() 335 self._scopes(definition) 336 self._raises(subprogram) 337 self._popup_end() 338 self._name_end() 339 self.stream.write("(") 340 self._parameters(subprogram) 341 self.stream.write(")") 342 self.stream.write(":\n") 343 self._comment(self._text(subprogram.full_name())) 344 self.stream.write("</div>\n") 345 346 self.stream.write("<div class='body'>\n") 347 self._doc(node) 348 self.dispatch(node.code) 349 self.stream.write("</div>\n") 350 self.stream.write("</div>\n") 351 352 def visitIf(self, node): 353 self.stream.write("<div class='if'>\n") 354 first = 1 355 conditional = node._node 356 for compare, stmt in node.tests: 357 self.stream.write("<div>\n") 358 self.stream.write("<span class='conditional'>\n") 359 if first: 360 self._keyword("if") 361 else: 362 self._keyword("elif") 363 self._popup_start() 364 self._invocations(conditional.test) 365 self._popup_end() 366 self.stream.write("</span>\n") 367 self.dispatch(compare) 368 self.stream.write(":\n") 369 self.stream.write("</div>\n") 370 self.stream.write("<div class='body'>\n") 371 self.dispatch(stmt) 372 self.stream.write("</div>\n") 373 if conditional.else_: 374 conditional = conditional.else_[0] 375 else: 376 conditional = None 377 first = 0 378 if node.else_ is not None: 379 self.stream.write("<div>\n") 380 self._keyword("else") 381 self.stream.write(":\n") 382 self.stream.write("</div>\n") 383 self.stream.write("<div class='body'>\n") 384 self.dispatch(node.else_) 385 self.stream.write("</div>\n") 386 self.stream.write("</div>\n") 387 388 def visitPass(self, node): 389 self.stream.write("<div class='pass'>\n") 390 self._keyword("pass") 391 self.stream.write("</div>\n") 392 393 def visitRaise(self, node): 394 self.stream.write("<div class='raise'>\n") 395 self._keyword("raise") 396 self.dispatch(node.expr1) 397 if node.expr2 is not None: 398 self.stream.write(",\n") 399 self.dispatch(node.expr2) 400 if node.expr3 is not None: 401 self.stream.write(",\n") 402 self.dispatch(node.expr3) 403 self.stream.write("</div>\n") 404 405 def visitReturn(self, node): 406 self.stream.write("<div class='return'>\n") 407 self.stream.write("<span class='returns'>\n") 408 self._keyword("return") 409 self._popup_start() 410 self._types(node._node) 411 self._popup_end() 412 self.stream.write("</span>\n") 413 self.dispatch(node.value) 414 self.stream.write("</div>\n") 415 416 def visitStmt(self, node): 417 self.stream.write("<div class='stmt'>\n") 418 self.default(node) 419 self.stream.write("</div>\n") 420 421 def visitTryExcept(self, node): 422 self.stream.write("<div class='tryexcept'>\n") 423 self.stream.write("<div>\n") 424 self._keyword("try") 425 self.stream.write(":\n") 426 self.stream.write("</div>\n") 427 self.stream.write("<div class='body'>\n") 428 self.dispatch(node.body) 429 self.stream.write("</div>\n") 430 for spec, assign, statement in node.handlers: 431 self.stream.write("<div>\n") 432 self._keyword("except") 433 if spec is not None: 434 self.dispatch(spec) 435 if assign is not None: 436 self.stream.write(",\n") 437 self.dispatch(assign) 438 self.stream.write(":\n") 439 self.stream.write("</div>\n") 440 self.stream.write("<div class='body'>\n") 441 self.dispatch(statement) 442 self.stream.write("</div>\n") 443 if node.else_ is not None: 444 self.stream.write("<div>\n") 445 self._keyword("else") 446 self.stream.write(":\n") 447 self.stream.write("</div>\n") 448 self.stream.write("<div class='body'>\n") 449 self.dispatch(node.else_) 450 self.stream.write("</div>\n") 451 self.stream.write("</div>\n") 452 453 def visitTryFinally(self, node): 454 self.stream.write("<div class='tryfinally'>\n") 455 self.stream.write("<div>\n") 456 self._keyword("try") 457 self.stream.write(":\n") 458 self.stream.write("</div>\n") 459 self.stream.write("<div class='body'>\n") 460 self.dispatch(node.body) 461 self.stream.write("</div>\n") 462 self.stream.write("<div>\n") 463 self._keyword("finally") 464 self.stream.write(":\n") 465 self.stream.write("</div>\n") 466 self.stream.write("<div class='body'>\n") 467 self.dispatch(node.final) 468 self.stream.write("</div>\n") 469 self.stream.write("</div>\n") 470 471 def visitWhile(self, node): 472 self.stream.write("<div class='while'>\n") 473 self.stream.write("<div>\n") 474 self.stream.write("<span class='conditional'>\n") 475 self._keyword("while") 476 self._popup_start() 477 self._invocations(node.test) 478 self._popup_end() 479 self.stream.write("</span>\n") 480 self.dispatch(node.test) 481 self.stream.write(":\n") 482 self.stream.write("</div>\n") 483 self.stream.write("<div class='body'>\n") 484 self.dispatch(node.body) 485 self.stream.write("</div>\n") 486 if node.else_ is not None: 487 self.stream.write("<div>\n") 488 self._keyword("else") 489 self.stream.write(":\n") 490 self.stream.write("</div>\n") 491 self.stream.write("<div class='body'>\n") 492 self.dispatch(node.else_) 493 self.stream.write("</div>\n") 494 self.stream.write("</div>\n") 495 496 # Expressions. 497 498 def _visitBinary(self, node, name, symbol): 499 self.stream.write("<span class='%s'>\n" % name) 500 self.dispatch(node.left) 501 self.stream.write("<span class='operator'>\n") 502 self.stream.write(symbol) 503 self._popup_start() 504 self.stream.write("<div class='invocations'>\n") 505 self._invocations_list(node._node.body[0].expr) # NOTE: See visitAdd in simplify. 506 self._invocations_list(node._node.handler[0].body[0].expr) # NOTE: See visitAdd in simplify. 507 self.stream.write("</div>\n") 508 self._popup_end() 509 self.stream.write("</span>\n") 510 self.dispatch(node.right) 511 self.stream.write("</span>") 512 513 def visitAdd(self, node): 514 self._visitBinary(node, "add", "+") 515 516 def visitAnd(self, node): 517 self.stream.write("<span class='and'>\n") 518 first = 1 519 for n in node.nodes: 520 if not first: 521 self._keyword("and") 522 self.dispatch(n) 523 first = 0 524 self.stream.write("</span>") 525 526 def visitAssAttr(self, node): 527 self.stream.write("<span class='assattr'>\n") 528 self.dispatch(node.expr) 529 self.stream.write("<span class='attr'>\n") 530 self.stream.write(".%s\n" % self._text(node.attrname)) 531 if hasattr(node, "_node"): 532 self._popup_start() 533 self._types(node._node) 534 self._scopes(node._node) 535 self._popup_end() 536 else: 537 raise ValueError, node 538 self.stream.write("</span>\n") 539 self.stream.write("</span>\n") 540 541 def visitAssList(self, node): 542 self.stream.write("<span class='list'>\n") 543 self.stream.write("[") 544 self._sequence(node) 545 self.stream.write("]\n") 546 self.stream.write("</span>\n") 547 548 def visitAssName(self, node): 549 if hasattr(node, "_node"): 550 self._name_start(node._node.name) 551 self._popup_start() 552 self._types(node._node.expr) 553 self._scopes(node._node) 554 self._popup_end() 555 self._name_end() 556 else: 557 raise ValueError, node 558 self._name(node.name) 559 560 def visitAssTuple(self, node): 561 self.stream.write("<span class='tuple'>\n") 562 self.stream.write("(") 563 self._sequence(node) 564 self.stream.write(")\n") 565 self.stream.write("</span>\n") 566 567 def visitCallFunc(self, node): 568 self.stream.write("<span class='callfunc'>\n") 569 self.dispatch(node.node) 570 self.stream.write("(") 571 first = 1 572 for arg in node.args: 573 if not first: 574 self.stream.write(",\n") 575 self.dispatch(arg) 576 first = 0 577 if node.star_args is not None: 578 if not first: 579 self.stream.write(", *\n") 580 self.dispatch(node.star_args) 581 first = 0 582 if node.dstar_args is not None: 583 if not first: 584 self.stream.write(", **\n") 585 self.dispatch(node.dstar_args) 586 first = 0 587 self.stream.write(")\n") 588 self.stream.write("</span>\n") 589 590 def visitCompare(self, node): 591 self.stream.write("<span class='compare'>\n") 592 self.dispatch(node.expr) 593 for (op_name, expr), _op in map(None, node.ops, node._ops): 594 self.stream.write("<span class='op'>\n") 595 self.stream.write(op_name) 596 self._popup_start() 597 self._op(op_name, _op) 598 self._popup_end() 599 self.stream.write("</span>\n") 600 self.dispatch(expr) 601 self.stream.write("</span>\n") 602 603 def visitConst(self, node): 604 self.stream.write(repr(node.value)) 605 606 def visitDiv(self, node): 607 self._visitBinary(node, "div", "/") 608 609 def visitGetattr(self, node): 610 self.stream.write("<span class='getattr'>\n") 611 self.dispatch(node.expr) 612 self.stream.write("<span class='attr'>\n") 613 self.stream.write(".%s\n" % self._text(node.attrname)) 614 if hasattr(node, "_node"): 615 self._popup_start() 616 self._types(node._node) 617 self._scopes(node._node) 618 self._popup_end() 619 else: 620 raise ValueError, node 621 self.stream.write("</span>\n") 622 self.stream.write("</span>\n") 623 624 def visitKeyword(self, node): 625 self.stream.write("<span class='keyword'>\n") 626 self.stream.write(node.name) 627 self.stream.write("=") 628 self.dispatch(node.expr) 629 self.stream.write("</span>\n") 630 631 def visitLambda(self, node): 632 definition = node._node 633 subprogram = definition.expr.ref 634 self.stream.write("<span class='lambda'>\n") 635 self._keyword("lambda") 636 self._parameters(subprogram) 637 self.dispatch(node.code) 638 self.stream.write("</span>\n") 639 640 visitList = visitAssList 641 642 def visitMul(self, node): 643 self._visitBinary(node, "mul", "*") 644 645 def visitName(self, node): 646 if hasattr(node, "_node"): 647 self._name_start(node._node.name) 648 self._popup_start() 649 self._types(node._node) 650 self._scopes(node._node) 651 self._popup_end() 652 self._name_end() 653 else: 654 raise ValueError, node 655 self._name(node.name) 656 657 def visitNot(self, node): 658 self.stream.write("<span class='not'>\n") 659 self._keyword("not") 660 self.dispatch(node.expr) 661 self.stream.write("</span>") 662 663 def visitOr(self, node): 664 self.stream.write("<span class='or'>\n") 665 first = 1 666 for n in node.nodes: 667 if not first: 668 self._keyword("or") 669 self.dispatch(n) 670 first = 0 671 self.stream.write("</span>") 672 673 def visitSlice(self, node): 674 self.stream.write("<span class='slice'>\n") 675 self.dispatch(node.expr) 676 self.stream.write("[") 677 if node.lower: 678 self.dispatch(node.lower) 679 self.stream.write(":") 680 if node.upper: 681 self.dispatch(node.upper) 682 # NOTE: Step? 683 self.stream.write("]") 684 self.stream.write("</span>\n") 685 686 def visitSub(self, node): 687 self._visitBinary(node, "sub", "-") 688 689 def visitSubscript(self, node): 690 self.stream.write("<span class='subscript'>\n") 691 self.dispatch(node.expr) 692 self.stream.write("[") 693 first = 1 694 for sub in node.subs: 695 if not first: 696 self.stream.write(", ") 697 self.dispatch(sub) 698 first = 0 699 self.stream.write("]") 700 self.stream.write("</span>\n") 701 702 visitTuple = visitAssTuple 703 704 # Output preparation methods. 705 706 def _text(self, text): 707 return text.replace("&", "&").replace("<", "<").replace(">", ">") 708 709 def _attr(self, attr): 710 return self._text(attr).replace("'", "'").replace('"', """) 711 712 def _url(self, url): 713 return self._attr(url).replace("#", "%23").replace("-", "%2d") 714 715 def _comment(self, comment): 716 self.stream.write("<span class='comment'># %s</span>\n" % comment) 717 718 def _keyword(self, kw): 719 self.stream.write("<span class='keyword'>%s</span> " % kw) 720 721 def _doc(self, node): 722 if node.doc is not None: 723 self.stream.write("<pre class='doc'>\n") 724 self.stream.write('"""') 725 output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"')) 726 self.stream.write(self._text(output)) 727 self.stream.write('"""') 728 self.stream.write("</pre>\n") 729 730 def _sequence(self, node): 731 first = 1 732 for n in node.nodes: 733 if not first: 734 self.stream.write(",\n") 735 self.dispatch(n) 736 first = 0 737 738 def _parameters(self, subprogram): 739 first = 1 740 for param, default in subprogram.params: 741 if not first: 742 self.stream.write(",\n") 743 self._parameter(subprogram, param, default) 744 first = 0 745 if subprogram.star is not None: 746 if not first: 747 self.stream.write(", *\n") 748 param, default = subprogram.star 749 self._parameter(subprogram, param, default) 750 first = 0 751 if subprogram.dstar is not None: 752 if not first: 753 self.stream.write(", **\n") 754 param, default = subprogram.dstar 755 self._parameter(subprogram, param, default) 756 first = 0 757 758 def _parameter(self, subprogram, param, default): 759 self._name_start(param) 760 if hasattr(subprogram, "paramtypes"): 761 self._popup_start() 762 self._types_list(subprogram.paramtypes[param]) 763 self._popup_end() 764 self._name_end() 765 if default is not None and default.original is not None: 766 self.stream.write("=\n") 767 self.dispatch(default.original) 768 769 def _name(self, name): 770 self.stream.write("<span class='name'>%s</span>\n" % name) 771 772 def _name_start(self, name): 773 self.stream.write("<span class='name'>%s\n" % name) 774 775 def _name_end(self): 776 self.stream.write("</span>\n") 777 778 def _popup_start(self): 779 self.stream.write("<span class='popup'>\n") 780 781 def _popup_end(self): 782 self.stream.write("</span>\n") 783 784 def _op(self, op_name, op): 785 if op is not None: 786 if isinstance(op, Not): 787 self._invocations(op.expr) 788 else: 789 self._invocations(op) 790 791 def _invocations(self, node): 792 self.stream.write("<div class='invocations'>\n") 793 self._invocations_list(node) 794 self.stream.write("</div>\n") 795 796 def _invocations_list(self, node): 797 if hasattr(node, "invocations"): 798 for invocation in node.invocations: 799 fn = invocation.full_name() 800 module = invocation.module.name 801 name = invocation.name 802 structures = [x.name for x in invocation.structures] 803 self.stream.write("<div class='invocation'>") 804 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn))) 805 self.stream.write(self._text(".".join([module] + structures + [name]))) 806 self.stream.write("</a>") 807 self.stream.write("</div>\n") 808 809 def _types(self, node): 810 if hasattr(node, "types"): 811 if node.types: 812 self._types_list(node.types) 813 else: 814 self.stream.write("<div class='types'>\n") 815 self.stream.write("no types\n") 816 self.stream.write("</div>\n") 817 else: 818 self.stream.write("<div class='types'>\n") 819 self.stream.write("unvisited\n") 820 self.stream.write("</div>\n") 821 822 def _types_list(self, types, style_class="types"): 823 self.stream.write("<div class='%s'>\n" % style_class) 824 for type in types: 825 fn = type.type.full_name() 826 self.stream.write("<div class='type'>") 827 self.stream.write(self._text(fn)) 828 self.stream.write("</div>\n") 829 self.stream.write("</div>\n") 830 831 def _raises(self, node): 832 if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises: 833 self._types_list(node.namespace.raises, style_class="raises") 834 835 def _scopes(self, node): 836 if not isinstance(node, LoadName): 837 if hasattr(node, "writes") and node.writes or hasattr(node, "accesses") and node.accesses: 838 self.stream.write("<div class='scopes'>\n") 839 for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): 840 fn = ref.full_name() 841 self.stream.write("<div class='scope'>") 842 self.stream.write(self._text(fn)) 843 self.stream.write("</div>\n") 844 self.stream.write("</div>\n") 845 if hasattr(node, "non_accesses") and node.non_accesses: 846 self._types_list(node.non_accesses, style_class="non-accesses") 847 if hasattr(node, "non_writes") and node.non_writes: 848 self._types_list(node.non_writes, style_class="non-writes") 849 850 # Utility functions. 851 852 def flatten(lists): 853 result = [] 854 for l in lists: 855 for attr in l: 856 if attr not in result: 857 result.append(attr) 858 return result 859 860 # Convenience functions. 861 862 def view(module, stream=None): 863 viewer = Viewer(stream or sys.stdout) 864 viewer.process(module.original) 865 866 def browse(module, stream=None): 867 browser = Browser(stream or sys.stdout) 868 browser.process(module.original) 869 870 def makedoc(module, filename): 871 stream = open(filename, "wb") 872 try: 873 browser = Browser(stream) 874 browser.process(module.original) 875 finally: 876 stream.close() 877 878 # vim: tabstop=4 expandtab shiftwidth=4