837 lines
16 KiB
Python
837 lines
16 KiB
Python
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
from sys import version_info
|
|
|
|
import pytest
|
|
from pies.overrides import *
|
|
|
|
from frosted import messages as m
|
|
|
|
from .utils import flakes
|
|
|
|
|
|
def test_unusedImport():
|
|
flakes('import fu, bar', m.UnusedImport, m.UnusedImport)
|
|
flakes('from baz import fu, bar', m.UnusedImport, m.UnusedImport)
|
|
|
|
|
|
def test_aliasedImport():
|
|
flakes('import fu as FU, bar as FU',
|
|
m.RedefinedWhileUnused, m.UnusedImport)
|
|
flakes('from moo import fu as FU, bar as FU',
|
|
m.RedefinedWhileUnused, m.UnusedImport)
|
|
|
|
|
|
def test_usedImport():
|
|
flakes('import fu; print(fu)')
|
|
flakes('from baz import fu; print(fu)')
|
|
flakes('import fu; del fu')
|
|
|
|
|
|
def test_redefinedWhileUnused():
|
|
flakes('import fu; fu = 3', m.RedefinedWhileUnused)
|
|
flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused)
|
|
flakes('import fu; [fu, bar] = 3', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedIf():
|
|
"""Test that importing a module twice within an if block does raise a warning."""
|
|
flakes('''
|
|
i = 2
|
|
if i==1:
|
|
import os
|
|
import os
|
|
os.path''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedIfElse():
|
|
"""Test that importing a module twice in if and else blocks does not raise a warning."""
|
|
flakes('''
|
|
i = 2
|
|
if i==1:
|
|
import os
|
|
else:
|
|
import os
|
|
os.path''')
|
|
|
|
|
|
def test_redefinedTry():
|
|
"""Test that importing a module twice in an try block does raise a warning."""
|
|
flakes('''
|
|
try:
|
|
import os
|
|
import os
|
|
except Exception:
|
|
pass
|
|
os.path''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedTryExcept():
|
|
"""Test that importing a module twice in an try and except block does not raise a warning."""
|
|
flakes('''
|
|
try:
|
|
import os
|
|
except Exception:
|
|
import os
|
|
os.path''')
|
|
|
|
|
|
def test_redefinedTryNested():
|
|
"""Test that importing a module twice using a nested try/except and if blocks does not issue a warning."""
|
|
flakes('''
|
|
try:
|
|
if True:
|
|
if True:
|
|
import os
|
|
except Exception:
|
|
import os
|
|
os.path''')
|
|
|
|
|
|
def test_redefinedTryExceptMulti():
|
|
flakes("""
|
|
try:
|
|
from aa import mixer
|
|
except AttributeError:
|
|
from bb import mixer
|
|
except RuntimeError:
|
|
from cc import mixer
|
|
except Exception:
|
|
from dd import mixer
|
|
mixer(123)
|
|
""")
|
|
|
|
|
|
def test_redefinedTryElse():
|
|
flakes("""
|
|
try:
|
|
from aa import mixer
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
from bb import mixer
|
|
mixer(123)
|
|
""", m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedTryExceptElse():
|
|
flakes("""
|
|
try:
|
|
import funca
|
|
except ImportError:
|
|
from bb import funca
|
|
from bb import funcb
|
|
else:
|
|
from bbb import funcb
|
|
print(funca, funcb)
|
|
""")
|
|
|
|
|
|
def test_redefinedTryExceptFinally():
|
|
flakes("""
|
|
try:
|
|
from aa import a
|
|
except ImportError:
|
|
from bb import a
|
|
finally:
|
|
a = 42
|
|
print(a)
|
|
""")
|
|
|
|
|
|
def test_redefinedTryExceptElseFinally():
|
|
flakes("""
|
|
try:
|
|
import b
|
|
except ImportError:
|
|
b = Ellipsis
|
|
from bb import a
|
|
else:
|
|
from aa import a
|
|
finally:
|
|
a = 42
|
|
print(a, b)
|
|
""")
|
|
|
|
|
|
def test_redefinedByFunction():
|
|
flakes('''
|
|
import fu
|
|
def fu():
|
|
pass
|
|
''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedInNestedFunction():
|
|
"""Test that shadowing a global name with a nested function definition generates a warning."""
|
|
flakes('''
|
|
import fu
|
|
def bar():
|
|
def baz():
|
|
def fu():
|
|
pass
|
|
''', m.RedefinedWhileUnused, m.UnusedImport)
|
|
|
|
|
|
def test_redefinedByClass():
|
|
flakes('''
|
|
import fu
|
|
class fu:
|
|
pass
|
|
''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_redefinedBySubclass():
|
|
"""If an imported name is redefined by a class statement
|
|
|
|
which also uses that name in the bases list, no warning is emitted.
|
|
|
|
"""
|
|
flakes('''
|
|
from fu import bar
|
|
class bar(bar):
|
|
pass
|
|
''')
|
|
|
|
|
|
def test_redefinedInClass():
|
|
"""Test that shadowing a global with a class attribute does not produce a warning."""
|
|
flakes('''
|
|
import fu
|
|
class bar:
|
|
fu = 1
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_usedInFunction():
|
|
flakes('''
|
|
import fu
|
|
def fun():
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_shadowedByParameter():
|
|
flakes('''
|
|
import fu
|
|
def fun(fu):
|
|
print(fu)
|
|
''', m.UnusedImport)
|
|
|
|
flakes('''
|
|
import fu
|
|
def fun(fu):
|
|
print(fu)
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_newAssignment():
|
|
flakes('fu = None')
|
|
|
|
|
|
def test_usedInGetattr():
|
|
flakes('import fu; fu.bar.baz')
|
|
flakes('import fu; "bar".fu.baz', m.UnusedImport)
|
|
|
|
|
|
def test_usedInSlice():
|
|
flakes('import fu; print(fu.bar[1:])')
|
|
|
|
|
|
def test_usedInIfBody():
|
|
flakes('''
|
|
import fu
|
|
if True: print(fu)
|
|
''')
|
|
|
|
|
|
def test_usedInIfConditional():
|
|
flakes('''
|
|
import fu
|
|
if fu: pass
|
|
''')
|
|
|
|
|
|
def test_usedInElifConditional():
|
|
flakes('''
|
|
import fu
|
|
if False: pass
|
|
elif fu: pass
|
|
''')
|
|
|
|
|
|
def test_usedInElse():
|
|
flakes('''
|
|
import fu
|
|
if False: pass
|
|
else: print(fu)
|
|
''')
|
|
|
|
|
|
def test_usedInCall():
|
|
flakes('import fu; fu.bar()')
|
|
|
|
|
|
def test_usedInClass():
|
|
flakes('''
|
|
import fu
|
|
class bar:
|
|
bar = fu
|
|
''')
|
|
|
|
|
|
def test_usedInClassBase():
|
|
flakes('''
|
|
import fu
|
|
class bar(object, fu.baz):
|
|
pass
|
|
''')
|
|
|
|
|
|
def test_notUsedInNestedScope():
|
|
flakes('''
|
|
import fu
|
|
def bleh():
|
|
pass
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_usedInFor():
|
|
flakes('''
|
|
import fu
|
|
for bar in range(9):
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_usedInForElse():
|
|
flakes('''
|
|
import fu
|
|
for bar in range(10):
|
|
pass
|
|
else:
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_redefinedByFor():
|
|
flakes('''
|
|
import fu
|
|
for fu in range(2):
|
|
pass
|
|
''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_shadowedByFor():
|
|
"""Test that shadowing a global name with a for loop variable generates a warning."""
|
|
flakes('''
|
|
import fu
|
|
fu.bar()
|
|
for fu in ():
|
|
pass
|
|
''', m.ImportShadowedByLoopVar)
|
|
|
|
|
|
def test_shadowedByForDeep():
|
|
"""Test that shadowing a global name with a for loop variable nested in a tuple unpack generates a warning."""
|
|
flakes('''
|
|
import fu
|
|
fu.bar()
|
|
for (x, y, z, (a, b, c, (fu,))) in ():
|
|
pass
|
|
''', m.ImportShadowedByLoopVar)
|
|
|
|
|
|
def test_usedInReturn():
|
|
flakes('''
|
|
import fu
|
|
def fun():
|
|
return fu
|
|
''')
|
|
|
|
|
|
def test_usedInOperators():
|
|
flakes('import fu; 3 + fu.bar')
|
|
flakes('import fu; 3 % fu.bar')
|
|
flakes('import fu; 3 - fu.bar')
|
|
flakes('import fu; 3 * fu.bar')
|
|
flakes('import fu; 3 ** fu.bar')
|
|
flakes('import fu; 3 / fu.bar')
|
|
flakes('import fu; 3 // fu.bar')
|
|
flakes('import fu; -fu.bar')
|
|
flakes('import fu; ~fu.bar')
|
|
flakes('import fu; 1 == fu.bar')
|
|
flakes('import fu; 1 | fu.bar')
|
|
flakes('import fu; 1 & fu.bar')
|
|
flakes('import fu; 1 ^ fu.bar')
|
|
flakes('import fu; 1 >> fu.bar')
|
|
flakes('import fu; 1 << fu.bar')
|
|
|
|
|
|
def test_usedInAssert():
|
|
flakes('import fu; assert fu.bar')
|
|
|
|
|
|
def test_usedInSubscript():
|
|
flakes('import fu; fu.bar[1]')
|
|
|
|
|
|
def test_usedInLogic():
|
|
flakes('import fu; fu and False')
|
|
flakes('import fu; fu or False')
|
|
flakes('import fu; not fu.bar')
|
|
|
|
|
|
def test_usedInList():
|
|
flakes('import fu; [fu]')
|
|
|
|
|
|
def test_usedInTuple():
|
|
flakes('import fu; (fu,)')
|
|
|
|
|
|
def test_usedInTry():
|
|
flakes('''
|
|
import fu
|
|
try: fu
|
|
except Exception: pass
|
|
''')
|
|
|
|
|
|
def test_usedInExcept():
|
|
flakes('''
|
|
import fu
|
|
try: fu
|
|
except Exception: pass
|
|
''')
|
|
|
|
|
|
def test_redefinedByExcept():
|
|
as_exc = ', ' if version_info < (2, 6) else ' as '
|
|
flakes('''
|
|
import fu
|
|
try: pass
|
|
except Exception%sfu: pass
|
|
''' % as_exc, m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_usedInRaise():
|
|
flakes('''
|
|
import fu
|
|
raise fu.bar
|
|
''')
|
|
|
|
|
|
def test_usedInYield():
|
|
flakes('''
|
|
import fu
|
|
def gen():
|
|
yield fu
|
|
''')
|
|
|
|
|
|
def test_usedInDict():
|
|
flakes('import fu; {fu:None}')
|
|
flakes('import fu; {1:fu}')
|
|
|
|
|
|
def test_usedInParameterDefault():
|
|
flakes('''
|
|
import fu
|
|
def f(bar=fu):
|
|
pass
|
|
''')
|
|
|
|
|
|
def test_usedInAttributeAssign():
|
|
flakes('import fu; fu.bar = 1')
|
|
|
|
|
|
def test_usedInKeywordArg():
|
|
flakes('import fu; fu.bar(stuff=fu)')
|
|
|
|
|
|
def test_usedInAssignment():
|
|
flakes('import fu; bar=fu')
|
|
flakes('import fu; n=0; n+=fu')
|
|
|
|
|
|
def test_usedInListComp():
|
|
flakes('import fu; [fu for _ in range(1)]')
|
|
flakes('import fu; [1 for _ in range(1) if fu]')
|
|
|
|
|
|
def test_redefinedByListComp():
|
|
flakes('import fu; [1 for fu in range(1)]', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_usedInTryFinally():
|
|
flakes('''
|
|
import fu
|
|
try: pass
|
|
finally: fu
|
|
''')
|
|
|
|
flakes('''
|
|
import fu
|
|
try: fu
|
|
finally: pass
|
|
''')
|
|
|
|
|
|
def test_usedInWhile():
|
|
flakes('''
|
|
import fu
|
|
while 0:
|
|
fu
|
|
''')
|
|
|
|
flakes('''
|
|
import fu
|
|
while fu: pass
|
|
''')
|
|
|
|
|
|
def test_usedInGlobal():
|
|
flakes('''
|
|
import fu
|
|
def f(): global fu
|
|
''', m.UnusedImport)
|
|
|
|
|
|
@pytest.mark.skipif("version_info >= (3,)")
|
|
def test_usedInBackquote():
|
|
flakes('import fu; `fu`')
|
|
|
|
|
|
def test_usedInExec():
|
|
if version_info < (3,):
|
|
exec_stmt = 'exec "print 1" in fu.bar'
|
|
else:
|
|
exec_stmt = 'exec("print(1)", fu.bar)'
|
|
flakes('import fu; %s' % exec_stmt)
|
|
|
|
|
|
def test_usedInLambda():
|
|
flakes('import fu; lambda: fu')
|
|
|
|
|
|
def test_shadowedByLambda():
|
|
flakes('import fu; lambda fu: fu', m.UnusedImport)
|
|
|
|
|
|
def test_usedInSliceObj():
|
|
flakes('import fu; "meow"[::fu]')
|
|
|
|
|
|
def test_unusedInNestedScope():
|
|
flakes('''
|
|
def bar():
|
|
import fu
|
|
fu
|
|
''', m.UnusedImport, m.UndefinedName)
|
|
|
|
|
|
def test_methodsDontUseClassScope():
|
|
flakes('''
|
|
class bar:
|
|
import fu
|
|
def fun():
|
|
fu
|
|
''', m.UnusedImport, m.UndefinedName)
|
|
|
|
|
|
def test_nestedFunctionsNestScope():
|
|
flakes('''
|
|
def a():
|
|
def b():
|
|
fu
|
|
import fu
|
|
''')
|
|
|
|
|
|
def test_nestedClassAndFunctionScope():
|
|
flakes('''
|
|
def a():
|
|
import fu
|
|
class b:
|
|
def c():
|
|
print(fu)
|
|
''')
|
|
|
|
|
|
def test_importStar():
|
|
flakes('from fu import *', m.ImportStarUsed, ignore_frosted_errors=[])
|
|
|
|
|
|
def test_packageImport():
|
|
"""If a dotted name is imported and used, no warning is reported."""
|
|
flakes('''
|
|
import fu.bar
|
|
fu.bar
|
|
''')
|
|
|
|
|
|
def test_unusedPackageImport():
|
|
"""If a dotted name is imported and not used, an unused import warning is reported."""
|
|
flakes('import fu.bar', m.UnusedImport)
|
|
|
|
|
|
def test_duplicateSubmoduleImport():
|
|
"""If a submodule of a package is imported twice, an unused
|
|
|
|
import warning and a redefined while unused warning are reported.
|
|
|
|
"""
|
|
flakes('''
|
|
import fu.bar, fu.bar
|
|
fu.bar
|
|
''', m.RedefinedWhileUnused)
|
|
flakes('''
|
|
import fu.bar
|
|
import fu.bar
|
|
fu.bar
|
|
''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_differentSubmoduleImport():
|
|
"""If two different submodules of a package are imported,
|
|
|
|
no duplicate import warning is reported for the package.
|
|
|
|
"""
|
|
flakes('''
|
|
import fu.bar, fu.baz
|
|
fu.bar, fu.baz
|
|
''')
|
|
flakes('''
|
|
import fu.bar
|
|
import fu.baz
|
|
fu.bar, fu.baz
|
|
''')
|
|
|
|
|
|
def test_assignRHSFirst():
|
|
flakes('import fu; fu = fu')
|
|
flakes('import fu; fu, bar = fu')
|
|
flakes('import fu; [fu, bar] = fu')
|
|
flakes('import fu; fu += fu')
|
|
|
|
|
|
def test_tryingMultipleImports():
|
|
flakes('''
|
|
try:
|
|
import fu
|
|
except ImportError:
|
|
import bar as fu
|
|
fu
|
|
''')
|
|
|
|
|
|
def test_nonGlobalDoesNotRedefine():
|
|
flakes('''
|
|
import fu
|
|
def a():
|
|
fu = 3
|
|
return fu
|
|
fu
|
|
''')
|
|
|
|
|
|
def test_functionsRunLater():
|
|
flakes('''
|
|
def a():
|
|
fu
|
|
import fu
|
|
''')
|
|
|
|
|
|
def test_functionNamesAreBoundNow():
|
|
flakes('''
|
|
import fu
|
|
def fu():
|
|
fu
|
|
fu
|
|
''', m.RedefinedWhileUnused)
|
|
|
|
|
|
def test_ignoreNonImportRedefinitions():
|
|
flakes('a = 1; a = 2')
|
|
|
|
|
|
@pytest.mark.skipif("'todo'")
|
|
def test_importingForImportError():
|
|
flakes('''
|
|
try:
|
|
import fu
|
|
except ImportError:
|
|
pass
|
|
''')
|
|
|
|
|
|
@pytest.mark.skipif("'todo: requires evaluating attribute access'")
|
|
def test_importedInClass():
|
|
"""Imports in class scope can be used through."""
|
|
flakes('''
|
|
class c:
|
|
import i
|
|
def __init__():
|
|
i
|
|
''')
|
|
|
|
|
|
def test_futureImport():
|
|
"""__future__ is special."""
|
|
flakes('from __future__ import division')
|
|
flakes('''
|
|
"docstring is allowed before future import"
|
|
from __future__ import division
|
|
''')
|
|
|
|
|
|
def test_futureImportFirst():
|
|
"""__future__ imports must come before anything else."""
|
|
flakes('''
|
|
x = 5
|
|
from __future__ import division
|
|
''', m.LateFutureImport)
|
|
flakes('''
|
|
from foo import bar
|
|
from __future__ import division
|
|
bar
|
|
''', m.LateFutureImport)
|
|
|
|
|
|
def test_ignoredInFunction():
|
|
"""An C{__all__} definition does not suppress unused import warnings in a function scope."""
|
|
flakes('''
|
|
def foo():
|
|
import bar
|
|
__all__ = ["bar"]
|
|
''', m.UnusedImport, m.UnusedVariable)
|
|
|
|
|
|
def test_ignoredInClass():
|
|
"""An C{__all__} definition does not suppress unused import warnings in a class scope."""
|
|
flakes('''
|
|
class foo:
|
|
import bar
|
|
__all__ = ["bar"]
|
|
''', m.UnusedImport)
|
|
|
|
|
|
def test_warningSuppressed():
|
|
"""If a name is imported and unused but is named in C{__all__}, no warning is reported."""
|
|
flakes('''
|
|
import foo
|
|
__all__ = ["foo"]
|
|
''')
|
|
|
|
|
|
def test_unrecognizable():
|
|
"""If C{__all__} is defined in a way that can't be recognized statically, it is ignored."""
|
|
flakes('''
|
|
import foo
|
|
__all__ = ["f" + "oo"]
|
|
''', m.UnusedImport)
|
|
flakes('''
|
|
import foo
|
|
__all__ = [] + ["foo"]
|
|
''', m.UnusedImport)
|
|
|
|
|
|
def test_unboundExported():
|
|
"""If C{__all__} includes a name which is not bound, a warning is emitted."""
|
|
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"]:
|
|
flakes('''
|
|
__all__ = ["foo"]
|
|
''', filename=filename, **{'ignore_frosted_errors_for___init__.py': ['E101', 'E103']})
|
|
|
|
|
|
def test_importStarExported():
|
|
"""Do not report undefined if import * is used"""
|
|
flakes('''
|
|
from foolib import *
|
|
__all__ = ["foo"]
|
|
''', m.ImportStarUsed)
|
|
|
|
|
|
def test_usedInGenExp():
|
|
"""Using a global in a generator expression results in no warnings."""
|
|
flakes('import fu; (fu for _ in range(1))')
|
|
flakes('import fu; (1 for _ in range(1) if fu)')
|
|
|
|
|
|
def test_redefinedByGenExp():
|
|
""" Re-using a global name as the loop variable for a generator
|
|
|
|
expression results in a redefinition warning.
|
|
|
|
"""
|
|
flakes('import fu; (1 for fu in range(1))', m.RedefinedWhileUnused, m.UnusedImport)
|
|
|
|
|
|
def test_usedAsDecorator():
|
|
"""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.
|
|
|
|
"""
|
|
flakes('''
|
|
from interior import decorate
|
|
@decorate
|
|
def f():
|
|
return "hello"
|
|
''')
|
|
|
|
flakes('''
|
|
from interior import decorate
|
|
@decorate('value')
|
|
def f():
|
|
return "hello"
|
|
''')
|
|
|
|
flakes('''
|
|
@decorate
|
|
def f():
|
|
return "hello"
|
|
''', m.UndefinedName)
|
|
|
|
|
|
def test_usedAsClassDecorator():
|
|
"""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.
|
|
|
|
"""
|
|
flakes('''
|
|
from interior import decorate
|
|
@decorate
|
|
class foo:
|
|
pass
|
|
''')
|
|
|
|
flakes('''
|
|
from interior import decorate
|
|
@decorate("foo")
|
|
class bar:
|
|
pass
|
|
''')
|
|
|
|
flakes('''
|
|
@decorate
|
|
class foo:
|
|
pass
|
|
''', m.UndefinedName)
|