def testAnalyzeFile(self): path = os.path.abspath('File.py') source = dedent("""\ CONSTANT = 1 """) expectedDict = ModuleDict() expectedDict.enterModule('File') expectedDict.addProperty(None, 'CONSTANT') outDict = analyzeFile(path, getSafeTree(source, 1)) self.assertEqual(outDict, expectedDict, '%r != %r' % (outDict._modules, expectedDict._modules))
def testAnalyzeFile(self): path = os.path.abspath('File.py') source = dedent("""\ CONSTANT = 1 """) expectedDict = ModuleDict() expectedDict.enterModule('File') expectedDict.addProperty(None, 'CONSTANT') outDict = analyzeFile(path, getSafeTree(source, 1)) self.assertEquals(outDict, expectedDict, '%r != %r' % (outDict._modules, expectedDict._modules))
def testGetNames(self): source = dedent("""\ from something import Class a = Class() class D(object): pass """) expectedNames = {'Class': 'something.Class', 'a': 'Class()'} self.assertEquals(getNames(getSafeTree(source, 3)), (expectedNames, ['D']))
def testInferParentsTricky(self): source = dedent("""\ from something.this import other as another class AClass(another.bother): def amethod(self, other): other.do_something() self. def another(self): pass""") klass, parents = getClassAndParents(getSafeTree(source, 5), 5) self.assertEquals(klass, 'AClass') self.assertEquals(parents, ['something.this.other.bother'])
def testGetNames(self): source = dedent("""\ from something import Class a = Class() class D(object): pass """).replace('\n', '\r\n') expectedNames = {'Class': 'something.Class', 'a': 'Class()'} self.assertEqual(getNames(getSafeTree(source, 3)), (expectedNames, ['D']))
def testInferParentsTricky(self): source = dedent("""\ from something.this import other as another class AClass(another.bother): def amethod(self, other): other.do_something() self. def another(self): pass""") klass, parents = getClassAndParents(getSafeTree(source, 5), 5) self.assertEqual(klass, 'AClass') self.assertEqual(parents, ['something.this.other.bother'])
def testInferSelfMultipleClasses(self): source = dedent("""\ import something class AClass(object): def amethod(self, other): other.do_something() class Sneak(object): def sth(self): class EvenSneakier(object): pass pass pass def another(self): pass class BClass(object): def newmethod(self, something): wibble = [i for i in self.a] pass def newerMethod(self, somethingelse): if Bugger: self.ass """) self.assertEquals(getClassAndParents(getSafeTree(source, 1), 1)[0], None, 'no class yet!') for line in range(2, 5): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'AClass', 'wrong class %s in line %d' % (klass, line)) for line in range(5, 7): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'Sneak', 'wrong class %s in line %d' % (klass, line)) for line in range(7, 9): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'EvenSneakier', 'wrong class %s in line %d' % (klass, line)) line = 9 klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'Sneak', 'wrong class %s in line %d' % (klass, line)) for line in range(10, 17): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'AClass', 'wrong class %s in line %d' % (klass, line)) for line in range(17, 51): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEquals(klass, 'BClass', 'wrong class %s in line %d' % (klass, line))
def testInferSelfMultipleClasses(self): source = dedent("""\ import something class AClass(object): def amethod(self, other): other.do_something() class Sneak(object): def sth(self): class EvenSneakier(object): pass pass pass def another(self): pass class BClass(object): def newmethod(self, something): wibble = [i for i in self.a] pass def newerMethod(self, somethingelse): if Bugger: self.ass """) self.assertEqual(getClassAndParents(getSafeTree(source, 1), 1)[0], None, 'no class yet!') for line in range(2, 5): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'AClass', 'wrong class %s in line %d' % (klass, line)) for line in range(5, 7): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'Sneak', 'wrong class %s in line %d' % (klass, line)) for line in range(7, 9): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'EvenSneakier', 'wrong class %s in line %d' % (klass, line)) line = 9 klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'Sneak', 'wrong class %s in line %d' % (klass, line)) for line in range(10, 17): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'AClass', 'wrong class %s in line %d' % (klass, line)) for line in range(17, 51): klass, _ = getClassAndParents(getSafeTree(source, line), line) self.assertEqual(klass, 'BClass', 'wrong class %s in line %d' % (klass, line))
def testInferSelfSimple(self): source = dedent("""\ import something class AClass(object): def amethod(self, other): other.do_something() self. def another(self): pass """) klass, parents = getClassAndParents(getSafeTree(source, 5), 5) self.assertEquals(klass, 'AClass') self.assertEquals(parents, ['object'])
def testInferSelfSimple(self): source = dedent("""\ import something class AClass(object): \tdef amethod(self, other): \t\tother.do_something() \t\tself. \tdef another(self): \t\tpass """) klass, parents = getClassAndParents(getSafeTree(source, 5), 5) self.assertEqual(klass, 'AClass') self.assertEqual(parents, ['object'])
def testInferParents(self): source = dedent("""\ import something from something import father as stepfather class AClass(something.mother, stepfather): def amethod(self, other): other.do_something() self. def another(self): pass """) klass, parents = getClassAndParents(getSafeTree(source, 6), 6) self.assertEquals(klass, 'AClass') self.assertEquals(parents, ['something.mother', 'something.father'])
def testInferParents(self): source = dedent("""\ import something from something import father as stepfather class AClass(something.mother, stepfather): def amethod(self, other): other.do_something() self. def another(self): pass """) klass, parents = getClassAndParents(getSafeTree(source, 6), 6) self.assertEqual(klass, 'AClass') self.assertEqual(parents, ['something.mother', 'something.father'])
def detectCompletionType(fullPath, origSource, lineNo, origCol, base, PYSMELLDICT, update=True): """ Return a CompletionOptions instance describing the type of the completion, along with extra parameters. args: fullPath -> The full path and filename of the file that is edited origSource -> The source of the edited file (it's probably not saved) lineNo -> The line number the cursor is in, 1-based origCol -> The column number the cursor is in, 0-based base -> The string that will be replaced when the completion is inserted PYSMELLDICT -> The loaded PYSMELLDICT Note that Vim deletes the "base" when a completion is requested so extra trickery must be performed to get it from the source. """ AST = getSafeTree(origSource, lineNo) if update: currentDict = analyzeFile(fullPath, AST) if currentDict is not None: updatePySmellDict(PYSMELLDICT, currentDict) origLineText = origSource.splitlines()[lineNo - 1] # lineNo is 1 based leftSide, rightSide = origLineText[:origCol], origLineText[origCol:] leftSideStripped = leftSide.lstrip() isImportCompletion = (leftSideStripped.startswith("from ") or leftSideStripped.startswith("import ")) if isImportCompletion: module = leftSideStripped.split(" ")[1] if "." in module and " import " not in leftSideStripped: module, _ = module.rsplit(".", 1) showMembers = False if " import " in leftSide: showMembers = True return CompletionOptions(Types.MODULE, module=module, showMembers=showMembers) isAttrLookup = "." in leftSide and not isImportCompletion isArgCompletion = base.endswith('(') and leftSide.endswith(base) if isArgCompletion: rindex = None if rightSide.startswith(')'): rindex = -1 funcName = None lindex = leftSide.rfind('.') + 1 #rfind will return -1, so with +1 it will be zero funcName = leftSide[lindex:-1].lstrip() if isAttrLookup: return CompletionOptions(Types.METHOD, parents=[], klass=None, name=funcName, rindex=rindex) else: return CompletionOptions(Types.FUNCTION, name=funcName, rindex=rindex) if isAttrLookup and AST is not None: var = leftSideStripped[:leftSideStripped.rindex('.')] isClassLookup = var == 'self' if isClassLookup: klass, parents = inferClass(fullPath, AST, lineNo, PYSMELLDICT) return CompletionOptions(Types.INSTANCE, klass=klass, parents=parents) else: chain = getChain(leftSideStripped)[:-1] # strip dot possibleModule = inferModule(chain, AST, lineNo) if possibleModule is not None: return CompletionOptions(Types.MODULE, module=possibleModule, showMembers=True) klass, parents = inferInstance(fullPath, AST, lineNo, var, PYSMELLDICT) return CompletionOptions(Types.INSTANCE, klass=klass, parents=parents) return CompletionOptions(Types.TOPLEVEL)
def detectCompletionType(fullPath, origSource, lineNo, origCol, base, PYSMELLDICT, update=True): """ Return a CompletionOptions instance describing the type of the completion, along with extra parameters. args: fullPath -> The full path and filename of the file that is edited origSource -> The source of the edited file (it's probably not saved) lineNo -> The line number the cursor is in, 1-based origCol -> The column number the cursor is in, 0-based base -> The string that will be replaced when the completion is inserted PYSMELLDICT -> The loaded PYSMELLDICT Note that Vim deletes the "base" when a completion is requested so extra trickery must be performed to get it from the source. """ AST = getSafeTree(origSource, lineNo) if update: currentDict = analyzeFile(fullPath, AST) if currentDict is not None: updatePySmellDict(PYSMELLDICT, currentDict) origLineText = origSource.splitlines()[lineNo - 1] # lineNo is 1 based leftSide, rightSide = origLineText[:origCol], origLineText[origCol:] leftSideStripped = leftSide.lstrip() isImportCompletion = (leftSideStripped.startswith("from ") or leftSideStripped.startswith("import ")) if isImportCompletion: module = leftSideStripped.split(" ")[1] if "." in module and " import " not in leftSideStripped: module, _ = module.rsplit(".", 1) showMembers = False if " import " in leftSide: showMembers = True return CompletionOptions(Types.MODULE, module=module, showMembers=showMembers) isAttrLookup = "." in leftSide and not isImportCompletion isArgCompletion = base.endswith('(') and leftSide.endswith(base) if isArgCompletion: rindex = None if rightSide.startswith(')'): rindex = -1 funcName = None lindex = leftSide.rfind( '.') + 1 #rfind will return -1, so with +1 it will be zero funcName = leftSide[lindex:-1].lstrip() if isAttrLookup: return CompletionOptions(Types.METHOD, parents=[], klass=None, name=funcName, rindex=rindex) else: return CompletionOptions(Types.FUNCTION, name=funcName, rindex=rindex) if isAttrLookup and AST is not None: var = leftSideStripped[:leftSideStripped.rindex('.')] isClassLookup = var == 'self' if isClassLookup: klass, parents = inferClass(fullPath, AST, lineNo, PYSMELLDICT) return CompletionOptions(Types.INSTANCE, klass=klass, parents=parents) else: chain = getChain(leftSideStripped) # strip dot if base and chain.endswith(base): chain = chain[:-len(base)] if chain.endswith('.'): chain = chain[:-1] possibleModule = inferModule(chain, AST, lineNo) if possibleModule is not None: return CompletionOptions(Types.MODULE, module=possibleModule, showMembers=True) klass, parents = inferInstance(fullPath, AST, lineNo, var, PYSMELLDICT) return CompletionOptions(Types.INSTANCE, klass=klass, parents=parents) return CompletionOptions(Types.TOPLEVEL)