1 #!/usr/bin/env python 2 3 """ 4 View annotated sources. 5 6 Copyright (C) 2006, 2007, 2010, 2011 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.common import * 23 from micropython.data import * 24 from os.path import exists, extsep, join 25 import sys 26 import os 27 import textwrap 28 29 # Classes. 30 31 # HTML-related output production. 32 33 html_header = """<?xml version="1.0" encoding="iso-8859-15"?> 34 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 35 <html xmlns="http://www.w3.org/1999/xhtml"> 36 <head> 37 <title>%(title)s</title> 38 <style type="text/css"> 39 html { 40 background-color: black; color: white; 41 } 42 43 body { 44 padding-bottom: 4em; 45 font-size: 14pt; font-family: monospace; 46 background-color: black; color: white; 47 margin-top: 2em; 48 } 49 50 a { 51 text-decoration: none; 52 } 53 54 .nowrap { white-space: nowrap; } 55 .label { font-size: smaller; } 56 57 .class { margin-top: 1em; margin-bottom: 1em; } 58 .function { margin-top: 1em; margin-bottom: 1em; } 59 .body { padding-left: 2em; } 60 .for, .if, .tryexcept, .tryfinally, .while { margin-bottom: 1em; } 61 .keyword { color: yellow; } 62 .comment { color: blue; } 63 .class-name { color: cyan; } 64 .function-name { color: cyan; } 65 .specific-ref { color: #07F; } 66 .str { color: #FF00FF; } 67 .doc { color: #FF00FF; margin-top: 1em; margin-bottom: 1em; } 68 69 .popup { 70 display: none; 71 position: absolute; 72 top: 3ex; left: 0; 73 color: white; 74 border-bottom: 0.5ex solid #000; 75 border-right: 0.5ex solid #000; 76 z-index: 3; 77 } 78 79 .types-popup { 80 display: none; 81 position: absolute; 82 bottom: 3ex; left: 0; 83 color: white; 84 border-bottom: 0.5ex solid #000; 85 border-right: 0.5ex solid #000; 86 z-index: 3; 87 } 88 89 .accessor, 90 .name, 91 .operation { 92 position: relative; 93 background-color: #300; 94 color: white; 95 } 96 97 .accessor:hover, 98 .name:hover, 99 .operation:hover { 100 background-color: #500; 101 padding-top: 0.5ex; 102 padding-bottom: 0.5ex; 103 z-index: 2; 104 } 105 106 .accessor:hover .types-popup, 107 .name:hover .popup, 108 .operation:hover .popup { 109 display: block; 110 } 111 112 .attrnames, 113 .opnames, 114 .scope, 115 .typenames { 116 padding: 0.5em; 117 background-color: #700; 118 } 119 120 .opnames a { 121 color: white; 122 } 123 124 .summary-class { 125 vertical-align: top; 126 } 127 128 th.summary-class { 129 font-weight: normal; 130 } 131 132 .summary-attr { 133 background-color: #070; 134 } 135 136 .summary-interface, 137 .summary-attr { 138 font-size: smaller; 139 } 140 141 .summary-interface.complete { 142 background-color: #050; 143 } 144 145 .summary-interface.partial { 146 background-color: #005; 147 } 148 149 .summary-attr-absent { 150 border-left: 0.2em solid #070; 151 font-size: small; 152 } 153 154 .summary-class-attr { 155 background-color: #007; 156 font-size: smaller; 157 } 158 159 .summary-class-attr-absent { 160 border-left: 0.2em solid #007; 161 font-size: small; 162 } 163 164 .summary-ref { 165 color: white; 166 } 167 168 </style> 169 </head> 170 <body> 171 """ 172 173 html_footer = """</body> 174 </html> 175 """ 176 177 # Utility classes. 178 179 class Writer: 180 181 "A utility class providing useful HTML output methods." 182 183 # Methods which return strings. 184 185 def _text(self, text): 186 return text.replace("&", "&").replace("<", "<").replace(">", ">") 187 188 def _attr(self, attr): 189 return self._text(attr).replace("'", "'").replace('"', """) 190 191 def _url(self, url): 192 return self._attr(url).replace("#", "%23").replace("-", "%2d") 193 194 # Methods which write to the stream. 195 196 def _span_start(self, classes=None): 197 self.stream.write("<span class='%s'>" % (classes or "")) 198 199 def _span_end(self): 200 self.stream.write("</span>") 201 202 def _span(self, value, classes=None): 203 self._span_start(classes) 204 self.stream.write(self._text(value)) 205 self._span_end() 206 207 def _name_start(self, classes=None): 208 self._span_start(classes or "name") 209 210 _name_end = _span_end 211 212 def _name(self, name, classes=None): 213 self._name_start(classes) 214 self.stream.write(self._text(name)) 215 self._name_end() 216 217 def _popup_start(self, classes=None): 218 self._span_start(classes or "popup") 219 220 _popup_end = _span_end 221 222 def _comment(self, comment): 223 self._span("# %s" % comment, "comment") 224 self.stream.write("\n") 225 226 def _reserved(self, token, classes, leading=0, trailing=1): 227 if leading: 228 self.stream.write(" ") 229 self._span(token, classes) 230 if trailing: 231 self.stream.write(" ") 232 233 def _keyword(self, kw, leading=0, trailing=1): 234 self._reserved(kw, "keyword", leading, trailing) 235 236 def _doc(self, node): 237 if node.doc is not None: 238 self._docstring(node.doc) 239 240 def _docstring(self, s): 241 self.stream.write("<pre class='doc'>") 242 self.stream.write('"""') 243 output = textwrap.dedent(s.replace('"""', '\\"\\"\\"')) 244 self.stream.write(self._text(output)) 245 self.stream.write('"""') 246 self.stream.write("</pre>\n") 247 248 def _object_name_def(self, module, obj, classes=None): 249 250 """ 251 Link to the summary for 'module' using 'obj'. The optional 'classes' 252 can be used to customise the CSS classes employed. 253 """ 254 255 if isinstance(obj, Class) or (isinstance(obj, Function) and obj.is_method()): 256 self._summary_link(module.full_name(), obj.full_name(), obj.name, classes) 257 else: 258 self._span(obj.name, classes) 259 260 def _object_name_ref(self, module, obj, name=None, classes=None): 261 262 """ 263 Link to the definition for 'module' using 'obj' with the optional 'name' 264 used as the label (instead of the name of 'obj'). The optional 'classes' 265 can be used to customise the CSS classes employed. 266 """ 267 268 self._name_link(module.full_name(), obj.full_name(), name or obj.name, classes) 269 270 def _summary_link(self, module_name, full_name, name, classes=None): 271 self._name_link("%s-summary" % module_name, full_name, name, classes) 272 273 def _name_link(self, module_name, full_name, name, classes=None): 274 self.stream.write("<a class='%s' href='%s%sxhtml#%s'>%s</a>" % ( 275 classes or "specific-ref", module_name, os.path.extsep, 276 self._attr(full_name), self._text(name))) 277 278 def _scope(self, scope): 279 self.stream.write("<div class='scope'><span class='label'>scope</span><br />%s</div>\n" % scope) 280 281 def _assname(self, name, node): 282 self._span_start("assname") 283 if not self._attrcombined(name, node): 284 self._span(name) 285 self._span_end() 286 287 def _op(self, symbol, name=None, leading=0, trailing=1): 288 if leading: 289 self.stream.write(" ") 290 self._span_start(name and "operation" or None) 291 self._span(symbol, "operator") 292 if name is not None: 293 self._popup_start() 294 self.stream.write("<div class='opnames'>") 295 self._name_link("operator", "operator.%s" % name, name) 296 self.stream.write("</div>\n") 297 self._popup_end() 298 self._span_end() 299 if trailing: 300 self.stream.write(" ") 301 302 def _names_list(self, names, label, classes): 303 if not names: 304 return 305 names = list(names) 306 names.sort() 307 308 self.stream.write("<div class='%s'><span class='label'>%s</span><br />" % (classes, label)) 309 first = 1 310 for name in names: 311 if not first: 312 self.stream.write("<br />") 313 self.stream.write(name) 314 first = 0 315 self.stream.write("</div>\n") 316 317 def _attrcombined(self, name, node): 318 attrcombined = hasattr(node, "_attrcombined") and node._attrcombined.get(name) or [] 319 320 for attrnames in attrcombined: 321 if attrnames: 322 break 323 else: 324 return 0 325 326 self._name_start() 327 self.stream.write(name) 328 self._popup_start() 329 for attrnames in attrcombined: 330 self._attrnames(attrnames) 331 self._popup_end() 332 self._name_end() 333 return 1 334 335 def _attrnames(self, attrnames): 336 self._names_list(attrnames, "attributes", "attrnames") 337 338 def _typenames(self, typenames): 339 self._names_list(typenames, "types", "typenames") 340 341 def _accessor_start(self, target_names): 342 if target_names: 343 self._span_start("accessor") 344 self._popup_start("types-popup") 345 self._typenames(target_names) 346 self._popup_end() 347 348 def _accessor_end(self, target_names): 349 if target_names: 350 self._span_end() 351 352 # Summary classes. 353 354 class Summary(Writer): 355 356 "Summarise classes and attributes in modules." 357 358 def __init__(self, module, program): 359 self.module = module 360 self.program = program 361 362 def to_stream(self, stream): 363 364 "Write the summary to the given 'stream'." 365 366 self.stream = stream 367 self.stream.write(html_header % { 368 "title" : "Module: %s" % self.module.full_name() 369 }) 370 self._write_classes(self.module) 371 self.stream.write(html_footer) 372 373 def _write_classes(self, module): 374 375 all_classes = {} 376 377 for obj in self.module.all_objects: 378 if isinstance(obj, Class): 379 all_classes[obj.name] = obj 380 381 if all_classes: 382 383 all_class_names = all_classes.keys() 384 all_class_names.sort() 385 386 self.stream.write("<table cellspacing='5' cellpadding='5'>\n") 387 self.stream.write("<thead>\n") 388 self.stream.write("<tr>\n") 389 self.stream.write("<th>Classes</th><th>Attributes</th>\n") 390 self.stream.write("</tr>\n") 391 self.stream.write("</thead>\n") 392 393 for name in all_class_names: 394 self._write_class(all_classes[name]) 395 396 self.stream.write("</table>\n") 397 398 def _write_class(self, obj): 399 400 # Write the class... 401 402 self.stream.write("<tbody class='class'>\n") 403 self.stream.write("<tr>\n") 404 self.stream.write("<th class='summary-class' id='%s' rowspan='2'>" % self._attr(obj.full_name())) 405 self._object_name_ref(self.module, obj, classes="class-name") 406 self.stream.write("</th>\n") 407 408 # ...and instance attribute names in order... 409 410 attrs = obj.instance_attributes().values() 411 attrs.sort(cmp=lambda x, y: cmp(x.position, y.position)) 412 413 if attrs: 414 for attr in attrs: 415 self.stream.write("<td class='summary-attr'>%s</td>\n" % self._text(attr.name)) 416 else: 417 self.stream.write("<td class='summary-attr-absent'>None</td>\n") 418 419 self.stream.write("</tr>\n") 420 self.stream.write("<tr>\n") 421 422 # ...and class attribute names in order. 423 424 attrs = obj.class_attributes().values() 425 attrs.sort(cmp=lambda x, y: cmp(x.position, y.position)) 426 427 if attrs: 428 for attr in attrs: 429 if attr.is_strict_constant(): 430 value = attr.get_value() 431 if not isinstance(value, Const): 432 self.stream.write("<td class='summary-class-attr' id='%s'>" % self._attr(value.full_name())) 433 self._object_name_ref(self.module, value, attr.name, classes="summary-ref") 434 self.stream.write("</td>\n") 435 else: 436 self.stream.write("<td class='summary-class-attr'>%s</td>\n" % self._text(attr.name)) 437 else: 438 self.stream.write("<td class='summary-class-attr'>%s</td>\n" % self._text(attr.name)) 439 else: 440 self.stream.write("<td class='summary-class-attr-absent'>None</td>\n") 441 442 self.stream.write("</tr>\n") 443 self.stream.write("</tbody>\n") 444 445 class Interfaces(Writer): 446 447 "Summarise the interfaces used by reading the object table cache." 448 449 def __init__(self, program): 450 self.program = program 451 452 def to_stream(self, stream): 453 454 "Write the summary to the given 'stream'." 455 456 self.stream = stream 457 self.stream.write(html_header % { 458 "title" : "Interfaces" 459 }) 460 self._write_interfaces() 461 self.stream.write(html_footer) 462 463 def _write_interfaces(self): 464 objtable = self.program.get_object_table() 465 all_interfaces = objtable.all_cache.items() 466 any_interfaces = objtable.any_cache.items() 467 all_interfaces.sort() 468 any_interfaces.sort() 469 470 self.stream.write("<table cellspacing='5' cellpadding='5'>\n") 471 self.stream.write("<thead>\n") 472 self.stream.write("<tr>\n") 473 self.stream.write("<th>Complete Interfaces</th>\n") 474 self.stream.write("</tr>\n") 475 self.stream.write("</thead>\n") 476 self._write_interface_type(all_interfaces, "complete") 477 self.stream.write("<thead>\n") 478 self.stream.write("<tr>\n") 479 self.stream.write("<th>Partial Interfaces</th>\n") 480 self.stream.write("</tr>\n") 481 self.stream.write("</thead>\n") 482 self._write_interface_type(any_interfaces, "partial") 483 self.stream.write("</table>\n") 484 485 def _write_interface_type(self, interfaces, classes=""): 486 self.stream.write("<tbody>\n") 487 488 for names, objects in interfaces: 489 if names: 490 names = list(names) 491 names.sort() 492 self.stream.write("<tr>\n") 493 self.stream.write("<td class='summary-interface %s'>%s</td>" % (classes, ", ".join(names))) 494 self.stream.write("</tr>\n") 495 496 self.stream.write("</tbody>\n") 497 498 # Source code classes. 499 500 class AnnotatedSource(ASTVisitor, Writer): 501 502 "A module source code browser." 503 504 def __init__(self, module, program): 505 self.visitor = self 506 self.module = module 507 self.program = program 508 self.objtable = self.program.get_object_table() 509 self.paramtable = self.program.get_parameter_table() 510 511 def to_stream(self, stream): 512 513 "Write the annotated code to the given 'stream'." 514 515 self.stream = stream 516 self.stream.write(html_header % { 517 "title" : "Module: %s" % self.module.full_name() 518 }) 519 self.dispatch(self.module.astnode) 520 self.stream.write(html_footer) 521 522 def visitModule(self, node): 523 self.default(node) 524 525 # Statements. 526 527 def visitAssert(self, node): 528 self.stream.write("<div class='assert nowrap'>\n") 529 self._keyword("assert") 530 self.dispatch(node.test) 531 if node.fail: 532 self.stream.write(", ") 533 self.dispatch(node.fail) 534 self.stream.write("</div>\n") 535 536 def visitAssign(self, node): 537 self.stream.write("<div class='assign nowrap'>\n") 538 for lvalue in node.nodes: 539 self.dispatch(lvalue) 540 self.stream.write(" = ") 541 self.dispatch(node.expr) 542 self.stream.write("</div>\n") 543 544 def visitAugAssign(self, node): 545 self.stream.write("<div class='augassign nowrap'>\n") 546 self.dispatch(node.node) 547 self._op(node.op, operator_functions[node.op], 1) 548 self.dispatch(node.expr) 549 self.stream.write("</div>\n") 550 551 def visitBreak(self, node): 552 self.stream.write("<div class='break nowrap'>\n") 553 self._keyword("break") 554 self.stream.write("</div>\n") 555 556 def visitClass(self, node): 557 if not used_by_unit(node): 558 self._docstring('"Class %s not generated."' % node.name) 559 return 560 561 # Use inspected details where possible. 562 563 if hasattr(node, "unit"): 564 cls = node.unit 565 bases = cls.bases 566 self.stream.write("<div class='class nowrap' id='%s'>\n" % cls.full_name()) 567 else: 568 print "Warning: class %s not recognised!" % node.name 569 return 570 571 # Write the declaration line. 572 573 self.stream.write("<div>\n") 574 self._keyword("class") 575 self._object_name_def(self.module, cls, "class-name") 576 577 # Suppress the "object" class appearing alone. 578 579 if bases and not (len(bases) == 1 and bases[0].name == "object"): 580 self.stream.write("(") 581 first = 1 582 for base in bases: 583 if not first: 584 self.stream.write(", ") 585 586 self._object_name_ref(base.module, base) 587 588 first = 0 589 self.stream.write(")") 590 591 self.stream.write(":\n") 592 self.stream.write("</div>\n") 593 594 # Write the docstring and class body. 595 596 self.stream.write("<div class='body nowrap'>\n") 597 self._doc(node) 598 599 # NOTE: Some streams may not support tell. 600 601 x = self.stream.tell() 602 603 self.default(node.code) 604 605 # Check for no output. 606 607 if x == self.stream.tell(): 608 self.visitPass(None) 609 610 self.stream.write("</div>\n") 611 self.stream.write("</div>\n") 612 613 def visitContinue(self, node): 614 self.stream.write("<div class='continue nowrap'>\n") 615 self._keyword("continue") 616 self.stream.write("</div>\n") 617 618 def visitDiscard(self, node): 619 self.stream.write("<div class='discard nowrap'>\n") 620 self.default(node) 621 self.stream.write("</div>\n") 622 623 def visitFor(self, node): 624 self.stream.write("<div class='if nowrap'>\n") 625 self.stream.write("<div>\n") 626 self._keyword("for") 627 self.dispatch(node.assign) 628 self._keyword("in", 1) 629 self.dispatch(node.list) 630 self.stream.write(":\n") 631 self.stream.write("</div>\n") 632 self.stream.write("<div class='body nowrap'>\n") 633 self.dispatch(node.body) 634 self.stream.write("</div>\n") 635 if node.else_ is not None: 636 self.stream.write("<div>\n") 637 self._keyword("else", trailing=0) 638 self.stream.write(":\n") 639 self.stream.write("</div>\n") 640 self.stream.write("<div class='body nowrap'>\n") 641 self.dispatch(node.else_) 642 self.stream.write("</div>\n") 643 self.stream.write("</div>\n") 644 645 def visitFrom(self, node): 646 self.stream.write("<div class='from nowrap'>\n") 647 self._keyword("from") 648 self._name(node.modname) 649 self._keyword("import", 1) 650 first = 1 651 for name, alias in node.names: 652 if not first: 653 self.stream.write(", ") 654 if alias: 655 self.stream.write(name + " ") 656 self._keyword("as", 1) 657 self._name(alias or name) 658 first = 0 659 self.stream.write("</div>\n") 660 661 def visitFunction(self, node): 662 if not used_by_unit(node): 663 self._docstring('"Function %s not generated."' % node.name) 664 return 665 666 if hasattr(node, "unit"): 667 fn = node.unit 668 self.stream.write("<div class='function nowrap' id='%s'>\n" % fn.full_name()) 669 else: 670 print "Warning: function %s not recognised!" % node.name 671 return 672 673 # Write the declaration line. 674 675 self.stream.write("<div>\n") 676 self._keyword("def") 677 self._object_name_def(self.module, fn, "function-name") 678 679 self.stream.write("(") 680 self._parameters(fn, node) 681 self.stream.write(")") 682 self.stream.write(":\n") 683 self.stream.write("</div>\n") 684 685 self.stream.write("<div class='body nowrap'>\n") 686 self._doc(node) 687 self.dispatch(node.code) 688 self.stream.write("</div>\n") 689 self.stream.write("</div>\n") 690 691 def visitGlobal(self, node): 692 self.stream.write("<div class='global nowrap'>\n") 693 self._keyword("global") 694 first = 1 695 for name in node.names: 696 if not first: 697 self.stream.write(", ") 698 self.stream.write(name) 699 first = 0 700 self.stream.write("</div>\n") 701 702 def visitIf(self, node): 703 self.stream.write("<div class='if nowrap'>\n") 704 first = 1 705 for compare, stmt in node.tests: 706 self.stream.write("<div>\n") 707 if first: 708 self._keyword("if") 709 else: 710 self._keyword("elif") 711 self.dispatch(compare) 712 self.stream.write(":\n") 713 self.stream.write("</div>\n") 714 self.stream.write("<div class='body nowrap'>\n") 715 self.dispatch(stmt) 716 self.stream.write("</div>\n") 717 first = 0 718 if node.else_ is not None: 719 self.stream.write("<div>\n") 720 self._keyword("else", trailing=0) 721 self.stream.write(":\n") 722 self.stream.write("</div>\n") 723 self.stream.write("<div class='body nowrap'>\n") 724 self.dispatch(node.else_) 725 self.stream.write("</div>\n") 726 self.stream.write("</div>\n") 727 728 def visitImport(self, node): 729 self.stream.write("<div class='import nowrap'>\n") 730 self._keyword("import") 731 first = 1 732 for name, alias in node.names: 733 if not first: 734 self.stream.write(",\n") 735 if alias: 736 self.stream.write(name + " ") 737 self._keyword("as", 1) 738 self._name(alias or name) 739 first = 0 740 self.stream.write("</div>\n") 741 742 def visitPass(self, node): 743 self.stream.write("<div class='pass nowrap'>\n") 744 self._keyword("pass") 745 self.stream.write("</div>\n") 746 747 def visitPrint(self, node): 748 self.stream.write("<div class='print nowrap'>\n") 749 self._keyword("print") 750 if node.dest is not None: 751 self.stream.write(">>\n") 752 self.dispatch(node.dest) 753 for n in node.nodes: 754 self.dispatch(n) 755 self.stream.write(",\n") 756 self.stream.write("</div>\n") 757 758 def visitPrintnl(self, node): 759 self.stream.write("<div class='printnl nowrap'>\n") 760 self._keyword("print") 761 if node.dest is not None: 762 self.stream.write(">>\n") 763 self.dispatch(node.dest) 764 first = 1 765 for n in node.nodes: 766 if not first: 767 self.stream.write(",\n") 768 self.dispatch(n) 769 first = 0 770 self.stream.write("</div>\n") 771 772 def visitRaise(self, node): 773 self.stream.write("<div class='raise nowrap'>\n") 774 self._keyword("raise") 775 if node.expr1 is not None: 776 self.dispatch(node.expr1) 777 if node.expr2 is not None: 778 self.stream.write(",\n") 779 self.dispatch(node.expr2) 780 if node.expr3 is not None: 781 self.stream.write(",\n") 782 self.dispatch(node.expr3) 783 self.stream.write("</div>\n") 784 785 def visitReturn(self, node): 786 self.stream.write("<div class='return nowrap'>\n") 787 self._keyword("return") 788 self.dispatch(node.value) 789 self.stream.write("</div>\n") 790 791 def visitStmt(self, node): 792 self.stream.write("<div class='stmt nowrap'>\n") 793 self.default(node) 794 self.stream.write("</div>\n") 795 796 def visitTryExcept(self, node): 797 self.stream.write("<div class='tryexcept nowrap'>\n") 798 self.stream.write("<div>\n") 799 self._keyword("try", trailing=0) 800 self.stream.write(":\n") 801 self.stream.write("</div>\n") 802 self.stream.write("<div class='body nowrap'>\n") 803 self.dispatch(node.body) 804 self.stream.write("</div>\n") 805 for spec, assign, statement in node.handlers: 806 self.stream.write("<div>\n") 807 self._keyword("except") 808 if spec is not None: 809 self.dispatch(spec) 810 if assign is not None: 811 self.stream.write(",\n") 812 self.dispatch(assign) 813 self.stream.write(":\n") 814 self.stream.write("</div>\n") 815 self.stream.write("<div class='body nowrap'>\n") 816 self.dispatch(statement) 817 self.stream.write("</div>\n") 818 if node.else_ is not None: 819 self.stream.write("<div>\n") 820 self._keyword("else", trailing=0) 821 self.stream.write(":\n") 822 self.stream.write("</div>\n") 823 self.stream.write("<div class='body nowrap'>\n") 824 self.dispatch(node.else_) 825 self.stream.write("</div>\n") 826 self.stream.write("</div>\n") 827 828 def visitTryFinally(self, node): 829 self.stream.write("<div class='tryfinally nowrap'>\n") 830 self.stream.write("<div>\n") 831 self._keyword("try", trailing=0) 832 self.stream.write(":\n") 833 self.stream.write("</div>\n") 834 self.stream.write("<div class='body nowrap'>\n") 835 self.dispatch(node.body) 836 self.stream.write("</div>\n") 837 self.stream.write("<div>\n") 838 self._keyword("finally", trailing=0) 839 self.stream.write(":\n") 840 self.stream.write("</div>\n") 841 self.stream.write("<div class='body nowrap'>\n") 842 self.dispatch(node.final) 843 self.stream.write("</div>\n") 844 self.stream.write("</div>\n") 845 846 def visitWhile(self, node): 847 self.stream.write("<div class='while nowrap'>\n") 848 self.stream.write("<div>\n") 849 self._keyword("while") 850 self.dispatch(node.test) 851 self.stream.write(":\n") 852 self.stream.write("</div>\n") 853 self.stream.write("<div class='body nowrap'>\n") 854 self.dispatch(node.body) 855 self.stream.write("</div>\n") 856 if node.else_ is not None: 857 self.stream.write("<div>\n") 858 self._keyword("else", trailing=0) 859 self.stream.write(":\n") 860 self.stream.write("</div>\n") 861 self.stream.write("<div class='body nowrap'>\n") 862 self.dispatch(node.else_) 863 self.stream.write("</div>\n") 864 self.stream.write("</div>\n") 865 866 def visitYield(self, node): 867 self.stream.write("<div class='yield nowrap'>\n") 868 self._keyword("yield") 869 self.dispatch(node.value) 870 self.stream.write("</div>\n") 871 872 # Expression-related helper methods. 873 874 def _visitBitBinary(self, node, name, symbol): 875 self._span_start(name) 876 first = 1 877 for node in node.nodes: 878 if not first: 879 self._op(symbol, name, 1) 880 self.dispatch(node) 881 first = 0 882 self._span_end() 883 884 def _visitBinary(self, node, name, symbol): 885 self._span_start(name) 886 self.dispatch(node.left) 887 self._op(symbol, name, 1) 888 self.dispatch(node.right) 889 self._span_end() 890 891 def _visitUnary(self, node, name, symbol): 892 self._span_start(name) 893 self._op(symbol, name) 894 self.dispatch(node.expr) 895 self._span_end() 896 897 # Expressions. 898 899 def visitAdd(self, node): 900 self._visitBinary(node, "add", "+") 901 902 def visitAnd(self, node): 903 self._span_start("and") 904 first = 1 905 for n in node.nodes: 906 if not first: 907 self._keyword("and", 1) 908 self.dispatch(n) 909 first = 0 910 self._span_end() 911 912 def visitAssAttr(self, node): 913 target_names = ["%s%s" % (is_static and "class " or "", target_name) 914 for target_name, is_static in self.possible_accessor_types(node, defining_users=0)] 915 self._span_start("assattr") 916 self._accessor_start(target_names) 917 self.dispatch(node.expr) 918 self._accessor_end(target_names) 919 self._span_start("attr") 920 self.stream.write(".") 921 self._span(node.attrname, "attrname") 922 self._span_end() 923 self._span_end() 924 925 def visitAssList(self, node): 926 self._span_start("list") 927 self.stream.write("[") 928 self._sequence(node) 929 self.stream.write("]") 930 self._span_end() 931 932 def visitAssName(self, node): 933 self._assname(node.name, node) 934 935 def visitAssTuple(self, node): 936 self._span_start("tuple") 937 self.stream.write("(") 938 self._sequence(node) 939 self.stream.write(")") 940 self._span_end() 941 942 def visitBitand(self, node): 943 self._visitBitBinary(node, "bitand", "&") 944 945 def visitBitor(self, node): 946 self._visitBitBinary(node, "bitor", "|") 947 948 def visitBitxor(self, node): 949 self._visitBitBinary(node, "bitxor", "^") 950 951 def visitCallFunc(self, node): 952 self._span_start("callfunc") 953 self.dispatch(node.node) 954 self._span_start("call") 955 self.stream.write("(") 956 first = 1 957 for arg in node.args: 958 if not first: 959 self.stream.write(", ") 960 self.dispatch(arg) 961 first = 0 962 if node.star_args is not None: 963 if not first: 964 self.stream.write(", *") 965 self.dispatch(node.star_args) 966 first = 0 967 if node.dstar_args is not None: 968 if not first: 969 self.stream.write(", **") 970 self.dispatch(node.dstar_args) 971 first = 0 972 self.stream.write(")") 973 self._span_end() 974 self._span_end() 975 976 def visitCompare(self, node): 977 self._span_start("compare") 978 self.dispatch(node.expr) 979 for op_name, expr in node.ops: 980 self._op(op_name, operator_functions.get(op_name), 1) 981 self.dispatch(expr) 982 self._span_end() 983 984 def visitConst(self, node): 985 if isinstance(node.value, (str, unicode)): 986 self._span_start("str") 987 self.stream.write(self._text(repr(node.value))) 988 if isinstance(node.value, (str, unicode)): 989 self._span_end() 990 991 def visitDict(self, node): 992 self._span_start("dict") 993 self.stream.write("{") 994 self._mapping(node) 995 self.stream.write("}") 996 self._span_end() 997 998 def visitDiv(self, node): 999 self._visitBinary(node, "div", "/") 1000 1001 def visitFloorDiv(self, node): 1002 self._visitBinary(node, "floordiv", "//") 1003 1004 def visitGetattr(self, node): 1005 target_names = ["%s%s" % (is_static and "class " or "", target_name) 1006 for target_name, is_static in self.possible_accessor_types(node, defining_users=0)] 1007 self._span_start("getattr") 1008 self._accessor_start(target_names) 1009 self.dispatch(node.expr) 1010 self._accessor_end(target_names) 1011 self._span_start("attr") 1012 self.stream.write(".") 1013 self._span(node.attrname, "attrname") 1014 self._span_end() 1015 self._span_end() 1016 1017 def visitGenExpr(self, node): 1018 self._span_start("genexpr") 1019 self.stream.write("(") 1020 self.dispatch(node.code) 1021 self.stream.write(")") 1022 self._span_end() 1023 1024 def visitGenExprFor(self, node): 1025 self._span_start("genexprfor") 1026 self._keyword("for", 1) 1027 self._span_start("item") 1028 self.dispatch(node.assign) 1029 self._span_end() 1030 self._keyword("in", 1) 1031 self._span_start("collection") 1032 self.dispatch(node.iter) 1033 self._span_end() 1034 for if_ in node.ifs: 1035 self.dispatch(if_) 1036 self._span_end() 1037 1038 def visitGenExprIf(self, node): 1039 self._span_start("genexprif") 1040 self._span_start("conditional") 1041 self._keyword("if", 1) 1042 self.dispatch(node.test) 1043 self._span_end() 1044 self._span_end() 1045 1046 def visitGenExprInner(self, node): 1047 self._span_start("genexprinner") 1048 self.dispatch(node.expr) 1049 for qual in node.quals: 1050 self.dispatch(qual) 1051 self._span_end() 1052 1053 def visitIfExp(self, node): 1054 self._span_start("ifexp") 1055 self.dispatch(node.then) 1056 self._keyword("if") 1057 self.dispatch(node.test) 1058 self._keyword("else") 1059 self.dispatch(node.else_) 1060 self._span_end() 1061 1062 def visitKeyword(self, node): 1063 self._span_start("keyword-arg") 1064 self.stream.write(node.name) 1065 self.stream.write("=") 1066 self.dispatch(node.expr) 1067 self._span_end() 1068 1069 def visitLambda(self, node): 1070 if hasattr(node, "unit"): 1071 fn = node.unit 1072 else: 1073 print "Warning: function %s not recognised!" % node.name 1074 return 1075 1076 self._span_start("lambda") 1077 self._keyword("lambda") 1078 self._parameters(fn, node) 1079 self.stream.write(": ") 1080 self._span_start("code") 1081 self.dispatch(node.code) 1082 self._span_end() 1083 self._span_end() 1084 1085 def visitLeftShift(self, node): 1086 self._visitBinary(node, "lshift", "<<") 1087 1088 visitList = visitAssList 1089 1090 def visitListComp(self, node): 1091 self._span_start("listcomp") 1092 self.stream.write("[") 1093 self.dispatch(node.expr) 1094 for qual in node.quals: 1095 self.dispatch(qual) 1096 self.stream.write("]") 1097 self._span_end() 1098 1099 def visitListCompFor(self, node): 1100 self._span_start("listcompfor") 1101 self._keyword("for", 1) 1102 self._span_start("item") 1103 self.dispatch(node.assign) 1104 self._span_end() 1105 self._keyword("in", 1) 1106 self._span_start("collection") 1107 self.dispatch(node.list) 1108 self._span_end() 1109 for if_ in node.ifs: 1110 self.dispatch(if_) 1111 self._span_end() 1112 1113 def visitListCompIf(self, node): 1114 self._span_start("listcompif") 1115 self._span_start("conditional") 1116 self._keyword("if", 1) 1117 self.dispatch(node.test) 1118 self._span_end() 1119 self._span_end() 1120 1121 def visitMod(self, node): 1122 self._visitBinary(node, "mod", "%") 1123 1124 def visitMul(self, node): 1125 self._visitBinary(node, "mul", "*") 1126 1127 def visitName(self, node): 1128 if hasattr(node, "_scope"): 1129 scope = node._scope 1130 self._name_start() 1131 self.stream.write(node.name) 1132 self._popup_start() 1133 self._scope(scope) 1134 self._popup_end() 1135 self._name_end() 1136 else: 1137 self._span(node.name) 1138 1139 def visitNot(self, node): 1140 self._span_start("not") 1141 self._keyword("not") 1142 self.dispatch(node.expr) 1143 self._span_end() 1144 1145 def visitOr(self, node): 1146 self._span_start("or") 1147 first = 1 1148 for n in node.nodes: 1149 if not first: 1150 self._keyword("or", 1) 1151 self.dispatch(n) 1152 first = 0 1153 self._span_end() 1154 1155 def visitPower(self, node): 1156 self._visitBinary(node, "pow", "**") 1157 1158 def visitRightShift(self, node): 1159 self._visitBinary(node, "rshift", ">>") 1160 1161 def visitSlice(self, node): 1162 self._span_start("slice") 1163 self.dispatch(node.expr) 1164 self.stream.write("[") 1165 if node.lower: 1166 self.dispatch(node.lower) 1167 self.stream.write(":") 1168 if node.upper: 1169 self.dispatch(node.upper) 1170 # NOTE: Step? 1171 self.stream.write("]") 1172 self._span_end() 1173 1174 def visitSliceobj(self, node): 1175 self._span_start("sliceobj") 1176 first = 1 1177 for n in node.nodes: 1178 if not first: 1179 self.stream.write(":") 1180 self.dispatch(n) 1181 self._span_end() 1182 1183 def visitSub(self, node): 1184 self._visitBinary(node, "sub", "-") 1185 1186 def visitSubscript(self, node): 1187 self._span_start("subscript") 1188 self.dispatch(node.expr) 1189 self.stream.write("[") 1190 first = 1 1191 for sub in node.subs: 1192 if not first: 1193 self.stream.write(", ") 1194 self.dispatch(sub) 1195 first = 0 1196 self.stream.write("]") 1197 self._span_end() 1198 1199 visitTuple = visitAssTuple 1200 1201 def visitUnaryAdd(self, node): 1202 self._visitUnary(node, "add", "+") 1203 1204 def visitUnarySub(self, node): 1205 self._visitUnary(node, "sub", "-") 1206 1207 # Output preparation methods. 1208 1209 def _sequence(self, node): 1210 first = 1 1211 for n in node.nodes: 1212 if not first: 1213 self.stream.write(", ") 1214 self.dispatch(n) 1215 first = 0 1216 1217 def _mapping(self, node): 1218 first = 1 1219 for k, v in node.items: 1220 if not first: 1221 self.stream.write(", ") 1222 self.dispatch(k) 1223 self.stream.write(" : ") 1224 self.dispatch(v) 1225 first = 0 1226 1227 def _parameters(self, fn, node): 1228 nparams = len(fn.positional_names) 1229 ndefaults = len(fn.defaults) 1230 first_with_default = nparams - ndefaults 1231 1232 first = 1 1233 for n, param in enumerate(fn.positional_names): 1234 if not first: 1235 self.stream.write(", ") 1236 1237 # Handle tuple parameters. 1238 1239 if isinstance(param, tuple): 1240 self._tuple_parameter(param, node) 1241 else: 1242 self._assname(param, node) 1243 1244 n_default = n - first_with_default 1245 if n_default >= 0: 1246 self._default(fn.defaults[n_default]) 1247 first = 0 1248 1249 if fn.has_star: 1250 if not first: 1251 self.stream.write(", *") 1252 self._name(fn.star_name) 1253 1254 if fn.has_dstar: 1255 if not first: 1256 self.stream.write(", **") 1257 self._name(fn.dstar_name) 1258 1259 def _tuple_parameter(self, parameters, node): 1260 self.stream.write("(") 1261 1262 first = 1 1263 for param in parameters: 1264 if not first: 1265 self.stream.write(", ") 1266 1267 # Handle tuples. 1268 1269 if isinstance(param, tuple): 1270 self._tuple_parameter(param, node) 1271 else: 1272 self._assname(param, node) 1273 1274 first = 0 1275 1276 self.stream.write(")") 1277 1278 def _default(self, default): 1279 self.stream.write("=") 1280 self.dispatch(default) 1281 1282 # Convenience functions. 1283 1284 def summarise(module, program, filename): 1285 stream = open(filename, "wb") 1286 try: 1287 summary = Summary(module, program) 1288 summary.to_stream(stream) 1289 finally: 1290 stream.close() 1291 1292 def annotate(module, program, filename): 1293 stream = open(filename, "wb") 1294 try: 1295 source = AnnotatedSource(module, program) 1296 source.to_stream(stream) 1297 finally: 1298 stream.close() 1299 1300 def interfaces(program, filename): 1301 stream = open(filename, "wb") 1302 try: 1303 source = Interfaces(program) 1304 source.to_stream(stream) 1305 finally: 1306 stream.close() 1307 1308 def report(program, directory): 1309 if not exists(directory): 1310 os.mkdir(directory) 1311 1312 for module in program.get_importer().get_modules(): 1313 annotate(module, program, join(directory, "%s%sxhtml" % (module.full_name(), extsep))) 1314 summarise(module, program, join(directory, "%s-summary%sxhtml" % (module.full_name(), extsep))) 1315 1316 interfaces(program, join(directory, "-interfaces%sxhtml" % extsep)) 1317 1318 # vim: tabstop=4 expandtab shiftwidth=4