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