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