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