simplify

viewer.py

195:0e071a782022
2007-02-15 paulb Made a distinct raises annotation on subprograms.
     1 #!/usr/bin/env python     2      3 """     4 View annotated sources.     5      6 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>     7      8 This software is free software; you can redistribute it and/or     9 modify it under the terms of the GNU General Public License as    10 published by the Free Software Foundation; either version 2 of    11 the License, or (at your option) any later version.    12     13 This software is distributed in the hope that it will be useful,    14 but WITHOUT ANY WARRANTY; without even the implied warranty of    15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    16 GNU General Public License for more details.    17     18 You should have received a copy of the GNU General Public    19 License along with this library; see the file LICENCE.txt    20 If not, write to the Free Software Foundation, Inc.,    21 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA    22 """    23     24 from compiler.visitor import ASTVisitor    25 from simplified import *    26 import sys    27 import os    28 import textwrap    29     30 # Classes.    31     32 # HTML-related output production.    33     34 html_header = """<?xml version="1.0" encoding="iso-8859-15"?>    35 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">    36 <html xmlns="http://www.w3.org/1999/xhtml">    37 <head>    38   <title>Module</title>    39   <style type="text/css">    40     body {    41       padding-bottom: 4em;    42       font-size: 14pt; font-family: monospace;    43       background-color: black; color: white;    44     }    45     46     .class { margin-top: 1em; margin-bottom: 1em; }    47     .function { margin-top: 1em; margin-bottom: 1em; }    48     .body { padding-left: 2em; }    49     .keyword { color: yellow; }    50     .comment { color: blue; }    51     .str { color: #FF00FF; }    52     .doc { color: #FF00FF; margin-top: 1em; margin-bottom: 1em; }    53     .invocation a { color: white; text-decoration: none; }    54     55     .popup {    56       display: none; z-index: 2;    57       position: absolute; top: 1em; left: 0.5em;    58       padding: 0.2em; background-color: #000000;    59       border: 2px solid #dddddd;    60     }    61     62     .invocations {    63       padding: 0.5em; background-color: #770000;    64       clear: all;    65     }    66     67     .types {    68       padding: 0.5em; background-color: #0000FF;    69       float: right;    70     }    71     72     .raises {    73       padding: 0.5em; background-color: #7700FF;    74       float: right;    75     }    76     77     .scopes {    78       padding: 0.5em; background-color: #007700;    79       float: left;    80     }    81     82     .non-writes, .non-accesses {    83       padding: 0.5em; background-color: #FF0000;    84       float: right;    85     }    86     87     .op,    88     .name,    89     .attr,    90     .conditional,    91     .operator,    92     .iterator,    93     .call,    94     .returns    95     {    96       position: relative;    97     }    98     99     .op:hover > .popup,   100     .name:hover > .popup,   101     .attr:hover > .popup,   102     .conditional:hover > .popup,   103     .operator:hover > .popup,   104     .iterator:hover > .popup,   105     .call:hover > .popup,   106     .returns:hover > .popup   107     {   108        display: block;   109     }   110    111   </style>   112 </head>   113 <body>   114 """   115    116 html_footer = """</body>   117 </html>   118 """   119    120 # Browser classes.   121    122 class Browser(ASTVisitor):   123    124     """   125     A browsing visitor for AST nodes.   126    127     Covered: Add, And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign,   128              Break, CallFunc, Class, Compare, Const, Continue, Dict, Discard,   129              Div, FloorDiv, For, From, Function, Getattr, Global, If, Import,   130              Keyword, Lambda, List, Mod, Module, Mul, Name, Not, Or, Pass,   131              Power, Print, Printnl, Raise, Return, Slice, Stmt, Sub, Subscript,   132              TryExcept, TryFinally, Tuple, UnaryAdd, UnarySub, While.   133    134     Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Ellipsis,   135              Exec, Invert, LeftShift, ListComp, ListCompFor, ListCompIf,   136              RightShift, Sliceobj, Yield.   137     """   138    139     def __init__(self, stream):   140         ASTVisitor.__init__(self)   141         self.visitor = self   142         self.stream = stream   143    144     def process(self, module):   145         self.stream.write(html_header)   146         self.dispatch(module)   147         self.stream.write(html_footer)   148    149     def visitModule(self, node):   150         self.default(node)   151    152     # Statements.   153    154     def visitAssign(self, node):   155         self.stream.write("<div class='assign'>\n")   156         for lvalue in node.nodes:   157             self.dispatch(lvalue)   158             self.stream.write("=\n")   159         self.dispatch(node.expr)   160         self.stream.write("</div>\n")   161    162     def visitAugAssign(self, node):   163         self.stream.write("<div class='augassign'>\n")   164         self.dispatch(node.node)   165         self.stream.write("<span class='operator'>\n")   166         self.stream.write("%s\n" % node.op)   167         self._popup_start()   168         self.stream.write("<div class='invocations'>\n")   169         self._invocations_list(node._op_call)   170         self.stream.write("</div>\n")   171         self._popup_end()   172         self.stream.write("</span>\n")   173         self.dispatch(node.expr)   174         self.stream.write("</div>\n")   175    176     def visitBreak(self, node):   177         self.stream.write("<div class='break'>\n")   178         self._keyword("break")   179         self.stream.write("</div>\n")   180    181     def visitClass(self, node):   182         definition = node._node   183         definitions = getattr(node, "_nodes", [definition])   184         structure = definition.expr.ref   185         self.stream.write("<div class='class' id='%s'>\n" % self._url(structure.full_name()))   186         self.stream.write("<div>\n")   187         self._keyword("class")   188         self._name_start(structure.name)   189         self._popup_start()   190         self._scopes(definitions)   191         self._popup_end()   192         self._name_end()   193         bases = structure.bases   194    195         # Suppress the "object" class appearing alone.   196    197         if bases and not (len(bases) == 1 and bases[0].name == "object"):   198             self.stream.write("(")   199             first = 1   200             for base in bases:   201                 if not first:   202                     self.stream.write(",\n")   203                 self._name_start(base.name)   204                 self._popup_start()   205                 self._scopes([base])   206                 self._types([base])   207                 self._popup_end()   208                 self._name_end()   209                 first = 0   210             self.stream.write(")")   211    212         self.stream.write(":\n")   213         self._comment(self._text(structure.full_name()))   214         self.stream.write("</div>\n")   215    216         self.stream.write("<div class='body'>\n")   217         self._doc(node)   218         self.dispatch(node.code)   219         self.stream.write("</div>\n")   220         self.stream.write("</div>\n")   221    222     def visitContinue(self, node):   223         self.stream.write("<div class='continue'>\n")   224         self._keyword("continue")   225         self.stream.write("</div>\n")   226    227     def visitDiscard(self, node):   228         self.stream.write("<div class='discard'>\n")   229         self.default(node)   230         self.stream.write("</div>\n")   231    232     def visitFor(self, node):   233         self.stream.write("<div class='if'>\n")   234         self.stream.write("<div>\n")   235         self.stream.write("<span class='iterator'>\n")   236         self._keyword("for")   237         self._popup_start()   238         self._invocations(node._next_call)   239         self._popup_end()   240         self.stream.write("</span>\n")   241         self.dispatch(node.assign)   242         self.stream.write("<span class='iterator'>\n")   243         self._keyword("in")   244         self._popup_start()   245         self._invocations(node._iter_call)   246         self._popup_end()   247         self.stream.write("</span>\n")   248         self.dispatch(node.list)   249         self.stream.write(":\n")   250         self.stream.write("</div>\n")   251         self.stream.write("<div class='body'>\n")   252         self.dispatch(node.body)   253         self.stream.write("</div>\n")   254         if node.else_ is not None:   255             self.stream.write("<div>\n")   256             self._keyword("else")   257             self.stream.write(":\n")   258             self.stream.write("</div>\n")   259             self.stream.write("<div class='body'>\n")   260             self.dispatch(node.else_)   261             self.stream.write("</div>\n")   262         self.stream.write("</div>\n")   263    264     def visitFrom(self, node):   265         self.stream.write("<div class='from'>\n")   266         self._keyword("from")   267         self.stream.write("<span class='name'>\n")   268         self.stream.write(node.modname)   269         self._popup_start()   270         self._types([node._modname])   271         self._popup_end()   272         self.stream.write("</span>\n")   273         self._keyword("import")   274         first = 1   275         for (name, alias), _name in map(None, node.names, node._names):   276             if not first:   277                 self.stream.write(",\n")   278             if alias:   279                 self.stream.write(name + " ")   280                 self._keyword("as")   281             self.stream.write("<span class='name'>\n")   282             self.stream.write(alias or name)   283             self._popup_start()   284             self._types([_name])   285             self._popup_end()   286             self.stream.write("</span>\n")   287             first = 0   288         self.stream.write("</div>\n")   289    290     def visitFunction(self, node):   291         definition = node._node   292         definitions = [n for n in getattr(node, "_nodes", [definition]) if not isinstance(n, Subprogram)]   293         subprogram = definition.expr.ref   294         subprograms = [n for n in getattr(node, "_nodes", [subprogram]) if isinstance(n, Subprogram)]   295         self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name()))   296         self.stream.write("<div>\n")   297         self._keyword("def")   298         self._name_start(subprogram.name)   299         self._popup_start()   300         self._scopes([definition]) # not dependent on subprograms   301         self._raises(subprograms)   302         self._popup_end()   303         self._name_end()   304         self.stream.write("(")   305         self._parameters(subprogram, subprograms)   306         self.stream.write(")")   307         self.stream.write(":\n")   308         self._comment(self._text(subprogram.full_name()))   309         self.stream.write("</div>\n")   310    311         self.stream.write("<div class='body'>\n")   312         self._doc(node)   313         self.dispatch(node.code)   314         self.stream.write("</div>\n")   315         self.stream.write("</div>\n")   316    317     def visitGlobal(self, node):   318         self.stream.write("<div class='global'>\n")   319         self._keyword("global")   320         first = 1   321         for name in node.names:   322             if not first:   323                 self.stream.write(",\n")   324             self.stream.write(name)   325             first = 0   326         self.stream.write("</div>\n")   327    328     def visitIf(self, node):   329         self.stream.write("<div class='if'>\n")   330         first = 1   331         conditional = node._node   332         conditionals = getattr(node, "_nodes", [conditional])   333         for compare, stmt in node.tests:   334             self.stream.write("<div>\n")   335             self.stream.write("<span class='conditional'>\n")   336             if first:   337                 self._keyword("if")   338             else:   339                 self._keyword("elif")   340             self._popup_start()   341             self._invocations(conditional.test)   342             self._popup_end()   343             self.stream.write("</span>\n")   344             self.dispatch(compare)   345             self.stream.write(":\n")   346             self.stream.write("</div>\n")   347             self.stream.write("<div class='body'>\n")   348             self.dispatch(stmt)   349             self.stream.write("</div>\n")   350             if conditional.else_:   351                 conditional = conditional.else_[0]   352             else:   353                 conditional = None   354             first = 0   355         if node.else_ is not None:   356             self.stream.write("<div>\n")   357             self._keyword("else")   358             self.stream.write(":\n")   359             self.stream.write("</div>\n")   360             self.stream.write("<div class='body'>\n")   361             self.dispatch(node.else_)   362             self.stream.write("</div>\n")   363         self.stream.write("</div>\n")   364    365     def visitImport(self, node):   366         self.stream.write("<div class='import'>\n")   367         self._keyword("import")   368         first = 1   369         for (name, alias), _name in map(None, node.names, node._names):   370             if not first:   371                 self.stream.write(",\n")   372             if alias:   373                 self.stream.write(name + " ")   374                 self._keyword("as")   375             self.stream.write("<span class='name'>\n")   376             self.stream.write(alias or name)   377             self._popup_start()   378             self._types([_name])   379             self._popup_end()   380             self.stream.write("</span>\n")   381             first = 0   382         self.stream.write("</div>\n")   383    384     def visitPass(self, node):   385         self.stream.write("<div class='pass'>\n")   386         self._keyword("pass")   387         self.stream.write("</div>\n")   388    389     def visitPrint(self, node):   390         self.stream.write("<div class='print'>\n")   391         self._keyword("print")   392         if node.dest is not None:   393             self.stream.write(">>\n")   394             self.dispatch(node.dest)   395         for n in node.nodes:   396             self.dispatch(n)   397             self.stream.write(",\n")   398         self.stream.write("</div>\n")   399    400     def visitPrintnl(self, node):   401         self.stream.write("<div class='printnl'>\n")   402         self._keyword("print")   403         if node.dest is not None:   404             self.stream.write(">>\n")   405             self.dispatch(node.dest)   406         first = 1   407         for n in node.nodes:   408             if not first:   409                 self.stream.write(",\n")   410             self.dispatch(n)   411             first = 0   412         self.stream.write("</div>\n")   413    414     def visitRaise(self, node):   415         self.stream.write("<div class='raise'>\n")   416         self._keyword("raise")   417         self.dispatch(node.expr1)   418         if node.expr2 is not None:   419             self.stream.write(",\n")   420             self.dispatch(node.expr2)   421         if node.expr3 is not None:   422             self.stream.write(",\n")   423             self.dispatch(node.expr3)   424         self.stream.write("</div>\n")   425    426     def visitReturn(self, node):   427         value = node._node   428         values = getattr(node, "_nodes", [value])   429         self.stream.write("<div class='return'>\n")   430         self.stream.write("<span class='returns'>\n")   431         self._keyword("return")   432         self._popup_start()   433         self._types(values)   434         self._popup_end()   435         self.stream.write("</span>\n")   436         self.dispatch(node.value)   437         self.stream.write("</div>\n")   438    439     def visitStmt(self, node):   440         self.stream.write("<div class='stmt'>\n")   441         self.default(node)   442         self.stream.write("</div>\n")   443    444     def visitTryExcept(self, node):   445         self.stream.write("<div class='tryexcept'>\n")   446         self.stream.write("<div>\n")   447         self._keyword("try")   448         self.stream.write(":\n")   449         self.stream.write("</div>\n")   450         self.stream.write("<div class='body'>\n")   451         self.dispatch(node.body)   452         self.stream.write("</div>\n")   453         for spec, assign, statement in node.handlers:   454             self.stream.write("<div>\n")   455             self._keyword("except")   456             if spec is not None:   457                 self.dispatch(spec)   458             if assign is not None:   459                 self.stream.write(",\n")   460                 self.dispatch(assign)   461             self.stream.write(":\n")   462             self.stream.write("</div>\n")   463             self.stream.write("<div class='body'>\n")   464             self.dispatch(statement)   465             self.stream.write("</div>\n")   466         if node.else_ is not None:   467             self.stream.write("<div>\n")   468             self._keyword("else")   469             self.stream.write(":\n")   470             self.stream.write("</div>\n")   471             self.stream.write("<div class='body'>\n")   472             self.dispatch(node.else_)   473             self.stream.write("</div>\n")   474         self.stream.write("</div>\n")   475    476     def visitTryFinally(self, node):   477         self.stream.write("<div class='tryfinally'>\n")   478         self.stream.write("<div>\n")   479         self._keyword("try")   480         self.stream.write(":\n")   481         self.stream.write("</div>\n")   482         self.stream.write("<div class='body'>\n")   483         self.dispatch(node.body)   484         self.stream.write("</div>\n")   485         self.stream.write("<div>\n")   486         self._keyword("finally")   487         self.stream.write(":\n")   488         self.stream.write("</div>\n")   489         self.stream.write("<div class='body'>\n")   490         self.dispatch(node.final)   491         self.stream.write("</div>\n")   492         self.stream.write("</div>\n")   493    494     def visitWhile(self, node):   495         self.stream.write("<div class='while'>\n")   496         self.stream.write("<div>\n")   497         self.stream.write("<span class='conditional'>\n")   498         self._keyword("while")   499         self._popup_start()   500         self._invocations(node._test_call)   501         self._popup_end()   502         self.stream.write("</span>\n")   503         self.dispatch(node.test)   504         self.stream.write(":\n")   505         self.stream.write("</div>\n")   506         self.stream.write("<div class='body'>\n")   507         self.dispatch(node.body)   508         self.stream.write("</div>\n")   509         if node.else_ is not None:   510             self.stream.write("<div>\n")   511             self._keyword("else")   512             self.stream.write(":\n")   513             self.stream.write("</div>\n")   514             self.stream.write("<div class='body'>\n")   515             self.dispatch(node.else_)   516             self.stream.write("</div>\n")   517         self.stream.write("</div>\n")   518    519     # Expression-related helper methods.   520    521     def _visitBinary(self, node, name, symbol):   522         self.stream.write("<span class='%s'>\n" % name)   523         self.dispatch(node.left)   524         self.stream.write("<span class='operator'>\n")   525         self.stream.write(symbol)   526         self._popup_start()   527         self.stream.write("<div class='invocations'>\n")   528         self._invocations_list(node._left_call)   529         self._invocations_list(node._right_call)   530         self.stream.write("</div>\n")   531         self._popup_end()   532         self.stream.write("</span>\n")   533         self.dispatch(node.right)   534         self.stream.write("</span>")   535    536     def _visitUnary(self, node, name, symbol):   537         self.stream.write("<span class='%s'>\n" % name)   538         self.stream.write("<span class='operator'>\n")   539         self.stream.write(symbol)   540         self._popup_start()   541         self.stream.write("<div class='invocations'>\n")   542         self._invocations_list(node._unary_call)   543         self.stream.write("</div>\n")   544         self._popup_end()   545         self.stream.write("</span>\n")   546         self.dispatch(node.expr)   547         self.stream.write("</span>")   548    549     # Expressions.   550    551     def visitAdd(self, node):   552         self._visitBinary(node, "add", "+")   553    554     def visitAnd(self, node):   555         self.stream.write("<span class='and'>\n")   556         first = 1   557         for n in node.nodes:   558             if not first:   559                 self._keyword("and")   560             self.dispatch(n)   561             first = 0   562         self.stream.write("</span>")   563    564     def visitAssAttr(self, node):   565         target = node._node   566         targets = getattr(node, "_nodes", [target])   567         self.stream.write("<span class='assattr'>\n")   568         self.dispatch(node.expr)   569         self.stream.write("<span class='attr'>\n")   570         self.stream.write(".%s\n" % self._text(node.attrname))   571         self._popup_start()   572         self._scopes(targets)   573         self._types(targets)   574         self._popup_end()   575         self.stream.write("</span>\n")   576         self.stream.write("</span>\n")   577    578     def visitAssList(self, node):   579         self.stream.write("<span class='list'>\n")   580         self.stream.write("[")   581         self._sequence(node)   582         self.stream.write("]\n")   583         self.stream.write("</span>\n")   584    585     def visitAssName(self, node):   586         target = node._node   587         targets = getattr(node, "_nodes", [target])   588         self._name_start(target.name)   589         self._popup_start()   590         self._scopes(targets)   591         self._types(targets)   592         self._popup_end()   593         self._name_end()   594    595     def visitAssTuple(self, node):   596         self.stream.write("<span class='tuple'>\n")   597         self.stream.write("(")   598         self._sequence(node)   599         self.stream.write(")\n")   600         self.stream.write("</span>\n")   601    602     def visitCallFunc(self, node):   603         target = node._node   604         targets = getattr(node, "_nodes", [target])   605         self.stream.write("<span class='callfunc'>\n")   606         self.dispatch(node.node)   607         self.stream.write("<span class='call'>\n")   608         self.stream.write("(")   609         self._popup_start()   610         self._invocations(target)   611         self._popup_end()   612         self.stream.write("</span>\n")   613         first = 1   614         for arg in node.args:   615             if not first:   616                 self.stream.write(",\n")   617             self.dispatch(arg)   618             first = 0   619         if node.star_args is not None:   620             if not first:   621                 self.stream.write(", *\n")   622             self.dispatch(node.star_args)   623             first = 0   624         if node.dstar_args is not None:   625             if not first:   626                 self.stream.write(", **\n")   627             self.dispatch(node.dstar_args)   628             first = 0   629         self.stream.write(")\n")   630         self.stream.write("</span>\n")   631    632     def visitCompare(self, node):   633         self.stream.write("<span class='compare'>\n")   634         self.dispatch(node.expr)   635         for op in node._ops:   636             self.stream.write("<span class='op'>\n")   637             self.stream.write(op.name)   638             self._popup_start()   639             self._op(op)   640             self._popup_end()   641             self.stream.write("</span>\n")   642             self.dispatch(op.expr)   643         self.stream.write("</span>\n")   644    645     def visitConst(self, node):   646         self.stream.write(repr(node.value))   647    648     def visitDict(self, node):   649         self.stream.write("<span class='dict'>\n")   650         self.stream.write("{")   651         self._mapping(node)   652         self.stream.write("}\n")   653         self.stream.write("</span>\n")   654    655     def visitDiv(self, node):   656         self._visitBinary(node, "div", "/")   657    658     def visitFloorDiv(self, node):   659         self._visitBinary(node, "floordiv", "//")   660    661     def visitGetattr(self, node):   662         target = node._node   663         targets = getattr(node, "_nodes", [target])   664         self.stream.write("<span class='getattr'>\n")   665         self.dispatch(node.expr)   666         self.stream.write("<span class='attr'>\n")   667         self.stream.write(".%s\n" % self._text(node.attrname))   668         self._popup_start()   669         self._scopes(targets)   670         self._types(targets)   671         self._popup_end()   672         self.stream.write("</span>\n")   673         self.stream.write("</span>\n")   674    675     def visitKeyword(self, node):   676         self.stream.write("<span class='keyword-arg'>\n")   677         self.stream.write(node.name)   678         self.stream.write("=")   679         self.dispatch(node.expr)   680         self.stream.write("</span>\n")   681    682     def visitLambda(self, node):   683         definition = node._node   684         definitions = [n for n in getattr(node, "_nodes", [definition]) if not isinstance(n, Subprogram)]   685         subprogram = definition.expr.ref   686         subprograms = [n for n in getattr(node, "_nodes", [subprogram]) if isinstance(n, Subprogram)]   687         self.stream.write("<span class='lambda'>\n")   688         self._keyword("lambda")   689         self._parameters(subprogram, subprograms)   690         self.dispatch(node.code)   691         self.stream.write("</span>\n")   692    693     visitList = visitAssList   694    695     def visitMod(self, node):   696         self._visitBinary(node, "mod", "%")   697    698     def visitMul(self, node):   699         self._visitBinary(node, "mul", "*")   700    701     def visitName(self, node):   702         target = node._node   703         targets = getattr(node, "_nodes", [target])   704         self._name_start(target.name)   705         self._popup_start()   706         self._scopes(targets)   707         self._types(targets)   708         self._popup_end()   709         self._name_end()   710    711     def visitNot(self, node):   712         self.stream.write("<span class='not'>\n")   713         self._keyword("not")   714         self.dispatch(node.expr)   715         self.stream.write("</span>")   716    717     def visitOr(self, node):   718         self.stream.write("<span class='or'>\n")   719         first = 1   720         for n in node.nodes:   721             if not first:   722                 self._keyword("or")   723             self.dispatch(n)   724             first = 0   725         self.stream.write("</span>")   726    727     def visitPower(self, node):   728         self._visitBinary(node, "power", "**")   729    730     def visitSlice(self, node):   731         self.stream.write("<span class='slice'>\n")   732         self.dispatch(node.expr)   733         self.stream.write("[")   734         if node.lower:   735             self.dispatch(node.lower)   736         self.stream.write(":")   737         if node.upper:   738             self.dispatch(node.upper)   739         # NOTE: Step?   740         self.stream.write("]")   741         self.stream.write("</span>\n")   742    743     def visitSub(self, node):   744         self._visitBinary(node, "sub", "-")   745    746     def visitSubscript(self, node):   747         self.stream.write("<span class='subscript'>\n")   748         self.dispatch(node.expr)   749         self.stream.write("[")   750         first = 1   751         for sub in node.subs:   752             if not first:   753                 self.stream.write(", ")   754             self.dispatch(sub)   755             first = 0   756         self.stream.write("]")   757         self.stream.write("</span>\n")   758    759     visitTuple = visitAssTuple   760    761     def visitUnaryAdd(self, node):   762         self._visitUnary(node, "add", "+")   763    764     def visitUnarySub(self, node):   765         self._visitUnary(node, "sub", "-")   766    767     # Output preparation methods.   768    769     def _text(self, text):   770         return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")   771    772     def _attr(self, attr):   773         return self._text(attr).replace("'", "&apos;").replace('"', "&quot;")   774    775     def _url(self, url):   776         return self._attr(url).replace("#", "%23").replace("-", "%2d")   777    778     def _comment(self, comment):   779         self.stream.write("<span class='comment'># %s</span>\n" % comment)   780    781     def _keyword(self, kw):   782         self.stream.write("<span class='keyword'>%s</span> " % kw)   783    784     def _doc(self, node):   785         if node.doc is not None:   786             self.stream.write("<pre class='doc'>\n")   787             self.stream.write('"""')   788             output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"'))   789             self.stream.write(self._text(output))   790             self.stream.write('"""')   791             self.stream.write("</pre>\n")   792    793     def _sequence(self, node):   794         first = 1   795         for n in node.nodes:   796             if not first:   797                 self.stream.write(",\n")   798             self.dispatch(n)   799             first = 0   800    801     def _mapping(self, node):   802         first = 1   803         for k, v in node.items:   804             if not first:   805                 self.stream.write(",\n")   806             self.dispatch(k)   807             self.stream.write(":\n")   808             self.dispatch(v)   809             first = 0   810    811     def _parameters(self, subprogram, subprograms):   812    813         # Get all the parameter lists.   814    815         params = []   816         nparams = 0   817         for sub in subprograms:   818             params.append(sub.params)   819             nparams = max(nparams, len(sub.params))   820         stars = []   821         have_star = 0   822         for sub in subprograms:   823             stars.append(sub.star)   824             if sub.star is not None:   825                 have_star = 1   826         dstars = []   827         have_dstar = 0   828         for sub in subprograms:   829             dstars.append(sub.dstar)   830             if sub.dstar is not None:   831                 have_dstar = 1   832    833         # Traverse the parameter lists, choosing a "column" at a time.   834    835         first = 1   836         for n in range(0, nparams):   837             if not first:   838                 self.stream.write(",\n")   839             main_param, main_default = subprogram.params[n]   840             self._name_start(main_param)   841             self._popup_start()   842             self.stream.write("<div class='types'>\n")   843             for i in range(0, len(subprograms)):   844                 param, default = params[i][n]   845                 self._parameter(subprograms[i], param)   846             self.stream.write("</div>\n")   847             self._popup_end()   848             self._name_end()   849             self._default(main_default)   850             first = 0   851    852         if have_star:   853             if not first:   854                 self.stream.write(", *\n")   855             main_param, main_default = subprogram.star   856             self._name_start(main_param)   857             self._popup_start()   858             self.stream.write("<div class='types'>\n")   859             for i in range(0, len(subprograms)):   860                 param, default = stars[i]   861                 self._parameter(subprograms[i], param)   862             self.stream.write("</div>\n")   863             self._popup_end()   864             self._name_end()   865             self._default(main_default)   866             first = 0   867    868         if have_dstar:   869             if not first:   870                 self.stream.write(", **\n")   871             main_param, main_default = subprogram.dstar   872             self._name_start(main_param)   873             self._popup_start()   874             self.stream.write("<div class='types'>\n")   875             for i in range(0, len(subprograms)):   876                 param, default = dstars[i]   877                 self._parameter(subprograms[i], param)   878             self.stream.write("</div>\n")   879             self._popup_end()   880             self._name_end()   881             self._default(main_default)   882             first = 0   883    884     def _parameter(self, subprogram, param):   885         if hasattr(subprogram, "paramtypes"):   886             self._types_list(subprogram.paramtypes[param])   887    888     def _default(self, default):   889         if default is not None and default.original is not None:   890             self.stream.write("=\n")   891             self.dispatch(default.original)   892    893     def _name(self, name):   894         self.stream.write("<span class='name'>%s</span>\n" % name)   895    896     def _name_start(self, name):   897         self.stream.write("<span class='name'>%s\n" % name)   898    899     def _name_end(self):   900         self.stream.write("</span>\n")   901    902     def _popup_start(self):   903         self.stream.write("<span class='popup'>\n")   904    905     def _popup_end(self):   906         self.stream.write("</span>\n")   907    908     def _op(self, node):   909         self.stream.write("<div class='invocations'>\n")   910         if hasattr(node, "_left_call") and hasattr(node, "_right_call"):   911             self._invocations_list(node._left_call)   912             self._invocations_list(node._right_call)   913         else:   914             _node = node._node   915             if isinstance(_node, Not):   916                 _node = _node.expr   917             self._invocations_list(_node)   918         self.stream.write("</div>\n")   919    920     def _invocations(self, node):   921         self.stream.write("<div class='invocations'>\n")   922         self._invocations_list(node)   923         self.stream.write("</div>\n")   924    925     def _invocations_list(self, node):   926         if hasattr(node, "invocations"):   927             for invocation in node.invocations:   928                 fn = getattr(invocation, "copy_of", invocation).full_name()   929                 module = invocation.module.name   930                 name = invocation.name   931                 structures = [x.name for x in invocation.structures]   932                 qualified_name = ".".join([module] + structures + [name])   933                 self.stream.write("<div class='invocation'>")   934                 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn)))   935                 self.stream.write(self._text(qualified_name))   936                 self.stream.write("</a>")   937                 self.stream.write("</div>\n")   938    939     def _types(self, nodes):   940         self.stream.write("<div class='types'>\n")   941         for node in nodes:   942             self._type(node)   943         self.stream.write("</div>\n")   944    945     def _type(self, node):   946         if hasattr(node, "types"):   947             if node.types:   948                 self._types_list(node.types)   949             else:   950                 self._no_types()   951         elif hasattr(node, "writes"):   952             if node.writes:   953                 self._types_list(flatten(node.writes.values()))   954             else:   955                 self._no_types()   956         else:   957             self._unvisited()   958    959     def _unvisited(self):   960         self.stream.write("<div class='type'>")   961         self.stream.write("unvisited\n")   962         self.stream.write("</div>\n")   963    964     def _no_types(self):   965         self.stream.write("<div class='type'>")   966         self.stream.write("no types\n")   967         self.stream.write("</div>\n")   968    969     def _types_container(self, types, style_class):   970         self.stream.write("<div class='%s'>\n" % style_class)   971         self._types_list(types)   972         self.stream.write("</div>\n")   973    974     def _types_list(self, types):   975         for type in types:   976             fn = type.type.full_name()   977             self.stream.write("<div class='type'>")   978             self.stream.write(self._text(fn))    979             self.stream.write("</div>\n")   980    981     def _raises(self, nodes):   982    983         "Output the exception information for the given simplified 'nodes'."   984    985         self.stream.write("<div class='raises'>\n")   986         for node in nodes:   987             if hasattr(node, "raises") and node.raises:   988                 self._types_list(node.raises)   989         self.stream.write("</div>\n")   990    991     def _scopes(self, nodes):   992    993         "Output the scope information for the given simplified 'nodes'."   994    995         self.stream.write("<div class='scopes'>\n")   996         for node in nodes:   997             self._scope(node)   998         self.stream.write("</div>\n")   999   1000     def _scope(self, node):  1001   1002         "Output the scope information for the given simplified 'node'."  1003   1004         # Straightforward name loading/storing involves the local scope.  1005   1006         if isinstance(node, StoreName) or isinstance(node, LoadName):  1007             self.stream.write("<div class='scope'>")  1008             self.stream.write("(local)")   1009             self.stream.write("</div>\n")  1010   1011         # Other loading/storing involves attributes accessed on modules, classes  1012         # and objects.  1013   1014         else:  1015   1016             # Loading...  1017   1018             if hasattr(node, "accesses") and node.accesses:  1019                 for ref, accesses in node.accesses.items():  1020                     fn = ref.full_name()  1021                     for attr, access in accesses:  1022                         access_fn = access.full_name()  1023                         self.stream.write("<div class='scope'>")  1024                         self.stream.write(self._text(fn))  1025                         if ref != access:  1026                             self.stream.write(" (via " + self._text(access_fn) + ")")   1027                         self.stream.write("</div>\n")  1028   1029             # Storing...  1030   1031             if hasattr(node, "writes") and node.writes:  1032                 for ref in node.writes.keys():  1033                     fn = ref.full_name()  1034                     self.stream.write("<div class='scope'>")  1035                     self.stream.write(self._text(fn))  1036                     self.stream.write("</div>\n")  1037   1038             # Non-loading...  1039   1040             if hasattr(node, "non_accesses") and node.non_accesses:  1041                 self._types_container(node.non_accesses, "non-accesses")  1042   1043             # Non-storing...  1044   1045             if hasattr(node, "non_writes") and node.non_writes:  1046                 self._types_container(node.non_writes, "non-writes")  1047   1048 # Utility functions.  1049   1050 def flatten(lists):  1051     result = []  1052     for l in lists:  1053         for attr in l:  1054             if attr not in result:  1055                 result.append(attr)  1056     return result  1057   1058 # Convenience functions.  1059   1060 def browse(module, stream=None):  1061     browser = Browser(stream or sys.stdout)  1062     browser.process(module.original)  1063   1064 def makedoc(module, filename):  1065     stream = open(filename, "wb")  1066     try:  1067         browser = Browser(stream)  1068         browser.process(module.original)  1069     finally:  1070         stream.close()  1071   1072 def makedocs(module, modules, builtins):  1073     dirname = "%s-docs" % module.name  1074     if not os.path.exists(dirname):  1075         os.mkdir(dirname)  1076     for m in [module, builtins] + modules:  1077         makedoc(m, os.path.join(dirname, "%s%shtml" % (m.name, os.path.extsep)))  1078   1079 # vim: tabstop=4 expandtab shiftwidth=4