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