from __future__ import generators
from bike.query.common import Match,MatchFinder,\
getScopeForLine, indexToCoordinates, \
translateSourceCoordsIntoASTNode, scanScopeForMatches, \
isAMethod, convertNodeToMatchObject, walkLinesContainingStrings
from bike.parsing.parserutils import generateLogicalLines,\
generateLogicalLinesAndLineNumbers, \
splitLogicalLines, makeLineParseable
import compiler
from compiler.ast import Getattr,Name,AssName,AssAttr
from bike.parsing.fastparserast import getRoot,Package,Class,\
Module, Function, Instance
import re
from bike.query.getTypeOf import getTypeOfExpr,UnfoundType,\
isWordInLine, resolveImportedModuleOrPackage
from bike.parsing import visitor
from bike.parsing.visitor import walkAndGenerate
from bike.parsing.parserutils import makeLineParseable,splitLogicalLines
from bike.parsing.newstuff import getSourceNodesContainingRegex
from bike.parsing.load import getSourceNode
from bike import log
class CantFindDefinitionException:
pass
def findAllPossibleDefinitionsByCoords(filepath,lineno,col):
#try:
node = translateSourceCoordsIntoASTNode(filepath,lineno,col)
#except:
# import traceback
# traceback.print_exc()
if node is None:
raise "selected node type not supported"
scope = getScopeForLine(getSourceNode(filepath),lineno)
match = findDefinitionFromASTNode(scope,node)
if match is not None:
yield match
if isinstance(node,Getattr) and (match is None or match.confidence != 100):
root = getRoot()
name = node.attrname
for match in scanPythonPathForMatchingMethodNames(name,filepath):
yield match
print >>log.progress,"done"
def findDefinitionFromASTNode(scope,node):
assert node is not None
if isinstance(node,Name) or isinstance(node,AssName):
while 1:
# try scope children
childscope = scope.getChild(node.name)
if childscope is not None:
return convertNodeToMatchObject(childscope,100)
if isinstance(scope,Package):
scope = scope.getChild("__init__")
# try arguments and assignments
match = scanScopeAST(scope,node.name,
AssignmentAndFnArgsSearcher(node.name))
if match is not None:
return match
# try imports
match = searchImportedModulesForDefinition(scope,node)
if match is not None:
return match
if not isinstance(scope,Module):
# try parent scope
scope = scope.getParent()
else:
break
assert isinstance(scope,Module)
elif isinstance(node,Getattr) or isinstance(node,AssAttr):
exprtype = getTypeOfExpr(scope,node.expr)
if not (exprtype is None or isinstance(exprtype,UnfoundType)):
if isinstance(exprtype,Instance):
exprtype = exprtype.getType()
match = findDefinitionOfAttributeFromASTNode(exprtype,
node.attrname)
else:
match = findDefinitionFromASTNode(exprtype,
Name(node.attrname))
if match is not None:
return match
elif isinstance(node,compiler.ast.Function) or \
isinstance(node,compiler.ast.Class):
if isAMethod(scope,node):
match = findDefinitionOfAttributeFromASTNode(scope,
node.name)
else:
match = findDefinitionFromASTNode(scope,Name(node.name))
if match is not None:
return match
type = getTypeOfExpr(scope,node)
if type is not None and (not isinstance(type,UnfoundType)) and \
(not isinstance(type,Instance)):
return convertNodeToMatchObject(type,100)
else:
return None
def findDefinitionOfAttributeFromASTNode(type,name):
assert isinstance(type,Class)
attrfinder = AttrbuteDefnFinder([type],name)
# first scan the method names:
for child in type.getChildNodes():
if child.name == name:
return convertNodeToMatchObject(child,100)
# then scan the method source for attribues
for child in type.getChildNodes():
if isinstance(child,Function):
try:
return scanScopeForMatches(child.module.getSourceNode(),
child, attrfinder,
name).next()
except StopIteration:
continue
class AttrbuteDefnFinder(MatchFinder):
def __init__(self,targetClasses,targetAttribute):
self.targetClasses = targetClasses
self.targetAttributeName = targetAttribute
def visitAssAttr(self, node):
for c in node.getChildNodes():
self.visit(c)
if node.attrname == self.targetAttributeName:
exprtype = getTypeOfExpr(self.scope,node.expr)
if isinstance(exprtype,Instance) and \
exprtype.getType() in self.targetClasses:
self.appendMatch(self.targetAttributeName)
#else:
# self.appendMatch(self.targetAttributeName,50)
self.popWordsUpTo(node.attrname)
def searchImportedModulesForDefinition(scope,node):
lines = scope.module.getSourceNode().getLines()
for lineno in scope.getImportLineNumbers():
logicalline = getLogicalLine(lines,lineno)
logicalline = makeLineParseable(logicalline)
ast = compiler.parse(logicalline)
class ImportVisitor:
def __init__(self,node):
self.target = node
self.match = None
assert isinstance(self.target,Name), \
"Getattr not supported"
def visitFrom(self, node):
module = resolveImportedModuleOrPackage(scope,node.modname)
if module is None: # couldn't find module
return
if node.names[0][0] == '*': # e.g. from foo import *
match = findDefinitionFromASTNode(module,self.target)
if match is not None:
self.match = match
return
for name, alias in node.names:
if alias is None and name == self.target.name:
match = findDefinitionFromASTNode(module,self.target)
if match is not None:
self.match = match
return
match = visitor.walk(ast, ImportVisitor(node)).match
if match:
return match
# loop
def getLogicalLine(lines,lineno):
return generateLogicalLines(lines[lineno-1:]).next()
class AssignmentAndFnArgsSearcher(MatchFinder):
def __init__(self,name):
self.targetname = name
self.match = None
def visitAssName(self, node):
if node.name == self.targetname:
idx = self.getNextIndexOfWord(self.targetname)
self.match = idx
return
def visitFunction(self, node):
self.popWordsUpTo(node.name)
for arg, default in self.zipArgs(node.argnames, node.defaults):
if arg == self.targetname:
idx = self.getNextIndexOfWord(self.targetname)
self.match = idx
return
self.popWordsUpTo(arg)
if default is not None:
self.visit(default)
self.visit(node.code)
def getMatch(self):
return self.match
# scans for lines containing keyword, and then runs the visitor over
# the parsed AST for that line
def scanScopeAST(scope,keyword,matchfinder):
lines = scope.generateLinesNotIncludingThoseBelongingToChildScopes()
match = None
for line,linenum in generateLogicalLinesAndLineNumbers(lines):
if isWordInLine(keyword, line):
doctoredline = makeLineParseable(line)
ast = compiler.parse(doctoredline)
matchfinder.reset(line)
match = visitor.walk(ast,matchfinder).getMatch()
if match is not None:
column,yoffset = indexToCoordinates(line,match)
m = createMatch(scope,linenum + yoffset,column)
return m
return None
def createMatch(scope,lineno,x):
m = Match()
m.sourcenode = scope.module.getSourceNode()
m.filename = m.sourcenode.filename
m.lineno = lineno
m.colno = x
m.confidence = 100
return m
# scan for methods globally (from perspective of 'perspectiveFilename')
def scanPythonPathForMatchingMethodNames(name, contextFilename):
class MethodFinder:
def __init__(self,srcnode):
self.matches = []
self.srcnode = srcnode
def visitFunction(self,node):
node = getScopeForLine(self.srcnode, self.lineno)
if isinstance(node.getParent(),Class):
if node.name == name:
self.matches.append(convertNodeToMatchObject(node,50))
for srcnode in getSourceNodesContainingRegex(name,contextFilename):
m = MethodFinder(srcnode)
walkLinesContainingStrings(srcnode.fastparseroot,m,[name])
for match in m.matches:
yield match
def getIndexOfWord(line,targetword):
words = re.split("(\w+)", line)
idx = 0
for word in words:
if word == targetword:
break
idx += len(word)
return idx
|