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 = self.possible_accessor_types(node) 830 self._span_start("assattr") 831 self._accessor_start(target_names) 832 self.dispatch(node.expr) 833 self._accessor_end(target_names) 834 self._span_start("attr") 835 self.stream.write(".") 836 self._name(node.attrname) 837 self._span_end() 838 self._span_end() 839 840 def visitAssList(self, node): 841 self._span_start("list") 842 self.stream.write("[") 843 self._sequence(node) 844 self.stream.write("]") 845 self._span_end() 846 847 def visitAssName(self, node): 848 self._assname(node.name, node) 849 850 def visitAssTuple(self, node): 851 self._span_start("tuple") 852 self.stream.write("(") 853 self._sequence(node) 854 self.stream.write(")") 855 self._span_end() 856 857 def visitBitand(self, node): 858 self._visitBitBinary(node, "bitand", "&") 859 860 def visitCallFunc(self, node): 861 self._span_start("callfunc") 862 self.dispatch(node.node) 863 self._span_start("call") 864 self.stream.write("(") 865 first = 1 866 for arg in node.args: 867 if not first: 868 self.stream.write(", ") 869 self.dispatch(arg) 870 first = 0 871 if node.star_args is not None: 872 if not first: 873 self.stream.write(", *") 874 self.dispatch(node.star_args) 875 first = 0 876 if node.dstar_args is not None: 877 if not first: 878 self.stream.write(", **") 879 self.dispatch(node.dstar_args) 880 first = 0 881 self.stream.write(")") 882 self._span_end() 883 self._span_end() 884 885 def visitCompare(self, node): 886 self._span_start("compare") 887 self.dispatch(node.expr) 888 for op_name, expr in node.ops: 889 self._op(op_name, operator_functions.get(op_name), 1) 890 self.dispatch(expr) 891 self._span_end() 892 893 def visitConst(self, node): 894 if isinstance(node.value, (str, unicode)): 895 self._span_start("str") 896 self.stream.write(self._text(repr(node.value))) 897 if isinstance(node.value, (str, unicode)): 898 self._span_end() 899 900 def visitDict(self, node): 901 self._span_start("dict") 902 self.stream.write("{") 903 self._mapping(node) 904 self.stream.write("}") 905 self._span_end() 906 907 def visitDiv(self, node): 908 self._visitBinary(node, "div", "/") 909 910 def visitFloorDiv(self, node): 911 self._visitBinary(node, "floordiv", "//") 912 913 def visitGetattr(self, node): 914 target_names = self.possible_accessor_types(node) 915 self._span_start("getattr") 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 visitKeyword(self, node): 926 self._span_start("keyword-arg") 927 self.stream.write(node.name) 928 self.stream.write("=") 929 self.dispatch(node.expr) 930 self._span_end() 931 932 def visitLambda(self, node): 933 if hasattr(node, "unit"): 934 fn = node.unit 935 else: 936 print "Warning: function %s not recognised!" % node.name 937 return 938 939 self._span_start("lambda") 940 self._keyword("lambda") 941 self._parameters(fn, node) 942 self.stream.write(": ") 943 self._span_start("code") 944 self.dispatch(node.code) 945 self._span_end() 946 self._span_end() 947 948 visitList = visitAssList 949 950 def visitListComp(self, node): 951 self._span_start("listcomp") 952 self.stream.write("[") 953 self.dispatch(node.expr) 954 for qual in node.quals: 955 self.dispatch(qual) 956 self.stream.write("]") 957 self._span_end() 958 959 def visitListCompFor(self, node): 960 self._span_start("listcompfor") 961 self._keyword("for", 1) 962 self._span_start("item") 963 self.dispatch(node.assign) 964 self._span_end() 965 self._keyword("in", 1) 966 self._span_start("collection") 967 self.dispatch(node.list) 968 self._span_end() 969 for if_ in node.ifs: 970 self.dispatch(if_) 971 self._span_end() 972 973 def visitListCompIf(self, node): 974 self._span_start("listcompif") 975 self._span_start("conditional") 976 self._keyword("if", 1) 977 self.dispatch(node.test) 978 self._span_end() 979 self._span_end() 980 981 def visitMod(self, node): 982 self._visitBinary(node, "mod", "%") 983 984 def visitMul(self, node): 985 self._visitBinary(node, "mul", "*") 986 987 def visitName(self, node): 988 if hasattr(node, "_scope"): 989 scope = node._scope 990 self._name_start() 991 self.stream.write(node.name) 992 self._popup_start() 993 self._scope(scope) 994 self._popup_end() 995 self._name_end() 996 else: 997 self._span(node.name) 998 999 def visitNot(self, node): 1000 self._span_start("not") 1001 self._keyword("not") 1002 self.dispatch(node.expr) 1003 self._span_end() 1004 1005 def visitOr(self, node): 1006 self._span_start("or") 1007 first = 1 1008 for n in node.nodes: 1009 if not first: 1010 self._keyword("or", 1) 1011 self.dispatch(n) 1012 first = 0 1013 self._span_end() 1014 1015 def visitPower(self, node): 1016 self._visitBinary(node, "pow", "**") 1017 1018 def visitSlice(self, node): 1019 self._span_start("slice") 1020 self.dispatch(node.expr) 1021 self.stream.write("[") 1022 if node.lower: 1023 self.dispatch(node.lower) 1024 self.stream.write(":") 1025 if node.upper: 1026 self.dispatch(node.upper) 1027 # NOTE: Step? 1028 self.stream.write("]") 1029 self._span_end() 1030 1031 def visitSliceobj(self, node): 1032 self._span_start("sliceobj") 1033 first = 1 1034 for n in node.nodes: 1035 if not first: 1036 self.stream.write(":") 1037 self.dispatch(n) 1038 self._span_end() 1039 1040 def visitSub(self, node): 1041 self._visitBinary(node, "sub", "-") 1042 1043 def visitSubscript(self, node): 1044 self._span_start("subscript") 1045 self.dispatch(node.expr) 1046 self.stream.write("[") 1047 first = 1 1048 for sub in node.subs: 1049 if not first: 1050 self.stream.write(", ") 1051 self.dispatch(sub) 1052 first = 0 1053 self.stream.write("]") 1054 self._span_end() 1055 1056 visitTuple = visitAssTuple 1057 1058 def visitUnaryAdd(self, node): 1059 self._visitUnary(node, "add", "+") 1060 1061 def visitUnarySub(self, node): 1062 self._visitUnary(node, "sub", "-") 1063 1064 # Output preparation methods. 1065 1066 def _sequence(self, node): 1067 first = 1 1068 for n in node.nodes: 1069 if not first: 1070 self.stream.write(", ") 1071 self.dispatch(n) 1072 first = 0 1073 1074 def _mapping(self, node): 1075 first = 1 1076 for k, v in node.items: 1077 if not first: 1078 self.stream.write(", ") 1079 self.dispatch(k) 1080 self.stream.write(" : ") 1081 self.dispatch(v) 1082 first = 0 1083 1084 def _parameters(self, fn, node): 1085 nparams = len(fn.positional_names) 1086 ndefaults = len(fn.defaults) 1087 first_with_default = nparams - ndefaults 1088 1089 first = 1 1090 for n, param in enumerate(fn.positional_names): 1091 if not first: 1092 self.stream.write(", ") 1093 1094 # Handle tuple parameters. 1095 1096 if isinstance(param, tuple): 1097 self._tuple_parameter(param, node) 1098 else: 1099 self._assname(param, node) 1100 1101 n_default = n - first_with_default 1102 if n_default >= 0: 1103 self._default(fn.defaults[n_default]) 1104 first = 0 1105 1106 if fn.has_star: 1107 if not first: 1108 self.stream.write(", *") 1109 self._name(fn.star_name) 1110 1111 if fn.has_dstar: 1112 if not first: 1113 self.stream.write(", **") 1114 self._name(fn.dstar_name) 1115 1116 def _tuple_parameter(self, parameters, node): 1117 self.stream.write("(") 1118 1119 first = 1 1120 for param in parameters: 1121 if not first: 1122 self.stream.write(", ") 1123 1124 # Handle tuples. 1125 1126 if isinstance(param, tuple): 1127 self._tuple_parameter(param, node) 1128 else: 1129 self._assname(param, node) 1130 1131 first = 0 1132 1133 self.stream.write(")") 1134 1135 def _default(self, default): 1136 self.stream.write("=") 1137 self.dispatch(default) 1138 1139 # Convenience functions. 1140 1141 def summarise(module, program, filename): 1142 stream = open(filename, "wb") 1143 try: 1144 summary = Summary(module, program) 1145 summary.to_stream(stream) 1146 finally: 1147 stream.close() 1148 1149 def annotate(module, program, filename): 1150 stream = open(filename, "wb") 1151 try: 1152 source = AnnotatedSource(module, program) 1153 source.to_stream(stream) 1154 finally: 1155 stream.close() 1156 1157 def report(program, directory): 1158 if not exists(directory): 1159 os.mkdir(directory) 1160 1161 for module in program.get_importer().get_modules(): 1162 annotate(module, program, join(directory, "%s%sxhtml" % (module.full_name(), extsep))) 1163 summarise(module, program, join(directory, "%s-summary%sxhtml" % (module.full_name(), extsep))) 1164 1165 # vim: tabstop=4 expandtab shiftwidth=4