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 textwrap 28 29 # Exceptions. 30 31 class ViewerError(SimplifiedError): 32 33 "An error in viewing." 34 35 pass 36 37 # Classes. 38 39 class Viewer(ASTVisitor): 40 41 """ 42 A viewing visitor for AST nodes. 43 """ 44 45 def __init__(self, stream): 46 ASTVisitor.__init__(self) 47 self.cached_files = {} 48 self.printed_lines = {} 49 self.visitor = self 50 self.stream = stream 51 52 def process(self, module): 53 self.dispatch(module) 54 55 def dispatch(self, node): 56 self.dispatch_only(node) 57 ASTVisitor.dispatch(self, node) 58 59 def dispatch_only(self, node, every_time=0): 60 self.print_line(getattr(node, "filename", None), getattr(node, "lineno", None), every_time) 61 62 def print_line(self, filename, lineno, every_time): 63 last_printed = self.printed_lines.get(filename, 0) 64 if lineno > last_printed or every_time: 65 self.stream.write(self.get_line(filename, lineno)) 66 self.printed_lines[filename] = lineno 67 68 def get_line(self, filename, lineno): 69 if filename is None or lineno is None: 70 return "" 71 72 if self.cached_files.has_key(filename): 73 lines = self.cached_files[filename] 74 else: 75 f = open(filename) 76 try: 77 self.cached_files[filename] = lines = f.readlines() 78 finally: 79 f.close() 80 81 try: 82 return lines[lineno - 1] 83 except IndexError: 84 return "" 85 86 def report(self, exc): 87 self.stream.write("Exception was:\n\n" + str(exc.exc) + "\n\n") 88 self.stream.write("Nodes:\n\n") 89 for node in exc.nodes: 90 self.stream.write(repr(node) + "\n") 91 if node is not None: 92 self.dispatch_only(node.original, every_time=1) 93 self.stream.write("\nOriginal node was:\n\n" + repr(exc.nodes[0].original) + "\n") 94 self.stream.write("\nSimplified node was:\n\n") 95 exc.nodes[0].pprint(stream=self.stream) 96 97 # HTML-related output production. 98 99 html_header = """<?xml version="1.0" encoding="iso-8859-15"?> 100 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 101 <html xmlns="http://www.w3.org/1999/xhtml"> 102 <head> 103 <title>Module</title> 104 <style type="text/css"> 105 body { 106 padding-top: 4em; padding-bottom: 4em; 107 font-size: 14pt; font-family: monospace; 108 background-color: black; color: white; 109 } 110 111 .class { margin-bottom: 1em; } 112 .function { margin-bottom: 1em; } 113 .body { padding-left: 2em; } 114 .keyword { color: yellow; } 115 .comment { color: blue; } 116 .str { color: #FF00FF; } 117 .doc { color: #FF00FF; margin-bottom: 1em; } 118 .invocation a { color: white; text-decoration: none; } 119 120 .popup { 121 display: none; z-index: 2; 122 position: absolute; top: 1em; left: 0.5em; 123 padding: 0.2em; background-color: #000000; 124 } 125 126 .invocations { 127 padding: 0.5em; background-color: #770000; 128 clear: all; 129 } 130 131 .types { 132 padding: 0.5em; background-color: #0000FF; 133 float: right; 134 } 135 136 .raises { 137 padding: 0.5em; background-color: #7700FF; 138 float: right; 139 } 140 141 .scopes { 142 padding: 0.5em; background-color: #007700; 143 float: left; 144 } 145 146 .non-writes, .non-accesses { 147 padding: 0.5em; background-color: #FF0000; 148 float: right; 149 } 150 151 .op, 152 .name, 153 .attr, 154 .conditional, 155 .operator 156 { 157 position: relative; 158 } 159 160 .op:hover > .popup, 161 .name:hover > .popup, 162 .attr:hover > .popup, 163 .conditional:hover > .popup, 164 .operator:hover > .popup 165 { 166 display: block; 167 } 168 169 </style> 170 </head> 171 <body> 172 """ 173 174 html_footer = """</body> 175 </html> 176 """ 177 178 # Browser classes. 179 180 class Browser(ASTVisitor): 181 182 """ 183 A browsing visitor for AST nodes. 184 185 Covered: Add, And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign, 186 Break, CallFunc, Class, Compare, Const, Continue, Dict, Discard, 187 For, Function, Getattr, If, Keyword, Lambda, List, Module, Name, 188 Not, Or, Pass, Raise, Return, Slice, Stmt, Subscript, TryExcept, 189 TryFinally, Tuple, While. 190 191 Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Div, 192 Ellipsis, Exec, FloorDiv, From, Global, Import, Invert, LeftShift, 193 ListComp, ListCompFor, ListCompIf, Mod, Mul, Not, Or, Power, Print, 194 Printnl, RightShift, Sliceobj, Sub, UnaryAdd, UnarySub, Yield. 195 """ 196 197 def __init__(self, stream): 198 ASTVisitor.__init__(self) 199 self.visitor = self 200 self.stream = stream 201 202 def process(self, module): 203 self.stream.write(html_header) 204 self.dispatch(module) 205 self.stream.write(html_footer) 206 207 def dispatch(self, node): 208 try: 209 ASTVisitor.dispatch(self, node) 210 except ViewerError, exc: 211 exc.add(node) 212 raise 213 except Exception, exc: 214 raise ViewerError(exc, node) 215 216 def visitModule(self, node): 217 self.default(node) 218 219 # Statements. 220 221 def visitAssign(self, node): 222 self.stream.write("<div class='assign'>\n") 223 for lvalue in node.nodes: 224 self.dispatch(lvalue) 225 self.stream.write("=\n") 226 self.dispatch(node.expr) 227 self.stream.write("</div>\n") 228 229 def visitAugAssign(self, node): 230 self.stream.write("<div class='augassign'>\n") 231 self.dispatch(node.node) 232 self.stream.write("%s\n" % node.op) 233 self.dispatch(node.expr) 234 self.stream.write("</div>\n") 235 236 def visitBreak(self, node): 237 self.stream.write("<div class='break'>\n") 238 self._keyword("break") 239 self.stream.write("</div>\n") 240 241 def visitClass(self, node): 242 definition = node._node 243 structure = definition.expr.ref 244 self.stream.write("<div class='class' id='%s'>\n" % self._url(structure.full_name())) 245 self.stream.write("<div>\n") 246 self._keyword("class") 247 self._name_start(structure.name) 248 self._popup_start() 249 self._scopes(definition) 250 self._popup_end() 251 self._name_end() 252 bases = structure.bases 253 254 # Suppress the "object" class appearing alone. 255 256 if bases and not (len(bases) == 1 and bases[0].name == "object"): 257 self.stream.write("(") 258 first = 1 259 for base in bases: 260 if not first: 261 self.stream.write(",\n") 262 self._name_start(base.name) 263 self._popup_start() 264 self._types(base) 265 self._scopes(base) 266 self._popup_end() 267 self._name_end() 268 first = 0 269 self.stream.write(")") 270 271 self.stream.write(":\n") 272 self._comment(self._text(structure.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 visitContinue(self, node): 282 self.stream.write("<div class='continue'>\n") 283 self._keyword("continue") 284 self.stream.write("</div>\n") 285 286 def visitDiscard(self, node): 287 self.stream.write("<div class='discard'>\n") 288 self.default(node) 289 self.stream.write("</div>\n") 290 291 def visitFor(self, node): 292 self.stream.write("<div class='if'>\n") 293 self.stream.write("<div>\n") 294 self._keyword("for") 295 self.dispatch(node.assign) 296 self._keyword("in") 297 self.dispatch(node.list) 298 self.stream.write(":\n") 299 self.stream.write("</div>\n") 300 self.stream.write("<div class='body'>\n") 301 self.dispatch(node.body) 302 self.stream.write("</div>\n") 303 if node.else_ is not None: 304 self.stream.write("<div>\n") 305 self._keyword("else") 306 self.stream.write(":\n") 307 self.stream.write("</div>\n") 308 self.stream.write("<div class='body'>\n") 309 self.dispatch(node.else_) 310 self.stream.write("</div>\n") 311 self.stream.write("</div>\n") 312 313 def visitFunction(self, node): 314 definition = node._node 315 subprogram = definition.expr.ref 316 self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name())) 317 self.stream.write("<div>\n") 318 self._keyword("def") 319 self._name_start(subprogram.name) 320 self._popup_start() 321 self._scopes(definition) 322 self._raises(subprogram) 323 self._popup_end() 324 self._name_end() 325 self.stream.write("(") 326 self._parameters(subprogram) 327 self.stream.write(")") 328 self.stream.write(":\n") 329 self._comment(self._text(subprogram.full_name())) 330 self.stream.write("</div>\n") 331 332 self.stream.write("<div class='body'>\n") 333 self._doc(node) 334 self.dispatch(node.code) 335 self.stream.write("</div>\n") 336 self.stream.write("</div>\n") 337 338 def visitIf(self, node): 339 self.stream.write("<div class='if'>\n") 340 first = 1 341 conditional = node._node 342 for compare, stmt in node.tests: 343 self.stream.write("<div>\n") 344 self.stream.write("<span class='conditional'>\n") 345 if first: 346 self._keyword("if") 347 else: 348 self._keyword("elif") 349 self._popup_start() 350 self._invocations(conditional.test) 351 self._popup_end() 352 self.stream.write("</span>\n") 353 self.dispatch(compare) 354 self.stream.write(":\n") 355 self.stream.write("</div>\n") 356 self.stream.write("<div class='body'>\n") 357 self.dispatch(stmt) 358 self.stream.write("</div>\n") 359 if conditional.else_: 360 conditional = conditional.else_[0] 361 else: 362 conditional = None 363 first = 0 364 if node.else_ is not None: 365 self.stream.write("<div>\n") 366 self._keyword("else") 367 self.stream.write(":\n") 368 self.stream.write("</div>\n") 369 self.stream.write("<div class='body'>\n") 370 self.dispatch(node.else_) 371 self.stream.write("</div>\n") 372 self.stream.write("</div>\n") 373 374 def visitPass(self, node): 375 self.stream.write("<div class='pass'>\n") 376 self._keyword("pass") 377 self.stream.write("</div>\n") 378 379 def visitRaise(self, node): 380 self.stream.write("<div class='raise'>\n") 381 self._keyword("raise") 382 self.dispatch(node.expr1) 383 if node.expr2 is not None: 384 self.stream.write(",\n") 385 self.dispatch(node.expr2) 386 if node.expr3 is not None: 387 self.stream.write(",\n") 388 self.dispatch(node.expr3) 389 self.stream.write("</div>\n") 390 391 def visitReturn(self, node): 392 self.stream.write("<div class='return'>\n") 393 self._keyword("return") 394 self.dispatch(node.value) 395 self.stream.write("</div>\n") 396 397 def visitStmt(self, node): 398 self.stream.write("<div class='stmt'>\n") 399 self.default(node) 400 self.stream.write("</div>\n") 401 402 def visitTryExcept(self, node): 403 self.stream.write("<div class='tryexcept'>\n") 404 self.stream.write("<div>\n") 405 self._keyword("try") 406 self.stream.write(":\n") 407 self.stream.write("</div>\n") 408 self.stream.write("<div class='body'>\n") 409 self.dispatch(node.body) 410 self.stream.write("</div>\n") 411 for spec, assign, statement in node.handlers: 412 self.stream.write("<div>\n") 413 self._keyword("except") 414 if spec is not None: 415 self.dispatch(spec) 416 if assign is not None: 417 self.stream.write(",\n") 418 self.dispatch(assign) 419 self.stream.write(":\n") 420 self.stream.write("</div>\n") 421 self.stream.write("<div class='body'>\n") 422 self.dispatch(statement) 423 self.stream.write("</div>\n") 424 if node.else_ is not None: 425 self.stream.write("<div>\n") 426 self._keyword("else") 427 self.stream.write(":\n") 428 self.stream.write("</div>\n") 429 self.stream.write("<div class='body'>\n") 430 self.dispatch(node.else_) 431 self.stream.write("</div>\n") 432 self.stream.write("</div>\n") 433 434 def visitTryFinally(self, node): 435 self.stream.write("<div class='tryfinally'>\n") 436 self.stream.write("<div>\n") 437 self._keyword("try") 438 self.stream.write(":\n") 439 self.stream.write("</div>\n") 440 self.stream.write("<div class='body'>\n") 441 self.dispatch(node.body) 442 self.stream.write("</div>\n") 443 self.stream.write("<div>\n") 444 self._keyword("finally") 445 self.stream.write(":\n") 446 self.stream.write("</div>\n") 447 self.stream.write("<div class='body'>\n") 448 self.dispatch(node.final) 449 self.stream.write("</div>\n") 450 self.stream.write("</div>\n") 451 452 def visitWhile(self, node): 453 self.stream.write("<div class='while'>\n") 454 self.stream.write("<div>\n") 455 self.stream.write("<span class='conditional'>\n") 456 self._keyword("while") 457 self._popup_start() 458 self._invocations(node.test) 459 self._popup_end() 460 self.stream.write("</span>\n") 461 self.dispatch(node.test) 462 self.stream.write(":\n") 463 self.stream.write("</div>\n") 464 self.stream.write("<div class='body'>\n") 465 self.dispatch(node.body) 466 self.stream.write("</div>\n") 467 if node.else_ is not None: 468 self.stream.write("<div>\n") 469 self._keyword("else") 470 self.stream.write(":\n") 471 self.stream.write("</div>\n") 472 self.stream.write("<div class='body'>\n") 473 self.dispatch(node.else_) 474 self.stream.write("</div>\n") 475 self.stream.write("</div>\n") 476 477 # Expressions. 478 479 def visitAdd(self, node): 480 self.stream.write("<span class='add'>\n") 481 self.dispatch(node.left) 482 self.stream.write("<span class='operator'>\n") 483 self.stream.write("+") 484 self._popup_start() 485 self.stream.write("<div class='invocations'>\n") 486 self._invocations_list(node._node.body[0].expr) # NOTE: See visitAdd in simplify. 487 self._invocations_list(node._node.handler[0].body[0].expr) # NOTE: See visitAdd in simplify. 488 self.stream.write("</div>\n") 489 self._popup_end() 490 self.stream.write("</span>\n") 491 self.dispatch(node.right) 492 self.stream.write("</span>") 493 494 def visitAnd(self, node): 495 self.stream.write("<span class='and'>\n") 496 first = 1 497 for n in node.nodes: 498 if not first: 499 self._keyword("and") 500 self.dispatch(n) 501 first = 0 502 self.stream.write("</span>") 503 504 def visitAssAttr(self, node): 505 self.stream.write("<span class='assattr'>\n") 506 self.dispatch(node.expr) 507 self.stream.write("<span class='attr'>\n") 508 self.stream.write(".%s\n" % self._text(node.attrname)) 509 if hasattr(node, "_node"): 510 self._popup_start() 511 self._types(node._node) 512 self._scopes(node._node) 513 self._popup_end() 514 else: 515 raise ValueError, node 516 self.stream.write("</span>\n") 517 self.stream.write("</span>\n") 518 519 def visitAssList(self, node): 520 self.stream.write("<span class='list'>\n") 521 self.stream.write("[") 522 self._sequence(node) 523 self.stream.write("]\n") 524 self.stream.write("</span>\n") 525 526 def visitAssName(self, node): 527 if hasattr(node, "_node"): 528 self._name_start(node._node.name) 529 self._popup_start() 530 self._types(node._node.expr) 531 self._scopes(node._node) 532 self._popup_end() 533 self._name_end() 534 else: 535 raise ValueError, node 536 self._name(node.name) 537 538 def visitAssTuple(self, node): 539 self.stream.write("<span class='tuple'>\n") 540 self.stream.write("(") 541 self._sequence(node) 542 self.stream.write(")\n") 543 self.stream.write("</span>\n") 544 545 def visitCallFunc(self, node): 546 self.stream.write("<span class='callfunc'>\n") 547 self.dispatch(node.node) 548 self.stream.write("(") 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 visitGetattr(self, node): 585 self.stream.write("<span class='getattr'>\n") 586 self.dispatch(node.expr) 587 self.stream.write("<span class='attr'>\n") 588 self.stream.write(".%s\n" % self._text(node.attrname)) 589 if hasattr(node, "_node"): 590 self._popup_start() 591 self._types(node._node) 592 self._scopes(node._node) 593 self._popup_end() 594 else: 595 raise ValueError, node 596 self.stream.write("</span>\n") 597 self.stream.write("</span>\n") 598 599 def visitKeyword(self, node): 600 self.stream.write("<span class='keyword'>\n") 601 self.stream.write(node.name) 602 self.stream.write("=") 603 self.dispatch(node.expr) 604 self.stream.write("</span>\n") 605 606 def visitLambda(self, node): 607 definition = node._node 608 subprogram = definition.expr.ref 609 self.stream.write("<span class='lambda'>\n") 610 self._keyword("lambda") 611 self._parameters(subprogram) 612 self.dispatch(node.code) 613 self.stream.write("</span>\n") 614 615 visitList = visitAssList 616 617 def visitName(self, node): 618 if hasattr(node, "_node"): 619 self._name_start(node._node.name) 620 self._popup_start() 621 self._types(node._node) 622 self._scopes(node._node) 623 self._popup_end() 624 self._name_end() 625 else: 626 raise ValueError, node 627 self._name(node.name) 628 629 def visitNot(self, node): 630 self.stream.write("<span class='not'>\n") 631 self._keyword("not") 632 self.dispatch(node.expr) 633 self.stream.write("</span>") 634 635 def visitOr(self, node): 636 self.stream.write("<span class='or'>\n") 637 first = 1 638 for n in node.nodes: 639 if not first: 640 self._keyword("or") 641 self.dispatch(n) 642 first = 0 643 self.stream.write("</span>") 644 645 def visitSlice(self, node): 646 self.stream.write("<span class='slice'>\n") 647 self.dispatch(node.expr) 648 self.stream.write("[") 649 if node.lower: 650 self.dispatch(node.lower) 651 self.stream.write(":") 652 if node.upper: 653 self.dispatch(node.upper) 654 # NOTE: Step? 655 self.stream.write("]") 656 self.stream.write("</span>\n") 657 658 def visitSubscript(self, node): 659 self.stream.write("<span class='subscript'>\n") 660 self.dispatch(node.expr) 661 self.stream.write("[") 662 first = 1 663 for sub in node.subs: 664 if not first: 665 self.stream.write(", ") 666 self.dispatch(sub) 667 first = 0 668 self.stream.write("]") 669 self.stream.write("</span>\n") 670 671 visitTuple = visitAssTuple 672 673 # Output preparation methods. 674 675 def _text(self, text): 676 return text.replace("&", "&").replace("<", "<").replace(">", ">") 677 678 def _attr(self, attr): 679 return self._text(attr).replace("'", "'").replace('"', """) 680 681 def _url(self, url): 682 return self._attr(url).replace("#", "%23").replace("-", "%2d") 683 684 def _comment(self, comment): 685 self.stream.write("<span class='comment'># %s</span>\n" % comment) 686 687 def _keyword(self, kw): 688 self.stream.write("<span class='keyword'>%s</span> " % kw) 689 690 def _doc(self, node): 691 if node.doc is not None: 692 self.stream.write("<pre class='doc'>\n") 693 self.stream.write('"""') 694 output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"')) 695 self.stream.write(self._text(output)) 696 self.stream.write('"""') 697 self.stream.write("</pre>\n") 698 699 def _sequence(self, node): 700 first = 1 701 for n in node.nodes: 702 if not first: 703 self.stream.write(",\n") 704 self.dispatch(n) 705 first = 0 706 707 def _parameters(self, subprogram): 708 first = 1 709 for param, default in subprogram.params: 710 if not first: 711 self.stream.write(",\n") 712 self._parameter(subprogram, param, default) 713 first = 0 714 if subprogram.star is not None: 715 if not first: 716 self.stream.write(", *\n") 717 param, default = subprogram.star 718 self._parameter(subprogram, param, default) 719 first = 0 720 if subprogram.dstar is not None: 721 if not first: 722 self.stream.write(", **\n") 723 param, default = subprogram.dstar 724 self._parameter(subprogram, param, default) 725 first = 0 726 727 def _parameter(self, subprogram, param, default): 728 self._name_start(param) 729 if hasattr(subprogram, "paramtypes"): 730 self._popup_start() 731 self._types_list(subprogram.paramtypes[param]) 732 self._popup_end() 733 self._name_end() 734 if default is not None and default.original is not None: 735 self.stream.write("=\n") 736 self.dispatch(default.original) 737 738 def _name(self, name): 739 self.stream.write("<span class='name'>%s</span>\n" % name) 740 741 def _name_start(self, name): 742 self.stream.write("<span class='name'>%s\n" % name) 743 744 def _name_end(self): 745 self.stream.write("</span>\n") 746 747 def _popup_start(self): 748 self.stream.write("<span class='popup'>\n") 749 750 def _popup_end(self): 751 self.stream.write("</span>\n") 752 753 def _op(self, op_name, op): 754 if op is not None: 755 self._invocations(op) 756 757 def _invocations(self, node): 758 self.stream.write("<div class='invocations'>\n") 759 self._invocations_list(node) 760 self.stream.write("</div>\n") 761 762 def _invocations_list(self, node): 763 if hasattr(node, "invocations"): 764 for invocation in node.invocations: 765 fn = invocation.full_name() 766 module = invocation.module.name 767 name = invocation.name 768 structures = [x.name for x in invocation.structures] 769 self.stream.write("<div class='invocation'>") 770 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn))) 771 self.stream.write(self._text(".".join([module] + structures + [name]))) 772 self.stream.write("</a>") 773 self.stream.write("</div>\n") 774 775 def _types(self, node): 776 if hasattr(node, "types"): 777 if node.types: 778 self._types_list(node.types) 779 else: 780 self.stream.write("<div class='types'>\n") 781 self.stream.write("no types\n") 782 self.stream.write("</div>\n") 783 else: 784 self.stream.write("<div class='types'>\n") 785 self.stream.write("unvisited\n") 786 self.stream.write("</div>\n") 787 788 def _types_list(self, types, style_class="types"): 789 self.stream.write("<div class='%s'>\n" % style_class) 790 for type in types: 791 fn = type.type.full_name() 792 self.stream.write("<div class='type'>") 793 self.stream.write(self._text(fn)) 794 self.stream.write("</div>\n") 795 self.stream.write("</div>\n") 796 797 def _raises(self, node): 798 if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises: 799 self._types_list(node.namespace.raises, style_class="raises") 800 801 def _scopes(self, node): 802 if not isinstance(node, LoadName): 803 if hasattr(node, "writes") and node.writes or hasattr(node, "accesses") and node.accesses: 804 self.stream.write("<div class='scopes'>\n") 805 for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): 806 fn = ref.full_name() 807 self.stream.write("<div class='scope'>") 808 self.stream.write(self._text(fn)) 809 self.stream.write("</div>\n") 810 self.stream.write("</div>\n") 811 if hasattr(node, "non_accesses") and node.non_accesses: 812 self._types_list(node.non_accesses, style_class="non-accesses") 813 if hasattr(node, "non_writes") and node.non_writes: 814 self._types_list(node.non_writes, style_class="non-writes") 815 816 # Utility functions. 817 818 def flatten(lists): 819 result = [] 820 for l in lists: 821 for attr in l: 822 if attr not in result: 823 result.append(attr) 824 return result 825 826 # Convenience functions. 827 828 def view(module, stream=None): 829 viewer = Viewer(stream or sys.stdout) 830 viewer.process(module.original) 831 832 def browse(module, stream=None): 833 browser = Browser(stream or sys.stdout) 834 browser.process(module.original) 835 836 def makedoc(module, filename): 837 stream = open(filename, "wb") 838 try: 839 browser = Browser(stream) 840 browser.process(module.original) 841 finally: 842 stream.close() 843 844 # vim: tabstop=4 expandtab shiftwidth=4