| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819 |
- import ast
- from pyflakes import messages as m, checker
- from pyflakes.test.harness import TestCase, skip
- class Test(TestCase):
- 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_undefinedInListComp(self):
- self.flakes('''
- [a for a in range(10)]
- a
- ''',
- m.UndefinedName)
- def test_undefinedExceptionName(self):
- """Exception names can't be used after the except: block.
- The exc variable is unused inside the exception handler."""
- self.flakes('''
- try:
- raise ValueError('ve')
- except ValueError as exc:
- pass
- exc
- ''', m.UndefinedName, m.UnusedVariable)
- def test_namesDeclaredInExceptBlocks(self):
- """Locals declared in except: blocks can be used after the block.
- This shows the example in test_undefinedExceptionName is
- different."""
- self.flakes('''
- try:
- raise ValueError('ve')
- except ValueError as exc:
- e = exc
- e
- ''')
- @skip('error reporting disabled due to false positives below')
- def test_undefinedExceptionNameObscuringLocalVariable(self):
- """Exception names obscure locals, can't be used after.
- Last line will raise UnboundLocalError on Python 3 after exiting
- the except: block. Note next two examples for false positives to
- watch out for."""
- self.flakes('''
- exc = 'Original value'
- try:
- raise ValueError('ve')
- except ValueError as exc:
- pass
- exc
- ''',
- m.UndefinedName)
- def test_undefinedExceptionNameObscuringLocalVariable2(self):
- """Exception names are unbound after the `except:` block.
- Last line will raise UnboundLocalError.
- The exc variable is unused inside the exception handler.
- """
- self.flakes('''
- try:
- raise ValueError('ve')
- except ValueError as exc:
- pass
- print(exc)
- exc = 'Original value'
- ''', m.UndefinedName, m.UnusedVariable)
- def test_undefinedExceptionNameObscuringLocalVariableFalsePositive1(self):
- """Exception names obscure locals, can't be used after. Unless.
- Last line will never raise UnboundLocalError because it's only
- entered if no exception was raised."""
- self.flakes('''
- exc = 'Original value'
- try:
- raise ValueError('ve')
- except ValueError as exc:
- print('exception logged')
- raise
- exc
- ''', m.UnusedVariable)
- def test_delExceptionInExcept(self):
- """The exception name can be deleted in the except: block."""
- self.flakes('''
- try:
- pass
- except Exception as exc:
- del exc
- ''')
- def test_undefinedExceptionNameObscuringLocalVariableFalsePositive2(self):
- """Exception names obscure locals, can't be used after. Unless.
- Last line will never raise UnboundLocalError because `error` is
- only falsy if the `except:` block has not been entered."""
- self.flakes('''
- exc = 'Original value'
- error = None
- try:
- raise ValueError('ve')
- except ValueError as exc:
- error = 'exception logged'
- if error:
- print(error)
- else:
- exc
- ''', m.UnusedVariable)
- @skip('error reporting disabled due to false positives below')
- def test_undefinedExceptionNameObscuringGlobalVariable(self):
- """Exception names obscure globals, can't be used after.
- Last line will raise UnboundLocalError because the existence of that
- exception name creates a local scope placeholder for it, obscuring any
- globals, etc."""
- self.flakes('''
- exc = 'Original value'
- def func():
- try:
- pass # nothing is raised
- except ValueError as exc:
- pass # block never entered, exc stays unbound
- exc
- ''',
- m.UndefinedLocal)
- @skip('error reporting disabled due to false positives below')
- def test_undefinedExceptionNameObscuringGlobalVariable2(self):
- """Exception names obscure globals, can't be used after.
- Last line will raise NameError on Python 3 because the name is
- locally unbound after the `except:` block, even if it's
- nonlocal. We should issue an error in this case because code
- only working correctly if an exception isn't raised, is invalid.
- Unless it's explicitly silenced, see false positives below."""
- self.flakes('''
- exc = 'Original value'
- def func():
- global exc
- try:
- raise ValueError('ve')
- except ValueError as exc:
- pass # block never entered, exc stays unbound
- exc
- ''',
- m.UndefinedLocal)
- def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive1(self):
- """Exception names obscure globals, can't be used after. Unless.
- Last line will never raise NameError because it's only entered
- if no exception was raised."""
- self.flakes('''
- exc = 'Original value'
- def func():
- global exc
- try:
- raise ValueError('ve')
- except ValueError as exc:
- print('exception logged')
- raise
- exc
- ''', m.UnusedVariable)
- def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive2(self):
- """Exception names obscure globals, can't be used after. Unless.
- Last line will never raise NameError because `error` is only
- falsy if the `except:` block has not been entered."""
- self.flakes('''
- exc = 'Original value'
- def func():
- global exc
- error = None
- try:
- raise ValueError('ve')
- except ValueError as exc:
- error = 'exception logged'
- if error:
- print(error)
- else:
- exc
- ''', m.UnusedVariable)
- def test_functionsNeedGlobalScope(self):
- self.flakes('''
- class a:
- def b():
- fu
- fu = 1
- ''')
- def test_builtins(self):
- self.flakes('range(10)')
- def test_builtinWindowsError(self):
- """
- C{WindowsError} is sometimes a builtin name, so no warning is emitted
- for using it.
- """
- self.flakes('WindowsError')
- def test_moduleAnnotations(self):
- """
- Use of the C{__annotations__} in module scope should not emit
- an undefined name warning when version is greater than or equal to 3.6.
- """
- self.flakes('__annotations__')
- 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_magicModuleInClassScope(self):
- """
- Use of the C{__module__} magic builtin should not emit an undefined
- name warning if used in class scope.
- """
- self.flakes('__module__', m.UndefinedName)
- self.flakes('''
- class Foo:
- __module__
- ''')
- self.flakes('''
- class Foo:
- def bar(self):
- __module__
- ''', m.UndefinedName)
- def test_magicQualnameInClassScope(self):
- """
- Use of the C{__qualname__} magic builtin should not emit an undefined
- name warning if used in class scope.
- """
- self.flakes('__qualname__', m.UndefinedName)
- self.flakes('''
- class Foo:
- __qualname__
- ''')
- self.flakes('''
- class Foo:
- def bar(self):
- __qualname__
- ''', m.UndefinedName)
- def test_globalImportStar(self):
- """Can't find undefined names with import *."""
- self.flakes('from fu import *; bar',
- m.ImportStarUsed, m.ImportStarUsage)
- 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
- ''')
- self.flakes('''
- def c(): bar
- def b(): global bar; bar = 1
- ''')
- def test_definedByGlobalMultipleNames(self):
- """
- "global" can accept multiple names.
- """
- self.flakes('''
- def a(): global fu, bar; fu = 1; bar = 2
- def b(): fu; bar
- ''')
- 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_global_reset_name_only(self):
- """A global statement does not prevent other names being undefined."""
- # Only different undefined names are reported.
- # See following test that fails where the same name is used.
- self.flakes('''
- def f1():
- s
- def f2():
- global m
- ''', m.UndefinedName)
- @skip("todo")
- def test_unused_global(self):
- """An unused global statement does not define the name."""
- self.flakes('''
- def f1():
- m
- def f2():
- global m
- ''', 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_delConditional(self):
- """
- Ignores conditional bindings deletion.
- """
- self.flakes('''
- context = None
- test = True
- if False:
- del(test)
- assert(test)
- ''')
- def test_delConditionalNested(self):
- """
- Ignored conditional bindings deletion even if they are nested in other
- blocks.
- """
- self.flakes('''
- context = None
- test = True
- if False:
- with context():
- del(test)
- assert(test)
- ''')
- def test_delWhile(self):
- """
- Ignore bindings deletion if called inside the body of a while
- statement.
- """
- self.flakes('''
- def test():
- foo = 'bar'
- while False:
- del foo
- assert(foo)
- ''')
- def test_delWhileTestUsage(self):
- """
- Ignore bindings deletion if called inside the body of a while
- statement and name is used inside while's test part.
- """
- self.flakes('''
- def _worker():
- o = True
- while o is not True:
- del o
- o = False
- ''')
- def test_delWhileNested(self):
- """
- Ignore bindings deletions if node is part of while's test, even when
- del is in a nested block.
- """
- self.flakes('''
- context = None
- def _worker():
- o = True
- while o is not True:
- while True:
- with context():
- del o
- o = False
- ''')
- 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]
- # _DoctestMixin.flakes adds two lines preceding the code above.
- expected_line_num = 7 if self.withDoctest else 5
- self.assertEqual(exc.message_args, ('x', expected_line_num))
- 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_undefinedAugmentedAssignment(self):
- self.flakes(
- '''
- def f(seq):
- a = 0
- seq[a] += 1
- seq[b] /= 2
- c[0] *= 2
- a -= 3
- d += 4
- e[any] = 5
- ''',
- m.UndefinedName, # b
- m.UndefinedName, # c
- m.UndefinedName, m.UnusedVariable, # d
- m.UndefinedName, # e
- )
- 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_definedAsStarUnpack(self):
- """Star names in unpack are defined."""
- self.flakes('''
- a, *b = range(10)
- print(a, b)
- ''')
- self.flakes('''
- *a, b = range(10)
- print(a, b)
- ''')
- self.flakes('''
- a, *b, c = range(10)
- print(a, b, c)
- ''')
- def test_usedAsStarUnpack(self):
- """
- Star names in unpack are used if RHS is not a tuple/list literal.
- """
- self.flakes('''
- def f():
- a, *b = range(10)
- ''')
- self.flakes('''
- def f():
- (*a, b) = range(10)
- ''')
- self.flakes('''
- def f():
- [a, *b, c] = range(10)
- ''')
- def test_unusedAsStarUnpack(self):
- """
- Star names in unpack are unused if RHS is a tuple/list literal.
- """
- self.flakes('''
- def f():
- a, *b = any, all, 4, 2, 'un'
- ''', m.UnusedVariable, m.UnusedVariable)
- self.flakes('''
- def f():
- (*a, b) = [bool, int, float, complex]
- ''', m.UnusedVariable, m.UnusedVariable)
- self.flakes('''
- def f():
- [a, *b, c] = 9, 8, 7, 6, 5, 4
- ''', m.UnusedVariable, m.UnusedVariable, m.UnusedVariable)
- def test_keywordOnlyArgs(self):
- """Keyword-only arg names are defined."""
- self.flakes('''
- def f(*, a, b=None):
- print(a, b)
- ''')
- self.flakes('''
- import default_b
- def f(*, a, b=default_b):
- print(a, b)
- ''')
- def test_keywordOnlyArgsUndefined(self):
- """Typo in kwonly name."""
- self.flakes('''
- def f(*, a, b=default_c):
- print(a, b)
- ''', m.UndefinedName)
- def test_annotationUndefined(self):
- """Undefined annotations."""
- self.flakes('''
- from abc import note1, note2, note3, note4, note5
- def func(a: note1, *args: note2,
- b: note3=12, **kw: note4) -> note5: pass
- ''')
- self.flakes('''
- def func():
- d = e = 42
- def func(a: {1, d}) -> (lambda c: e): pass
- ''')
- def test_metaClassUndefined(self):
- self.flakes('''
- from abc import ABCMeta
- class A(metaclass=ABCMeta): pass
- ''')
- def test_definedInGenExp(self):
- """
- Using the loop variable of a generator expression results in no
- warnings.
- """
- self.flakes('(a for a in [1, 2, 3] if a)')
- self.flakes('(b for b in (a for a in [1, 2, 3] if a) if b)')
- def test_undefinedInGenExpNested(self):
- """
- The loop variables of generator expressions nested together are
- not defined in the other generator.
- """
- self.flakes('(b for b in (a for a in [1, 2, 3] if b) if b)',
- m.UndefinedName)
- self.flakes('(b for b in (a for a in [1, 2, 3] if a) if a)',
- m.UndefinedName)
- def test_undefinedWithErrorHandler(self):
- """
- Some compatibility code checks explicitly for NameError.
- It should not trigger warnings.
- """
- self.flakes('''
- try:
- socket_map
- except NameError:
- socket_map = {}
- ''')
- self.flakes('''
- try:
- _memoryview.contiguous
- except (NameError, AttributeError):
- raise RuntimeError("Python >= 3.3 is required")
- ''')
- # If NameError is not explicitly handled, generate a warning
- self.flakes('''
- try:
- socket_map
- except:
- socket_map = {}
- ''', m.UndefinedName)
- self.flakes('''
- try:
- socket_map
- except Exception:
- socket_map = {}
- ''', m.UndefinedName)
- def test_definedInClass(self):
- """
- Defined name for generator expressions and dict/set comprehension.
- """
- self.flakes('''
- class A:
- T = range(10)
- Z = (x for x in T)
- L = [x for x in T]
- B = dict((i, str(i)) for i in T)
- ''')
- self.flakes('''
- class A:
- T = range(10)
- X = {x for x in T}
- Y = {x:x for x in T}
- ''')
- def test_definedInClassNested(self):
- """Defined name for nested generator expressions in a class."""
- self.flakes('''
- class A:
- T = range(10)
- Z = (x for x in (a for a in T))
- ''')
- def test_undefinedInLoop(self):
- """
- The loop variable is defined after the expression is computed.
- """
- self.flakes('''
- for i in range(i):
- print(i)
- ''', m.UndefinedName)
- self.flakes('''
- [42 for i in range(i)]
- ''', m.UndefinedName)
- self.flakes('''
- (42 for i in range(i))
- ''', m.UndefinedName)
- def test_definedFromLambdaInDictionaryComprehension(self):
- """
- Defined name referenced from a lambda function within a dict/set
- comprehension.
- """
- self.flakes('''
- {lambda: id(x) for x in range(10)}
- ''')
- def test_definedFromLambdaInGenerator(self):
- """
- Defined name referenced from a lambda function within a generator
- expression.
- """
- self.flakes('''
- any(lambda: id(x) for x in range(10))
- ''')
- def test_undefinedFromLambdaInDictionaryComprehension(self):
- """
- Undefined name referenced from a lambda function within a dict/set
- comprehension.
- """
- self.flakes('''
- {lambda: id(y) for x in range(10)}
- ''', m.UndefinedName)
- def test_undefinedFromLambdaInComprehension(self):
- """
- Undefined name referenced from a lambda function within a generator
- expression.
- """
- self.flakes('''
- any(lambda: id(y) for x in range(10))
- ''', m.UndefinedName)
- def test_dunderClass(self):
- code = '''
- class Test(object):
- def __init__(self):
- print(__class__.__name__)
- self.x = 1
- t = Test()
- '''
- self.flakes(code)
- 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 = ast.parse("x = 10")
- # Make it into something unrecognizable.
- tree.body[0].targets[0].ctx = object()
- self.assertRaises(RuntimeError, checker.Checker, tree)
|