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