mirror of
https://github.com/PyCQA/flake8.git
synced 2026-04-16 09:09:52 +00:00
fixed mccabe for global loops and nested functions
This commit is contained in:
parent
ab24ceea5a
commit
e6068a3a38
9 changed files with 82 additions and 1696 deletions
|
|
@ -67,18 +67,30 @@ class PathGraphingAstVisitor(compiler.visitor.ASTVisitor):
|
||||||
self.tail = None
|
self.tail = None
|
||||||
|
|
||||||
def visitFunction(self, node):
|
def visitFunction(self, node):
|
||||||
|
|
||||||
if self.classname:
|
if self.classname:
|
||||||
entity = '%s%s' % (self.classname, node.name)
|
entity = '%s%s' % (self.classname, node.name)
|
||||||
else:
|
else:
|
||||||
entity = node.name
|
entity = node.name
|
||||||
|
|
||||||
name = '%d:1: %r' % (node.lineno, entity)
|
name = '%d:1: %r' % (node.lineno, entity)
|
||||||
self.graph = PathGraph(name)
|
|
||||||
pathnode = PathNode(name)
|
if self.graph is not None:
|
||||||
self.tail = pathnode
|
# closure
|
||||||
self.default(node)
|
pathnode = self.appendPathNode(name)
|
||||||
self.graphs["%s%s" % (self.classname, node.name)] = self.graph
|
self.tail = pathnode
|
||||||
self.reset()
|
self.default(node)
|
||||||
|
bottom = PathNode("", look='point')
|
||||||
|
self.graph.connect(self.tail, bottom)
|
||||||
|
self.graph.connect(pathnode, bottom)
|
||||||
|
self.tail = bottom
|
||||||
|
else:
|
||||||
|
self.graph = PathGraph(name)
|
||||||
|
pathnode = PathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node)
|
||||||
|
self.graphs["%s%s" % (self.classname, node.name)] = self.graph
|
||||||
|
self.reset()
|
||||||
|
|
||||||
def visitClass(self, node):
|
def visitClass(self, node):
|
||||||
old_classname = self.classname
|
old_classname = self.classname
|
||||||
|
|
@ -96,7 +108,11 @@ class PathGraphingAstVisitor(compiler.visitor.ASTVisitor):
|
||||||
return pathnode
|
return pathnode
|
||||||
|
|
||||||
def visitSimpleStatement(self, node):
|
def visitSimpleStatement(self, node):
|
||||||
name = "Stmt %d" % node.lineno
|
if node.lineno is None:
|
||||||
|
lineno = 0
|
||||||
|
else:
|
||||||
|
lineno = node.lineno
|
||||||
|
name = "Stmt %d" % lineno
|
||||||
self.appendPathNode(name)
|
self.appendPathNode(name)
|
||||||
|
|
||||||
visitAssert = visitAssign = visitAssTuple = visitPrint = \
|
visitAssert = visitAssign = visitAssTuple = visitPrint = \
|
||||||
|
|
@ -106,13 +122,24 @@ class PathGraphingAstVisitor(compiler.visitor.ASTVisitor):
|
||||||
|
|
||||||
def visitLoop(self, node):
|
def visitLoop(self, node):
|
||||||
name = "Loop %d" % node.lineno
|
name = "Loop %d" % node.lineno
|
||||||
pathnode = self.appendPathNode(name)
|
|
||||||
self.tail = pathnode
|
if self.graph is None:
|
||||||
self.default(node.body)
|
# global loop
|
||||||
bottom = PathNode("", look='point')
|
self.graph = PathGraph(name)
|
||||||
self.graph.connect(self.tail, bottom)
|
pathnode = PathNode(name)
|
||||||
self.graph.connect(pathnode, bottom)
|
self.tail = pathnode
|
||||||
self.tail = bottom
|
self.default(node)
|
||||||
|
self.graphs["%s%s" % (self.classname, name)] = self.graph
|
||||||
|
self.reset()
|
||||||
|
else:
|
||||||
|
pathnode = self.appendPathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node.body)
|
||||||
|
bottom = PathNode("", look='point')
|
||||||
|
self.graph.connect(self.tail, bottom)
|
||||||
|
self.graph.connect(pathnode, bottom)
|
||||||
|
self.tail = bottom
|
||||||
|
|
||||||
# TODO: else clause in node.else_
|
# TODO: else clause in node.else_
|
||||||
|
|
||||||
visitFor = visitWhile = visitLoop
|
visitFor = visitWhile = visitLoop
|
||||||
|
|
@ -147,12 +174,7 @@ def get_code_complexity(code, min=7, filename='stdin'):
|
||||||
complex = []
|
complex = []
|
||||||
ast = compiler.parse(code)
|
ast = compiler.parse(code)
|
||||||
visitor = PathGraphingAstVisitor()
|
visitor = PathGraphingAstVisitor()
|
||||||
try:
|
visitor.preorder(ast, visitor)
|
||||||
visitor.preorder(ast, visitor)
|
|
||||||
except AttributeError:
|
|
||||||
print('McCabe: Could not parse code')
|
|
||||||
return -1
|
|
||||||
|
|
||||||
for graph in visitor.graphs.values():
|
for graph in visitor.graphs.values():
|
||||||
if graph is None:
|
if graph is None:
|
||||||
# ?
|
# ?
|
||||||
|
|
@ -172,10 +194,7 @@ def get_code_complexity(code, min=7, filename='stdin'):
|
||||||
def get_module_complexity(module_path, min=7):
|
def get_module_complexity(module_path, min=7):
|
||||||
"""Returns the complexity of a module"""
|
"""Returns the complexity of a module"""
|
||||||
code = open(module_path, "rU").read() + '\n\n'
|
code = open(module_path, "rU").read() + '\n\n'
|
||||||
res = get_code_complexity(code, min, filename=module_path)
|
return get_code_complexity(code, min, filename=module_path)
|
||||||
if res == -1:
|
|
||||||
print('McCabe: Problem with %s' % module_path)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
|
|
||||||
import textwrap
|
|
||||||
import _ast
|
|
||||||
|
|
||||||
from twisted.trial import unittest
|
|
||||||
|
|
||||||
from pyflakes import checker
|
|
||||||
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
|
||||||
|
|
||||||
def flakes(self, input, *expectedOutputs, **kw):
|
|
||||||
ast = compile(textwrap.dedent(input), "<test>", "exec",
|
|
||||||
_ast.PyCF_ONLY_AST)
|
|
||||||
w = checker.Checker(ast, **kw)
|
|
||||||
outputs = [type(o) for o in w.messages]
|
|
||||||
expectedOutputs = list(expectedOutputs)
|
|
||||||
outputs.sort()
|
|
||||||
expectedOutputs.sort()
|
|
||||||
self.assert_(outputs == expectedOutputs, '''\
|
|
||||||
for input:
|
|
||||||
%s
|
|
||||||
expected outputs:
|
|
||||||
%s
|
|
||||||
but got:
|
|
||||||
%s''' % (input, repr(expectedOutputs), '\n'.join(map(str, w.messages))))
|
|
||||||
return w
|
|
||||||
|
|
@ -1,662 +0,0 @@
|
||||||
|
|
||||||
from sys import version_info
|
|
||||||
|
|
||||||
from pyflakes import messages as m
|
|
||||||
from pyflakes.test import harness
|
|
||||||
|
|
||||||
|
|
||||||
class Test(harness.Test):
|
|
||||||
|
|
||||||
def test_unusedImport(self):
|
|
||||||
self.flakes('import fu, bar', m.UnusedImport, m.UnusedImport)
|
|
||||||
self.flakes('from baz import fu, bar', m.UnusedImport, m.UnusedImport)
|
|
||||||
|
|
||||||
def test_aliasedImport(self):
|
|
||||||
self.flakes('import fu as FU, bar as FU',
|
|
||||||
m.RedefinedWhileUnused, m.UnusedImport)
|
|
||||||
self.flakes('from moo import fu as FU, bar as FU',
|
|
||||||
m.RedefinedWhileUnused, m.UnusedImport)
|
|
||||||
|
|
||||||
def test_usedImport(self):
|
|
||||||
self.flakes('import fu; print fu')
|
|
||||||
self.flakes('from baz import fu; print fu')
|
|
||||||
|
|
||||||
def test_redefinedWhileUnused(self):
|
|
||||||
self.flakes('import fu; fu = 3', m.RedefinedWhileUnused)
|
|
||||||
self.flakes('import fu; del fu', m.RedefinedWhileUnused)
|
|
||||||
self.flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused)
|
|
||||||
self.flakes('import fu; [fu, bar] = 3', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_redefinedByFunction(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fu():
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_redefinedInNestedFunction(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a global name with a nested function definition
|
|
||||||
generates a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def bar():
|
|
||||||
def baz():
|
|
||||||
def fu():
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused, m.UnusedImport)
|
|
||||||
|
|
||||||
def test_redefinedByClass(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
class fu:
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_redefinedBySubclass(self):
|
|
||||||
"""
|
|
||||||
If an imported name is redefined by a class statement which also uses
|
|
||||||
that name in the bases list, no warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from fu import bar
|
|
||||||
class bar(bar):
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_redefinedInClass(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a global with a class attribute does not produce a
|
|
||||||
warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
class bar:
|
|
||||||
fu = 1
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInFunction(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fun():
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_shadowedByParameter(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fun(fu):
|
|
||||||
print fu
|
|
||||||
''', m.UnusedImport)
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fun(fu):
|
|
||||||
print fu
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_newAssignment(self):
|
|
||||||
self.flakes('fu = None')
|
|
||||||
|
|
||||||
def test_usedInGetattr(self):
|
|
||||||
self.flakes('import fu; fu.bar.baz')
|
|
||||||
self.flakes('import fu; "bar".fu.baz', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_usedInSlice(self):
|
|
||||||
self.flakes('import fu; print fu.bar[1:]')
|
|
||||||
|
|
||||||
def test_usedInIfBody(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
if True: print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInIfConditional(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
if fu: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInElifConditional(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
if False: pass
|
|
||||||
elif fu: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInElse(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
if False: pass
|
|
||||||
else: print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInCall(self):
|
|
||||||
self.flakes('import fu; fu.bar()')
|
|
||||||
|
|
||||||
def test_usedInClass(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
class bar:
|
|
||||||
bar = fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInClassBase(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
class bar(object, fu.baz):
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_notUsedInNestedScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def bleh():
|
|
||||||
pass
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInFor(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
for bar in range(9):
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInForElse(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
for bar in range(10):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_redefinedByFor(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
for fu in range(2):
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_shadowedByFor(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a global name with a for loop variable generates a
|
|
||||||
warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
fu.bar()
|
|
||||||
for fu in ():
|
|
||||||
pass
|
|
||||||
''', m.ImportShadowedByLoopVar)
|
|
||||||
|
|
||||||
def test_shadowedByForDeep(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a global name with a for loop variable nested in a
|
|
||||||
tuple unpack generates a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
fu.bar()
|
|
||||||
for (x, y, z, (a, b, c, (fu,))) in ():
|
|
||||||
pass
|
|
||||||
''', m.ImportShadowedByLoopVar)
|
|
||||||
|
|
||||||
def test_usedInReturn(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fun():
|
|
||||||
return fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInOperators(self):
|
|
||||||
self.flakes('import fu; 3 + fu.bar')
|
|
||||||
self.flakes('import fu; 3 % fu.bar')
|
|
||||||
self.flakes('import fu; 3 - fu.bar')
|
|
||||||
self.flakes('import fu; 3 * fu.bar')
|
|
||||||
self.flakes('import fu; 3 ** fu.bar')
|
|
||||||
self.flakes('import fu; 3 / fu.bar')
|
|
||||||
self.flakes('import fu; 3 // fu.bar')
|
|
||||||
self.flakes('import fu; -fu.bar')
|
|
||||||
self.flakes('import fu; ~fu.bar')
|
|
||||||
self.flakes('import fu; 1 == fu.bar')
|
|
||||||
self.flakes('import fu; 1 | fu.bar')
|
|
||||||
self.flakes('import fu; 1 & fu.bar')
|
|
||||||
self.flakes('import fu; 1 ^ fu.bar')
|
|
||||||
self.flakes('import fu; 1 >> fu.bar')
|
|
||||||
self.flakes('import fu; 1 << fu.bar')
|
|
||||||
|
|
||||||
def test_usedInAssert(self):
|
|
||||||
self.flakes('import fu; assert fu.bar')
|
|
||||||
|
|
||||||
def test_usedInSubscript(self):
|
|
||||||
self.flakes('import fu; fu.bar[1]')
|
|
||||||
|
|
||||||
def test_usedInLogic(self):
|
|
||||||
self.flakes('import fu; fu and False')
|
|
||||||
self.flakes('import fu; fu or False')
|
|
||||||
self.flakes('import fu; not fu.bar')
|
|
||||||
|
|
||||||
def test_usedInList(self):
|
|
||||||
self.flakes('import fu; [fu]')
|
|
||||||
|
|
||||||
def test_usedInTuple(self):
|
|
||||||
self.flakes('import fu; (fu,)')
|
|
||||||
|
|
||||||
def test_usedInTry(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
try: fu
|
|
||||||
except: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInExcept(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
try: fu
|
|
||||||
except: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_redefinedByExcept(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
try: pass
|
|
||||||
except Exception, fu: pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_usedInRaise(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
raise fu.bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInYield(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def gen():
|
|
||||||
yield fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInDict(self):
|
|
||||||
self.flakes('import fu; {fu:None}')
|
|
||||||
self.flakes('import fu; {1:fu}')
|
|
||||||
|
|
||||||
def test_usedInParameterDefault(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def f(bar=fu):
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInAttributeAssign(self):
|
|
||||||
self.flakes('import fu; fu.bar = 1')
|
|
||||||
|
|
||||||
def test_usedInKeywordArg(self):
|
|
||||||
self.flakes('import fu; fu.bar(stuff=fu)')
|
|
||||||
|
|
||||||
def test_usedInAssignment(self):
|
|
||||||
self.flakes('import fu; bar=fu')
|
|
||||||
self.flakes('import fu; n=0; n+=fu')
|
|
||||||
|
|
||||||
def test_usedInListComp(self):
|
|
||||||
self.flakes('import fu; [fu for _ in range(1)]')
|
|
||||||
self.flakes('import fu; [1 for _ in range(1) if fu]')
|
|
||||||
|
|
||||||
def test_redefinedByListComp(self):
|
|
||||||
self.flakes('import fu; [1 for fu in range(1)]',
|
|
||||||
m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_usedInTryFinally(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
try: pass
|
|
||||||
finally: fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
try: fu
|
|
||||||
finally: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInWhile(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
while 0:
|
|
||||||
fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
while fu: pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_usedInGlobal(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def f(): global fu
|
|
||||||
''', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_usedInBackquote(self):
|
|
||||||
self.flakes('import fu; `fu`')
|
|
||||||
|
|
||||||
def test_usedInExec(self):
|
|
||||||
self.flakes('import fu; exec "print 1" in fu.bar')
|
|
||||||
|
|
||||||
def test_usedInLambda(self):
|
|
||||||
self.flakes('import fu; lambda: fu')
|
|
||||||
|
|
||||||
def test_shadowedByLambda(self):
|
|
||||||
self.flakes('import fu; lambda fu: fu', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_usedInSliceObj(self):
|
|
||||||
self.flakes('import fu; "meow"[::fu]')
|
|
||||||
|
|
||||||
def test_unusedInNestedScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
def bar():
|
|
||||||
import fu
|
|
||||||
fu
|
|
||||||
''', m.UnusedImport, m.UndefinedName)
|
|
||||||
|
|
||||||
def test_methodsDontUseClassScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
class bar:
|
|
||||||
import fu
|
|
||||||
def fun(self):
|
|
||||||
fu
|
|
||||||
''', m.UnusedImport, m.UndefinedName)
|
|
||||||
|
|
||||||
def test_nestedFunctionsNestScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
def a():
|
|
||||||
def b():
|
|
||||||
fu
|
|
||||||
import fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_nestedClassAndFunctionScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
def a():
|
|
||||||
import fu
|
|
||||||
class b:
|
|
||||||
def c(self):
|
|
||||||
print fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_importStar(self):
|
|
||||||
self.flakes('from fu import *', m.ImportStarUsed)
|
|
||||||
|
|
||||||
def test_packageImport(self):
|
|
||||||
"""
|
|
||||||
If a dotted name is imported and used, no warning is reported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu.bar
|
|
||||||
fu.bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_unusedPackageImport(self):
|
|
||||||
"""
|
|
||||||
If a dotted name is imported and not used, an unused import warning is
|
|
||||||
reported.
|
|
||||||
"""
|
|
||||||
self.flakes('import fu.bar', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_duplicateSubmoduleImport(self):
|
|
||||||
"""
|
|
||||||
If a submodule of a package is imported twice, an unused import warning
|
|
||||||
and a redefined while unused warning are reported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu.bar, fu.bar
|
|
||||||
fu.bar
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
self.flakes('''
|
|
||||||
import fu.bar
|
|
||||||
import fu.bar
|
|
||||||
fu.bar
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_differentSubmoduleImport(self):
|
|
||||||
"""
|
|
||||||
If two different submodules of a package are imported, no duplicate
|
|
||||||
import warning is reported for the package.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import fu.bar, fu.baz
|
|
||||||
fu.bar, fu.baz
|
|
||||||
''')
|
|
||||||
self.flakes('''
|
|
||||||
import fu.bar
|
|
||||||
import fu.baz
|
|
||||||
fu.bar, fu.baz
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_assignRHSFirst(self):
|
|
||||||
self.flakes('import fu; fu = fu')
|
|
||||||
self.flakes('import fu; fu, bar = fu')
|
|
||||||
self.flakes('import fu; [fu, bar] = fu')
|
|
||||||
self.flakes('import fu; fu += fu')
|
|
||||||
|
|
||||||
def test_tryingMultipleImports(self):
|
|
||||||
self.flakes('''
|
|
||||||
try:
|
|
||||||
import fu
|
|
||||||
except ImportError:
|
|
||||||
import bar as fu
|
|
||||||
''')
|
|
||||||
test_tryingMultipleImports.todo = ''
|
|
||||||
|
|
||||||
def test_nonGlobalDoesNotRedefine(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def a():
|
|
||||||
fu = 3
|
|
||||||
return fu
|
|
||||||
fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_functionsRunLater(self):
|
|
||||||
self.flakes('''
|
|
||||||
def a():
|
|
||||||
fu
|
|
||||||
import fu
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_functionNamesAreBoundNow(self):
|
|
||||||
self.flakes('''
|
|
||||||
import fu
|
|
||||||
def fu():
|
|
||||||
fu
|
|
||||||
fu
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_ignoreNonImportRedefinitions(self):
|
|
||||||
self.flakes('a = 1; a = 2')
|
|
||||||
|
|
||||||
def test_importingForImportError(self):
|
|
||||||
self.flakes('''
|
|
||||||
try:
|
|
||||||
import fu
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
test_importingForImportError.todo = ''
|
|
||||||
|
|
||||||
def test_importedInClass(self):
|
|
||||||
'''Imports in class scope can be used through self'''
|
|
||||||
self.flakes('''
|
|
||||||
class c:
|
|
||||||
import i
|
|
||||||
def __init__(self):
|
|
||||||
self.i
|
|
||||||
''')
|
|
||||||
test_importedInClass.todo = 'requires evaluating attribute access'
|
|
||||||
|
|
||||||
def test_futureImport(self):
|
|
||||||
'''__future__ is special'''
|
|
||||||
self.flakes('from __future__ import division')
|
|
||||||
self.flakes('''
|
|
||||||
"docstring is allowed before future import"
|
|
||||||
from __future__ import division
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_futureImportFirst(self):
|
|
||||||
"""
|
|
||||||
__future__ imports must come before anything else.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
x = 5
|
|
||||||
from __future__ import division
|
|
||||||
''', m.LateFutureImport)
|
|
||||||
self.flakes('''
|
|
||||||
from foo import bar
|
|
||||||
from __future__ import division
|
|
||||||
bar
|
|
||||||
''', m.LateFutureImport)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSpecialAll(harness.Test):
|
|
||||||
"""
|
|
||||||
Tests for suppression of unused import warnings by C{__all__}.
|
|
||||||
"""
|
|
||||||
def test_ignoredInFunction(self):
|
|
||||||
"""
|
|
||||||
An C{__all__} definition does not suppress unused import warnings in a
|
|
||||||
function scope.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def foo():
|
|
||||||
import bar
|
|
||||||
__all__ = ["bar"]
|
|
||||||
''', m.UnusedImport, m.UnusedVariable)
|
|
||||||
|
|
||||||
def test_ignoredInClass(self):
|
|
||||||
"""
|
|
||||||
An C{__all__} definition does not suppress unused import warnings in a
|
|
||||||
class scope.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
class foo:
|
|
||||||
import bar
|
|
||||||
__all__ = ["bar"]
|
|
||||||
''', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_warningSuppressed(self):
|
|
||||||
"""
|
|
||||||
If a name is imported and unused but is named in C{__all__}, no warning
|
|
||||||
is reported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import foo
|
|
||||||
__all__ = ["foo"]
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_unrecognizable(self):
|
|
||||||
"""
|
|
||||||
If C{__all__} is defined in a way that can't be recognized statically,
|
|
||||||
it is ignored.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
import foo
|
|
||||||
__all__ = ["f" + "oo"]
|
|
||||||
''', m.UnusedImport)
|
|
||||||
self.flakes('''
|
|
||||||
import foo
|
|
||||||
__all__ = [] + ["foo"]
|
|
||||||
''', m.UnusedImport)
|
|
||||||
|
|
||||||
def test_unboundExported(self):
|
|
||||||
"""
|
|
||||||
If C{__all__} includes a name which is not bound, a warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
__all__ = ["foo"]
|
|
||||||
''', m.UndefinedExport)
|
|
||||||
|
|
||||||
# Skip this in __init__.py though, since the rules there are a little
|
|
||||||
# different.
|
|
||||||
for filename in ["foo/__init__.py", "__init__.py"]:
|
|
||||||
self.flakes('''
|
|
||||||
__all__ = ["foo"]
|
|
||||||
''', filename=filename)
|
|
||||||
|
|
||||||
def test_usedInGenExp(self):
|
|
||||||
"""
|
|
||||||
Using a global in a generator expression results in no warnings.
|
|
||||||
"""
|
|
||||||
self.flakes('import fu; (fu for _ in range(1))')
|
|
||||||
self.flakes('import fu; (1 for _ in range(1) if fu)')
|
|
||||||
|
|
||||||
def test_redefinedByGenExp(self):
|
|
||||||
"""
|
|
||||||
Re-using a global name as the loop variable for a generator
|
|
||||||
expression results in a redefinition warning.
|
|
||||||
"""
|
|
||||||
self.flakes('import fu; (1 for fu in range(1))',
|
|
||||||
m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_usedAsDecorator(self):
|
|
||||||
"""
|
|
||||||
Using a global name in a decorator statement results in no warnings,
|
|
||||||
but using an undefined name in a decorator statement results in an
|
|
||||||
undefined name warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from interior import decorate
|
|
||||||
@decorate
|
|
||||||
def f():
|
|
||||||
return "hello"
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
from interior import decorate
|
|
||||||
@decorate('value')
|
|
||||||
def f():
|
|
||||||
return "hello"
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
@decorate
|
|
||||||
def f():
|
|
||||||
return "hello"
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
|
|
||||||
class Python26Tests(harness.Test):
|
|
||||||
"""
|
|
||||||
Tests for checking of syntax which is valid in PYthon 2.6 and newer.
|
|
||||||
"""
|
|
||||||
if version_info < (2, 6):
|
|
||||||
skip = "Python 2.6 required for class decorator tests."
|
|
||||||
|
|
||||||
def test_usedAsClassDecorator(self):
|
|
||||||
"""
|
|
||||||
Using an imported name as a class decorator results in no warnings,
|
|
||||||
but using an undefined name as a class decorator results in an
|
|
||||||
undefined name warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from interior import decorate
|
|
||||||
@decorate
|
|
||||||
class foo:
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
from interior import decorate
|
|
||||||
@decorate("foo")
|
|
||||||
class bar:
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
@decorate
|
|
||||||
class foo:
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
@ -1,542 +0,0 @@
|
||||||
# (c) 2005-2010 Divmod, Inc.
|
|
||||||
# See LICENSE file for details
|
|
||||||
|
|
||||||
"""
|
|
||||||
Tests for various Pyflakes behavior.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from sys import version_info
|
|
||||||
|
|
||||||
from pyflakes import messages as m
|
|
||||||
from pyflakes.test import harness
|
|
||||||
|
|
||||||
|
|
||||||
class Test(harness.Test):
|
|
||||||
|
|
||||||
def test_duplicateArgs(self):
|
|
||||||
self.flakes('def fu(bar, bar): pass', m.DuplicateArgument)
|
|
||||||
|
|
||||||
def test_localReferencedBeforeAssignment(self):
|
|
||||||
self.flakes('''
|
|
||||||
a = 1
|
|
||||||
def f():
|
|
||||||
a; a=1
|
|
||||||
f()
|
|
||||||
''', m.UndefinedName)
|
|
||||||
test_localReferencedBeforeAssignment.todo = 'this requires finding all ' \
|
|
||||||
'assignments in the function body first'
|
|
||||||
|
|
||||||
def test_redefinedFunction(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a function definition with another one raises a
|
|
||||||
warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def a(): pass
|
|
||||||
def a(): pass
|
|
||||||
''', m.RedefinedFunction)
|
|
||||||
|
|
||||||
def test_redefinedClassFunction(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a function definition in a class suite with another
|
|
||||||
one raises a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
class A:
|
|
||||||
def a(): pass
|
|
||||||
def a(): pass
|
|
||||||
''', m.RedefinedFunction)
|
|
||||||
|
|
||||||
def test_functionDecorator(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a function definition with a decorated version of
|
|
||||||
that function does not raise a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from somewhere import somedecorator
|
|
||||||
|
|
||||||
def a(): pass
|
|
||||||
a = somedecorator(a)
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_classFunctionDecorator(self):
|
|
||||||
"""
|
|
||||||
Test that shadowing a function definition in a class suite with a
|
|
||||||
decorated version of that function does not raise a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
class A:
|
|
||||||
def a(): pass
|
|
||||||
a = classmethod(a)
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_unaryPlus(self):
|
|
||||||
'''Don't die on unary +'''
|
|
||||||
self.flakes('+1')
|
|
||||||
|
|
||||||
def test_undefinedBaseClass(self):
|
|
||||||
"""
|
|
||||||
If a name in the base list of a class definition is undefined, a
|
|
||||||
warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
class foo(foo):
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_classNameUndefinedInClassBody(self):
|
|
||||||
"""
|
|
||||||
If a class name is used in the body of that class's definition and
|
|
||||||
the name is not already defined, a warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
class foo:
|
|
||||||
foo
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_classNameDefinedPreviously(self):
|
|
||||||
"""
|
|
||||||
If a class name is used in the body of that class's definition and
|
|
||||||
the name was previously defined in some other way, no warning is
|
|
||||||
emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
foo = None
|
|
||||||
class foo:
|
|
||||||
foo
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_comparison(self):
|
|
||||||
"""
|
|
||||||
If a defined name is used on either side of any of the six comparison
|
|
||||||
operators, no warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
x = 10
|
|
||||||
y = 20
|
|
||||||
x < y
|
|
||||||
x <= y
|
|
||||||
x == y
|
|
||||||
x != y
|
|
||||||
x >= y
|
|
||||||
x > y
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_identity(self):
|
|
||||||
"""
|
|
||||||
If a deefined name is used on either side of an identity test, no
|
|
||||||
warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
x = 10
|
|
||||||
y = 20
|
|
||||||
x is y
|
|
||||||
x is not y
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_containment(self):
|
|
||||||
"""
|
|
||||||
If a defined name is used on either side of a containment test, no
|
|
||||||
warning is emitted.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
x = 10
|
|
||||||
y = 20
|
|
||||||
x in y
|
|
||||||
x not in y
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_loopControl(self):
|
|
||||||
"""
|
|
||||||
break and continue statements are supported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
for x in [1, 2]:
|
|
||||||
break
|
|
||||||
''')
|
|
||||||
self.flakes('''
|
|
||||||
for x in [1, 2]:
|
|
||||||
continue
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_ellipsis(self):
|
|
||||||
"""
|
|
||||||
Ellipsis in a slice is supported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
[1, 2][...]
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_extendedSlice(self):
|
|
||||||
"""
|
|
||||||
Extended slices are supported.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
x = 3
|
|
||||||
[1, 2][x,:]
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class TestUnusedAssignment(harness.Test):
|
|
||||||
"""
|
|
||||||
Tests for warning about unused assignments.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_unusedVariable(self):
|
|
||||||
"""
|
|
||||||
Warn when a variable in a function is assigned a value that's never
|
|
||||||
used.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def a():
|
|
||||||
b = 1
|
|
||||||
''', m.UnusedVariable)
|
|
||||||
|
|
||||||
def test_assignToGlobal(self):
|
|
||||||
"""
|
|
||||||
Assigning to a global and then not using that global is perfectly
|
|
||||||
acceptable. Do not mistake it for an unused local variable.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
b = 0
|
|
||||||
def a():
|
|
||||||
global b
|
|
||||||
b = 1
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_assignToMember(self):
|
|
||||||
"""
|
|
||||||
Assigning to a member of another object and then not using that member
|
|
||||||
variable is perfectly acceptable. Do not mistake it for an unused
|
|
||||||
local variable.
|
|
||||||
"""
|
|
||||||
# XXX: Adding this test didn't generate a failure. Maybe not
|
|
||||||
# necessary?
|
|
||||||
self.flakes('''
|
|
||||||
class b:
|
|
||||||
pass
|
|
||||||
def a():
|
|
||||||
b.foo = 1
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_assignInForLoop(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable in a for loop is assigned to but not used.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
for i in range(10):
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_assignInListComprehension(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable in a list comprehension is assigned to but
|
|
||||||
not used.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
[None for i in range(10)]
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_generatorExpression(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable in a generator expression is assigned to
|
|
||||||
but not used.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
(None for i in range(10))
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_assignmentInsideLoop(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable assignment occurs lexically after its use.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
x = None
|
|
||||||
for i in range(10):
|
|
||||||
if i > 2:
|
|
||||||
return x
|
|
||||||
x = i * 2
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_tupleUnpacking(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable included in tuple unpacking is unused. It's
|
|
||||||
very common for variables in a tuple unpacking assignment to be unused
|
|
||||||
in good Python code, so warning will only create false positives.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
(x, y) = 1, 2
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_listUnpacking(self):
|
|
||||||
"""
|
|
||||||
Don't warn when a variable included in list unpacking is unused.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
[x, y] = [1, 2]
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_closedOver(self):
|
|
||||||
"""
|
|
||||||
Don't warn when the assignment is used in an inner function.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def barMaker():
|
|
||||||
foo = 5
|
|
||||||
def bar():
|
|
||||||
return foo
|
|
||||||
return bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_doubleClosedOver(self):
|
|
||||||
"""
|
|
||||||
Don't warn when the assignment is used in an inner function, even if
|
|
||||||
that inner function itself is in an inner function.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def barMaker():
|
|
||||||
foo = 5
|
|
||||||
def bar():
|
|
||||||
def baz():
|
|
||||||
return foo
|
|
||||||
return bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
class Python25Test(harness.Test):
|
|
||||||
"""
|
|
||||||
Tests for checking of syntax only available in Python 2.5 and newer.
|
|
||||||
"""
|
|
||||||
if version_info < (2, 5):
|
|
||||||
skip = "Python 2.5 required for if-else and with tests"
|
|
||||||
|
|
||||||
def test_ifexp(self):
|
|
||||||
"""
|
|
||||||
Test C{foo if bar else baz} statements.
|
|
||||||
"""
|
|
||||||
self.flakes("a = 'moo' if True else 'oink'")
|
|
||||||
self.flakes("a = foo if True else 'oink'", m.UndefinedName)
|
|
||||||
self.flakes("a = 'moo' if True else bar", m.UndefinedName)
|
|
||||||
|
|
||||||
def test_withStatementNoNames(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using inside or after a nameless C{with}
|
|
||||||
statement a name defined beforehand.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
bar = None
|
|
||||||
with open("foo"):
|
|
||||||
bar
|
|
||||||
bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementSingleName(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using a name defined by a C{with} statement
|
|
||||||
within the suite or afterwards.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with open('foo') as bar:
|
|
||||||
bar
|
|
||||||
bar
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementAttributeName(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using an attribute as the target of a
|
|
||||||
C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
import foo
|
|
||||||
with open('foo') as foo.bar:
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementSubscript(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using a subscript as the target of a
|
|
||||||
C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
import foo
|
|
||||||
with open('foo') as foo[0]:
|
|
||||||
pass
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementSubscriptUndefined(self):
|
|
||||||
"""
|
|
||||||
An undefined name warning is emitted if the subscript used as the
|
|
||||||
target of a C{with} statement is not defined.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
import foo
|
|
||||||
with open('foo') as foo[bar]:
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_withStatementTupleNames(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using any of the tuple of names defined by
|
|
||||||
a C{with} statement within the suite or afterwards.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with open('foo') as (bar, baz):
|
|
||||||
bar, baz
|
|
||||||
bar, baz
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementListNames(self):
|
|
||||||
"""
|
|
||||||
No warnings are emitted for using any of the list of names defined by a
|
|
||||||
C{with} statement within the suite or afterwards.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with open('foo') as [bar, baz]:
|
|
||||||
bar, baz
|
|
||||||
bar, baz
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementComplicatedTarget(self):
|
|
||||||
"""
|
|
||||||
If the target of a C{with} statement uses any or all of the valid forms
|
|
||||||
for that part of the grammar (See
|
|
||||||
U{http://docs.python.org/reference/
|
|
||||||
compound_stmts.html#the-with-statement}),
|
|
||||||
the names involved are checked both for definedness and any bindings
|
|
||||||
created are respected in the suite of the statement and afterwards.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
c = d = e = g = h = i = None
|
|
||||||
with open('foo') as [(a, b), c[d], e.f, g[h:i]]:
|
|
||||||
a, b, c, d, e, g, h, i
|
|
||||||
a, b, c, d, e, g, h, i
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementSingleNameUndefined(self):
|
|
||||||
"""
|
|
||||||
An undefined name warning is emitted if the name first defined by a
|
|
||||||
C{with} statement is used before the C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
bar
|
|
||||||
with open('foo') as bar:
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_withStatementTupleNamesUndefined(self):
|
|
||||||
"""
|
|
||||||
An undefined name warning is emitted if a name first defined by a the
|
|
||||||
tuple-unpacking form of the C{with} statement is used before the
|
|
||||||
C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
baz
|
|
||||||
with open('foo') as (bar, baz):
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_withStatementSingleNameRedefined(self):
|
|
||||||
"""
|
|
||||||
A redefined name warning is emitted if a name bound by an import is
|
|
||||||
rebound by the name defined by a C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
import bar
|
|
||||||
with open('foo') as bar:
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_withStatementTupleNamesRedefined(self):
|
|
||||||
"""
|
|
||||||
A redefined name warning is emitted if a name bound by an import is
|
|
||||||
rebound by one of the names defined by the tuple-unpacking form of a
|
|
||||||
C{with} statement.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
import bar
|
|
||||||
with open('foo') as (bar, baz):
|
|
||||||
pass
|
|
||||||
''', m.RedefinedWhileUnused)
|
|
||||||
|
|
||||||
def test_withStatementUndefinedInside(self):
|
|
||||||
"""
|
|
||||||
An undefined name warning is emitted if a name is used inside the
|
|
||||||
body of a C{with} statement without first being bound.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with open('foo') as bar:
|
|
||||||
baz
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_withStatementNameDefinedInBody(self):
|
|
||||||
"""
|
|
||||||
A name defined in the body of a C{with} statement can be used after
|
|
||||||
the body ends without warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with open('foo') as bar:
|
|
||||||
baz = 10
|
|
||||||
baz
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_withStatementUndefinedInExpression(self):
|
|
||||||
"""
|
|
||||||
An undefined name warning is emitted if a name in the I{test}
|
|
||||||
expression of a C{with} statement is undefined.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with bar as baz:
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
self.flakes('''
|
|
||||||
from __future__ import with_statement
|
|
||||||
with bar as bar:
|
|
||||||
pass
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
|
|
||||||
class Python27Test(harness.Test):
|
|
||||||
"""
|
|
||||||
Tests for checking of syntax only available in Python 2.7 and newer.
|
|
||||||
"""
|
|
||||||
if version_info < (2, 7):
|
|
||||||
skip = "Python 2.7 required for dict/set comprehension tests"
|
|
||||||
|
|
||||||
def test_dictComprehension(self):
|
|
||||||
"""
|
|
||||||
Dict comprehensions are properly handled.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
a = {1: x for x in range(10)}
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_setComprehensionAndLiteral(self):
|
|
||||||
"""
|
|
||||||
Set comprehensions are properly handled.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
a = {1, 2, 3}
|
|
||||||
b = {x for x in range(10)}
|
|
||||||
''')
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
|
|
||||||
"""
|
|
||||||
Tests for L{pyflakes.scripts.pyflakes}.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
from twisted.python.filepath import FilePath
|
|
||||||
from twisted.trial.unittest import TestCase
|
|
||||||
|
|
||||||
from pyflakes.scripts.pyflakes import checkPath
|
|
||||||
|
|
||||||
|
|
||||||
def withStderrTo(stderr, f):
|
|
||||||
"""
|
|
||||||
Call C{f} with C{sys.stderr} redirected to C{stderr}.
|
|
||||||
"""
|
|
||||||
(outer, sys.stderr) = (sys.stderr, stderr)
|
|
||||||
try:
|
|
||||||
return f()
|
|
||||||
finally:
|
|
||||||
sys.stderr = outer
|
|
||||||
|
|
||||||
|
|
||||||
class CheckTests(TestCase):
|
|
||||||
"""
|
|
||||||
Tests for L{check} and L{checkPath} which check a file for flakes.
|
|
||||||
"""
|
|
||||||
def test_missingTrailingNewline(self):
|
|
||||||
"""
|
|
||||||
Source which doesn't end with a newline shouldn't cause any
|
|
||||||
exception to be raised nor an error indicator to be returned by
|
|
||||||
L{check}.
|
|
||||||
"""
|
|
||||||
fName = self.mktemp()
|
|
||||||
FilePath(fName).setContent("def foo():\n\tpass\n\t")
|
|
||||||
self.assertFalse(checkPath(fName))
|
|
||||||
|
|
||||||
def test_checkPathNonExisting(self):
|
|
||||||
"""
|
|
||||||
L{checkPath} handles non-existing files.
|
|
||||||
"""
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath('extremo'))
|
|
||||||
self.assertEquals(err.getvalue(),
|
|
||||||
'extremo: No such file or directory\n')
|
|
||||||
self.assertEquals(count, 1)
|
|
||||||
|
|
||||||
def test_multilineSyntaxError(self):
|
|
||||||
"""
|
|
||||||
Source which includes a syntax error which results in the raised
|
|
||||||
L{SyntaxError.text} containing multiple lines of source are reported
|
|
||||||
with only the last line of that source.
|
|
||||||
"""
|
|
||||||
source = """\
|
|
||||||
def foo():
|
|
||||||
'''
|
|
||||||
|
|
||||||
def bar():
|
|
||||||
pass
|
|
||||||
|
|
||||||
def baz():
|
|
||||||
'''quux'''
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Sanity check - SyntaxError.text should be multiple lines, if it
|
|
||||||
# isn't, something this test was unprepared for has happened.
|
|
||||||
def evaluate(source):
|
|
||||||
exec source
|
|
||||||
exc = self.assertRaises(SyntaxError, evaluate, source)
|
|
||||||
self.assertTrue(exc.text.count('\n') > 1)
|
|
||||||
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent(source)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEqual(count, 1)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
err.getvalue(),
|
|
||||||
"""\
|
|
||||||
%s:8: invalid syntax
|
|
||||||
'''quux'''
|
|
||||||
^
|
|
||||||
""" % (sourcePath.path,))
|
|
||||||
|
|
||||||
def test_eofSyntaxError(self):
|
|
||||||
"""
|
|
||||||
The error reported for source files which end prematurely causing a
|
|
||||||
syntax error reflects the cause for the syntax error.
|
|
||||||
"""
|
|
||||||
source = "def foo("
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent(source)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEqual(count, 1)
|
|
||||||
self.assertEqual(
|
|
||||||
err.getvalue(),
|
|
||||||
"""\
|
|
||||||
%s:1: unexpected EOF while parsing
|
|
||||||
def foo(
|
|
||||||
^
|
|
||||||
""" % (sourcePath.path,))
|
|
||||||
|
|
||||||
def test_nonDefaultFollowsDefaultSyntaxError(self):
|
|
||||||
"""
|
|
||||||
Source which has a non-default argument following a default argument
|
|
||||||
should include the line number of the syntax error. However these
|
|
||||||
exceptions do not include an offset.
|
|
||||||
"""
|
|
||||||
source = """\
|
|
||||||
def foo(bar=baz, bax):
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent(source)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEqual(count, 1)
|
|
||||||
self.assertEqual(
|
|
||||||
err.getvalue(),
|
|
||||||
"""\
|
|
||||||
%s:1: non-default argument follows default argument
|
|
||||||
def foo(bar=baz, bax):
|
|
||||||
""" % (sourcePath.path,))
|
|
||||||
|
|
||||||
def test_nonKeywordAfterKeywordSyntaxError(self):
|
|
||||||
"""
|
|
||||||
Source which has a non-keyword argument after a keyword argument should
|
|
||||||
include the line number of the syntax error. However these exceptions
|
|
||||||
do not include an offset.
|
|
||||||
"""
|
|
||||||
source = """\
|
|
||||||
foo(bar=baz, bax)
|
|
||||||
"""
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent(source)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEqual(count, 1)
|
|
||||||
self.assertEqual(
|
|
||||||
err.getvalue(),
|
|
||||||
"""\
|
|
||||||
%s:1: non-keyword arg after keyword arg
|
|
||||||
foo(bar=baz, bax)
|
|
||||||
""" % (sourcePath.path,))
|
|
||||||
|
|
||||||
def test_permissionDenied(self):
|
|
||||||
"""
|
|
||||||
If the a source file is not readable, this is reported on standard
|
|
||||||
error.
|
|
||||||
"""
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent('')
|
|
||||||
sourcePath.chmod(0)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEquals(count, 1)
|
|
||||||
self.assertEquals(
|
|
||||||
err.getvalue(), "%s: Permission denied\n" % (sourcePath.path,))
|
|
||||||
|
|
||||||
def test_misencodedFile(self):
|
|
||||||
"""
|
|
||||||
If a source file contains bytes which cannot be decoded, this is
|
|
||||||
reported on stderr.
|
|
||||||
"""
|
|
||||||
source = u"""\
|
|
||||||
# coding: ascii
|
|
||||||
x = "\N{SNOWMAN}"
|
|
||||||
""".encode('utf-8')
|
|
||||||
sourcePath = FilePath(self.mktemp())
|
|
||||||
sourcePath.setContent(source)
|
|
||||||
err = StringIO()
|
|
||||||
count = withStderrTo(err, lambda: checkPath(sourcePath.path))
|
|
||||||
self.assertEquals(count, 1)
|
|
||||||
self.assertEquals(
|
|
||||||
err.getvalue(),
|
|
||||||
"%s: problem decoding source\n" % (sourcePath.path,))
|
|
||||||
|
|
@ -1,261 +0,0 @@
|
||||||
|
|
||||||
from _ast import PyCF_ONLY_AST
|
|
||||||
|
|
||||||
from twisted.trial.unittest import TestCase
|
|
||||||
|
|
||||||
from pyflakes import messages as m, checker
|
|
||||||
from pyflakes.test import harness
|
|
||||||
|
|
||||||
|
|
||||||
class Test(harness.Test):
|
|
||||||
def test_undefined(self):
|
|
||||||
self.flakes('bar', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_definedInListComp(self):
|
|
||||||
self.flakes('[a for a in range(10) if a]')
|
|
||||||
|
|
||||||
def test_functionsNeedGlobalScope(self):
|
|
||||||
self.flakes('''
|
|
||||||
class a:
|
|
||||||
def b():
|
|
||||||
fu
|
|
||||||
fu = 1
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_builtins(self):
|
|
||||||
self.flakes('range(10)')
|
|
||||||
|
|
||||||
def test_magicGlobalsFile(self):
|
|
||||||
"""
|
|
||||||
Use of the C{__file__} magic global should not emit an undefined name
|
|
||||||
warning.
|
|
||||||
"""
|
|
||||||
self.flakes('__file__')
|
|
||||||
|
|
||||||
def test_magicGlobalsBuiltins(self):
|
|
||||||
"""
|
|
||||||
Use of the C{__builtins__} magic global should not emit an undefined
|
|
||||||
name warning.
|
|
||||||
"""
|
|
||||||
self.flakes('__builtins__')
|
|
||||||
|
|
||||||
def test_magicGlobalsName(self):
|
|
||||||
"""
|
|
||||||
Use of the C{__name__} magic global should not emit an undefined name
|
|
||||||
warning.
|
|
||||||
"""
|
|
||||||
self.flakes('__name__')
|
|
||||||
|
|
||||||
def test_magicGlobalsPath(self):
|
|
||||||
"""
|
|
||||||
Use of the C{__path__} magic global should not emit an undefined name
|
|
||||||
warning, if you refer to it from a file called __init__.py.
|
|
||||||
"""
|
|
||||||
self.flakes('__path__', m.UndefinedName)
|
|
||||||
self.flakes('__path__', filename='package/__init__.py')
|
|
||||||
|
|
||||||
def test_globalImportStar(self):
|
|
||||||
'''Can't find undefined names with import *'''
|
|
||||||
self.flakes('from fu import *; bar', m.ImportStarUsed)
|
|
||||||
|
|
||||||
def test_localImportStar(self):
|
|
||||||
'''
|
|
||||||
A local import * still allows undefined names to be found
|
|
||||||
in upper scopes
|
|
||||||
'''
|
|
||||||
self.flakes('''
|
|
||||||
def a():
|
|
||||||
from fu import *
|
|
||||||
bar
|
|
||||||
''', m.ImportStarUsed, m.UndefinedName)
|
|
||||||
|
|
||||||
def test_unpackedParameter(self):
|
|
||||||
'''Unpacked function parameters create bindings'''
|
|
||||||
self.flakes('''
|
|
||||||
def a((bar, baz)):
|
|
||||||
bar; baz
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_definedByGlobal(self):
|
|
||||||
'''
|
|
||||||
"global" can make an otherwise undefined name
|
|
||||||
in another function defined
|
|
||||||
'''
|
|
||||||
self.flakes('''
|
|
||||||
def a(): global fu; fu = 1
|
|
||||||
def b(): fu
|
|
||||||
''')
|
|
||||||
test_definedByGlobal.todo = ''
|
|
||||||
|
|
||||||
def test_globalInGlobalScope(self):
|
|
||||||
"""
|
|
||||||
A global statement in the global scope is ignored.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
global x
|
|
||||||
def foo():
|
|
||||||
print x
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_del(self):
|
|
||||||
'''del deletes bindings'''
|
|
||||||
self.flakes('a = 1; del a; a', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_delGlobal(self):
|
|
||||||
'''del a global binding from a function'''
|
|
||||||
self.flakes('''
|
|
||||||
a = 1
|
|
||||||
def f():
|
|
||||||
global a
|
|
||||||
del a
|
|
||||||
a
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_delUndefined(self):
|
|
||||||
'''del an undefined name'''
|
|
||||||
self.flakes('del a', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_globalFromNestedScope(self):
|
|
||||||
'''global names are available from nested scopes'''
|
|
||||||
self.flakes('''
|
|
||||||
a = 1
|
|
||||||
def b():
|
|
||||||
def c():
|
|
||||||
a
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_laterRedefinedGlobalFromNestedScope(self):
|
|
||||||
"""
|
|
||||||
Test that referencing a local name that shadows a global, before it is
|
|
||||||
defined, generates a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
a = 1
|
|
||||||
def fun():
|
|
||||||
a
|
|
||||||
a = 2
|
|
||||||
return a
|
|
||||||
''', m.UndefinedLocal)
|
|
||||||
|
|
||||||
def test_laterRedefinedGlobalFromNestedScope2(self):
|
|
||||||
"""
|
|
||||||
Test that referencing a local name in a nested scope that shadows a
|
|
||||||
global declared in an enclosing scope, before it is defined, generates
|
|
||||||
a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
a = 1
|
|
||||||
def fun():
|
|
||||||
global a
|
|
||||||
def fun2():
|
|
||||||
a
|
|
||||||
a = 2
|
|
||||||
return a
|
|
||||||
''', m.UndefinedLocal)
|
|
||||||
|
|
||||||
def test_intermediateClassScopeIgnored(self):
|
|
||||||
"""
|
|
||||||
If a name defined in an enclosing scope is shadowed by a local variable
|
|
||||||
and the name is used locally before it is bound, an unbound local
|
|
||||||
warning is emitted, even if there is a class scope between
|
|
||||||
the enclosing scope and the local scope.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
x = 1
|
|
||||||
class g:
|
|
||||||
def h(self):
|
|
||||||
a = x
|
|
||||||
x = None
|
|
||||||
print x, a
|
|
||||||
print x
|
|
||||||
''', m.UndefinedLocal)
|
|
||||||
|
|
||||||
def test_doubleNestingReportsClosestName(self):
|
|
||||||
"""
|
|
||||||
Test that referencing a local name in a nested scope that shadows a
|
|
||||||
variable declared in two different outer scopes before it is defined
|
|
||||||
in the innermost scope generates an UnboundLocal warning which
|
|
||||||
refers to the nearest shadowed name.
|
|
||||||
"""
|
|
||||||
exc = self.flakes('''
|
|
||||||
def a():
|
|
||||||
x = 1
|
|
||||||
def b():
|
|
||||||
x = 2 # line 5
|
|
||||||
def c():
|
|
||||||
x
|
|
||||||
x = 3
|
|
||||||
return x
|
|
||||||
return x
|
|
||||||
return x
|
|
||||||
''', m.UndefinedLocal).messages[0]
|
|
||||||
self.assertEqual(exc.message_args, ('x', 5))
|
|
||||||
|
|
||||||
def test_laterRedefinedGlobalFromNestedScope3(self):
|
|
||||||
"""
|
|
||||||
Test that referencing a local name in a nested scope that shadows a
|
|
||||||
global, before it is defined, generates a warning.
|
|
||||||
"""
|
|
||||||
self.flakes('''
|
|
||||||
def fun():
|
|
||||||
a = 1
|
|
||||||
def fun2():
|
|
||||||
a
|
|
||||||
a = 1
|
|
||||||
return a
|
|
||||||
return a
|
|
||||||
''', m.UndefinedLocal)
|
|
||||||
|
|
||||||
def test_nestedClass(self):
|
|
||||||
'''nested classes can access enclosing scope'''
|
|
||||||
self.flakes('''
|
|
||||||
def f(foo):
|
|
||||||
class C:
|
|
||||||
bar = foo
|
|
||||||
def f(self):
|
|
||||||
return foo
|
|
||||||
return C()
|
|
||||||
|
|
||||||
f(123).f()
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_badNestedClass(self):
|
|
||||||
'''free variables in nested classes must bind at class creation'''
|
|
||||||
self.flakes('''
|
|
||||||
def f():
|
|
||||||
class C:
|
|
||||||
bar = foo
|
|
||||||
foo = 456
|
|
||||||
return foo
|
|
||||||
f()
|
|
||||||
''', m.UndefinedName)
|
|
||||||
|
|
||||||
def test_definedAsStarArgs(self):
|
|
||||||
'''star and double-star arg names are defined'''
|
|
||||||
self.flakes('''
|
|
||||||
def f(a, *b, **c):
|
|
||||||
print a, b, c
|
|
||||||
''')
|
|
||||||
|
|
||||||
def test_definedInGenExp(self):
|
|
||||||
"""
|
|
||||||
Using the loop variable of a generator expression results in no
|
|
||||||
warnings.
|
|
||||||
"""
|
|
||||||
self.flakes('(a for a in xrange(10) if a)')
|
|
||||||
|
|
||||||
|
|
||||||
class NameTests(TestCase):
|
|
||||||
"""
|
|
||||||
Tests for some extra cases of name handling.
|
|
||||||
"""
|
|
||||||
def test_impossibleContext(self):
|
|
||||||
"""
|
|
||||||
A Name node with an unrecognized context results in a RuntimeError
|
|
||||||
being raised.
|
|
||||||
"""
|
|
||||||
tree = compile("x = 10", "<test>", "exec", PyCF_ONLY_AST)
|
|
||||||
# Make it into something unrecognizable.
|
|
||||||
tree.body[0].targets[0].ctx = object()
|
|
||||||
self.assertRaises(RuntimeError, checker.Checker, tree)
|
|
||||||
1
flake8/tests/__init__.py
Normal file
1
flake8/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#
|
||||||
38
flake8/tests/test_mccabe.py
Normal file
38
flake8/tests/test_mccabe.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
from flake8.mccabe import get_code_complexity
|
||||||
|
|
||||||
|
|
||||||
|
_GLOBAL = """\
|
||||||
|
|
||||||
|
for i in range(10):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def a():
|
||||||
|
def b():
|
||||||
|
def c():
|
||||||
|
pass
|
||||||
|
c()
|
||||||
|
b()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class McCabeTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.old = sys.stdout
|
||||||
|
self.out = sys.stdout = StringIO()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
sys.sdtout = self.old
|
||||||
|
|
||||||
|
def test_sample(self):
|
||||||
|
self.assertEqual(get_code_complexity(_GLOBAL, 1), 2)
|
||||||
|
self.out.seek(0)
|
||||||
|
res = self.out.read().strip().split('\n')
|
||||||
|
wanted = ["stdin:5:1: 'a' is too complex (3)",
|
||||||
|
'stdin:Loop 2 is too complex (1)']
|
||||||
|
self.assertEqual(res, wanted)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue