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