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-top: 4em; 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, Function, Getattr, Global, If, Keyword, Lambda, 130 List, Module, Mul, Name, Not, Or, Pass, Print, Printnl, Raise, 131 Return, Slice, Stmt, Sub, Subscript, TryExcept, TryFinally, Tuple, 132 UnaryAdd, UnarySub, While. 133 134 Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Ellipsis, 135 Exec, From, Import, Invert, LeftShift, ListComp, ListCompFor, 136 ListCompIf, Mod, Power, 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 visitFunction(self, node): 257 definition = node._node 258 subprogram = definition.expr.ref 259 self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name())) 260 self.stream.write("<div>\n") 261 self._keyword("def") 262 self._name_start(subprogram.name) 263 self._popup_start() 264 self._scopes(definition) 265 self._raises(subprogram) 266 self._popup_end() 267 self._name_end() 268 self.stream.write("(") 269 self._parameters(subprogram) 270 self.stream.write(")") 271 self.stream.write(":\n") 272 self._comment(self._text(subprogram.full_name())) 273 self.stream.write("</div>\n") 274 275 self.stream.write("<div class='body'>\n") 276 self._doc(node) 277 self.dispatch(node.code) 278 self.stream.write("</div>\n") 279 self.stream.write("</div>\n") 280 281 def visitGlobal(self, node): 282 self.stream.write("<div class='global'>\n") 283 self._keyword("global") 284 first = 1 285 for name in node.names: 286 if not first: 287 self.stream.write(",\n") 288 self.stream.write(name) 289 first = 0 290 self.stream.write("</div>\n") 291 292 def visitIf(self, node): 293 self.stream.write("<div class='if'>\n") 294 first = 1 295 conditional = node._node 296 for compare, stmt in node.tests: 297 self.stream.write("<div>\n") 298 self.stream.write("<span class='conditional'>\n") 299 if first: 300 self._keyword("if") 301 else: 302 self._keyword("elif") 303 self._popup_start() 304 self._invocations(conditional.test) 305 self._popup_end() 306 self.stream.write("</span>\n") 307 self.dispatch(compare) 308 self.stream.write(":\n") 309 self.stream.write("</div>\n") 310 self.stream.write("<div class='body'>\n") 311 self.dispatch(stmt) 312 self.stream.write("</div>\n") 313 if conditional.else_: 314 conditional = conditional.else_[0] 315 else: 316 conditional = None 317 first = 0 318 if node.else_ is not None: 319 self.stream.write("<div>\n") 320 self._keyword("else") 321 self.stream.write(":\n") 322 self.stream.write("</div>\n") 323 self.stream.write("<div class='body'>\n") 324 self.dispatch(node.else_) 325 self.stream.write("</div>\n") 326 self.stream.write("</div>\n") 327 328 def visitPass(self, node): 329 self.stream.write("<div class='pass'>\n") 330 self._keyword("pass") 331 self.stream.write("</div>\n") 332 333 def visitPrint(self, node): 334 self.stream.write("<div class='print'>\n") 335 self._keyword("print") 336 if node.dest is not None: 337 self.stream.write(">>\n") 338 self.dispatch(node.dest) 339 for n in node.nodes: 340 self.dispatch(n) 341 self.stream.write(",\n") 342 self.stream.write("</div>\n") 343 344 def visitPrintnl(self, node): 345 self.stream.write("<div class='printnl'>\n") 346 self._keyword("print") 347 if node.dest is not None: 348 self.stream.write(">>\n") 349 self.dispatch(node.dest) 350 first = 1 351 for n in node.nodes: 352 if not first: 353 self.stream.write(",\n") 354 self.dispatch(n) 355 first = 0 356 self.stream.write("</div>\n") 357 358 def visitRaise(self, node): 359 self.stream.write("<div class='raise'>\n") 360 self._keyword("raise") 361 self.dispatch(node.expr1) 362 if node.expr2 is not None: 363 self.stream.write(",\n") 364 self.dispatch(node.expr2) 365 if node.expr3 is not None: 366 self.stream.write(",\n") 367 self.dispatch(node.expr3) 368 self.stream.write("</div>\n") 369 370 def visitReturn(self, node): 371 self.stream.write("<div class='return'>\n") 372 self.stream.write("<span class='returns'>\n") 373 self._keyword("return") 374 self._popup_start() 375 self._types(node._node) 376 self._popup_end() 377 self.stream.write("</span>\n") 378 self.dispatch(node.value) 379 self.stream.write("</div>\n") 380 381 def visitStmt(self, node): 382 self.stream.write("<div class='stmt'>\n") 383 self.default(node) 384 self.stream.write("</div>\n") 385 386 def visitTryExcept(self, node): 387 self.stream.write("<div class='tryexcept'>\n") 388 self.stream.write("<div>\n") 389 self._keyword("try") 390 self.stream.write(":\n") 391 self.stream.write("</div>\n") 392 self.stream.write("<div class='body'>\n") 393 self.dispatch(node.body) 394 self.stream.write("</div>\n") 395 for spec, assign, statement in node.handlers: 396 self.stream.write("<div>\n") 397 self._keyword("except") 398 if spec is not None: 399 self.dispatch(spec) 400 if assign is not None: 401 self.stream.write(",\n") 402 self.dispatch(assign) 403 self.stream.write(":\n") 404 self.stream.write("</div>\n") 405 self.stream.write("<div class='body'>\n") 406 self.dispatch(statement) 407 self.stream.write("</div>\n") 408 if node.else_ is not None: 409 self.stream.write("<div>\n") 410 self._keyword("else") 411 self.stream.write(":\n") 412 self.stream.write("</div>\n") 413 self.stream.write("<div class='body'>\n") 414 self.dispatch(node.else_) 415 self.stream.write("</div>\n") 416 self.stream.write("</div>\n") 417 418 def visitTryFinally(self, node): 419 self.stream.write("<div class='tryfinally'>\n") 420 self.stream.write("<div>\n") 421 self._keyword("try") 422 self.stream.write(":\n") 423 self.stream.write("</div>\n") 424 self.stream.write("<div class='body'>\n") 425 self.dispatch(node.body) 426 self.stream.write("</div>\n") 427 self.stream.write("<div>\n") 428 self._keyword("finally") 429 self.stream.write(":\n") 430 self.stream.write("</div>\n") 431 self.stream.write("<div class='body'>\n") 432 self.dispatch(node.final) 433 self.stream.write("</div>\n") 434 self.stream.write("</div>\n") 435 436 def visitWhile(self, node): 437 self.stream.write("<div class='while'>\n") 438 self.stream.write("<div>\n") 439 self.stream.write("<span class='conditional'>\n") 440 self._keyword("while") 441 self._popup_start() 442 self._invocations(node._test_call) 443 self._popup_end() 444 self.stream.write("</span>\n") 445 self.dispatch(node.test) 446 self.stream.write(":\n") 447 self.stream.write("</div>\n") 448 self.stream.write("<div class='body'>\n") 449 self.dispatch(node.body) 450 self.stream.write("</div>\n") 451 if node.else_ is not None: 452 self.stream.write("<div>\n") 453 self._keyword("else") 454 self.stream.write(":\n") 455 self.stream.write("</div>\n") 456 self.stream.write("<div class='body'>\n") 457 self.dispatch(node.else_) 458 self.stream.write("</div>\n") 459 self.stream.write("</div>\n") 460 461 # Expression-related helper methods. 462 463 def _visitBinary(self, node, name, symbol): 464 self.stream.write("<span class='%s'>\n" % name) 465 self.dispatch(node.left) 466 self.stream.write("<span class='operator'>\n") 467 self.stream.write(symbol) 468 self._popup_start() 469 self.stream.write("<div class='invocations'>\n") 470 self._invocations_list(node._left_call) 471 self._invocations_list(node._right_call) 472 self.stream.write("</div>\n") 473 self._popup_end() 474 self.stream.write("</span>\n") 475 self.dispatch(node.right) 476 self.stream.write("</span>") 477 478 def _visitUnary(self, node, name, symbol): 479 self.stream.write("<span class='%s'>\n" % name) 480 self.stream.write("<span class='operator'>\n") 481 self.stream.write(symbol) 482 self._popup_start() 483 self.stream.write("<div class='invocations'>\n") 484 self._invocations_list(node._unary_call) 485 self.stream.write("</div>\n") 486 self._popup_end() 487 self.stream.write("</span>\n") 488 self.dispatch(node.expr) 489 self.stream.write("</span>") 490 491 # Expressions. 492 493 def visitAdd(self, node): 494 self._visitBinary(node, "add", "+") 495 496 def visitAnd(self, node): 497 self.stream.write("<span class='and'>\n") 498 first = 1 499 for n in node.nodes: 500 if not first: 501 self._keyword("and") 502 self.dispatch(n) 503 first = 0 504 self.stream.write("</span>") 505 506 def visitAssAttr(self, node): 507 self.stream.write("<span class='assattr'>\n") 508 self.dispatch(node.expr) 509 self.stream.write("<span class='attr'>\n") 510 self.stream.write(".%s\n" % self._text(node.attrname)) 511 self._popup_start() 512 self._types(node._node) 513 self._scopes(node._node) 514 self._popup_end() 515 self.stream.write("</span>\n") 516 self.stream.write("</span>\n") 517 518 def visitAssList(self, node): 519 self.stream.write("<span class='list'>\n") 520 self.stream.write("[") 521 self._sequence(node) 522 self.stream.write("]\n") 523 self.stream.write("</span>\n") 524 525 def visitAssName(self, node): 526 self._name_start(node._node.name) 527 self._popup_start() 528 self._types(node._node.expr) 529 self._scopes(node._node) 530 self._popup_end() 531 self._name_end() 532 533 def visitAssTuple(self, node): 534 self.stream.write("<span class='tuple'>\n") 535 self.stream.write("(") 536 self._sequence(node) 537 self.stream.write(")\n") 538 self.stream.write("</span>\n") 539 540 def visitCallFunc(self, node): 541 self.stream.write("<span class='callfunc'>\n") 542 self.dispatch(node.node) 543 self.stream.write("<span class='call'>\n") 544 self.stream.write("(") 545 self._popup_start() 546 self._invocations(node._node) 547 self._popup_end() 548 self.stream.write("</span>\n") 549 first = 1 550 for arg in node.args: 551 if not first: 552 self.stream.write(",\n") 553 self.dispatch(arg) 554 first = 0 555 if node.star_args is not None: 556 if not first: 557 self.stream.write(", *\n") 558 self.dispatch(node.star_args) 559 first = 0 560 if node.dstar_args is not None: 561 if not first: 562 self.stream.write(", **\n") 563 self.dispatch(node.dstar_args) 564 first = 0 565 self.stream.write(")\n") 566 self.stream.write("</span>\n") 567 568 def visitCompare(self, node): 569 self.stream.write("<span class='compare'>\n") 570 self.dispatch(node.expr) 571 for (op_name, expr), _op in map(None, node.ops, node._ops): 572 self.stream.write("<span class='op'>\n") 573 self.stream.write(op_name) 574 self._popup_start() 575 self._op(op_name, _op) 576 self._popup_end() 577 self.stream.write("</span>\n") 578 self.dispatch(expr) 579 self.stream.write("</span>\n") 580 581 def visitConst(self, node): 582 self.stream.write(repr(node.value)) 583 584 def visitDiv(self, node): 585 self._visitBinary(node, "div", "/") 586 587 def visitFloorDiv(self, node): 588 self._visitBinary(node, "floordiv", "//") 589 590 def visitGetattr(self, node): 591 self.stream.write("<span class='getattr'>\n") 592 self.dispatch(node.expr) 593 self.stream.write("<span class='attr'>\n") 594 self.stream.write(".%s\n" % self._text(node.attrname)) 595 self._popup_start() 596 self._types(node._node) 597 self._scopes(node._node) 598 self._popup_end() 599 self.stream.write("</span>\n") 600 self.stream.write("</span>\n") 601 602 def visitKeyword(self, node): 603 self.stream.write("<span class='keyword-arg'>\n") 604 self.stream.write(node.name) 605 self.stream.write("=") 606 self.dispatch(node.expr) 607 self.stream.write("</span>\n") 608 609 def visitLambda(self, node): 610 definition = node._node 611 subprogram = definition.expr.ref 612 self.stream.write("<span class='lambda'>\n") 613 self._keyword("lambda") 614 self._parameters(subprogram) 615 self.dispatch(node.code) 616 self.stream.write("</span>\n") 617 618 visitList = visitAssList 619 620 def visitMul(self, node): 621 self._visitBinary(node, "mul", "*") 622 623 def visitName(self, node): 624 self._name_start(node._node.name) 625 self._popup_start() 626 self._types(node._node) 627 self._scopes(node._node) 628 self._popup_end() 629 self._name_end() 630 631 def visitNot(self, node): 632 self.stream.write("<span class='not'>\n") 633 self._keyword("not") 634 self.dispatch(node.expr) 635 self.stream.write("</span>") 636 637 def visitOr(self, node): 638 self.stream.write("<span class='or'>\n") 639 first = 1 640 for n in node.nodes: 641 if not first: 642 self._keyword("or") 643 self.dispatch(n) 644 first = 0 645 self.stream.write("</span>") 646 647 def visitPower(self, node): 648 self._visitBinary(node, "power", "**") 649 650 def visitSlice(self, node): 651 self.stream.write("<span class='slice'>\n") 652 self.dispatch(node.expr) 653 self.stream.write("[") 654 if node.lower: 655 self.dispatch(node.lower) 656 self.stream.write(":") 657 if node.upper: 658 self.dispatch(node.upper) 659 # NOTE: Step? 660 self.stream.write("]") 661 self.stream.write("</span>\n") 662 663 def visitSub(self, node): 664 self._visitBinary(node, "sub", "-") 665 666 def visitSubscript(self, node): 667 self.stream.write("<span class='subscript'>\n") 668 self.dispatch(node.expr) 669 self.stream.write("[") 670 first = 1 671 for sub in node.subs: 672 if not first: 673 self.stream.write(", ") 674 self.dispatch(sub) 675 first = 0 676 self.stream.write("]") 677 self.stream.write("</span>\n") 678 679 visitTuple = visitAssTuple 680 681 def visitUnaryAdd(self, node): 682 self._visitUnary(node, "add", "+") 683 684 def visitUnarySub(self, node): 685 self._visitUnary(node, "sub", "-") 686 687 # Output preparation methods. 688 689 def _text(self, text): 690 return text.replace("&", "&").replace("<", "<").replace(">", ">") 691 692 def _attr(self, attr): 693 return self._text(attr).replace("'", "'").replace('"', """) 694 695 def _url(self, url): 696 return self._attr(url).replace("#", "%23").replace("-", "%2d") 697 698 def _comment(self, comment): 699 self.stream.write("<span class='comment'># %s</span>\n" % comment) 700 701 def _keyword(self, kw): 702 self.stream.write("<span class='keyword'>%s</span> " % kw) 703 704 def _doc(self, node): 705 if node.doc is not None: 706 self.stream.write("<pre class='doc'>\n") 707 self.stream.write('"""') 708 output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"')) 709 self.stream.write(self._text(output)) 710 self.stream.write('"""') 711 self.stream.write("</pre>\n") 712 713 def _sequence(self, node): 714 first = 1 715 for n in node.nodes: 716 if not first: 717 self.stream.write(",\n") 718 self.dispatch(n) 719 first = 0 720 721 def _parameters(self, subprogram): 722 first = 1 723 for param, default in subprogram.params: 724 if not first: 725 self.stream.write(",\n") 726 self._parameter(subprogram, param, default) 727 first = 0 728 if subprogram.star is not None: 729 if not first: 730 self.stream.write(", *\n") 731 param, default = subprogram.star 732 self._parameter(subprogram, param, default) 733 first = 0 734 if subprogram.dstar is not None: 735 if not first: 736 self.stream.write(", **\n") 737 param, default = subprogram.dstar 738 self._parameter(subprogram, param, default) 739 first = 0 740 741 def _parameter(self, subprogram, param, default): 742 self._name_start(param) 743 if hasattr(subprogram, "paramtypes"): 744 self._popup_start() 745 self._types_list(subprogram.paramtypes[param]) 746 self._popup_end() 747 self._name_end() 748 if default is not None and default.original is not None: 749 self.stream.write("=\n") 750 self.dispatch(default.original) 751 752 def _name(self, name): 753 self.stream.write("<span class='name'>%s</span>\n" % name) 754 755 def _name_start(self, name): 756 self.stream.write("<span class='name'>%s\n" % name) 757 758 def _name_end(self): 759 self.stream.write("</span>\n") 760 761 def _popup_start(self): 762 self.stream.write("<span class='popup'>\n") 763 764 def _popup_end(self): 765 self.stream.write("</span>\n") 766 767 def _op(self, op_name, op): 768 if op is not None: 769 if isinstance(op, Not): 770 self._invocations(op.expr) 771 else: 772 self._invocations(op) 773 774 def _invocations(self, node): 775 self.stream.write("<div class='invocations'>\n") 776 self._invocations_list(node) 777 self.stream.write("</div>\n") 778 779 def _invocations_list(self, node): 780 if hasattr(node, "invocations"): 781 for invocation in node.invocations: 782 fn = invocation.full_name() 783 module = invocation.module.name 784 name = invocation.name 785 structures = [x.name for x in invocation.structures] 786 self.stream.write("<div class='invocation'>") 787 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn))) 788 self.stream.write(self._text(".".join([module] + structures + [name]))) 789 self.stream.write("</a>") 790 self.stream.write("</div>\n") 791 792 def _types(self, node): 793 if hasattr(node, "types"): 794 if node.types: 795 self._types_list(node.types) 796 else: 797 self._no_types() 798 elif hasattr(node, "writes"): 799 if node.writes: 800 self._types_list(flatten(node.writes.values())) 801 else: 802 self._no_types() 803 else: 804 self.stream.write("<div class='types'>\n") 805 self.stream.write("unvisited\n") 806 self.stream.write("</div>\n") 807 808 def _no_types(self): 809 self.stream.write("<div class='types'>\n") 810 self.stream.write("no types\n") 811 self.stream.write("</div>\n") 812 813 def _types_list(self, types, style_class="types"): 814 self.stream.write("<div class='%s'>\n" % style_class) 815 for type in types: 816 fn = type.type.full_name() 817 self.stream.write("<div class='type'>") 818 self.stream.write(self._text(fn)) 819 self.stream.write("</div>\n") 820 self.stream.write("</div>\n") 821 822 def _raises(self, node): 823 if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises: 824 self._types_list(node.namespace.raises, style_class="raises") 825 826 def _scopes(self, node): 827 if not isinstance(node, LoadName): 828 if hasattr(node, "writes") and node.writes or hasattr(node, "accesses") and node.accesses: 829 self.stream.write("<div class='scopes'>\n") 830 for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): 831 fn = ref.full_name() 832 self.stream.write("<div class='scope'>") 833 self.stream.write(self._text(fn)) 834 self.stream.write("</div>\n") 835 self.stream.write("</div>\n") 836 if hasattr(node, "non_accesses") and node.non_accesses: 837 self._types_list(node.non_accesses, style_class="non-accesses") 838 if hasattr(node, "non_writes") and node.non_writes: 839 self._types_list(node.non_writes, style_class="non-writes") 840 841 # Utility functions. 842 843 def flatten(lists): 844 result = [] 845 for l in lists: 846 for attr in l: 847 if attr not in result: 848 result.append(attr) 849 return result 850 851 # Convenience functions. 852 853 def browse(module, stream=None): 854 browser = Browser(stream or sys.stdout) 855 browser.process(module.original) 856 857 def makedoc(module, filename): 858 stream = open(filename, "wb") 859 try: 860 browser = Browser(stream) 861 browser.process(module.original) 862 finally: 863 stream.close() 864 865 def makedocs(module, modules, builtins): 866 dirname = "%s-docs" % module.name 867 if not os.path.exists(dirname): 868 os.mkdir(dirname) 869 for m in [module, builtins] + modules: 870 makedoc(m, os.path.join(dirname, "%s%shtml" % (m.name, os.path.extsep))) 871 872 # vim: tabstop=4 expandtab shiftwidth=4