from __future__ import generators from parserutils import generateLogicalLines, maskStringsAndComments, maskStringsAndRemoveComments import re import os import compiler from bike.transformer.save import resetOutputQueue TABWIDTH = 4 classNameRE = re.compile("^\s*class\s+(\w+)") fnNameRE = re.compile("^\s*def\s+(\w+)") _root = None def getRoot(): global _root if _root is None: resetRoot() return _root def resetRoot(root = None): global _root _root = root or Root() _root.unittestmode = False resetOutputQueue() def getModule(filename_path): from bike.parsing.load import CantLocateSourceNodeException, getSourceNode try: sourcenode = getSourceNode(filename_path) return sourcenode.fastparseroot except CantLocateSourceNodeException: return None def getPackage(directory_path): from bike.parsing.pathutils import getRootDirectory rootdir = getRootDirectory(directory_path) if rootdir == directory_path: return getRoot() else: return Package(directory_path, os.path.basename(directory_path)) class Root: def __init__(self, pythonpath = None): # singleton hack to allow functions in query package to appear # 'stateless' resetRoot(self) # this is to get round a python optimisation which reuses an # empty list as a default arg. unfortunately the client of # this method may fill that list, so it's not empty if not pythonpath: pythonpath = [] self.pythonpath = pythonpath def __repr__(self): return "Root()" #return "Root(%s)"%(self.getChildNodes()) # dummy method def getChild(self,name): return None class Package: def __init__(self, path, name): self.path = path self.name = name def getChild(self,name): from bike.parsing.newstuff import getModule return getModule(os.path.join(self.path,name+".py")) def __repr__(self): return "Package(%s,%s)"%(self.path, self.name) # used so that linenum can be an attribute class Line(str): pass class StructuralNode: def __init__(self, filename, srclines, modulesrc): self.childNodes = [] self.filename = filename self._parent = None self._modulesrc = modulesrc self._srclines = srclines self._maskedLines = None def addChild(self, node): self.childNodes.append(node) node.setParent(self) def setParent(self, parent): self._parent = parent def getParent(self): return self._parent def getChildNodes(self): return self.childNodes def getChild(self,name): matches = [c for c in self.getChildNodes() if c.name == name] if matches != []: return matches[0] def getLogicalLine(self,physicalLineno): return generateLogicalLines(self._srclines[physicalLineno-1:]).next() # badly named: actually returns line numbers of import statements def getImportLineNumbers(self): try: return self.importlines except AttributeError: return[] def getLinesNotIncludingThoseBelongingToChildScopes(self): srclines = self.getMaskedModuleLines() lines = [] lineno = self.getStartLine() for child in self.getChildNodes(): lines+=srclines[lineno-1: child.getStartLine()-1] lineno = child.getEndLine() lines+=srclines[lineno-1: self.getEndLine()-1] return lines def generateLinesNotIncludingThoseBelongingToChildScopes(self): srclines = self.getMaskedModuleLines() lines = [] lineno = self.getStartLine() for child in self.getChildNodes(): for line in srclines[lineno-1: child.getStartLine()-1]: yield self.attachLinenum(line,lineno) lineno +=1 lineno = child.getEndLine() for line in srclines[lineno-1: self.getEndLine()-1]: yield self.attachLinenum(line,lineno) lineno +=1 def generateLinesWithLineNumbers(self,startline=1): srclines = self.getMaskedModuleLines() for lineno in range(startline,len(srclines)+1): yield self.attachLinenum(srclines[lineno-1],lineno) def attachLinenum(self,line,lineno): line = Line(line) line.linenum = lineno return line def getMaskedModuleLines(self): from bike.parsing.load import Cache try: maskedlines = Cache.instance.maskedlinescache[self.filename] except: # make sure src is actually masked # (could just have keywords masked) maskedsrc = maskStringsAndComments(self._modulesrc) maskedlines = maskedsrc.splitlines(1) Cache.instance.maskedlinescache[self.filename] = maskedlines return maskedlines class Module(StructuralNode): def __init__(self, filename, name, srclines, maskedsrc): StructuralNode.__init__(self, filename, srclines, maskedsrc) self.name = name self.indent = -TABWIDTH self.flattenedNodes = [] self.module = self def getMaskedLines(self): return self.getMaskedModuleLines() def getFlattenedListOfChildNodes(self): return self.flattenedNodes def getStartLine(self): return 1 def getEndLine(self): return len(self.getMaskedModuleLines())+1 def getSourceNode(self): return self.sourcenode def setSourceNode(self, sourcenode): self.sourcenode = sourcenode def matchesCompilerNode(self,node): return isinstance(node,compiler.ast.Module) and \ node.name == self.name def getParent(self): if self._parent is not None: return self._parent else: from newstuff import getPackage return getPackage(os.path.dirname(self.filename)) def __str__(self): return "bike:Module:"+self.filename indentRE = re.compile("^(\s*)\S") class Node: # module = the module node # linenum = starting line number def __init__(self, name, module, linenum, indent): self.name = name self.module = module self.linenum = linenum self.endline = None self.indent = indent def getMaskedLines(self): return self.getMaskedModuleLines()[self.getStartLine()-1:self.getEndLine()-1] def getStartLine(self): return self.linenum def getEndLine(self): if self.endline is None: physicallines = self.getMaskedModuleLines() lineno = self.linenum logicallines = generateLogicalLines(physicallines[lineno-1:]) # skip the first line, because it's the declaration line = logicallines.next() lineno+=line.count("\n") # scan to the end of the fn for line in logicallines: #print lineno,":",line, match = indentRE.match(line) if match and match.end()-1 <= self.indent: break lineno+=line.count("\n") self.endline = lineno return self.endline # linenum starts at 0 def getLine(self, linenum): return self._srclines[(self.getStartLine()-1) + linenum] baseClassesRE = re.compile("class\s+[^(]+\(([^)]+)\):") class Class(StructuralNode, Node): def __init__(self, name, filename, module, linenum, indent, srclines, maskedmodulesrc): StructuralNode.__init__(self, filename, srclines, maskedmodulesrc) Node.__init__(self, name, module, linenum, indent) self.type = "Class" def getBaseClassNames(self): #line = self.getLine(0) line = self.getLogicalLine(self.getStartLine()) match = baseClassesRE.search(line) if match: return [s.strip()for s in match.group(1).split(",")] else: return [] def getColumnOfName(self): match = classNameRE.match(self.getLine(0)) return match.start(1) def __repr__(self): return "" % self.name def __str__(self): return "bike:Class:"+self.filename+":"+\ str(self.getStartLine())+":"+self.name def matchesCompilerNode(self,node): return isinstance(node,compiler.ast.Class) and \ node.name == self.name def __eq__(self,other): return isinstance(other,Class) and \ self.filename == other.filename and \ self.getStartLine() == other.getStartLine() # describes an instance of a class class Instance: def __init__(self, type): assert type is not None self._type = type def getType(self): return self._type def __str__(self): return "Instance(%s)"%(self.getType()) class Function(StructuralNode, Node): def __init__(self, name, filename, module, linenum, indent, srclines, maskedsrc): StructuralNode.__init__(self, filename, srclines, maskedsrc) Node.__init__(self, name, module, linenum, indent) self.type = "Function" def getColumnOfName(self): match = fnNameRE.match(self.getLine(0)) return match.start(1) def __repr__(self): return "" % self.name def __str__(self): return "bike:Function:"+self.filename+":"+\ str(self.getStartLine())+":"+self.name def matchesCompilerNode(self,node): return isinstance(node,compiler.ast.Function) and \ node.name == self.name