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