simplify

viewer.py

184:7ea581069c2e
2007-01-25 paulb Fixed nested scopes workaround to only add function names to namespaces - not module names as well.
     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, 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, Dict,   135              Ellipsis, Exec, Invert, LeftShift, ListComp, ListCompFor,   136              ListCompIf, 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         structure = definition.expr.ref   184         self.stream.write("<div class='class' id='%s'>\n" % self._url(structure.full_name()))   185         self.stream.write("<div>\n")   186         self._keyword("class")   187         self._name_start(structure.name)   188         self._popup_start()   189         self._scopes(definition)   190         self._popup_end()   191         self._name_end()   192         bases = structure.bases   193    194         # Suppress the "object" class appearing alone.   195    196         if bases and not (len(bases) == 1 and bases[0].name == "object"):   197             self.stream.write("(")   198             first = 1   199             for base in bases:   200                 if not first:   201                     self.stream.write(",\n")   202                 self._name_start(base.name)   203                 self._popup_start()   204                 self._types(base)   205                 self._scopes(base)   206                 self._popup_end()   207                 self._name_end()   208                 first = 0   209             self.stream.write(")")   210    211         self.stream.write(":\n")   212         self._comment(self._text(structure.full_name()))   213         self.stream.write("</div>\n")   214    215         self.stream.write("<div class='body'>\n")   216         self._doc(node)   217         self.dispatch(node.code)   218         self.stream.write("</div>\n")   219         self.stream.write("</div>\n")   220    221     def visitContinue(self, node):   222         self.stream.write("<div class='continue'>\n")   223         self._keyword("continue")   224         self.stream.write("</div>\n")   225    226     def visitDiscard(self, node):   227         self.stream.write("<div class='discard'>\n")   228         self.default(node)   229         self.stream.write("</div>\n")   230    231     def visitFor(self, node):   232         self.stream.write("<div class='if'>\n")   233         self.stream.write("<div>\n")   234         self.stream.write("<span class='iterator'>\n")   235         self._keyword("for")   236         self._popup_start()   237         self._invocations(node._next_call)   238         self._popup_end()   239         self.stream.write("</span>\n")   240         self.dispatch(node.assign)   241         self.stream.write("<span class='iterator'>\n")   242         self._keyword("in")   243         self._popup_start()   244         self._invocations(node._iter_call)   245         self._popup_end()   246         self.stream.write("</span>\n")   247         self.dispatch(node.list)   248         self.stream.write(":\n")   249         self.stream.write("</div>\n")   250         self.stream.write("<div class='body'>\n")   251         self.dispatch(node.body)   252         self.stream.write("</div>\n")   253         if node.else_ is not None:   254             self.stream.write("<div>\n")   255             self._keyword("else")   256             self.stream.write(":\n")   257             self.stream.write("</div>\n")   258             self.stream.write("<div class='body'>\n")   259             self.dispatch(node.else_)   260             self.stream.write("</div>\n")   261         self.stream.write("</div>\n")   262    263     def visitFrom(self, node):   264         self.stream.write("<div class='from'>\n")   265         self._keyword("from")   266         self.stream.write("<span class='name'>\n")   267         self.stream.write(node.modname)   268         self._popup_start()   269         self._types(node._modname)   270         self._popup_end()   271         self.stream.write("</span>\n")   272         self._keyword("import")   273         first = 1   274         for (name, alias), _name in map(None, node.names, node._names):   275             if not first:   276                 self.stream.write(",\n")   277             if alias:   278                 self.stream.write(name + " ")   279                 self._keyword("as")   280             self.stream.write("<span class='name'>\n")   281             self.stream.write(alias or name)   282             self._popup_start()   283             self._types(_name)   284             self._popup_end()   285             self.stream.write("</span>\n")   286             first = 0   287         self.stream.write("</div>\n")   288    289     def visitFunction(self, node):   290         definition = node._node   291         subprogram = definition.expr.ref   292         self.stream.write("<div class='function' id='%s'>\n" % self._url(subprogram.full_name()))   293         self.stream.write("<div>\n")   294         self._keyword("def")   295         self._name_start(subprogram.name)   296         self._popup_start()   297         self._scopes(definition)   298         self._raises(subprogram)   299         self._popup_end()   300         self._name_end()   301         self.stream.write("(")   302         self._parameters(subprogram)   303         self.stream.write(")")   304         self.stream.write(":\n")   305         self._comment(self._text(subprogram.full_name()))   306         self.stream.write("</div>\n")   307    308         self.stream.write("<div class='body'>\n")   309         self._doc(node)   310         self.dispatch(node.code)   311         self.stream.write("</div>\n")   312         self.stream.write("</div>\n")   313    314     def visitGlobal(self, node):   315         self.stream.write("<div class='global'>\n")   316         self._keyword("global")   317         first = 1   318         for name in node.names:   319             if not first:   320                 self.stream.write(",\n")   321             self.stream.write(name)   322             first = 0   323         self.stream.write("</div>\n")   324    325     def visitIf(self, node):   326         self.stream.write("<div class='if'>\n")   327         first = 1   328         conditional = node._node   329         for compare, stmt in node.tests:   330             self.stream.write("<div>\n")   331             self.stream.write("<span class='conditional'>\n")   332             if first:   333                 self._keyword("if")   334             else:   335                 self._keyword("elif")   336             self._popup_start()   337             self._invocations(conditional.test)   338             self._popup_end()   339             self.stream.write("</span>\n")   340             self.dispatch(compare)   341             self.stream.write(":\n")   342             self.stream.write("</div>\n")   343             self.stream.write("<div class='body'>\n")   344             self.dispatch(stmt)   345             self.stream.write("</div>\n")   346             if conditional.else_:   347                 conditional = conditional.else_[0]   348             else:   349                 conditional = None   350             first = 0   351         if node.else_ is not None:   352             self.stream.write("<div>\n")   353             self._keyword("else")   354             self.stream.write(":\n")   355             self.stream.write("</div>\n")   356             self.stream.write("<div class='body'>\n")   357             self.dispatch(node.else_)   358             self.stream.write("</div>\n")   359         self.stream.write("</div>\n")   360    361     def visitImport(self, node):   362         self.stream.write("<div class='import'>\n")   363         self._keyword("import")   364         first = 1   365         for (name, alias), _name in map(None, node.names, node._names):   366             if not first:   367                 self.stream.write(",\n")   368             if alias:   369                 self.stream.write(name + " ")   370                 self._keyword("as")   371             self.stream.write("<span class='name'>\n")   372             self.stream.write(alias or name)   373             self._popup_start()   374             self._types(_name)   375             self._popup_end()   376             self.stream.write("</span>\n")   377             first = 0   378         self.stream.write("</div>\n")   379    380     def visitPass(self, node):   381         self.stream.write("<div class='pass'>\n")   382         self._keyword("pass")   383         self.stream.write("</div>\n")   384    385     def visitPrint(self, node):   386         self.stream.write("<div class='print'>\n")   387         self._keyword("print")   388         if node.dest is not None:   389             self.stream.write(">>\n")   390             self.dispatch(node.dest)   391         for n in node.nodes:   392             self.dispatch(n)   393             self.stream.write(",\n")   394         self.stream.write("</div>\n")   395    396     def visitPrintnl(self, node):   397         self.stream.write("<div class='printnl'>\n")   398         self._keyword("print")   399         if node.dest is not None:   400             self.stream.write(">>\n")   401             self.dispatch(node.dest)   402         first = 1   403         for n in node.nodes:   404             if not first:   405                 self.stream.write(",\n")   406             self.dispatch(n)   407             first = 0   408         self.stream.write("</div>\n")   409    410     def visitRaise(self, node):   411         self.stream.write("<div class='raise'>\n")   412         self._keyword("raise")   413         self.dispatch(node.expr1)   414         if node.expr2 is not None:   415             self.stream.write(",\n")   416             self.dispatch(node.expr2)   417         if node.expr3 is not None:   418             self.stream.write(",\n")   419             self.dispatch(node.expr3)   420         self.stream.write("</div>\n")   421    422     def visitReturn(self, node):   423         self.stream.write("<div class='return'>\n")   424         self.stream.write("<span class='returns'>\n")   425         self._keyword("return")   426         self._popup_start()   427         self._types(node._node)   428         self._popup_end()   429         self.stream.write("</span>\n")   430         self.dispatch(node.value)   431         self.stream.write("</div>\n")   432    433     def visitStmt(self, node):   434         self.stream.write("<div class='stmt'>\n")   435         self.default(node)   436         self.stream.write("</div>\n")   437    438     def visitTryExcept(self, node):   439         self.stream.write("<div class='tryexcept'>\n")   440         self.stream.write("<div>\n")   441         self._keyword("try")   442         self.stream.write(":\n")   443         self.stream.write("</div>\n")   444         self.stream.write("<div class='body'>\n")   445         self.dispatch(node.body)   446         self.stream.write("</div>\n")   447         for spec, assign, statement in node.handlers:   448             self.stream.write("<div>\n")   449             self._keyword("except")   450             if spec is not None:   451                 self.dispatch(spec)   452             if assign is not None:   453                 self.stream.write(",\n")   454                 self.dispatch(assign)   455             self.stream.write(":\n")   456             self.stream.write("</div>\n")   457             self.stream.write("<div class='body'>\n")   458             self.dispatch(statement)   459             self.stream.write("</div>\n")   460         if node.else_ is not None:   461             self.stream.write("<div>\n")   462             self._keyword("else")   463             self.stream.write(":\n")   464             self.stream.write("</div>\n")   465             self.stream.write("<div class='body'>\n")   466             self.dispatch(node.else_)   467             self.stream.write("</div>\n")   468         self.stream.write("</div>\n")   469    470     def visitTryFinally(self, node):   471         self.stream.write("<div class='tryfinally'>\n")   472         self.stream.write("<div>\n")   473         self._keyword("try")   474         self.stream.write(":\n")   475         self.stream.write("</div>\n")   476         self.stream.write("<div class='body'>\n")   477         self.dispatch(node.body)   478         self.stream.write("</div>\n")   479         self.stream.write("<div>\n")   480         self._keyword("finally")   481         self.stream.write(":\n")   482         self.stream.write("</div>\n")   483         self.stream.write("<div class='body'>\n")   484         self.dispatch(node.final)   485         self.stream.write("</div>\n")   486         self.stream.write("</div>\n")   487    488     def visitWhile(self, node):   489         self.stream.write("<div class='while'>\n")   490         self.stream.write("<div>\n")   491         self.stream.write("<span class='conditional'>\n")   492         self._keyword("while")   493         self._popup_start()   494         self._invocations(node._test_call)   495         self._popup_end()   496         self.stream.write("</span>\n")   497         self.dispatch(node.test)   498         self.stream.write(":\n")   499         self.stream.write("</div>\n")   500         self.stream.write("<div class='body'>\n")   501         self.dispatch(node.body)   502         self.stream.write("</div>\n")   503         if node.else_ is not None:   504             self.stream.write("<div>\n")   505             self._keyword("else")   506             self.stream.write(":\n")   507             self.stream.write("</div>\n")   508             self.stream.write("<div class='body'>\n")   509             self.dispatch(node.else_)   510             self.stream.write("</div>\n")   511         self.stream.write("</div>\n")   512    513     # Expression-related helper methods.   514    515     def _visitBinary(self, node, name, symbol):   516         self.stream.write("<span class='%s'>\n" % name)   517         self.dispatch(node.left)   518         self.stream.write("<span class='operator'>\n")   519         self.stream.write(symbol)   520         self._popup_start()   521         self.stream.write("<div class='invocations'>\n")   522         self._invocations_list(node._left_call)   523         self._invocations_list(node._right_call)   524         self.stream.write("</div>\n")   525         self._popup_end()   526         self.stream.write("</span>\n")   527         self.dispatch(node.right)   528         self.stream.write("</span>")   529    530     def _visitUnary(self, node, name, symbol):   531         self.stream.write("<span class='%s'>\n" % name)   532         self.stream.write("<span class='operator'>\n")   533         self.stream.write(symbol)   534         self._popup_start()   535         self.stream.write("<div class='invocations'>\n")   536         self._invocations_list(node._unary_call)   537         self.stream.write("</div>\n")   538         self._popup_end()   539         self.stream.write("</span>\n")   540         self.dispatch(node.expr)   541         self.stream.write("</span>")   542    543     # Expressions.   544    545     def visitAdd(self, node):   546         self._visitBinary(node, "add", "+")   547    548     def visitAnd(self, node):   549         self.stream.write("<span class='and'>\n")   550         first = 1   551         for n in node.nodes:   552             if not first:   553                 self._keyword("and")   554             self.dispatch(n)   555             first = 0   556         self.stream.write("</span>")   557    558     def visitAssAttr(self, node):   559         self.stream.write("<span class='assattr'>\n")   560         self.dispatch(node.expr)   561         self.stream.write("<span class='attr'>\n")   562         self.stream.write(".%s\n" % self._text(node.attrname))   563         self._popup_start()   564         self._types(node._node)   565         self._scopes(node._node)   566         self._popup_end()   567         self.stream.write("</span>\n")   568         self.stream.write("</span>\n")   569    570     def visitAssList(self, node):   571         self.stream.write("<span class='list'>\n")   572         self.stream.write("[")   573         self._sequence(node)   574         self.stream.write("]\n")   575         self.stream.write("</span>\n")   576    577     def visitAssName(self, node):   578         self._name_start(node._node.name)   579         self._popup_start()   580         self._types(node._node.expr)   581         self._scopes(node._node)   582         self._popup_end()   583         self._name_end()   584    585     def visitAssTuple(self, node):   586         self.stream.write("<span class='tuple'>\n")   587         self.stream.write("(")   588         self._sequence(node)   589         self.stream.write(")\n")   590         self.stream.write("</span>\n")   591    592     def visitCallFunc(self, node):   593         self.stream.write("<span class='callfunc'>\n")   594         self.dispatch(node.node)   595         self.stream.write("<span class='call'>\n")   596         self.stream.write("(")   597         self._popup_start()   598         self._invocations(node._node)   599         self._popup_end()   600         self.stream.write("</span>\n")   601         first = 1   602         for arg in node.args:   603             if not first:   604                 self.stream.write(",\n")   605             self.dispatch(arg)   606             first = 0   607         if node.star_args is not None:   608             if not first:   609                 self.stream.write(", *\n")   610             self.dispatch(node.star_args)   611             first = 0   612         if node.dstar_args is not None:   613             if not first:   614                 self.stream.write(", **\n")   615             self.dispatch(node.dstar_args)   616             first = 0   617         self.stream.write(")\n")   618         self.stream.write("</span>\n")   619    620     def visitCompare(self, node):   621         self.stream.write("<span class='compare'>\n")   622         self.dispatch(node.expr)   623         for (op_name, expr), _op in map(None, node.ops, node._ops):   624             self.stream.write("<span class='op'>\n")   625             self.stream.write(op_name)   626             self._popup_start()   627             self._op(op_name, _op)   628             self._popup_end()   629             self.stream.write("</span>\n")   630             self.dispatch(expr)   631         self.stream.write("</span>\n")   632    633     def visitConst(self, node):   634         self.stream.write(repr(node.value))   635    636     def visitDiv(self, node):   637         self._visitBinary(node, "div", "/")   638    639     def visitFloorDiv(self, node):   640         self._visitBinary(node, "floordiv", "//")   641    642     def visitGetattr(self, node):   643         self.stream.write("<span class='getattr'>\n")   644         self.dispatch(node.expr)   645         self.stream.write("<span class='attr'>\n")   646         self.stream.write(".%s\n" % self._text(node.attrname))   647         self._popup_start()   648         self._types(node._node)   649         self._scopes(node._node)   650         self._popup_end()   651         self.stream.write("</span>\n")   652         self.stream.write("</span>\n")   653    654     def visitKeyword(self, node):   655         self.stream.write("<span class='keyword-arg'>\n")   656         self.stream.write(node.name)   657         self.stream.write("=")   658         self.dispatch(node.expr)   659         self.stream.write("</span>\n")   660    661     def visitLambda(self, node):   662         definition = node._node   663         subprogram = definition.expr.ref   664         self.stream.write("<span class='lambda'>\n")   665         self._keyword("lambda")   666         self._parameters(subprogram)   667         self.dispatch(node.code)   668         self.stream.write("</span>\n")   669    670     visitList = visitAssList   671    672     def visitMod(self, node):   673         self._visitBinary(node, "mod", "%")   674    675     def visitMul(self, node):   676         self._visitBinary(node, "mul", "*")   677    678     def visitName(self, node):   679         self._name_start(node._node.name)   680         self._popup_start()   681         self._types(node._node)   682         self._scopes(node._node)   683         self._popup_end()   684         self._name_end()   685    686     def visitNot(self, node):   687         self.stream.write("<span class='not'>\n")   688         self._keyword("not")   689         self.dispatch(node.expr)   690         self.stream.write("</span>")   691    692     def visitOr(self, node):   693         self.stream.write("<span class='or'>\n")   694         first = 1   695         for n in node.nodes:   696             if not first:   697                 self._keyword("or")   698             self.dispatch(n)   699             first = 0   700         self.stream.write("</span>")   701    702     def visitPower(self, node):   703         self._visitBinary(node, "power", "**")   704    705     def visitSlice(self, node):   706         self.stream.write("<span class='slice'>\n")   707         self.dispatch(node.expr)   708         self.stream.write("[")   709         if node.lower:   710             self.dispatch(node.lower)   711         self.stream.write(":")   712         if node.upper:   713             self.dispatch(node.upper)   714         # NOTE: Step?   715         self.stream.write("]")   716         self.stream.write("</span>\n")   717    718     def visitSub(self, node):   719         self._visitBinary(node, "sub", "-")   720    721     def visitSubscript(self, node):   722         self.stream.write("<span class='subscript'>\n")   723         self.dispatch(node.expr)   724         self.stream.write("[")   725         first = 1   726         for sub in node.subs:   727             if not first:   728                 self.stream.write(", ")   729             self.dispatch(sub)   730             first = 0   731         self.stream.write("]")   732         self.stream.write("</span>\n")   733    734     visitTuple = visitAssTuple   735    736     def visitUnaryAdd(self, node):   737         self._visitUnary(node, "add", "+")   738    739     def visitUnarySub(self, node):   740         self._visitUnary(node, "sub", "-")   741    742     # Output preparation methods.   743    744     def _text(self, text):   745         return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")   746    747     def _attr(self, attr):   748         return self._text(attr).replace("'", "&apos;").replace('"', "&quot;")   749    750     def _url(self, url):   751         return self._attr(url).replace("#", "%23").replace("-", "%2d")   752    753     def _comment(self, comment):   754         self.stream.write("<span class='comment'># %s</span>\n" % comment)   755    756     def _keyword(self, kw):   757         self.stream.write("<span class='keyword'>%s</span> " % kw)   758    759     def _doc(self, node):   760         if node.doc is not None:   761             self.stream.write("<pre class='doc'>\n")   762             self.stream.write('"""')   763             output = textwrap.dedent(node.doc.replace('"""', '\\"\\"\\"'))   764             self.stream.write(self._text(output))   765             self.stream.write('"""')   766             self.stream.write("</pre>\n")   767    768     def _sequence(self, node):   769         first = 1   770         for n in node.nodes:   771             if not first:   772                 self.stream.write(",\n")   773             self.dispatch(n)   774             first = 0   775    776     def _parameters(self, subprogram):   777         first = 1   778         for param, default in subprogram.params:   779             if not first:   780                 self.stream.write(",\n")   781             self._parameter(subprogram, param, default)   782             first = 0   783         if subprogram.star is not None:   784             if not first:   785                 self.stream.write(", *\n")   786             param, default = subprogram.star   787             self._parameter(subprogram, param, default)   788             first = 0   789         if subprogram.dstar is not None:   790             if not first:   791                 self.stream.write(", **\n")   792             param, default = subprogram.dstar   793             self._parameter(subprogram, param, default)   794             first = 0   795    796     def _parameter(self, subprogram, param, default):   797         self._name_start(param)   798         if hasattr(subprogram, "paramtypes"):   799             self._popup_start()   800             self._types_list(subprogram.paramtypes[param])   801             self._popup_end()   802         self._name_end()   803         if default is not None and default.original is not None:   804             self.stream.write("=\n")   805             self.dispatch(default.original)   806    807     def _name(self, name):   808         self.stream.write("<span class='name'>%s</span>\n" % name)   809    810     def _name_start(self, name):   811         self.stream.write("<span class='name'>%s\n" % name)   812    813     def _name_end(self):   814         self.stream.write("</span>\n")   815    816     def _popup_start(self):   817         self.stream.write("<span class='popup'>\n")   818    819     def _popup_end(self):   820         self.stream.write("</span>\n")   821    822     def _op(self, op_name, op):   823         if op is not None:   824             if isinstance(op, Not):   825                 self._invocations(op.expr)   826             else:   827                 self._invocations(op)   828    829     def _invocations(self, node):   830         self.stream.write("<div class='invocations'>\n")   831         self._invocations_list(node)   832         self.stream.write("</div>\n")   833    834     def _invocations_list(self, node):   835         if hasattr(node, "invocations"):   836             for invocation in node.invocations:   837                 fn = invocation.full_name()   838                 module = invocation.module.name   839                 name = invocation.name   840                 structures = [x.name for x in invocation.structures]   841                 self.stream.write("<div class='invocation'>")   842                 self.stream.write("<a href='%s.html#%s'>" % (self._url(module), self._url(fn)))   843                 self.stream.write(self._text(".".join([module] + structures + [name])))   844                 self.stream.write("</a>")   845                 self.stream.write("</div>\n")   846    847     def _types(self, node):   848         if hasattr(node, "types"):   849             if node.types:   850                 self._types_list(node.types)   851             else:   852                 self._no_types()   853         elif hasattr(node, "writes"):   854             if node.writes:   855                 self._types_list(flatten(node.writes.values()))   856             else:   857                 self._no_types()   858         else:   859             self.stream.write("<div class='types'>\n")   860             self.stream.write("unvisited\n")   861             self.stream.write("</div>\n")   862    863     def _no_types(self):   864         self.stream.write("<div class='types'>\n")   865         self.stream.write("no types\n")   866         self.stream.write("</div>\n")   867    868     def _types_list(self, types, style_class="types"):   869         self.stream.write("<div class='%s'>\n" % style_class)   870         for type in types:   871             fn = type.type.full_name()   872             self.stream.write("<div class='type'>")   873             self.stream.write(self._text(fn))    874             self.stream.write("</div>\n")   875         self.stream.write("</div>\n")   876    877     def _raises(self, node):   878         if hasattr(node, "namespace") and hasattr(node.namespace, "raises") and node.namespace.raises:   879             self._types_list(node.namespace.raises, style_class="raises")   880    881     def _scopes(self, node):   882    883         "Output the scope information for the given simplified 'node'."   884    885         # Straightforward name loading/storing involves the local scope.   886    887         if isinstance(node, StoreName) or isinstance(node, LoadName):   888             self.stream.write("<div class='scopes'>\n")   889             self.stream.write("<div class='scope'>")   890             self.stream.write("(local)")    891             self.stream.write("</div>\n")   892             self.stream.write("</div>\n")   893    894         # Other loading/storing involves attributes accessed on modules, classes   895         # and objects.   896    897         else:   898    899             # Loading...   900    901             if hasattr(node, "accesses") and node.accesses:   902                 self.stream.write("<div class='scopes'>\n")   903                 for ref, accesses in node.accesses.items():   904                     fn = ref.full_name()   905                     for attr, access in accesses:   906                         access_fn = access.full_name()   907                         self.stream.write("<div class='scope'>")   908                         self.stream.write(self._text(fn))   909                         if ref != access:   910                             self.stream.write(" (via " + self._text(access_fn) + ")")    911                         self.stream.write("</div>\n")   912                 self.stream.write("</div>\n")   913    914             # Storing...   915    916             if hasattr(node, "writes") and node.writes:   917                 self.stream.write("<div class='scopes'>\n")   918                 for ref in node.writes.keys():   919                     fn = ref.full_name()   920                     self.stream.write("<div class='scope'>")   921                     self.stream.write(self._text(fn))   922                     self.stream.write("</div>\n")   923                 self.stream.write("</div>\n")   924    925             # Non-loading...   926    927             if hasattr(node, "non_accesses") and node.non_accesses:   928                 self._types_list(node.non_accesses, style_class="non-accesses")   929    930             # Non-storing...   931    932             if hasattr(node, "non_writes") and node.non_writes:   933                 self._types_list(node.non_writes, style_class="non-writes")   934    935 # Utility functions.   936    937 def flatten(lists):   938     result = []   939     for l in lists:   940         for attr in l:   941             if attr not in result:   942                 result.append(attr)   943     return result   944    945 # Convenience functions.   946    947 def browse(module, stream=None):   948     browser = Browser(stream or sys.stdout)   949     browser.process(module.original)   950    951 def makedoc(module, filename):   952     stream = open(filename, "wb")   953     try:   954         browser = Browser(stream)   955         browser.process(module.original)   956     finally:   957         stream.close()   958    959 def makedocs(module, modules, builtins):   960     dirname = "%s-docs" % module.name   961     if not os.path.exists(dirname):   962         os.mkdir(dirname)   963     for m in [module, builtins] + modules:   964         makedoc(m, os.path.join(dirname, "%s%shtml" % (m.name, os.path.extsep)))   965    966 # vim: tabstop=4 expandtab shiftwidth=4