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 if hasattr(node, "_node"): 501 self._popup_start() 502 self._types(node._node) 503 self._scopes(node._node) 504 self._popup_end() 505 else: 506 raise ValueError, node 507 self.stream.write("</span>\n") 508 self.stream.write("</span>\n") 509 510 def visitAssList(self, node): 511 self.stream.write("<span class='list'>\n") 512 self.stream.write("[") 513 self._sequence(node) 514 self.stream.write("]\n") 515 self.stream.write("</span>\n") 516 517 def visitAssName(self, node): 518 if hasattr(node, "_node"): 519 self._name_start(node._node.name) 520 self._popup_start() 521 self._types(node._node.expr) 522 self._scopes(node._node) 523 self._popup_end() 524 self._name_end() 525 else: 526 raise ValueError, node 527 self._name(node.name) 528 529 def visitAssTuple(self, node): 530 self.stream.write("<span class='tuple'>\n") 531 self.stream.write("(") 532 self._sequence(node) 533 self.stream.write(")\n") 534 self.stream.write("</span>\n") 535 536 def visitCallFunc(self, node): 537 self.stream.write("<span class='callfunc'>\n") 538 self.dispatch(node.node) 539 self.stream.write("<span class='call'>\n") 540 self.stream.write("(") 541 self._popup_start() 542 self._invocations(node._node) 543 self._popup_end() 544 self.stream.write("</span>\n") 545 first = 1 546 for arg in node.args: 547 if not first: 548 self.stream.write(",\n") 549 self.dispatch(arg) 550 first = 0 551 if node.star_args is not None: 552 if not first: 553 self.stream.write(", *\n") 554 self.dispatch(node.star_args) 555 first = 0 556 if node.dstar_args is not None: 557 if not first: 558 self.stream.write(", **\n") 559 self.dispatch(node.dstar_args) 560 first = 0 561 self.stream.write(")\n") 562 self.stream.write("</span>\n") 563 564 def visitCompare(self, node): 565 self.stream.write("<span class='compare'>\n") 566 self.dispatch(node.expr) 567 for (op_name, expr), _op in map(None, node.ops, node._ops): 568 self.stream.write("<span class='op'>\n") 569 self.stream.write(op_name) 570 self._popup_start() 571 self._op(op_name, _op) 572 self._popup_end() 573 self.stream.write("</span>\n") 574 self.dispatch(expr) 575 self.stream.write("</span>\n") 576 577 def visitConst(self, node): 578 self.stream.write(repr(node.value)) 579 580 def visitDiv(self, node): 581 self._visitBinary(node, "div", "/") 582 583 def visitFloorDiv(self, node): 584 self._visitBinary(node, "floordiv", "//") 585 586 def visitGetattr(self, node): 587 self.stream.write("<span class='getattr'>\n") 588 self.dispatch(node.expr) 589 self.stream.write("<span class='attr'>\n") 590 self.stream.write(".%s\n" % self._text(node.attrname)) 591 if hasattr(node, "_node"): 592 self._popup_start() 593 self._types(node._node) 594 self._scopes(node._node) 595 self._popup_end() 596 else: 597 raise ValueError, node 598 self.stream.write("</span>\n") 599 self.stream.write("</span>\n") 600 601 def visitKeyword(self, node): 602 self.stream.write("<span class='keyword'>\n") 603 self.stream.write(node.name) 604 self.stream.write("=") 605 self.dispatch(node.expr) 606 self.stream.write("</span>\n") 607 608 def visitLambda(self, node): 609 definition = node._node 610 subprogram = definition.expr.ref 611 self.stream.write("<span class='lambda'>\n") 612 self._keyword("lambda") 613 self._parameters(subprogram) 614 self.dispatch(node.code) 615 self.stream.write("</span>\n") 616 617 visitList = visitAssList 618 619 def visitMul(self, node): 620 self._visitBinary(node, "mul", "*") 621 622 def visitName(self, node): 623 if hasattr(node, "_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 else: 631 raise ValueError, node 632 self._name(node.name) 633 634 def visitNot(self, node): 635 self.stream.write("<span class='not'>\n") 636 self._keyword("not") 637 self.dispatch(node.expr) 638 self.stream.write("</span>") 639 640 def visitOr(self, node): 641 self.stream.write("<span class='or'>\n") 642 first = 1 643 for n in node.nodes: 644 if not first: 645 self._keyword("or") 646 self.dispatch(n) 647 first = 0 648 self.stream.write("</span>") 649 650 def visitPower(self, node): 651 self._visitBinary(node, "power", "**") 652 653 def visitSlice(self, node): 654 self.stream.write("<span class='slice'>\n") 655 self.dispatch(node.expr) 656 self.stream.write("[") 657 if node.lower: 658 self.dispatch(node.lower) 659 self.stream.write(":") 660 if node.upper: 661 self.dispatch(node.upper) 662 # NOTE: Step? 663 self.stream.write("]") 664 self.stream.write("</span>\n") 665 666 def visitSub(self, node): 667 self._visitBinary(node, "sub", "-") 668 669 def visitSubscript(self, node): 670 self.stream.write("<span class='subscript'>\n") 671 self.dispatch(node.expr) 672 self.stream.write("[") 673 first = 1 674 for sub in node.subs: 675 if not first: 676 self.stream.write(", ") 677 self.dispatch(sub) 678 first = 0 679 self.stream.write("]") 680 self.stream.write("</span>\n") 681 682 visitTuple = visitAssTuple 683 684 def visitUnaryAdd(self, node): 685 self._visitUnary(node, "add", "+") 686 687 def visitUnarySub(self, node): 688 self._visitUnary(node, "sub", "-") 689 690 # Output preparation methods. 691 692 def _text(self, text): 693 return text.replace("&", "&").replace("<", "<").replace(">", ">") 694 695 def _attr(self, attr): 696 return self._text(attr).replace("'", "'").replace('"', """) 697 698 def _url(self, url): 699 return self._attr(url).replace("#", "%23").replace("-", "%2d") 700 701 def _comment(self, comment): 702 self.stream.write("<span class='comment'># %s</span>\n" % comment) 703 704 def _keyword(self, kw): 705 self.stream.write("<span class='keyword'>%s</span> " % kw) 706 707 def _doc(self, node): 708 if node.doc is not None: 709 self.stream.write("<pre class='doc'>\n") 710 self.stream.write('"""') 711 output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"')) 712 self.stream.write(self._text(output)) 713 self.stream.write('"""') 714 self.stream.write("</pre>\n") 715 716 def _sequence(self, node): 717 first = 1 718 for n in node.nodes: 719 if not first: 720 self.stream.write(",\n") 721 self.dispatch(n) 722 first = 0 723 724 def _parameters(self, subprogram): 725 first = 1 726 for param, default in subprogram.params: 727 if not first: 728 self.stream.write(",\n") 729 self._parameter(subprogram, param, default) 730 first = 0 731 if subprogram.star is not None: 732 if not first: 733 self.stream.write(", *\n") 734 param, default = subprogram.star 735 self._parameter(subprogram, param, default) 736 first = 0 737 if subprogram.dstar is not None: 738 if not first: 739 self.stream.write(", **\n") 740 param, default = subprogram.dstar 741 self._parameter(subprogram, param, default) 742 first = 0 743 744 def _parameter(self, subprogram, param, default): 745 self._name_start(param) 746 if hasattr(subprogram, "paramtypes"): 747 self._popup_start() 748 self._types_list(subprogram.paramtypes[param]) 749 self._popup_end() 750 self._name_end() 751 if default is not None and default.original is not None: 752 self.stream.write("=\n") 753 self.dispatch(default.original) 754 755 def _name(self, name): 756 self.stream.write("<span class='name'>%s</span>\n" % name) 757 758 def _name_start(self, name): 759 self.stream.write("<span class='name'>%s\n" % name) 760 761 def _name_end(self): 762 self.stream.write("</span>\n") 763 764 def _popup_start(self): 765 self.stream.write("<span class='popup'>\n") 766 767 def _popup_end(self): 768 self.stream.write("</span>\n") 769 770 def _op(self, op_name, op): 771 if op is not None: 772 if isinstance(op, Not): 773 self._invocations(op.expr) 774 else: 775 self._invocations(op) 776 777 def _invocations(self, node): 778 self.stream.write("<div class='invocations'>\n") 779 self._invocations_list(node) 780 self.stream.write("</div>\n") 781 782 def _invocations_list(self, node): 783 if hasattr(node, "invocations"): 784 for invocation in node.invocations: 785 fn = invocation.full_name() 786 module = invocation.module.name 787 name = invocation.name 788 structures = [x.name for x in invocation.structures] 789 self.stream.write("<div class='invocation'>") 790 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn))) 791 self.stream.write(self._text(".".join([module] + structures + [name]))) 792 self.stream.write("</a>") 793 self.stream.write("</div>\n") 794 795 def _types(self, node): 796 if hasattr(node, "types"): 797 if node.types: 798 self._types_list(node.types) 799 else: 800 self._no_types() 801 elif hasattr(node, "writes"): 802 if node.writes: 803 self._types_list(flatten(node.writes.values())) 804 else: 805 self._no_types() 806 else: 807 self.stream.write("<div class='types'>\n") 808 self.stream.write("unvisited\n") 809 self.stream.write("</div>\n") 810 811 def _no_types(self): 812 self.stream.write("<div class='types'>\n") 813 self.stream.write("no types\n") 814 self.stream.write("</div>\n") 815 816 def _types_list(self, types, style_class="types"): 817 self.stream.write("<div class='%s'>\n" % style_class) 818 for type in types: 819 fn = type.type.full_name() 820 self.stream.write("<div class='type'>") 821 self.stream.write(self._text(fn)) 822 self.stream.write("</div>\n") 823 self.stream.write("</div>\n") 824 825 def _raises(self, node): 826 if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises: 827 self._types_list(node.namespace.raises, style_class="raises") 828 829 def _scopes(self, node): 830 if not isinstance(node, LoadName): 831 if hasattr(node, "writes") and node.writes or hasattr(node, "accesses") and node.accesses: 832 self.stream.write("<div class='scopes'>\n") 833 for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): 834 fn = ref.full_name() 835 self.stream.write("<div class='scope'>") 836 self.stream.write(self._text(fn)) 837 self.stream.write("</div>\n") 838 self.stream.write("</div>\n") 839 if hasattr(node, "non_accesses") and node.non_accesses: 840 self._types_list(node.non_accesses, style_class="non-accesses") 841 if hasattr(node, "non_writes") and node.non_writes: 842 self._types_list(node.non_writes, style_class="non-writes") 843 844 # Utility functions. 845 846 def flatten(lists): 847 result = [] 848 for l in lists: 849 for attr in l: 850 if attr not in result: 851 result.append(attr) 852 return result 853 854 # Convenience functions. 855 856 def browse(module, stream=None): 857 browser = Browser(stream or sys.stdout) 858 browser.process(module.original) 859 860 def makedoc(module, filename): 861 stream = open(filename, "wb") 862 try: 863 browser = Browser(stream) 864 browser.process(module.original) 865 finally: 866 stream.close() 867 868 def makedocs(module, builtins): 869 dirname = "%s-docs" % module.name 870 if not os.path.exists(dirname): 871 os.mkdir(dirname) 872 makedoc(module, os.path.join(dirname, "%s%shtml" % (module.name, os.path.extsep))) 873 makedoc(builtins, os.path.join(dirname, "%s%shtml" % (builtins.name, os.path.extsep))) 874 875 # vim: tabstop=4 expandtab shiftwidth=4