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 border: 2px solid #dddddd; 125 } 126 127 .invocations { 128 padding: 0.5em; background-color: #770000; 129 clear: all; 130 } 131 132 .types { 133 padding: 0.5em; background-color: #0000FF; 134 float: right; 135 } 136 137 .raises { 138 padding: 0.5em; background-color: #7700FF; 139 float: right; 140 } 141 142 .scopes { 143 padding: 0.5em; background-color: #007700; 144 float: left; 145 } 146 147 .non-writes, .non-accesses { 148 padding: 0.5em; background-color: #FF0000; 149 float: right; 150 } 151 152 .op, 153 .name, 154 .attr, 155 .conditional, 156 .operator, 157 .iterator, 158 .returns 159 { 160 position: relative; 161 } 162 163 .op:hover > .popup, 164 .name:hover > .popup, 165 .attr:hover > .popup, 166 .conditional:hover > .popup, 167 .operator:hover > .popup, 168 .iterator:hover > .popup, 169 .returns:hover > .popup 170 { 171 display: block; 172 } 173 174 </style> 175 </head> 176 <body> 177 """ 178 179 html_footer = """</body> 180 </html> 181 """ 182 183 # Browser classes. 184 185 class Browser(ASTVisitor): 186 187 """ 188 A browsing visitor for AST nodes. 189 190 Covered: Add, And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign, 191 Break, CallFunc, Class, Compare, Const, Continue, Dict, Discard, 192 Div, FloorDiv, For, Function, Getattr, If, Keyword, Lambda, List, 193 Module, Mul, Name, Not, Or, Pass, Raise, Return, Slice, Stmt, Sub, 194 Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, UnarySub, While. 195 196 Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Ellipsis, 197 Exec, From, Global, Import, Invert, LeftShift, ListComp, 198 ListCompFor, ListCompIf, Mod, Power, Print, Printnl, RightShift, 199 Sliceobj, Yield. 200 """ 201 202 def __init__(self, stream): 203 ASTVisitor.__init__(self) 204 self.visitor = self 205 self.stream = stream 206 207 def process(self, module): 208 self.stream.write(html_header) 209 self.dispatch(module) 210 self.stream.write(html_footer) 211 212 def dispatch(self, node): 213 try: 214 ASTVisitor.dispatch(self, node) 215 except ViewerError, exc: 216 exc.add(node) 217 raise 218 except Exception, exc: 219 raise ViewerError(exc, node) 220 221 def visitModule(self, node): 222 self.default(node) 223 224 # Statements. 225 226 def visitAssign(self, node): 227 self.stream.write("<div class='assign'>\n") 228 for lvalue in node.nodes: 229 self.dispatch(lvalue) 230 self.stream.write("=\n") 231 self.dispatch(node.expr) 232 self.stream.write("</div>\n") 233 234 def visitAugAssign(self, node): 235 self.stream.write("<div class='augassign'>\n") 236 self.dispatch(node.node) 237 self.stream.write("%s\n" % node.op) 238 self.dispatch(node.expr) 239 self.stream.write("</div>\n") 240 241 def visitBreak(self, node): 242 self.stream.write("<div class='break'>\n") 243 self._keyword("break") 244 self.stream.write("</div>\n") 245 246 def visitClass(self, node): 247 definition = node._node 248 structure = definition.expr.ref 249 self.stream.write("<div class='class' id='%s'>\n" % self._url(structure.full_name())) 250 self.stream.write("<div>\n") 251 self._keyword("class") 252 self._name_start(structure.name) 253 self._popup_start() 254 self._scopes(definition) 255 self._popup_end() 256 self._name_end() 257 bases = structure.bases 258 259 # Suppress the "object" class appearing alone. 260 261 if bases and not (len(bases) == 1 and bases[0].name == "object"): 262 self.stream.write("(") 263 first = 1 264 for base in bases: 265 if not first: 266 self.stream.write(",\n") 267 self._name_start(base.name) 268 self._popup_start() 269 self._types(base) 270 self._scopes(base) 271 self._popup_end() 272 self._name_end() 273 first = 0 274 self.stream.write(")") 275 276 self.stream.write(":\n") 277 self._comment(self._text(structure.full_name())) 278 self.stream.write("</div>\n") 279 280 self.stream.write("<div class='body'>\n") 281 self._doc(node) 282 self.dispatch(node.code) 283 self.stream.write("</div>\n") 284 self.stream.write("</div>\n") 285 286 def visitContinue(self, node): 287 self.stream.write("<div class='continue'>\n") 288 self._keyword("continue") 289 self.stream.write("</div>\n") 290 291 def visitDiscard(self, node): 292 self.stream.write("<div class='discard'>\n") 293 self.default(node) 294 self.stream.write("</div>\n") 295 296 def visitFor(self, node): 297 self.stream.write("<div class='if'>\n") 298 self.stream.write("<div>\n") 299 self.stream.write("<span class='iterator'>\n") 300 self._keyword("for") 301 self._popup_start() 302 self._invocations(node._node.code[1].expr.ref.code[0].body[0].code[0].expr) # Link to next call in subprogram. 303 self._popup_end() 304 self.stream.write("</span>\n") 305 self.dispatch(node.assign) 306 self.stream.write("<span class='iterator'>\n") 307 self._keyword("in") 308 self._popup_start() 309 self._invocations(node._node.code[0].expr) # Link to __iter__ call. 310 self._popup_end() 311 self.stream.write("</span>\n") 312 self.dispatch(node.list) 313 self.stream.write(":\n") 314 self.stream.write("</div>\n") 315 self.stream.write("<div class='body'>\n") 316 self.dispatch(node.body) 317 self.stream.write("</div>\n") 318 if node.else_ is not None: 319 self.stream.write("<div>\n") 320 self._keyword("else") 321 self.stream.write(":\n") 322 self.stream.write("</div>\n") 323 self.stream.write("<div class='body'>\n") 324 self.dispatch(node.else_) 325 self.stream.write("</div>\n") 326 self.stream.write("</div>\n") 327 328 def visitFunction(self, node): 329 definition = node._node 330 subprogram = definition.expr.ref 331 self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name())) 332 self.stream.write("<div>\n") 333 self._keyword("def") 334 self._name_start(subprogram.name) 335 self._popup_start() 336 self._scopes(definition) 337 self._raises(subprogram) 338 self._popup_end() 339 self._name_end() 340 self.stream.write("(") 341 self._parameters(subprogram) 342 self.stream.write(")") 343 self.stream.write(":\n") 344 self._comment(self._text(subprogram.full_name())) 345 self.stream.write("</div>\n") 346 347 self.stream.write("<div class='body'>\n") 348 self._doc(node) 349 self.dispatch(node.code) 350 self.stream.write("</div>\n") 351 self.stream.write("</div>\n") 352 353 def visitIf(self, node): 354 self.stream.write("<div class='if'>\n") 355 first = 1 356 conditional = node._node 357 for compare, stmt in node.tests: 358 self.stream.write("<div>\n") 359 self.stream.write("<span class='conditional'>\n") 360 if first: 361 self._keyword("if") 362 else: 363 self._keyword("elif") 364 self._popup_start() 365 self._invocations(conditional.test) 366 self._popup_end() 367 self.stream.write("</span>\n") 368 self.dispatch(compare) 369 self.stream.write(":\n") 370 self.stream.write("</div>\n") 371 self.stream.write("<div class='body'>\n") 372 self.dispatch(stmt) 373 self.stream.write("</div>\n") 374 if conditional.else_: 375 conditional = conditional.else_[0] 376 else: 377 conditional = None 378 first = 0 379 if node.else_ is not None: 380 self.stream.write("<div>\n") 381 self._keyword("else") 382 self.stream.write(":\n") 383 self.stream.write("</div>\n") 384 self.stream.write("<div class='body'>\n") 385 self.dispatch(node.else_) 386 self.stream.write("</div>\n") 387 self.stream.write("</div>\n") 388 389 def visitPass(self, node): 390 self.stream.write("<div class='pass'>\n") 391 self._keyword("pass") 392 self.stream.write("</div>\n") 393 394 def visitRaise(self, node): 395 self.stream.write("<div class='raise'>\n") 396 self._keyword("raise") 397 self.dispatch(node.expr1) 398 if node.expr2 is not None: 399 self.stream.write(",\n") 400 self.dispatch(node.expr2) 401 if node.expr3 is not None: 402 self.stream.write(",\n") 403 self.dispatch(node.expr3) 404 self.stream.write("</div>\n") 405 406 def visitReturn(self, node): 407 self.stream.write("<div class='return'>\n") 408 self.stream.write("<span class='returns'>\n") 409 self._keyword("return") 410 self._popup_start() 411 self._types(node._node) 412 self._popup_end() 413 self.stream.write("</span>\n") 414 self.dispatch(node.value) 415 self.stream.write("</div>\n") 416 417 def visitStmt(self, node): 418 self.stream.write("<div class='stmt'>\n") 419 self.default(node) 420 self.stream.write("</div>\n") 421 422 def visitTryExcept(self, node): 423 self.stream.write("<div class='tryexcept'>\n") 424 self.stream.write("<div>\n") 425 self._keyword("try") 426 self.stream.write(":\n") 427 self.stream.write("</div>\n") 428 self.stream.write("<div class='body'>\n") 429 self.dispatch(node.body) 430 self.stream.write("</div>\n") 431 for spec, assign, statement in node.handlers: 432 self.stream.write("<div>\n") 433 self._keyword("except") 434 if spec is not None: 435 self.dispatch(spec) 436 if assign is not None: 437 self.stream.write(",\n") 438 self.dispatch(assign) 439 self.stream.write(":\n") 440 self.stream.write("</div>\n") 441 self.stream.write("<div class='body'>\n") 442 self.dispatch(statement) 443 self.stream.write("</div>\n") 444 if node.else_ is not None: 445 self.stream.write("<div>\n") 446 self._keyword("else") 447 self.stream.write(":\n") 448 self.stream.write("</div>\n") 449 self.stream.write("<div class='body'>\n") 450 self.dispatch(node.else_) 451 self.stream.write("</div>\n") 452 self.stream.write("</div>\n") 453 454 def visitTryFinally(self, node): 455 self.stream.write("<div class='tryfinally'>\n") 456 self.stream.write("<div>\n") 457 self._keyword("try") 458 self.stream.write(":\n") 459 self.stream.write("</div>\n") 460 self.stream.write("<div class='body'>\n") 461 self.dispatch(node.body) 462 self.stream.write("</div>\n") 463 self.stream.write("<div>\n") 464 self._keyword("finally") 465 self.stream.write(":\n") 466 self.stream.write("</div>\n") 467 self.stream.write("<div class='body'>\n") 468 self.dispatch(node.final) 469 self.stream.write("</div>\n") 470 self.stream.write("</div>\n") 471 472 def visitWhile(self, node): 473 self.stream.write("<div class='while'>\n") 474 self.stream.write("<div>\n") 475 self.stream.write("<span class='conditional'>\n") 476 self._keyword("while") 477 self._popup_start() 478 self._invocations(node.test) 479 self._popup_end() 480 self.stream.write("</span>\n") 481 self.dispatch(node.test) 482 self.stream.write(":\n") 483 self.stream.write("</div>\n") 484 self.stream.write("<div class='body'>\n") 485 self.dispatch(node.body) 486 self.stream.write("</div>\n") 487 if node.else_ is not None: 488 self.stream.write("<div>\n") 489 self._keyword("else") 490 self.stream.write(":\n") 491 self.stream.write("</div>\n") 492 self.stream.write("<div class='body'>\n") 493 self.dispatch(node.else_) 494 self.stream.write("</div>\n") 495 self.stream.write("</div>\n") 496 497 # Expression-related helper methods. 498 499 def _visitBinary(self, node, name, symbol): 500 self.stream.write("<span class='%s'>\n" % name) 501 self.dispatch(node.left) 502 self.stream.write("<span class='operator'>\n") 503 self.stream.write(symbol) 504 self._popup_start() 505 self.stream.write("<div class='invocations'>\n") 506 self._invocations_list(node._node.body[0].expr) # NOTE: See visitAdd in simplify. 507 self._invocations_list(node._node.handler[0].body[0].expr) # NOTE: See visitAdd in simplify. 508 self.stream.write("</div>\n") 509 self._popup_end() 510 self.stream.write("</span>\n") 511 self.dispatch(node.right) 512 self.stream.write("</span>") 513 514 def _visitUnary(self, node, name, symbol): 515 self.stream.write("<span class='%s'>\n" % name) 516 self.stream.write("<span class='operator'>\n") 517 self.stream.write(symbol) 518 self._popup_start() 519 self.stream.write("<div class='invocations'>\n") 520 self._invocations_list(node._node) # NOTE: See _visitUnary in simplify. 521 self.stream.write("</div>\n") 522 self._popup_end() 523 self.stream.write("</span>\n") 524 self.dispatch(node.expr) 525 self.stream.write("</span>") 526 527 # Expressions. 528 529 def visitAdd(self, node): 530 self._visitBinary(node, "add", "+") 531 532 def visitAnd(self, node): 533 self.stream.write("<span class='and'>\n") 534 first = 1 535 for n in node.nodes: 536 if not first: 537 self._keyword("and") 538 self.dispatch(n) 539 first = 0 540 self.stream.write("</span>") 541 542 def visitAssAttr(self, node): 543 self.stream.write("<span class='assattr'>\n") 544 self.dispatch(node.expr) 545 self.stream.write("<span class='attr'>\n") 546 self.stream.write(".%s\n" % self._text(node.attrname)) 547 if hasattr(node, "_node"): 548 self._popup_start() 549 self._types(node._node) 550 self._scopes(node._node) 551 self._popup_end() 552 else: 553 raise ValueError, node 554 self.stream.write("</span>\n") 555 self.stream.write("</span>\n") 556 557 def visitAssList(self, node): 558 self.stream.write("<span class='list'>\n") 559 self.stream.write("[") 560 self._sequence(node) 561 self.stream.write("]\n") 562 self.stream.write("</span>\n") 563 564 def visitAssName(self, node): 565 if hasattr(node, "_node"): 566 self._name_start(node._node.name) 567 self._popup_start() 568 self._types(node._node.expr) 569 self._scopes(node._node) 570 self._popup_end() 571 self._name_end() 572 else: 573 raise ValueError, node 574 self._name(node.name) 575 576 def visitAssTuple(self, node): 577 self.stream.write("<span class='tuple'>\n") 578 self.stream.write("(") 579 self._sequence(node) 580 self.stream.write(")\n") 581 self.stream.write("</span>\n") 582 583 def visitCallFunc(self, node): 584 self.stream.write("<span class='callfunc'>\n") 585 self.dispatch(node.node) 586 self.stream.write("(") 587 first = 1 588 for arg in node.args: 589 if not first: 590 self.stream.write(",\n") 591 self.dispatch(arg) 592 first = 0 593 if node.star_args is not None: 594 if not first: 595 self.stream.write(", *\n") 596 self.dispatch(node.star_args) 597 first = 0 598 if node.dstar_args is not None: 599 if not first: 600 self.stream.write(", **\n") 601 self.dispatch(node.dstar_args) 602 first = 0 603 self.stream.write(")\n") 604 self.stream.write("</span>\n") 605 606 def visitCompare(self, node): 607 self.stream.write("<span class='compare'>\n") 608 self.dispatch(node.expr) 609 for (op_name, expr), _op in map(None, node.ops, node._ops): 610 self.stream.write("<span class='op'>\n") 611 self.stream.write(op_name) 612 self._popup_start() 613 self._op(op_name, _op) 614 self._popup_end() 615 self.stream.write("</span>\n") 616 self.dispatch(expr) 617 self.stream.write("</span>\n") 618 619 def visitConst(self, node): 620 self.stream.write(repr(node.value)) 621 622 def visitDiv(self, node): 623 self._visitBinary(node, "div", "/") 624 625 def visitFloorDiv(self, node): 626 self._visitBinary(node, "floordiv", "//") 627 628 def visitGetattr(self, node): 629 self.stream.write("<span class='getattr'>\n") 630 self.dispatch(node.expr) 631 self.stream.write("<span class='attr'>\n") 632 self.stream.write(".%s\n" % self._text(node.attrname)) 633 if hasattr(node, "_node"): 634 self._popup_start() 635 self._types(node._node) 636 self._scopes(node._node) 637 self._popup_end() 638 else: 639 raise ValueError, node 640 self.stream.write("</span>\n") 641 self.stream.write("</span>\n") 642 643 def visitKeyword(self, node): 644 self.stream.write("<span class='keyword'>\n") 645 self.stream.write(node.name) 646 self.stream.write("=") 647 self.dispatch(node.expr) 648 self.stream.write("</span>\n") 649 650 def visitLambda(self, node): 651 definition = node._node 652 subprogram = definition.expr.ref 653 self.stream.write("<span class='lambda'>\n") 654 self._keyword("lambda") 655 self._parameters(subprogram) 656 self.dispatch(node.code) 657 self.stream.write("</span>\n") 658 659 visitList = visitAssList 660 661 def visitMul(self, node): 662 self._visitBinary(node, "mul", "*") 663 664 def visitName(self, node): 665 if hasattr(node, "_node"): 666 self._name_start(node._node.name) 667 self._popup_start() 668 self._types(node._node) 669 self._scopes(node._node) 670 self._popup_end() 671 self._name_end() 672 else: 673 raise ValueError, node 674 self._name(node.name) 675 676 def visitNot(self, node): 677 self.stream.write("<span class='not'>\n") 678 self._keyword("not") 679 self.dispatch(node.expr) 680 self.stream.write("</span>") 681 682 def visitOr(self, node): 683 self.stream.write("<span class='or'>\n") 684 first = 1 685 for n in node.nodes: 686 if not first: 687 self._keyword("or") 688 self.dispatch(n) 689 first = 0 690 self.stream.write("</span>") 691 692 def visitSlice(self, node): 693 self.stream.write("<span class='slice'>\n") 694 self.dispatch(node.expr) 695 self.stream.write("[") 696 if node.lower: 697 self.dispatch(node.lower) 698 self.stream.write(":") 699 if node.upper: 700 self.dispatch(node.upper) 701 # NOTE: Step? 702 self.stream.write("]") 703 self.stream.write("</span>\n") 704 705 def visitSub(self, node): 706 self._visitBinary(node, "sub", "-") 707 708 def visitSubscript(self, node): 709 self.stream.write("<span class='subscript'>\n") 710 self.dispatch(node.expr) 711 self.stream.write("[") 712 first = 1 713 for sub in node.subs: 714 if not first: 715 self.stream.write(", ") 716 self.dispatch(sub) 717 first = 0 718 self.stream.write("]") 719 self.stream.write("</span>\n") 720 721 visitTuple = visitAssTuple 722 723 def visitUnaryAdd(self, node): 724 self._visitUnary(node, "add", "+") 725 726 def visitUnarySub(self, node): 727 self._visitUnary(node, "sub", "-") 728 729 # Output preparation methods. 730 731 def _text(self, text): 732 return text.replace("&", "&").replace("<", "<").replace(">", ">") 733 734 def _attr(self, attr): 735 return self._text(attr).replace("'", "'").replace('"', """) 736 737 def _url(self, url): 738 return self._attr(url).replace("#", "%23").replace("-", "%2d") 739 740 def _comment(self, comment): 741 self.stream.write("<span class='comment'># %s</span>\n" % comment) 742 743 def _keyword(self, kw): 744 self.stream.write("<span class='keyword'>%s</span> " % kw) 745 746 def _doc(self, node): 747 if node.doc is not None: 748 self.stream.write("<pre class='doc'>\n") 749 self.stream.write('"""') 750 output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"')) 751 self.stream.write(self._text(output)) 752 self.stream.write('"""') 753 self.stream.write("</pre>\n") 754 755 def _sequence(self, node): 756 first = 1 757 for n in node.nodes: 758 if not first: 759 self.stream.write(",\n") 760 self.dispatch(n) 761 first = 0 762 763 def _parameters(self, subprogram): 764 first = 1 765 for param, default in subprogram.params: 766 if not first: 767 self.stream.write(",\n") 768 self._parameter(subprogram, param, default) 769 first = 0 770 if subprogram.star is not None: 771 if not first: 772 self.stream.write(", *\n") 773 param, default = subprogram.star 774 self._parameter(subprogram, param, default) 775 first = 0 776 if subprogram.dstar is not None: 777 if not first: 778 self.stream.write(", **\n") 779 param, default = subprogram.dstar 780 self._parameter(subprogram, param, default) 781 first = 0 782 783 def _parameter(self, subprogram, param, default): 784 self._name_start(param) 785 if hasattr(subprogram, "paramtypes"): 786 self._popup_start() 787 self._types_list(subprogram.paramtypes[param]) 788 self._popup_end() 789 self._name_end() 790 if default is not None and default.original is not None: 791 self.stream.write("=\n") 792 self.dispatch(default.original) 793 794 def _name(self, name): 795 self.stream.write("<span class='name'>%s</span>\n" % name) 796 797 def _name_start(self, name): 798 self.stream.write("<span class='name'>%s\n" % name) 799 800 def _name_end(self): 801 self.stream.write("</span>\n") 802 803 def _popup_start(self): 804 self.stream.write("<span class='popup'>\n") 805 806 def _popup_end(self): 807 self.stream.write("</span>\n") 808 809 def _op(self, op_name, op): 810 if op is not None: 811 if isinstance(op, Not): 812 self._invocations(op.expr) 813 else: 814 self._invocations(op) 815 816 def _invocations(self, node): 817 self.stream.write("<div class='invocations'>\n") 818 self._invocations_list(node) 819 self.stream.write("</div>\n") 820 821 def _invocations_list(self, node): 822 if hasattr(node, "invocations"): 823 for invocation in node.invocations: 824 fn = invocation.full_name() 825 module = invocation.module.name 826 name = invocation.name 827 structures = [x.name for x in invocation.structures] 828 self.stream.write("<div class='invocation'>") 829 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn))) 830 self.stream.write(self._text(".".join([module] + structures + [name]))) 831 self.stream.write("</a>") 832 self.stream.write("</div>\n") 833 834 def _types(self, node): 835 if hasattr(node, "types"): 836 if node.types: 837 self._types_list(node.types) 838 else: 839 self.stream.write("<div class='types'>\n") 840 self.stream.write("no types\n") 841 self.stream.write("</div>\n") 842 else: 843 self.stream.write("<div class='types'>\n") 844 self.stream.write("unvisited\n") 845 self.stream.write("</div>\n") 846 847 def _types_list(self, types, style_class="types"): 848 self.stream.write("<div class='%s'>\n" % style_class) 849 for type in types: 850 fn = type.type.full_name() 851 self.stream.write("<div class='type'>") 852 self.stream.write(self._text(fn)) 853 self.stream.write("</div>\n") 854 self.stream.write("</div>\n") 855 856 def _raises(self, node): 857 if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises: 858 self._types_list(node.namespace.raises, style_class="raises") 859 860 def _scopes(self, node): 861 if not isinstance(node, LoadName): 862 if hasattr(node, "writes") and node.writes or hasattr(node, "accesses") and node.accesses: 863 self.stream.write("<div class='scopes'>\n") 864 for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): 865 fn = ref.full_name() 866 self.stream.write("<div class='scope'>") 867 self.stream.write(self._text(fn)) 868 self.stream.write("</div>\n") 869 self.stream.write("</div>\n") 870 if hasattr(node, "non_accesses") and node.non_accesses: 871 self._types_list(node.non_accesses, style_class="non-accesses") 872 if hasattr(node, "non_writes") and node.non_writes: 873 self._types_list(node.non_writes, style_class="non-writes") 874 875 # Utility functions. 876 877 def flatten(lists): 878 result = [] 879 for l in lists: 880 for attr in l: 881 if attr not in result: 882 result.append(attr) 883 return result 884 885 # Convenience functions. 886 887 def view(module, stream=None): 888 viewer = Viewer(stream or sys.stdout) 889 viewer.process(module.original) 890 891 def browse(module, stream=None): 892 browser = Browser(stream or sys.stdout) 893 browser.process(module.original) 894 895 def makedoc(module, filename): 896 stream = open(filename, "wb") 897 try: 898 browser = Browser(stream) 899 browser.process(module.original) 900 finally: 901 stream.close() 902 903 # vim: tabstop=4 expandtab shiftwidth=4