1 #!/usr/bin/env python 2 3 """ 4 View annotated sources. 5 6 Copyright (C) 2006, 2007, 2010 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>Module: %(full_name)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 .summary-attr { 129 background-color: #070; 130 font-size: smaller; 131 } 132 133 .summary-attr-absent { 134 border-left: 0.2em solid #070; 135 font-size: small; 136 } 137 138 .summary-class-attr { 139 background-color: #007; 140 font-size: smaller; 141 } 142 143 .summary-class-attr-absent { 144 border-left: 0.2em solid #007; 145 font-size: small; 146 } 147 148 .summary-ref { 149 color: white; 150 } 151 152 </style> 153 </head> 154 <body> 155 """ 156 157 html_footer = """</body> 158 </html> 159 """ 160 161 # Utility classes. 162 163 class Writer: 164 165 "A utility class providing useful HTML output methods." 166 167 # Methods which return strings. 168 169 def _text(self, text): 170 return text.replace("&", "&").replace("<", "<").replace(">", ">") 171 172 def _attr(self, attr): 173 return self._text(attr).replace("'", "'").replace('"', """) 174 175 def _url(self, url): 176 return self._attr(url).replace("#", "%23").replace("-", "%2d") 177 178 # Methods which write to the stream. 179 180 def _span_start(self, classes=None): 181 self.stream.write("<span class='%s'>" % (classes or "")) 182 183 def _span_end(self): 184 self.stream.write("</span>") 185 186 def _span(self, value, classes=None): 187 self._span_start(classes) 188 self.stream.write(self._text(value)) 189 self._span_end() 190 191 def _name_start(self, classes=None): 192 self._span_start(classes or "name") 193 194 _name_end = _span_end 195 196 def _name(self, name, classes=None): 197 self._name_start(classes) 198 self.stream.write(self._text(name)) 199 self._name_end() 200 201 def _popup_start(self, classes=None): 202 self._span_start(classes or "popup") 203 204 _popup_end = _span_end 205 206 def _comment(self, comment): 207 self._span("# %s" % comment, "comment") 208 self.stream.write("\n") 209 210 def _reserved(self, token, classes, leading=0, trailing=1): 211 if leading: 212 self.stream.write(" ") 213 self._span(token, classes) 214 if trailing: 215 self.stream.write(" ") 216 217 def _keyword(self, kw, leading=0, trailing=1): 218 self._reserved(kw, "keyword", leading, trailing) 219 220 def _doc(self, node): 221 if node.doc is not None: 222 self._docstring(node.doc) 223 224 def _docstring(self, s): 225 self.stream.write("<pre class='doc'>") 226 self.stream.write('"""') 227 output = textwrap.dedent(s.replace('"""', '\\"\\"\\"')) 228 self.stream.write(self._text(output)) 229 self.stream.write('"""') 230 self.stream.write("</pre>\n") 231 232 def _object_name_def(self, module, obj, classes=None): 233 234 """ 235 Link to the summary for 'module' using 'obj'. The optional 'classes' 236 can be used to customise the CSS classes employed. 237 """ 238 239 self._summary_link(module.full_name(), obj.full_name(), obj.name, classes) 240 241 def _object_name_ref(self, module, obj, name=None, classes=None): 242 243 """ 244 Link to the definition for 'module' using 'obj' with the optional 'name' 245 used as the label (instead of the name of 'obj'). The optional 'classes' 246 can be used to customise the CSS classes employed. 247 """ 248 249 self._name_link(module.full_name(), obj.full_name(), name or obj.name, classes) 250 251 def _summary_link(self, module_name, full_name, name, classes=None): 252 self._name_link("%s-summary" % module_name, full_name, name, classes) 253 254 def _name_link(self, module_name, full_name, name, classes=None): 255 self.stream.write("<a class='%s' href='%s%sxhtml#%s'>%s</a>" % ( 256 classes or "specific-ref", module_name, os.path.extsep, 257 self._attr(full_name), self._text(name))) 258 259 def _scope(self, scope): 260 self.stream.write("<div class='scope'><span class='label'>scope</span><br />%s</div>\n" % scope) 261 262 def _assname(self, name, node): 263 self._span_start("assname") 264 if not self._attrcombined(name, node): 265 self._span(name) 266 self._span_end() 267 268 def _op(self, symbol, name=None, leading=0, trailing=1): 269 if leading: 270 self.stream.write(" ") 271 self._span_start("operation") 272 self._span(symbol, "operator") 273 if name is not None: 274 self._popup_start() 275 self.stream.write("<div class='opnames'>") 276 self._name_link("operator", "operator.%s" % name, name) 277 self.stream.write("</div>\n") 278 self._popup_end() 279 self._span_end() 280 if trailing: 281 self.stream.write(" ") 282 283 def _names_list(self, names, label, classes): 284 if not names: 285 return 286 names = list(names) 287 names.sort() 288 289 self.stream.write("<div class='%s'><span class='label'>%s</span><br />" % (classes, label)) 290 first = 1 291 for name in names: 292 if not first: 293 self.stream.write("<br />") 294 self.stream.write(name) 295 first = 0 296 self.stream.write("</div>\n") 297 298 def _attrcombined(self, name, node): 299 attrcombined = hasattr(node, "_attrcombined") and node._attrcombined.get(name) or [] 300 301 for attrnames in attrcombined: 302 if attrnames: 303 break 304 else: 305 return 0 306 307 self._name_start() 308 self.stream.write(name) 309 self._popup_start() 310 for attrnames in attrcombined: 311 self._attrnames(attrnames) 312 self._popup_end() 313 self._name_end() 314 return 1 315 316 def _attrnames(self, attrnames): 317 self._names_list(attrnames, "attributes", "attrnames") 318 319 def _typenames(self, typenames): 320 self._names_list(typenames, "types", "typenames") 321 322 def _accessor_start(self, target_names): 323 if target_names: 324 self._span_start("accessor") 325 self._popup_start("types-popup") 326 self._typenames(target_names) 327 self._popup_end() 328 329 def _accessor_end(self, target_names): 330 if target_names: 331 self._span_end() 332 333 # Summary classes. 334 335 class Summary(Writer): 336 337 "Summarise classes and attributes in modules." 338 339 def __init__(self, module, program): 340 self.module = module 341 self.program = program 342 343 def to_stream(self, stream): 344 345 "Write the summary to the given 'stream'." 346 347 self.stream = stream 348 self.stream.write(html_header % {"full_name" : self.module.full_name()}) 349 self._write_classes(self.module) 350 self.stream.write(html_footer) 351 352 def _write_classes(self, module): 353 354 all_classes = {} 355 356 for obj in self.module.all_objects: 357 if isinstance(obj, Class): 358 all_classes[obj.name] = obj 359 360 if all_classes: 361 362 all_class_names = all_classes.keys() 363 all_class_names.sort() 364 365 self.stream.write("<table cellspacing='5' cellpadding='5'>\n") 366 self.stream.write("<thead>\n") 367 self.stream.write("<tr>\n") 368 self.stream.write("<th>Classes</th><th>Attributes</th>\n") 369 self.stream.write("</tr>\n") 370 self.stream.write("</thead>\n") 371 372 for name in all_class_names: 373 self._write_class(all_classes[name]) 374 375 self.stream.write("</table>\n") 376 377 def _write_class(self, obj): 378 379 # Write the class... 380 381 self.stream.write("<tbody class='class'>\n") 382 self.stream.write("<tr>\n") 383 self.stream.write("<th class='summary-class' id='%s' rowspan='2'>" % self._attr(obj.full_name())) 384 self._object_name_ref(self.module, obj, classes="class-name") 385 self.stream.write("</th>\n") 386 387 # ...and instance attribute names in order... 388 389 attrs = obj.instance_attributes().values() 390 attrs.sort(cmp=lambda x, y: cmp(x.position, y.position)) 391 392 if attrs: 393 for attr in attrs: 394 self.stream.write("<td class='summary-attr'>%s</td>\n" % self._text(attr.name)) 395 else: 396 self.stream.write("<td class='summary-attr-absent'>None</td>\n") 397 398 self.stream.write("</tr>\n") 399 self.stream.write("<tr>\n") 400 401 # ...and class attribute names in order. 402 403 attrs = obj.class_attributes().values() 404 attrs.sort(cmp=lambda x, y: cmp(x.position, y.position)) 405 406 if attrs: 407 for attr in attrs: 408 if attr.is_strict_constant(): 409 value = attr.get_value() 410 self.stream.write("<td class='summary-class-attr' id='%s'>" % self._attr(value.full_name())) 411 self._object_name_ref(self.module, value, attr.name, classes="summary-ref") 412 self.stream.write("</td>\n") 413 else: 414 self.stream.write("<td class='summary-class-attr'>%s</td>\n" % self._text(attr.name)) 415 else: 416 self.stream.write("<td class='summary-class-attr-absent'>None</td>\n") 417 418 self.stream.write("</tr>\n") 419 self.stream.write("</tbody>\n") 420 421 # Source code classes. 422 423 class AnnotatedSource(ASTVisitor, Writer): 424 425 "A module source code browser." 426 427 def __init__(self, module, program): 428 ASTVisitor.__init__(self) 429 self.visitor = self 430 self.module = module 431 self.program = program 432 self.objtable = self.program.get_object_table() 433 self.paramtable = self.program.get_parameter_table() 434 435 def to_stream(self, stream): 436 437 "Write the annotated code to the given 'stream'." 438 439 self.stream = stream 440 self.stream.write(html_header % {"full_name" : self.module.full_name()}) 441 self.dispatch(self.module.astnode) 442 self.stream.write(html_footer) 443 444 def visitModule(self, node): 445 self.default(node) 446 447 # Statements. 448 449 def visitAssert(self, node): 450 self.stream.write("<div class='assert nowrap'>\n") 451 self._keyword("assert") 452 self.dispatch(node.test) 453 if node.fail: 454 self.stream.write(", ") 455 self.dispatch(node.fail) 456 self.stream.write("</div>\n") 457 458 def visitAssign(self, node): 459 self.stream.write("<div class='assign nowrap'>\n") 460 for lvalue in node.nodes: 461 self.dispatch(lvalue) 462 self.stream.write(" = ") 463 self.dispatch(node.expr) 464 self.stream.write("</div>\n") 465 466 def visitAugAssign(self, node): 467 self.stream.write("<div class='augassign nowrap'>\n") 468 self.dispatch(node.node) 469 self._op(node.op, operator_functions[node.op], 1) 470 self.dispatch(node.expr) 471 self.stream.write("</div>\n") 472 473 def visitBreak(self, node): 474 self.stream.write("<div class='break nowrap'>\n") 475 self._keyword("break") 476 self.stream.write("</div>\n") 477 478 def visitClass(self, node): 479 if not used_by_unit(node): 480 self._docstring('"Class %s not generated."' % node.name) 481 return 482 483 # Use inspected details where possible. 484 485 if hasattr(node, "unit"): 486 cls = node.unit 487 bases = cls.bases 488 self.stream.write("<div class='class nowrap' id='%s'>\n" % cls.full_name()) 489 else: 490 print "Warning: class %s not recognised!" % node.name 491 return 492 493 # Write the declaration line. 494 495 self.stream.write("<div>\n") 496 self._keyword("class") 497 self._object_name_def(self.module, cls, "class-name") 498 499 # Suppress the "object" class appearing alone. 500 501 if bases and not (len(bases) == 1 and bases[0].name == "object"): 502 self.stream.write("(") 503 first = 1 504 for base in bases: 505 if not first: 506 self.stream.write(", ") 507 508 self._object_name_ref(base.module, base) 509 510 first = 0 511 self.stream.write(")") 512 513 self.stream.write(":\n") 514 self.stream.write("</div>\n") 515 516 # Write the docstring and class body. 517 518 self.stream.write("<div class='body nowrap'>\n") 519 self._doc(node) 520 521 # NOTE: Some streams may not support tell. 522 523 x = self.stream.tell() 524 525 self.default(node.code) 526 527 # Check for no output. 528 529 if x == self.stream.tell(): 530 self.visitPass(None) 531 532 self.stream.write("</div>\n") 533 self.stream.write("</div>\n") 534 535 def visitContinue(self, node): 536 self.stream.write("<div class='continue nowrap'>\n") 537 self._keyword("continue") 538 self.stream.write("</div>\n") 539 540 def visitDiscard(self, node): 541 self.stream.write("<div class='discard nowrap'>\n") 542 self.default(node) 543 self.stream.write("</div>\n") 544 545 def visitFor(self, node): 546 self.stream.write("<div class='if nowrap'>\n") 547 self.stream.write("<div>\n") 548 self._keyword("for") 549 self.dispatch(node.assign) 550 self._keyword("in", 1) 551 self.dispatch(node.list) 552 self.stream.write(":\n") 553 self.stream.write("</div>\n") 554 self.stream.write("<div class='body nowrap'>\n") 555 self.dispatch(node.body) 556 self.stream.write("</div>\n") 557 if node.else_ is not None: 558 self.stream.write("<div>\n") 559 self._keyword("else", trailing=0) 560 self.stream.write(":\n") 561 self.stream.write("</div>\n") 562 self.stream.write("<div class='body nowrap'>\n") 563 self.dispatch(node.else_) 564 self.stream.write("</div>\n") 565 self.stream.write("</div>\n") 566 567 def visitFrom(self, node): 568 self.stream.write("<div class='from nowrap'>\n") 569 self._keyword("from") 570 self._name(node.modname) 571 self._keyword("import", 1) 572 first = 1 573 for name, alias in node.names: 574 if not first: 575 self.stream.write(", ") 576 if alias: 577 self.stream.write(name + " ") 578 self._keyword("as", 1) 579 self._name(alias or name) 580 first = 0 581 self.stream.write("</div>\n") 582 583 def visitFunction(self, node): 584 if not used_by_unit(node): 585 self._docstring('"Function %s not generated."' % node.name) 586 return 587 588 if hasattr(node, "unit"): 589 fn = node.unit 590 self.stream.write("<div class='function nowrap' id='%s'>\n" % fn.full_name()) 591 else: 592 print "Warning: function %s not recognised!" % node.name 593 return 594 595 # Write the declaration line. 596 597 self.stream.write("<div>\n") 598 self._keyword("def") 599 self._object_name_def(self.module, fn, "function-name") 600 601 self.stream.write("(") 602 self._parameters(fn, node) 603 self.stream.write(")") 604 self.stream.write(":\n") 605 self.stream.write("</div>\n") 606 607 self.stream.write("<div class='body nowrap'>\n") 608 self._doc(node) 609 self.dispatch(node.code) 610 self.stream.write("</div>\n") 611 self.stream.write("</div>\n") 612 613 def visitGlobal(self, node): 614 self.stream.write("<div class='global nowrap'>\n") 615 self._keyword("global") 616 first = 1 617 for name in node.names: 618 if not first: 619 self.stream.write(", ") 620 self.stream.write(name) 621 first = 0 622 self.stream.write("</div>\n") 623 624 def visitIf(self, node): 625 self.stream.write("<div class='if nowrap'>\n") 626 first = 1 627 for compare, stmt in node.tests: 628 self.stream.write("<div>\n") 629 if first: 630 self._keyword("if") 631 else: 632 self._keyword("elif") 633 self.dispatch(compare) 634 self.stream.write(":\n") 635 self.stream.write("</div>\n") 636 self.stream.write("<div class='body nowrap'>\n") 637 self.dispatch(stmt) 638 self.stream.write("</div>\n") 639 first = 0 640 if node.else_ is not None: 641 self.stream.write("<div>\n") 642 self._keyword("else", trailing=0) 643 self.stream.write(":\n") 644 self.stream.write("</div>\n") 645 self.stream.write("<div class='body nowrap'>\n") 646 self.dispatch(node.else_) 647 self.stream.write("</div>\n") 648 self.stream.write("</div>\n") 649 650 def visitImport(self, node): 651 self.stream.write("<div class='import nowrap'>\n") 652 self._keyword("import") 653 first = 1 654 for name, alias in node.names: 655 if not first: 656 self.stream.write(",\n") 657 if alias: 658 self.stream.write(name + " ") 659 self._keyword("as", 1) 660 self._name(alias or name) 661 first = 0 662 self.stream.write("</div>\n") 663 664 def visitPass(self, node): 665 self.stream.write("<div class='pass nowrap'>\n") 666 self._keyword("pass") 667 self.stream.write("</div>\n") 668 669 def visitPrint(self, node): 670 self.stream.write("<div class='print nowrap'>\n") 671 self._keyword("print") 672 if node.dest is not None: 673 self.stream.write(">>\n") 674 self.dispatch(node.dest) 675 for n in node.nodes: 676 self.dispatch(n) 677 self.stream.write(",\n") 678 self.stream.write("</div>\n") 679 680 def visitPrintnl(self, node): 681 self.stream.write("<div class='printnl nowrap'>\n") 682 self._keyword("print") 683 if node.dest is not None: 684 self.stream.write(">>\n") 685 self.dispatch(node.dest) 686 first = 1 687 for n in node.nodes: 688 if not first: 689 self.stream.write(",\n") 690 self.dispatch(n) 691 first = 0 692 self.stream.write("</div>\n") 693 694 def visitRaise(self, node): 695 self.stream.write("<div class='raise nowrap'>\n") 696 self._keyword("raise") 697 if node.expr1 is not None: 698 self.dispatch(node.expr1) 699 if node.expr2 is not None: 700 self.stream.write(",\n") 701 self.dispatch(node.expr2) 702 if node.expr3 is not None: 703 self.stream.write(",\n") 704 self.dispatch(node.expr3) 705 self.stream.write("</div>\n") 706 707 def visitReturn(self, node): 708 self.stream.write("<div class='return nowrap'>\n") 709 self._keyword("return") 710 self.dispatch(node.value) 711 self.stream.write("</div>\n") 712 713 def visitStmt(self, node): 714 self.stream.write("<div class='stmt nowrap'>\n") 715 self.default(node) 716 self.stream.write("</div>\n") 717 718 def visitTryExcept(self, node): 719 self.stream.write("<div class='tryexcept nowrap'>\n") 720 self.stream.write("<div>\n") 721 self._keyword("try", trailing=0) 722 self.stream.write(":\n") 723 self.stream.write("</div>\n") 724 self.stream.write("<div class='body nowrap'>\n") 725 self.dispatch(node.body) 726 self.stream.write("</div>\n") 727 for spec, assign, statement in node.handlers: 728 self.stream.write("<div>\n") 729 self._keyword("except") 730 if spec is not None: 731 self.dispatch(spec) 732 if assign is not None: 733 self.stream.write(",\n") 734 self.dispatch(assign) 735 self.stream.write(":\n") 736 self.stream.write("</div>\n") 737 self.stream.write("<div class='body nowrap'>\n") 738 self.dispatch(statement) 739 self.stream.write("</div>\n") 740 if node.else_ is not None: 741 self.stream.write("<div>\n") 742 self._keyword("else", trailing=0) 743 self.stream.write(":\n") 744 self.stream.write("</div>\n") 745 self.stream.write("<div class='body nowrap'>\n") 746 self.dispatch(node.else_) 747 self.stream.write("</div>\n") 748 self.stream.write("</div>\n") 749 750 def visitTryFinally(self, node): 751 self.stream.write("<div class='tryfinally nowrap'>\n") 752 self.stream.write("<div>\n") 753 self._keyword("try", trailing=0) 754 self.stream.write(":\n") 755 self.stream.write("</div>\n") 756 self.stream.write("<div class='body nowrap'>\n") 757 self.dispatch(node.body) 758 self.stream.write("</div>\n") 759 self.stream.write("<div>\n") 760 self._keyword("finally", trailing=0) 761 self.stream.write(":\n") 762 self.stream.write("</div>\n") 763 self.stream.write("<div class='body nowrap'>\n") 764 self.dispatch(node.final) 765 self.stream.write("</div>\n") 766 self.stream.write("</div>\n") 767 768 def visitWhile(self, node): 769 self.stream.write("<div class='while nowrap'>\n") 770 self.stream.write("<div>\n") 771 self._keyword("while") 772 self.dispatch(node.test) 773 self.stream.write(":\n") 774 self.stream.write("</div>\n") 775 self.stream.write("<div class='body nowrap'>\n") 776 self.dispatch(node.body) 777 self.stream.write("</div>\n") 778 if node.else_ is not None: 779 self.stream.write("<div>\n") 780 self._keyword("else", trailing=0) 781 self.stream.write(":\n") 782 self.stream.write("</div>\n") 783 self.stream.write("<div class='body nowrap'>\n") 784 self.dispatch(node.else_) 785 self.stream.write("</div>\n") 786 self.stream.write("</div>\n") 787 788 # Expression-related helper methods. 789 790 def _visitBitBinary(self, node, name, symbol): 791 self._span_start(name) 792 first = 1 793 for node in node.nodes: 794 if not first: 795 self._op(symbol, name, 1) 796 self.dispatch(node) 797 first = 0 798 self._span_end() 799 800 def _visitBinary(self, node, name, symbol): 801 self._span_start(name) 802 self.dispatch(node.left) 803 self._op(symbol, name, 1) 804 self.dispatch(node.right) 805 self._span_end() 806 807 def _visitUnary(self, node, name, symbol): 808 self._span_start(name) 809 self._op(symbol, name) 810 self.dispatch(node.expr) 811 self._span_end() 812 813 # Expressions. 814 815 def visitAdd(self, node): 816 self._visitBinary(node, "add", "+") 817 818 def visitAnd(self, node): 819 self._span_start("and") 820 first = 1 821 for n in node.nodes: 822 if not first: 823 self._keyword("and", 1) 824 self.dispatch(n) 825 first = 0 826 self._span_end() 827 828 def visitAssAttr(self, node): 829 target_names = ["%s%s" % (is_static and "class " or "", target_name) 830 for target_name, is_static in self.possible_accessor_types(node)] 831 self._span_start("assattr") 832 self._accessor_start(target_names) 833 self.dispatch(node.expr) 834 self._accessor_end(target_names) 835 self._span_start("attr") 836 self.stream.write(".") 837 self._name(node.attrname) 838 self._span_end() 839 self._span_end() 840 841 def visitAssList(self, node): 842 self._span_start("list") 843 self.stream.write("[") 844 self._sequence(node) 845 self.stream.write("]") 846 self._span_end() 847 848 def visitAssName(self, node): 849 self._assname(node.name, node) 850 851 def visitAssTuple(self, node): 852 self._span_start("tuple") 853 self.stream.write("(") 854 self._sequence(node) 855 self.stream.write(")") 856 self._span_end() 857 858 def visitBitand(self, node): 859 self._visitBitBinary(node, "bitand", "&") 860 861 def visitCallFunc(self, node): 862 self._span_start("callfunc") 863 self.dispatch(node.node) 864 self._span_start("call") 865 self.stream.write("(") 866 first = 1 867 for arg in node.args: 868 if not first: 869 self.stream.write(", ") 870 self.dispatch(arg) 871 first = 0 872 if node.star_args is not None: 873 if not first: 874 self.stream.write(", *") 875 self.dispatch(node.star_args) 876 first = 0 877 if node.dstar_args is not None: 878 if not first: 879 self.stream.write(", **") 880 self.dispatch(node.dstar_args) 881 first = 0 882 self.stream.write(")") 883 self._span_end() 884 self._span_end() 885 886 def visitCompare(self, node): 887 self._span_start("compare") 888 self.dispatch(node.expr) 889 for op_name, expr in node.ops: 890 self._op(op_name, operator_functions.get(op_name), 1) 891 self.dispatch(expr) 892 self._span_end() 893 894 def visitConst(self, node): 895 if isinstance(node.value, (str, unicode)): 896 self._span_start("str") 897 self.stream.write(self._text(repr(node.value))) 898 if isinstance(node.value, (str, unicode)): 899 self._span_end() 900 901 def visitDict(self, node): 902 self._span_start("dict") 903 self.stream.write("{") 904 self._mapping(node) 905 self.stream.write("}") 906 self._span_end() 907 908 def visitDiv(self, node): 909 self._visitBinary(node, "div", "/") 910 911 def visitFloorDiv(self, node): 912 self._visitBinary(node, "floordiv", "//") 913 914 def visitGetattr(self, node): 915 target_names = ["%s%s" % (is_static and "class " or "", target_name) 916 for target_name, is_static in self.possible_accessor_types(node)] 917 self._span_start("getattr") 918 self._accessor_start(target_names) 919 self.dispatch(node.expr) 920 self._accessor_end(target_names) 921 self._span_start("attr") 922 self.stream.write(".") 923 self._span(node.attrname, "attrname") 924 self._span_end() 925 self._span_end() 926 927 def visitKeyword(self, node): 928 self._span_start("keyword-arg") 929 self.stream.write(node.name) 930 self.stream.write("=") 931 self.dispatch(node.expr) 932 self._span_end() 933 934 def visitLambda(self, node): 935 if hasattr(node, "unit"): 936 fn = node.unit 937 else: 938 print "Warning: function %s not recognised!" % node.name 939 return 940 941 self._span_start("lambda") 942 self._keyword("lambda") 943 self._parameters(fn, node) 944 self.stream.write(": ") 945 self._span_start("code") 946 self.dispatch(node.code) 947 self._span_end() 948 self._span_end() 949 950 visitList = visitAssList 951 952 def visitListComp(self, node): 953 self._span_start("listcomp") 954 self.stream.write("[") 955 self.dispatch(node.expr) 956 for qual in node.quals: 957 self.dispatch(qual) 958 self.stream.write("]") 959 self._span_end() 960 961 def visitListCompFor(self, node): 962 self._span_start("listcompfor") 963 self._keyword("for", 1) 964 self._span_start("item") 965 self.dispatch(node.assign) 966 self._span_end() 967 self._keyword("in", 1) 968 self._span_start("collection") 969 self.dispatch(node.list) 970 self._span_end() 971 for if_ in node.ifs: 972 self.dispatch(if_) 973 self._span_end() 974 975 def visitListCompIf(self, node): 976 self._span_start("listcompif") 977 self._span_start("conditional") 978 self._keyword("if", 1) 979 self.dispatch(node.test) 980 self._span_end() 981 self._span_end() 982 983 def visitMod(self, node): 984 self._visitBinary(node, "mod", "%") 985 986 def visitMul(self, node): 987 self._visitBinary(node, "mul", "*") 988 989 def visitName(self, node): 990 if hasattr(node, "_scope"): 991 scope = node._scope 992 self._name_start() 993 self.stream.write(node.name) 994 self._popup_start() 995 self._scope(scope) 996 self._popup_end() 997 self._name_end() 998 else: 999 self._span(node.name) 1000 1001 def visitNot(self, node): 1002 self._span_start("not") 1003 self._keyword("not") 1004 self.dispatch(node.expr) 1005 self._span_end() 1006 1007 def visitOr(self, node): 1008 self._span_start("or") 1009 first = 1 1010 for n in node.nodes: 1011 if not first: 1012 self._keyword("or", 1) 1013 self.dispatch(n) 1014 first = 0 1015 self._span_end() 1016 1017 def visitPower(self, node): 1018 self._visitBinary(node, "pow", "**") 1019 1020 def visitSlice(self, node): 1021 self._span_start("slice") 1022 self.dispatch(node.expr) 1023 self.stream.write("[") 1024 if node.lower: 1025 self.dispatch(node.lower) 1026 self.stream.write(":") 1027 if node.upper: 1028 self.dispatch(node.upper) 1029 # NOTE: Step? 1030 self.stream.write("]") 1031 self._span_end() 1032 1033 def visitSliceobj(self, node): 1034 self._span_start("sliceobj") 1035 first = 1 1036 for n in node.nodes: 1037 if not first: 1038 self.stream.write(":") 1039 self.dispatch(n) 1040 self._span_end() 1041 1042 def visitSub(self, node): 1043 self._visitBinary(node, "sub", "-") 1044 1045 def visitSubscript(self, node): 1046 self._span_start("subscript") 1047 self.dispatch(node.expr) 1048 self.stream.write("[") 1049 first = 1 1050 for sub in node.subs: 1051 if not first: 1052 self.stream.write(", ") 1053 self.dispatch(sub) 1054 first = 0 1055 self.stream.write("]") 1056 self._span_end() 1057 1058 visitTuple = visitAssTuple 1059 1060 def visitUnaryAdd(self, node): 1061 self._visitUnary(node, "add", "+") 1062 1063 def visitUnarySub(self, node): 1064 self._visitUnary(node, "sub", "-") 1065 1066 # Output preparation methods. 1067 1068 def _sequence(self, node): 1069 first = 1 1070 for n in node.nodes: 1071 if not first: 1072 self.stream.write(", ") 1073 self.dispatch(n) 1074 first = 0 1075 1076 def _mapping(self, node): 1077 first = 1 1078 for k, v in node.items: 1079 if not first: 1080 self.stream.write(", ") 1081 self.dispatch(k) 1082 self.stream.write(" : ") 1083 self.dispatch(v) 1084 first = 0 1085 1086 def _parameters(self, fn, node): 1087 nparams = len(fn.positional_names) 1088 ndefaults = len(fn.defaults) 1089 first_with_default = nparams - ndefaults 1090 1091 first = 1 1092 for n, param in enumerate(fn.positional_names): 1093 if not first: 1094 self.stream.write(", ") 1095 1096 # Handle tuple parameters. 1097 1098 if isinstance(param, tuple): 1099 self._tuple_parameter(param, node) 1100 else: 1101 self._assname(param, node) 1102 1103 n_default = n - first_with_default 1104 if n_default >= 0: 1105 self._default(fn.defaults[n_default]) 1106 first = 0 1107 1108 if fn.has_star: 1109 if not first: 1110 self.stream.write(", *") 1111 self._name(fn.star_name) 1112 1113 if fn.has_dstar: 1114 if not first: 1115 self.stream.write(", **") 1116 self._name(fn.dstar_name) 1117 1118 def _tuple_parameter(self, parameters, node): 1119 self.stream.write("(") 1120 1121 first = 1 1122 for param in parameters: 1123 if not first: 1124 self.stream.write(", ") 1125 1126 # Handle tuples. 1127 1128 if isinstance(param, tuple): 1129 self._tuple_parameter(param, node) 1130 else: 1131 self._assname(param, node) 1132 1133 first = 0 1134 1135 self.stream.write(")") 1136 1137 def _default(self, default): 1138 self.stream.write("=") 1139 self.dispatch(default) 1140 1141 # Convenience functions. 1142 1143 def summarise(module, program, filename): 1144 stream = open(filename, "wb") 1145 try: 1146 summary = Summary(module, program) 1147 summary.to_stream(stream) 1148 finally: 1149 stream.close() 1150 1151 def annotate(module, program, filename): 1152 stream = open(filename, "wb") 1153 try: 1154 source = AnnotatedSource(module, program) 1155 source.to_stream(stream) 1156 finally: 1157 stream.close() 1158 1159 def report(program, directory): 1160 if not exists(directory): 1161 os.mkdir(directory) 1162 1163 for module in program.get_importer().get_modules(): 1164 annotate(module, program, join(directory, "%s%sxhtml" % (module.full_name(), extsep))) 1165 summarise(module, program, join(directory, "%s-summary%sxhtml" % (module.full_name(), extsep))) 1166 1167 # vim: tabstop=4 expandtab shiftwidth=4