0001 # Copyright 2003, Grant T. Olson, see License.txt for details 0002 0003 import sys, os 0004 import token 0005 import tokenize 0006 import symbol 0007 import string 0008 0009 import nsNode 0010 from astCrawler import astCrawler 0011 from lexicalTree import lexicalTree, getLibraryReference, libTree 0012 from misc import keywords, operators, pathToUrl, useLibraryReference, openPythonFile, InvalidSourceError 0013 0014 class astToHtml(astCrawler): 0015 """ 0016 Generates pprinted HTML Output from the ast 0017 0018 the asts lose the info we need to print whitespace and comments properly. 0019 So we also setup a token generator to grab a stream of tokens with the info 0020 (and we can't just use the tokens to begin with because that won't determine 0021 structure of the doc easily) 0022 0023 It's important to consume a token each time you discard a TOKEN node so that 0024 they stay in sync. This essentially means we need to call getNextToken() everytime 0025 we use a token durring processing, even if we ignore the actual token text in our 0026 output 0027 """ 0028 0029 def __init__(self, filename="", outputStream=sys.stdout.write): 0030 self.filename = filename 0031 self.outputStream = outputStream 0032 if useLibraryReference: 0033 self.lexicalScope = lexicalTree("", "", mapBuiltins=1) 0034 else: 0035 self.lexicalScope = lexicalTree("", "") 0036 self.oldToken = [[],[],[-1,-1],[-1,-1],""] 0037 self.currentToken = [[],[],[-1,-1],[-1,-1],""] 0038 infile = openPythonFile(filename) 0039 astCrawler.__init__(self, infile.read() + "\n\n") 0040 infile.close() 0041 infile = openPythonFile(filename) 0042 self.tokenGen = tokenize.generate_tokens(infile.readline) 0043 0044 currentLocation = nsNode.pathToNode(filename) 0045 if currentLocation and currentLocation.parent: #search locally first 0046 currentLocation = currentLocation.parent 0047 self.searchPath = [] 0048 self.searchPath.extend(currentLocation.getModules()) 0049 self.searchPath.extend(currentLocation.getPackages()) 0050 self.searchPath.extend(currentLocation.getDirectories()) 0051 self.searchPath.append(nsNode.nsNodeTree) 0052 else: 0053 self.searchPath = None 0054 0055 if useLibraryReference: 0056 libName = os.path.split(filename)[1] 0057 libName = libName[:-3] 0058 self.libReference = getLibraryReference(libName) 0059 else: 0060 self.libReference = None 0061 0062 def getTokenText(self, formatString="%s"): 0063 """ Makes sure we have proper whitespace""" 0064 retVal = "" 0065 if self.oldToken[3] != self.currentToken[2]: # some sort of whitespace 0066 if self.oldToken[3][0] != self.currentToken[2][0]: #new line 0067 retVal += self.oldToken[4][self.oldToken[3][1]:] + self.currentToken[4][:self.currentToken[2][1]] 0068 else: # normal whitespace 0069 retVal += self.currentToken[4][self.oldToken[3][1]:self.currentToken[2][1]] 0070 retVal += formatString % self.getSafeText(self.currentToken[1]) 0071 self.oldToken = self.currentToken 0072 retVal = retVal.expandtabs() #python assumes tabs are 8 spaces 0073 return retVal 0074 0075 def getSafeText(self, text): 0076 htmlFixes = [ ("&", "&"), ("<", "<"), (">", ">"), ] 0077 for fix in htmlFixes: 0078 text = text.replace(*fix) 0079 return text 0080 0081 def printText(self, text): 0082 """ sends general text to the output stream """ 0083 self.outputStream(text) 0084 0085 def printToken(self, formatString="%s"): 0086 """ 0087 Prints the current tokens text 0088 """ 0089 text = self.getTokenText(formatString) 0090 self.printText(text) 0091 0092 def printComment(self): 0093 self.printToken("<span class='comment'>%s</span>") 0094 0095 def getNextToken(self, validationText): 0096 self.currentToken = self.tokenGen.next() 0097 while self.currentToken[0] in (token.N_TOKENS, tokenize.NL): 0098 if self.currentToken[0] == token.N_TOKENS: 0099 self.printComment() 0100 if self.currentToken[0] == tokenize.NL: 0101 self.printToken() 0102 self.currentToken = self.tokenGen.next() 0103 #make sure we're synched with the tree. Note the real compile doesn't give text for NL's, INDENTS 0104 # so we have to account for that 0105 if self.currentToken[1] != validationText and \ 0106 (validationText == '' and self.currentToken[0] not in (token.NEWLINE, token.INDENT, token.DEDENT)): 0107 raise InvalidSourceError("[%s] tokenize doesn't match the AST tree ACTUAL: %s TOKENGEN:%s" % (self.filename, validationText, self.currentToken)) 0108 ### 0109 ### HANDLERS 0110 ### 0111 def registerHandlers(self): 0112 self.handlers[token.ENDMARKER] = self.endHandler 0113 self.handlers[token.STRING] = self.stringHandler 0114 self.handlers[symbol.funcdef]= self.functionHandler 0115 self.handlers[symbol.classdef] = self.classHandler 0116 self.handlers[token.NAME] = self.nameHandler 0117 #self.handlers[symbol.import_stmt] = self.importHandler 0118 self.handlers[symbol.expr_stmt] = self.expressionHandler 0119 self.handlers[token.OP] = self.opHandler 0120 self.handlers[symbol.power] = self.powerHandler 0121 if hasattr(symbol, "encoding_decl"): #python 2.3 0122 self.handlers[getattr(symbol, "encoding_decl")] = self.encodingDeclHandler 0123 0124 if hasattr(symbol, "import_name"): #python 2.4 tree 0125 self.handlers[symbol.import_as_names] = self.importAsNamesHandler 0126 self.handlers[symbol.dotted_as_names] = self.dottedAsNamesHandler 0127 self.handlers[symbol.import_from] = self.importFromHandler 0128 self.handlers[symbol.import_name] = self.importNameHandler 0129 else: #python < 2.4 tree 0130 self.handlers[symbol.import_stmt] = self.importHandler 0131 0132 if hasattr(symbol,"decorator"): # python 2.4 tree 0133 self.handlers[symbol.decorator] = self.decoratorHandler 0134 0135 def linkToLibReference(self, name): 0136 """ 0137 Links to library reference if possible. 0138 As a general rule, any reference to function/class/method xxx will 0139 link to source code if possible. If code doesn't exist but a library 0140 reference does, it will link directly to that. 0141 If code does exist and there is a library reference, the declaration 0142 of the function/class/method (the def or class statement) will link 0143 to the reference manual 0144 """ 0145 linkText = "" 0146 if self.libReference: 0147 currentScope = self.libReference 0148 for item in self.lexicalScope.getScopeList(self.libReference): 0149 currentScope = currentScope.findMember(item) 0150 if not currentScope: 0151 break 0152 if currentScope: 0153 linkText = "<span class='funcname'><a name='%s'><a href='%s'>%%s</a></a></span>" 0154 linkText = linkText % (self.lexicalScope.getName(), currentScope.ref) 0155 0156 if not linkText: 0157 linkText = "<span class='funcname'><a name='%s'>%%s</a></span>"% self.lexicalScope.getName() 0158 #print self.lexicalScope.getScopeList() 0159 self.printToken(linkText) 0160 0161 def defaultHandleToken(self, item): 0162 try: 0163 self.getNextToken(item) 0164 self.printToken() 0165 except StopIteration: 0166 if item: 0167 print "\nUnmatched token from tree '%s' %s" % (item, self.filename) 0168 0169 def endHandler(self, params): 0170 """tokenize doesn't spit this out, so we just ignore EOF""" 0171 pass 0172 0173 0174 def opHandler(self, params): 0175 if len(params) != 1: 0176 raise InvalidSourceError("[%s] Invalide Operator %s" % (self.filename, params)) 0177 self.printToken("<span class='operator'>%s</span>") 0178 0179 def nameHandler(self, params): 0180 if len(params) != 1: 0181 raise InvalidSourceError("[%s] invalid name %s" % (self.filename, params)) 0182 name = params[0] 0183 self.getNextToken(name) 0184 if name in keywords: 0185 self.printToken("<span class='keyword'>%s</span>") 0186 else: 0187 self.printToken("<span class='pythonName'>%s</span>") 0188 0189 def functionHandler(self, params): 0190 if hasattr(symbol, "decorators"): #python2.4 0191 if params[0][0] == symbol.decorators: 0192 offset = 1 0193 self.process(params[0]) 0194 else: 0195 offset = 0 0196 else: 0197 offset = 0 0198 0199 defConstant = params[0 + offset] 0200 funcName = params[1 + offset][1] 0201 rest = params[2 + offset:] 0202 0203 self.getNextToken(defConstant) 0204 self.printToken("<span class='funcdef'>%s</span>") 0205 self.getNextToken(funcName) 0206 self.lexicalScope = self.lexicalScope.addMember(funcName) 0207 self.linkToLibReference(funcName) 0208 map(self.process,rest) 0209 self.lexicalScope = self.lexicalScope.parent 0210 0211 def classHandler(self, params): 0212 classConst = params[0] 0213 className = params[1][1] 0214 rest = params[2:] 0215 0216 self.getNextToken(classConst) 0217 self.printToken("<span class='classdef'>%s</span>") 0218 self.getNextToken(className) 0219 self.lexicalScope = self.lexicalScope.addMember(className) 0220 self.linkToLibReference(className) 0221 if rest[0][1] == '(': #subclasses, if we want to try to resolve methods. 0222 openParen = rest[0] 0223 baseClasses = rest[1] 0224 closeParen = rest[2] 0225 rest = rest[3:] 0226 self.getNextToken(openParen) 0227 self.printToken() 0228 map(self.process, baseClasses[1:]) 0229 self.getNextToken(closeParen) 0230 self.printToken() 0231 map(self.process, rest) 0232 self.lexicalScope = self.lexicalScope.parent 0233 0234 def nsNodeToLexical(self, lexicalNode, nsNode, lexicalName=""): 0235 if not lexicalName: 0236 lexicalName = nsNode.name 0237 tmpNode = lexicalNode.addMember(lexicalName) 0238 tmpPath = nsNode.getPath() 0239 if tmpPath: 0240 tmpNode.ref = pathToUrl(tmpPath) 0241 for child in nsNode.children: 0242 self.nsNodeToLexical(tmpNode, child) 0243 0244 def graftLexicalNode(self, lexTree, lexicalNode, optName=""): 0245 if optName: 0246 lexTree.members[optName] = lexicalNode 0247 else: 0248 lexTree.members[lexicalNode.name] = lexicalNode 0249 0250 # 0251 # THe 'any'... functions deal with the fact that resolving references 0252 # to the "Library Reference" is completely different than resolving 0253 # references to nsNodes from the .py source. 0254 # 0255 # s'pose I should make a better interface for these classes 0256 def anyNodeToLexical(self, lexTree, node, lexicalName=""): 0257 if isinstance(node, lexicalTree): 0258 self.graftLexicalNode(lexTree, node, lexicalName) 0259 elif isinstance(node, nsNode.nsNode): 0260 self.nsNodeToLexical(lexTree, node, lexicalName) 0261 else: 0262 raise InvalidSourceError("Invalid node type. Can't merge with current lexical scope") 0263 0264 def anyChildToLexical(self, lexTree, node, childName, lexicalName=""): 0265 if isinstance(node, lexicalTree): 0266 tmpNode = node.findMember(childName) 0267 if tmpNode: 0268 self.graftLexicalNode(lexTree, tmpNode, lexicalName) 0269 elif isinstance(node, nsNode.nsNode): 0270 tmpNode = node.getAnyChild(childName) 0271 if tmpNode: 0272 if not lexicalName: lexicalName = childName 0273 url = "%s#%s:" % (pathToUrl(node.getPath()), childName) 0274 self.lexicalScope.addMember(lexicalName, url) 0275 #self.nsNodeToLexical(lexTree, tmpNode, lexicalName) 0276 else: 0277 raise InvalidSourceError("Invalid node type. Can't merge with current lexical scope") 0278 0279 def anyChildrenToLexical(self, lexTree, node): 0280 if isinstance(node, lexicalTree): 0281 for child in node.members.keys(): 0282 self.graftLexicalNode(lexTree, node.findMember(child)) 0283 elif isinstance(node, nsNode.nsNode): 0284 for child in node.children: 0285 self.nsNodeToLexical(lexTree, child) 0286 else: 0287 raise InvalidSourceError("Invalid node type. Can't merge with current lexical scope") 0288 0289 def anyPrintToken(self, node, name): 0290 """ 0291 prints a token with appropriate link to the listed node if possible 0292 """ 0293 formatString = "%s" 0294 if not node: 0295 pass 0296 elif isinstance(node, lexicalTree): 0297 childNode = node.findMember(name) 0298 if childNode: 0299 formatString = "<a href='%s'>%%s</a>" % childNode.ref 0300 elif isinstance(node, nsNode.nsNode): 0301 childNode = node.getAnyChild(name) 0302 if childNode: 0303 url = "%s#%s:" % (pathToUrl(node.getPath()), name) 0304 formatString = "<a href='%s'>%%s</a>" %(url) 0305 else: 0306 raise InvalidSourceError("Invalid node type. Can't merger with current Lexical scope") 0307 self.printToken(formatString) 0308 0309 def dottedName(self, dottedName): 0310 """ 0311 DEPRECIATED- use anyDottedName instead. This only accounts for imports of 0312 modules with python source (no .pyds) 0313 """ 0314 dottedNameString = "" 0315 for x in dottedName[1:]: 0316 self.getNextToken(x[1]) 0317 if x[1] == ".": 0318 self.printToken() 0319 else: 0320 if dottedNameString:dottedNameString += "/" 0321 dottedNameString = dottedNameString + x[1] 0322 tmpNode = nsNode.getModuleNode(dottedNameString, self.searchPath) 0323 if tmpNode: 0324 href = "<a href='%s'>%%s</a>" % pathToUrl(tmpNode.getPath()) 0325 else: 0326 href = "%s" 0327 self.printToken(href) 0328 return tmpNode 0329 0330 def anyDottedName(self, dottedName): 0331 """ 0332 Resolves an 'import' dotted name to something we can integrate into our current 0333 lexical tree. This 'something' could be either an nsNode or a lexicalTree node, 0334 so it should always be grafted in via the anyNodeToLexicalNode method 0335 """ 0336 firstPass = 1 0337 tmpNode = None 0338 for x in dottedName[1:]: 0339 self.getNextToken(x[1]) 0340 if x[1] == ".": 0341 self.printToken() 0342 else: 0343 modName = x[1] 0344 # locate node if we can 0345 if firstPass: 0346 # try to locate lexically 0347 tmpNode = nsNode.getModuleNode(modName, self.searchPath) 0348 if not tmpNode and useLibraryReference and libTree.findMember(modName): 0349 tmpNode = libTree.findMember(modName) 0350 firstPass = 0 0351 else: 0352 #try to locate based on current tmpNode value 0353 if not tmpNode: 0354 pass 0355 elif isinstance(tmpNode, nsNode.nsNode): 0356 tmpNode = tmpNode.getAnyChild(modName) 0357 elif isinstance(tmpNode, lexicalTree): 0358 if tmpNode.members.has_key(modName): 0359 tmpNode = tmpNode.members[modName] 0360 else: 0361 tmpNode = None 0362 else: 0363 raise InvalidSourceError("Never should have gotten here") 0364 0365 # Determine how to print, if node exists there's a hyperlink 0366 if not tmpNode: 0367 href = "%s" 0368 elif isinstance(tmpNode, nsNode.nsNode): 0369 href = "<a href='%s'>%%s</a>" % pathToUrl(tmpNode.getPath()) 0370 elif isinstance(tmpNode, lexicalTree): 0371 href = "<a href='%s'>%%s</a>" % tmpNode.ref 0372 else: 0373 raise InvalidSourceError("Never should have gotten here") 0374 self.printToken(href) 0375 return tmpNode 0376 0377 def dottedAsNamesHandler(self, params): 0378 pass 0379 0380 def importAsNamesHandler(self, params): 0381 pass 0382 0383 def decoratorHandler(self, params): 0384 ampersand = params[0] 0385 decorator = params[1] 0386 rest = params[2:] 0387 0388 self.getNextToken("@") 0389 self.printToken() 0390 0391 tmpNode = self.anyDottedName(decorator) 0392 0393 map(self.process,rest) 0394 0395 def importFromHandler(self, params): 0396 """Handler for python 2.4+ ast tress. Not used in 2.3 and lower""" 0397 importType = params[0][1] 0398 if importType != "from": 0399 raise InvalidSourceError("[%s] Invalid ImportFrom Syntax: %s" % (self.filename, params)) 0400 0401 fromConst = params[0][1] 0402 dottedName = params[1] 0403 importConst = params[2][1] 0404 rest = params [3:] 0405 0406 self.getNextToken(importType) 0407 self.printToken("<span class='keyword'>%s</span>") 0408 0409 tmpNode = self.anyDottedName(dottedName) 0410 self.getNextToken(importType) 0411 self.printToken("<span class='keyword'>%s</span>") 0412 0413 for param in rest: 0414 if param[0] in (token.LPAR, token.RPAR): 0415 self.getNextToken(param[1]) 0416 self.printToken() 0417 elif param[0] == token.STAR: 0418 self.getNextToken(param[1]) 0419 self.printToken() 0420 if tmpNode: 0421 self.anyChildrenToLexical(self.lexicalScope, tmpNode) 0422 elif param[0] == symbol.import_as_names: 0423 for namesParam in param[1:]: 0424 if namesParam[0] == token.COMMA: 0425 self.getNextToken(namesParam[1]) 0426 self.printToken() 0427 continue 0428 name = namesParam[1][1] 0429 rest = namesParam[2:] 0430 0431 self.getNextToken(name) 0432 if tmpNode: 0433 self.anyPrintToken(tmpNode, name) 0434 else: 0435 self.printToken() 0436 if rest: 0437 self.getNextToken("as") 0438 self.printToken("<span class='keyword'>%s</span>") 0439 asName = rest[1][1] 0440 if tmpNode: 0441 self.anyChildToLexical(self.lexicalScope, tmpNode, name, asName) 0442 self.getNextToken(asName) 0443 self.printToken() 0444 else: 0445 if tmpNode: 0446 self.anyChildToLexical(self.lexicalScope, tmpNode, name) 0447 else: 0448 raise InvalidSourceError("[%s] Invalid Syntax for import %s" % (self.filename,param)) 0449 0450 def importNameHandler(self, params): 0451 """Handler for python 2.4+ ast tress. Not used in 2.3 and lower""" 0452 first = params[0] 0453 rest = params[1:] 0454 if first[1] == "import": 0455 self.getNextToken(first[1]) 0456 self.printToken("<span class='keyword'>%s</span>") 0457 0458 if rest[0][0] != symbol.dotted_as_names: 0459 raise InvalidSourceError("[%s] Apparently invalid import: %s" % (self.filename, params)) 0460 else: 0461 rest = rest[0][1:] 0462 for param in rest: 0463 if param[0] == symbol.dotted_as_name: 0464 dottedName = param[1] 0465 asAndName = param[2:] 0466 tmpNode = self.anyDottedName(dottedName) 0467 0468 if asAndName: 0469 self.getNextToken('as') 0470 self.printToken("<span class='keyword'>%s</span>") 0471 asName = param[3][1] 0472 self.getNextToken(asName) 0473 self.printToken() 0474 if tmpNode: 0475 self.anyNodeToLexical(self.lexicalScope, tmpNode,asName) 0476 else: 0477 if tmpNode: 0478 self.anyNodeToLexical(self.lexicalScope, tmpNode) 0479 0480 elif param[0] == token.COMMA: 0481 self.getNextToken(param[1]) 0482 self.printToken() 0483 else: 0484 raise InvalidSourceError("[%s] Apparently invalid ImportName %s" % (self.filename, params)) 0485 else: 0486 raise InvalidSourceError("[%s] invalid import_name statement" % (self.filename, params)) 0487 0488 def importHandler(self, params): 0489 """Legacy handler for pre 2.4 ast trees. Not used in 2.4+""" 0490 importType = params[0][1] 0491 if importType == "import": 0492 self.getNextToken(importType) 0493 self.printToken("<span class='keyword'>%s</span>") 0494 dottedName = [] 0495 0496 for param in params[1:]: 0497 #self.getNextToken(param[1]) 0498 if param[0] == symbol.dotted_as_name: 0499 dottedName = param[1] 0500 rest = param[2:] 0501 tmpNode = self.anyDottedName(dottedName) 0502 0503 if rest: 0504 self.getNextToken('as') 0505 self.printToken("<span class='keyword'>%s</span>") 0506 asName = param[3][1] 0507 self.getNextToken(asName) 0508 self.printToken() 0509 if tmpNode: 0510 self.anyNodeToLexical(self.lexicalScope, tmpNode,asName) 0511 else: 0512 if tmpNode: 0513 self.anyNodeToLexical(self.lexicalScope, tmpNode) 0514 else: 0515 self.process(param) 0516 elif importType == "from": 0517 fromConst = params[0][1] 0518 dottedName = params[1] 0519 importConst = params[2][1] 0520 rest = params [3:] 0521 0522 self.getNextToken(importType) 0523 self.printToken("<span class='keyword'>%s</span>") 0524 0525 tmpNode = self.anyDottedName(dottedName) 0526 self.getNextToken(importType) 0527 self.printToken("<span class='keyword'>%s</span>") 0528 0529 for x in rest: 0530 if x[1] == ",": 0531 self.getNextToken(",") 0532 self.printToken() 0533 elif x[1] == "*": 0534 self.getNextToken("*") 0535 self.printToken() 0536 if tmpNode: 0537 self.anyChildrenToLexical(self.lexicalScope, tmpNode) 0538 else: 0539 if x[0] != symbol.import_as_name: 0540 raise RuntimeError("Invalid Syntax for import %s" % self.filename) 0541 name = x[1][1] 0542 rest = x[2:] 0543 self.getNextToken(name) 0544 if tmpNode: 0545 self.anyPrintToken(tmpNode, name) 0546 else: 0547 self.printToken() 0548 if rest: 0549 self.getNextToken("as") 0550 self.printToken() 0551 asName = rest[1][1] 0552 if tmpNode: 0553 self.anyChildToLexical(self.lexicalScope, tmpNode, name, asName) 0554 self.getNextToken(asName) 0555 self.printToken() 0556 else: 0557 if tmpNode: 0558 self.anyChildToLexical(self.lexicalScope, tmpNode, name) 0559 else: 0560 raise Exception("Invalid Import Syntax: %s" % params) 0561 0562 def expressionHandler(self,params): 0563 map(self.process, params) 0564 0565 def stringHandler(self, params): 0566 if len(params) != 1: 0567 raise InvalidSourceError("[%s] string has invalid number of params %s" % (self.filename, params)) 0568 self.getNextToken(params[0]) 0569 self.printToken("<span class='string'>%s</span>") 0570 0571 def dottedNameHandler(self, dottedName, rest): 0572 0573 if dottedName[0] != 'self': #what's the real way to do this? 0574 currentMember = self.lexicalScope.findMember(dottedName[0]) 0575 elif self.lexicalScope.parent: 0576 currentMember = self.lexicalScope.parent 0577 else: 0578 currentMember = "n/a" 0579 if hasattr(currentMember, "getUrl"): 0580 url = currentMember.getUrl() 0581 formatString = "<a href='%s'>%%s</a>" % url 0582 else: 0583 formatString = "%s" 0584 currentMember = None 0585 0586 self.getNextToken(dottedName[0]) 0587 self.printToken(formatString) 0588 for name in dottedName[1:]: 0589 self.getNextToken(".") 0590 self.printToken("%s") 0591 0592 if currentMember and currentMember.members.has_key(name): 0593 currentMember = currentMember.members[name] 0594 url = currentMember.getUrl() 0595 formatString = "<a href='%s'>%%s</a>" % url 0596 else: 0597 currentMember = None 0598 formatString = "%s" 0599 self.getNextToken(name) 0600 self.printToken(formatString) 0601 map(self.process, rest) 0602 0603 def powerHandler(self, params): 0604 def getDottedName(params): 0605 if params[0][0] != symbol.atom or params[0][1][0] != token.NAME: 0606 return None 0607 dottedName = [ params[0][1][1] ] 0608 i = 1 0609 for param in params[1:]: 0610 if param[0] != symbol.trailer: 0611 break 0612 if param[1][0] != token.DOT: 0613 break 0614 if param[2][0] != token.NAME: 0615 break 0616 dottedName.append(param[2][1]) 0617 i += 1 0618 if i < len(params): 0619 return dottedName, params[i:] 0620 else: 0621 return dottedName, [] 0622 0623 name = getDottedName(params) 0624 if name: 0625 self.dottedNameHandler(*name) 0626 else: 0627 map(self.process, params) 0628 0629 def encodingDeclHandler(self, params): 0630 self.process(params[0]) 0631 0632 if __name__ == "__main__": 0633 x = astToHtml("c:\\python24\\lib\\bsddb\\__init__.py", sys.stdout.write) 0634 x.process() 0635 0636
Generated by PyXR 0.9.4