├── pyflakes ├── scripts │ ├── __init__.py │ └── pyflakes.py ├── test │ ├── __init__.py │ ├── harness.py │ ├── test_script.py │ ├── test_undefined_names.py │ ├── test_other.py │ └── test_imports.py ├── __init__.py ├── messages.py └── checker.py ├── .gitignore ├── README.md ├── bin └── pyflakes ├── setup.py ├── LICENSE └── NEWS.txt /pyflakes/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyflakes/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyflakes/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | __version__ = '0.4.0' 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .svn 3 | _trial_temp/ 4 | _trial_temp* 5 | build/ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyflakes has [moved](https://github.com/pyflakes/pyflakes). 301 y'all 2 | -------------------------------------------------------------------------------- /bin/pyflakes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pyflakes.scripts.pyflakes import main 4 | main() 5 | -------------------------------------------------------------------------------- /pyflakes/test/harness.py: -------------------------------------------------------------------------------- 1 | 2 | import textwrap 3 | import _ast 4 | 5 | from twisted.trial import unittest 6 | 7 | from pyflakes import checker 8 | 9 | 10 | class Test(unittest.TestCase): 11 | 12 | def flakes(self, input, *expectedOutputs, **kw): 13 | ast = compile(textwrap.dedent(input), "", "exec", 14 | _ast.PyCF_ONLY_AST) 15 | w = checker.Checker(ast, **kw) 16 | outputs = [type(o) for o in w.messages] 17 | expectedOutputs = list(expectedOutputs) 18 | outputs.sort() 19 | expectedOutputs.sort() 20 | self.assert_(outputs == expectedOutputs, '''\ 21 | for input: 22 | %s 23 | expected outputs: 24 | %s 25 | but got: 26 | %s''' % (input, repr(expectedOutputs), '\n'.join([str(o) for o in w.messages]))) 27 | return w 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # (c) 2005-2009 Divmod, Inc. See LICENSE file for details 3 | 4 | from distutils.core import setup 5 | 6 | setup( 7 | name="pyflakes", 8 | license="MIT", 9 | version="0.4.0", 10 | description="passive checker of Python programs", 11 | author="Phil Frost", 12 | maintainer="Moe Aboulkheir", 13 | maintainer_email="moe@divmod.com", 14 | url="http://www.divmod.org/trac/wiki/DivmodPyflakes", 15 | packages=["pyflakes", "pyflakes.scripts", "pyflakes.test"], 16 | scripts=["bin/pyflakes"], 17 | long_description="""Pyflakes is program to analyze Python programs and detect various errors. It 18 | works by parsing the source file, not importing it, so it is safe to use on 19 | modules with side effects. It's also much faster.""", 20 | classifiers=[ 21 | "Development Status :: 6 - Mature", 22 | "Environment :: Console", 23 | "Intended Audience :: Developers", 24 | "License :: OSI Approved :: MIT License", 25 | "Programming Language :: Python", 26 | "Topic :: Software Development", 27 | "Topic :: Utilities", 28 | ]) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2005 Divmod, Inc., http://www.divmod.com/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /NEWS.txt: -------------------------------------------------------------------------------- 1 | 0.4.1 (xxx): 2 | - Don't issue Unused Variable warning when using locals() in current scope 3 | 4 | 0.4.0 (2009-11-25): 5 | - Fix reporting for certain SyntaxErrors which lack line number 6 | information. 7 | - Check for syntax errors more rigorously. 8 | - Support checking names used with the class decorator syntax in versions 9 | of Python which have it. 10 | - Detect local variables which are bound but never used. 11 | - Handle permission errors when trying to read source files. 12 | - Handle problems with the encoding of source files. 13 | - Support importing dotted names so as not to incorrectly report them as 14 | redefined unused names. 15 | - Support all forms of the with statement. 16 | - Consider static `__all__` definitions and avoid reporting unused names 17 | if the names are listed there. 18 | - Fix incorrect checking of class names with respect to the names of their 19 | bases in the class statement. 20 | - Support the `__path__` global in `__init__.py`. 21 | 22 | 0.3.0 (2009-01-30): 23 | - Display more informative SyntaxError messages. 24 | - Don't hang flymake with unmatched triple quotes (only report a single 25 | line of source for a multiline syntax error). 26 | - Recognize __builtins__ as a defined name. 27 | - Improve pyflakes support for python versions 2.3-2.5 28 | - Support for if-else expressions and with statements. 29 | - Warn instead of error on non-existant file paths. 30 | - Check for __future__ imports after other statements. 31 | - Add reporting for some types of import shadowing. 32 | - Improve reporting of unbound locals 33 | -------------------------------------------------------------------------------- /pyflakes/scripts/pyflakes.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Implementation of the command-line I{pyflakes} tool. 4 | """ 5 | 6 | import sys 7 | import os 8 | import _ast 9 | 10 | checker = __import__('pyflakes.checker').checker 11 | 12 | def check(codeString, filename): 13 | """ 14 | Check the Python source given by C{codeString} for flakes. 15 | 16 | @param codeString: The Python source to check. 17 | @type codeString: C{str} 18 | 19 | @param filename: The name of the file the source came from, used to report 20 | errors. 21 | @type filename: C{str} 22 | 23 | @return: The number of warnings emitted. 24 | @rtype: C{int} 25 | """ 26 | # First, compile into an AST and handle syntax errors. 27 | try: 28 | tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST) 29 | except SyntaxError, value: 30 | msg = value.args[0] 31 | 32 | (lineno, offset, text) = value.lineno, value.offset, value.text 33 | 34 | # If there's an encoding problem with the file, the text is None. 35 | if text is None: 36 | # Avoid using msg, since for the only known case, it contains a 37 | # bogus message that claims the encoding the file declared was 38 | # unknown. 39 | print >> sys.stderr, "%s: problem decoding source" % (filename, ) 40 | else: 41 | line = text.splitlines()[-1] 42 | 43 | if offset is not None: 44 | offset = offset - (len(text) - len(line)) 45 | 46 | print >> sys.stderr, '%s:%d: %s' % (filename, lineno, msg) 47 | print >> sys.stderr, line 48 | 49 | if offset is not None: 50 | print >> sys.stderr, " " * offset, "^" 51 | 52 | return 1 53 | else: 54 | # Okay, it's syntactically valid. Now check it. 55 | w = checker.Checker(tree, filename) 56 | w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) 57 | for warning in w.messages: 58 | print warning 59 | return len(w.messages) 60 | 61 | 62 | def checkPath(filename): 63 | """ 64 | Check the given path, printing out any warnings detected. 65 | 66 | @return: the number of warnings printed 67 | """ 68 | try: 69 | return check(file(filename, 'U').read() + '\n', filename) 70 | except IOError, msg: 71 | print >> sys.stderr, "%s: %s" % (filename, msg.args[1]) 72 | return 1 73 | 74 | 75 | def main(): 76 | warnings = 0 77 | args = sys.argv[1:] 78 | if args: 79 | for arg in args: 80 | if os.path.isdir(arg): 81 | for dirpath, dirnames, filenames in os.walk(arg): 82 | for filename in filenames: 83 | if filename.endswith('.py'): 84 | warnings += checkPath(os.path.join(dirpath, filename)) 85 | else: 86 | warnings += checkPath(arg) 87 | else: 88 | warnings += check(sys.stdin.read(), '') 89 | 90 | raise SystemExit(warnings > 0) 91 | -------------------------------------------------------------------------------- /pyflakes/messages.py: -------------------------------------------------------------------------------- 1 | # (c) 2005 Divmod, Inc. See LICENSE file for details 2 | 3 | class Message(object): 4 | message = '' 5 | message_args = () 6 | def __init__(self, filename, loc, use_column=True): 7 | self.filename = filename 8 | self.lineno = loc.lineno 9 | self.col = getattr(loc, 'col_offset', None) if use_column else None 10 | 11 | def __str__(self): 12 | return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args) 13 | 14 | 15 | class UnusedImport(Message): 16 | message = '%r imported but unused' 17 | def __init__(self, filename, loc, name): 18 | Message.__init__(self, filename, loc, use_column=False) 19 | self.message_args = (name,) 20 | 21 | 22 | class RedefinedWhileUnused(Message): 23 | message = 'redefinition of unused %r from line %r' 24 | def __init__(self, filename, loc, name, orig_loc): 25 | Message.__init__(self, filename, loc) 26 | self.message_args = (name, orig_loc.lineno) 27 | 28 | 29 | class ImportShadowedByLoopVar(Message): 30 | message = 'import %r from line %r shadowed by loop variable' 31 | def __init__(self, filename, loc, name, orig_loc): 32 | Message.__init__(self, filename, loc) 33 | self.message_args = (name, orig_loc.lineno) 34 | 35 | 36 | class ImportStarUsed(Message): 37 | message = "'from %s import *' used; unable to detect undefined names" 38 | def __init__(self, filename, loc, modname): 39 | Message.__init__(self, filename, loc) 40 | self.message_args = (modname,) 41 | 42 | 43 | class UndefinedName(Message): 44 | message = 'undefined name %r' 45 | def __init__(self, filename, loc, name): 46 | Message.__init__(self, filename, loc) 47 | self.message_args = (name,) 48 | 49 | 50 | 51 | class UndefinedExport(Message): 52 | message = 'undefined name %r in __all__' 53 | def __init__(self, filename, loc, name): 54 | Message.__init__(self, filename, loc) 55 | self.message_args = (name,) 56 | 57 | 58 | 59 | class UndefinedLocal(Message): 60 | message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment" 61 | def __init__(self, filename, loc, name, orig_loc): 62 | Message.__init__(self, filename, loc) 63 | self.message_args = (name, orig_loc.lineno) 64 | 65 | 66 | class DuplicateArgument(Message): 67 | message = 'duplicate argument %r in function definition' 68 | def __init__(self, filename, loc, name): 69 | Message.__init__(self, filename, loc) 70 | self.message_args = (name,) 71 | 72 | 73 | class RedefinedFunction(Message): 74 | message = 'redefinition of function %r from line %r' 75 | def __init__(self, filename, loc, name, orig_loc): 76 | Message.__init__(self, filename, loc) 77 | self.message_args = (name, orig_loc.lineno) 78 | 79 | 80 | class LateFutureImport(Message): 81 | message = 'future import(s) %r after other statements' 82 | def __init__(self, filename, loc, names): 83 | Message.__init__(self, filename, loc) 84 | self.message_args = (names,) 85 | 86 | 87 | class UnusedVariable(Message): 88 | """ 89 | Indicates that a variable has been explicity assigned to but not actually 90 | used. 91 | """ 92 | 93 | message = 'local variable %r is assigned to but never used' 94 | def __init__(self, filename, loc, names): 95 | Message.__init__(self, filename, loc) 96 | self.message_args = (names,) 97 | -------------------------------------------------------------------------------- /pyflakes/test/test_script.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Tests for L{pyflakes.scripts.pyflakes}. 4 | """ 5 | 6 | import sys 7 | from StringIO import StringIO 8 | 9 | from twisted.python.filepath import FilePath 10 | from twisted.trial.unittest import TestCase 11 | 12 | from pyflakes.scripts.pyflakes import checkPath 13 | 14 | def withStderrTo(stderr, f): 15 | """ 16 | Call C{f} with C{sys.stderr} redirected to C{stderr}. 17 | """ 18 | (outer, sys.stderr) = (sys.stderr, stderr) 19 | try: 20 | return f() 21 | finally: 22 | sys.stderr = outer 23 | 24 | 25 | 26 | class CheckTests(TestCase): 27 | """ 28 | Tests for L{check} and L{checkPath} which check a file for flakes. 29 | """ 30 | def test_missingTrailingNewline(self): 31 | """ 32 | Source which doesn't end with a newline shouldn't cause any 33 | exception to be raised nor an error indicator to be returned by 34 | L{check}. 35 | """ 36 | fName = self.mktemp() 37 | FilePath(fName).setContent("def foo():\n\tpass\n\t") 38 | self.assertFalse(checkPath(fName)) 39 | 40 | 41 | def test_checkPathNonExisting(self): 42 | """ 43 | L{checkPath} handles non-existing files. 44 | """ 45 | err = StringIO() 46 | count = withStderrTo(err, lambda: checkPath('extremo')) 47 | self.assertEquals(err.getvalue(), 'extremo: No such file or directory\n') 48 | self.assertEquals(count, 1) 49 | 50 | 51 | def test_multilineSyntaxError(self): 52 | """ 53 | Source which includes a syntax error which results in the raised 54 | L{SyntaxError.text} containing multiple lines of source are reported 55 | with only the last line of that source. 56 | """ 57 | source = """\ 58 | def foo(): 59 | ''' 60 | 61 | def bar(): 62 | pass 63 | 64 | def baz(): 65 | '''quux''' 66 | """ 67 | 68 | # Sanity check - SyntaxError.text should be multiple lines, if it 69 | # isn't, something this test was unprepared for has happened. 70 | def evaluate(source): 71 | exec source 72 | exc = self.assertRaises(SyntaxError, evaluate, source) 73 | self.assertTrue(exc.text.count('\n') > 1) 74 | 75 | sourcePath = FilePath(self.mktemp()) 76 | sourcePath.setContent(source) 77 | err = StringIO() 78 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 79 | self.assertEqual(count, 1) 80 | 81 | self.assertEqual( 82 | err.getvalue(), 83 | """\ 84 | %s:8: invalid syntax 85 | '''quux''' 86 | ^ 87 | """ % (sourcePath.path,)) 88 | 89 | 90 | def test_eofSyntaxError(self): 91 | """ 92 | The error reported for source files which end prematurely causing a 93 | syntax error reflects the cause for the syntax error. 94 | """ 95 | source = "def foo(" 96 | sourcePath = FilePath(self.mktemp()) 97 | sourcePath.setContent(source) 98 | err = StringIO() 99 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 100 | self.assertEqual(count, 1) 101 | self.assertEqual( 102 | err.getvalue(), 103 | """\ 104 | %s:1: unexpected EOF while parsing 105 | def foo( 106 | ^ 107 | """ % (sourcePath.path,)) 108 | 109 | 110 | def test_nonDefaultFollowsDefaultSyntaxError(self): 111 | """ 112 | Source which has a non-default argument following a default argument 113 | should include the line number of the syntax error. However these 114 | exceptions do not include an offset. 115 | """ 116 | source = """\ 117 | def foo(bar=baz, bax): 118 | pass 119 | """ 120 | sourcePath = FilePath(self.mktemp()) 121 | sourcePath.setContent(source) 122 | err = StringIO() 123 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 124 | self.assertEqual(count, 1) 125 | self.assertEqual( 126 | err.getvalue(), 127 | """\ 128 | %s:1: non-default argument follows default argument 129 | def foo(bar=baz, bax): 130 | """ % (sourcePath.path,)) 131 | 132 | 133 | def test_nonKeywordAfterKeywordSyntaxError(self): 134 | """ 135 | Source which has a non-keyword argument after a keyword argument should 136 | include the line number of the syntax error. However these exceptions 137 | do not include an offset. 138 | """ 139 | source = """\ 140 | foo(bar=baz, bax) 141 | """ 142 | sourcePath = FilePath(self.mktemp()) 143 | sourcePath.setContent(source) 144 | err = StringIO() 145 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 146 | self.assertEqual(count, 1) 147 | self.assertEqual( 148 | err.getvalue(), 149 | """\ 150 | %s:1: non-keyword arg after keyword arg 151 | foo(bar=baz, bax) 152 | """ % (sourcePath.path,)) 153 | 154 | 155 | def test_permissionDenied(self): 156 | """ 157 | If the a source file is not readable, this is reported on standard 158 | error. 159 | """ 160 | sourcePath = FilePath(self.mktemp()) 161 | sourcePath.setContent('') 162 | sourcePath.chmod(0) 163 | err = StringIO() 164 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 165 | self.assertEquals(count, 1) 166 | self.assertEquals( 167 | err.getvalue(), "%s: Permission denied\n" % (sourcePath.path,)) 168 | 169 | 170 | def test_misencodedFile(self): 171 | """ 172 | If a source file contains bytes which cannot be decoded, this is 173 | reported on stderr. 174 | """ 175 | source = u"""\ 176 | # coding: ascii 177 | x = "\N{SNOWMAN}" 178 | """.encode('utf-8') 179 | sourcePath = FilePath(self.mktemp()) 180 | sourcePath.setContent(source) 181 | err = StringIO() 182 | count = withStderrTo(err, lambda: checkPath(sourcePath.path)) 183 | self.assertEquals(count, 1) 184 | self.assertEquals( 185 | err.getvalue(), "%s: problem decoding source\n" % (sourcePath.path,)) 186 | -------------------------------------------------------------------------------- /pyflakes/test/test_undefined_names.py: -------------------------------------------------------------------------------- 1 | 2 | from _ast import PyCF_ONLY_AST 3 | 4 | from twisted.trial.unittest import TestCase 5 | 6 | from pyflakes import messages as m, checker 7 | from pyflakes.test import harness 8 | 9 | 10 | class Test(harness.Test): 11 | def test_undefined(self): 12 | self.flakes('bar', m.UndefinedName) 13 | 14 | def test_definedInListComp(self): 15 | self.flakes('[a for a in range(10) if a]') 16 | 17 | 18 | def test_functionsNeedGlobalScope(self): 19 | self.flakes(''' 20 | class a: 21 | def b(): 22 | fu 23 | fu = 1 24 | ''') 25 | 26 | def test_builtins(self): 27 | self.flakes('range(10)') 28 | 29 | 30 | def test_magicGlobalsFile(self): 31 | """ 32 | Use of the C{__file__} magic global should not emit an undefined name 33 | warning. 34 | """ 35 | self.flakes('__file__') 36 | 37 | 38 | def test_magicGlobalsBuiltins(self): 39 | """ 40 | Use of the C{__builtins__} magic global should not emit an undefined 41 | name warning. 42 | """ 43 | self.flakes('__builtins__') 44 | 45 | 46 | def test_magicGlobalsName(self): 47 | """ 48 | Use of the C{__name__} magic global should not emit an undefined name 49 | warning. 50 | """ 51 | self.flakes('__name__') 52 | 53 | 54 | def test_magicGlobalsPath(self): 55 | """ 56 | Use of the C{__path__} magic global should not emit an undefined name 57 | warning, if you refer to it from a file called __init__.py. 58 | """ 59 | self.flakes('__path__', m.UndefinedName) 60 | self.flakes('__path__', filename='package/__init__.py') 61 | 62 | 63 | def test_globalImportStar(self): 64 | '''Can't find undefined names with import *''' 65 | self.flakes('from fu import *; bar', m.ImportStarUsed) 66 | 67 | def test_localImportStar(self): 68 | '''A local import * still allows undefined names to be found in upper scopes''' 69 | self.flakes(''' 70 | def a(): 71 | from fu import * 72 | bar 73 | ''', m.ImportStarUsed, m.UndefinedName) 74 | 75 | def test_unpackedParameter(self): 76 | '''Unpacked function parameters create bindings''' 77 | self.flakes(''' 78 | def a((bar, baz)): 79 | bar; baz 80 | ''') 81 | 82 | def test_definedByGlobal(self): 83 | '''"global" can make an otherwise undefined name in another function defined''' 84 | self.flakes(''' 85 | def a(): global fu; fu = 1 86 | def b(): fu 87 | ''') 88 | test_definedByGlobal.todo = '' 89 | 90 | def test_globalInGlobalScope(self): 91 | """ 92 | A global statement in the global scope is ignored. 93 | """ 94 | self.flakes(''' 95 | global x 96 | def foo(): 97 | print x 98 | ''', m.UndefinedName) 99 | 100 | def test_del(self): 101 | '''del deletes bindings''' 102 | self.flakes('a = 1; del a; a', m.UndefinedName) 103 | 104 | def test_delGlobal(self): 105 | '''del a global binding from a function''' 106 | self.flakes(''' 107 | a = 1 108 | def f(): 109 | global a 110 | del a 111 | a 112 | ''') 113 | 114 | def test_delUndefined(self): 115 | '''del an undefined name''' 116 | self.flakes('del a', m.UndefinedName) 117 | 118 | def test_globalFromNestedScope(self): 119 | '''global names are available from nested scopes''' 120 | self.flakes(''' 121 | a = 1 122 | def b(): 123 | def c(): 124 | a 125 | ''') 126 | 127 | def test_laterRedefinedGlobalFromNestedScope(self): 128 | """ 129 | Test that referencing a local name that shadows a global, before it is 130 | defined, generates a warning. 131 | """ 132 | self.flakes(''' 133 | a = 1 134 | def fun(): 135 | a 136 | a = 2 137 | return a 138 | ''', m.UndefinedLocal) 139 | 140 | def test_laterRedefinedGlobalFromNestedScope2(self): 141 | """ 142 | Test that referencing a local name in a nested scope that shadows a 143 | global declared in an enclosing scope, before it is defined, generates 144 | a warning. 145 | """ 146 | self.flakes(''' 147 | a = 1 148 | def fun(): 149 | global a 150 | def fun2(): 151 | a 152 | a = 2 153 | return a 154 | ''', m.UndefinedLocal) 155 | 156 | 157 | def test_intermediateClassScopeIgnored(self): 158 | """ 159 | If a name defined in an enclosing scope is shadowed by a local variable 160 | and the name is used locally before it is bound, an unbound local 161 | warning is emitted, even if there is a class scope between the enclosing 162 | scope and the local scope. 163 | """ 164 | self.flakes(''' 165 | def f(): 166 | x = 1 167 | class g: 168 | def h(self): 169 | a = x 170 | x = None 171 | print x, a 172 | print x 173 | ''', m.UndefinedLocal) 174 | 175 | 176 | def test_doubleNestingReportsClosestName(self): 177 | """ 178 | Test that referencing a local name in a nested scope that shadows a 179 | variable declared in two different outer scopes before it is defined 180 | in the innermost scope generates an UnboundLocal warning which 181 | refers to the nearest shadowed name. 182 | """ 183 | exc = self.flakes(''' 184 | def a(): 185 | x = 1 186 | def b(): 187 | x = 2 # line 5 188 | def c(): 189 | x 190 | x = 3 191 | return x 192 | return x 193 | return x 194 | ''', m.UndefinedLocal).messages[0] 195 | self.assertEqual(exc.message_args, ('x', 5)) 196 | 197 | 198 | def test_laterRedefinedGlobalFromNestedScope3(self): 199 | """ 200 | Test that referencing a local name in a nested scope that shadows a 201 | global, before it is defined, generates a warning. 202 | """ 203 | self.flakes(''' 204 | def fun(): 205 | a = 1 206 | def fun2(): 207 | a 208 | a = 1 209 | return a 210 | return a 211 | ''', m.UndefinedLocal) 212 | 213 | def test_nestedClass(self): 214 | '''nested classes can access enclosing scope''' 215 | self.flakes(''' 216 | def f(foo): 217 | class C: 218 | bar = foo 219 | def f(self): 220 | return foo 221 | return C() 222 | 223 | f(123).f() 224 | ''') 225 | 226 | def test_badNestedClass(self): 227 | '''free variables in nested classes must bind at class creation''' 228 | self.flakes(''' 229 | def f(): 230 | class C: 231 | bar = foo 232 | foo = 456 233 | return foo 234 | f() 235 | ''', m.UndefinedName) 236 | 237 | def test_definedAsStarArgs(self): 238 | '''star and double-star arg names are defined''' 239 | self.flakes(''' 240 | def f(a, *b, **c): 241 | print a, b, c 242 | ''') 243 | 244 | def test_definedInGenExp(self): 245 | """ 246 | Using the loop variable of a generator expression results in no 247 | warnings. 248 | """ 249 | self.flakes('(a for a in xrange(10) if a)') 250 | 251 | 252 | 253 | class NameTests(TestCase): 254 | """ 255 | Tests for some extra cases of name handling. 256 | """ 257 | def test_impossibleContext(self): 258 | """ 259 | A Name node with an unrecognized context results in a RuntimeError being 260 | raised. 261 | """ 262 | tree = compile("x = 10", "", "exec", PyCF_ONLY_AST) 263 | # Make it into something unrecognizable. 264 | tree.body[0].targets[0].ctx = object() 265 | self.assertRaises(RuntimeError, checker.Checker, tree) 266 | -------------------------------------------------------------------------------- /pyflakes/test/test_other.py: -------------------------------------------------------------------------------- 1 | # (c) 2005-2010 Divmod, Inc. 2 | # See LICENSE file for details 3 | 4 | """ 5 | Tests for various Pyflakes behavior. 6 | """ 7 | 8 | from sys import version_info 9 | 10 | from pyflakes import messages as m 11 | from pyflakes.test import harness 12 | 13 | 14 | class Test(harness.Test): 15 | 16 | def test_duplicateArgs(self): 17 | self.flakes('def fu(bar, bar): pass', m.DuplicateArgument) 18 | 19 | def test_localReferencedBeforeAssignment(self): 20 | self.flakes(''' 21 | a = 1 22 | def f(): 23 | a; a=1 24 | f() 25 | ''', m.UndefinedName) 26 | test_localReferencedBeforeAssignment.todo = 'this requires finding all assignments in the function body first' 27 | 28 | def test_redefinedFunction(self): 29 | """ 30 | Test that shadowing a function definition with another one raises a 31 | warning. 32 | """ 33 | self.flakes(''' 34 | def a(): pass 35 | def a(): pass 36 | ''', m.RedefinedFunction) 37 | 38 | def test_redefinedClassFunction(self): 39 | """ 40 | Test that shadowing a function definition in a class suite with another 41 | one raises a warning. 42 | """ 43 | self.flakes(''' 44 | class A: 45 | def a(): pass 46 | def a(): pass 47 | ''', m.RedefinedFunction) 48 | 49 | def test_redefinedIfElseFunction(self): 50 | """ 51 | Test that shadowing a function definition twice in an if 52 | and else block does not raise a warning. 53 | 54 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 55 | """ 56 | self.flakes(''' 57 | if True: 58 | def a(): pass 59 | else: 60 | def a(): pass 61 | ''') 62 | 63 | def test_redefinedIfFunction(self): 64 | """ 65 | Test that shadowing a function definition within an if block 66 | raises a warning. 67 | 68 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 69 | """ 70 | self.flakes(''' 71 | if True: 72 | def a(): pass 73 | def a(): pass 74 | ''', m.RedefinedFunction) 75 | 76 | def test_redefinedTryExceptFunction(self): 77 | """ 78 | Test that shadowing a function definition twice in try 79 | and except block does not raise a warning. 80 | 81 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 82 | """ 83 | self.flakes(''' 84 | try: 85 | def a(): pass 86 | except: 87 | def a(): pass 88 | ''') 89 | 90 | def test_redefinedTryFunction(self): 91 | """ 92 | Test that shadowing a function definition within a try block 93 | raises a warning. 94 | 95 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 96 | """ 97 | self.flakes(''' 98 | try: 99 | def a(): pass 100 | def a(): pass 101 | except: 102 | pass 103 | ''', m.RedefinedFunction) 104 | 105 | def test_functionDecorator(self): 106 | """ 107 | Test that shadowing a function definition with a decorated version of 108 | that function does not raise a warning. 109 | """ 110 | self.flakes(''' 111 | from somewhere import somedecorator 112 | 113 | def a(): pass 114 | a = somedecorator(a) 115 | ''') 116 | 117 | def test_classFunctionDecorator(self): 118 | """ 119 | Test that shadowing a function definition in a class suite with a 120 | decorated version of that function does not raise a warning. 121 | """ 122 | self.flakes(''' 123 | class A: 124 | def a(): pass 125 | a = classmethod(a) 126 | ''') 127 | 128 | def test_unaryPlus(self): 129 | '''Don't die on unary +''' 130 | self.flakes('+1') 131 | 132 | 133 | def test_undefinedBaseClass(self): 134 | """ 135 | If a name in the base list of a class definition is undefined, a 136 | warning is emitted. 137 | """ 138 | self.flakes(''' 139 | class foo(foo): 140 | pass 141 | ''', m.UndefinedName) 142 | 143 | 144 | def test_classNameUndefinedInClassBody(self): 145 | """ 146 | If a class name is used in the body of that class's definition and 147 | the name is not already defined, a warning is emitted. 148 | """ 149 | self.flakes(''' 150 | class foo: 151 | foo 152 | ''', m.UndefinedName) 153 | 154 | 155 | def test_classNameDefinedPreviously(self): 156 | """ 157 | If a class name is used in the body of that class's definition and 158 | the name was previously defined in some other way, no warning is 159 | emitted. 160 | """ 161 | self.flakes(''' 162 | foo = None 163 | class foo: 164 | foo 165 | ''') 166 | 167 | 168 | def test_comparison(self): 169 | """ 170 | If a defined name is used on either side of any of the six comparison 171 | operators, no warning is emitted. 172 | """ 173 | self.flakes(''' 174 | x = 10 175 | y = 20 176 | x < y 177 | x <= y 178 | x == y 179 | x != y 180 | x >= y 181 | x > y 182 | ''') 183 | 184 | 185 | def test_identity(self): 186 | """ 187 | If a deefined name is used on either side of an identity test, no 188 | warning is emitted. 189 | """ 190 | self.flakes(''' 191 | x = 10 192 | y = 20 193 | x is y 194 | x is not y 195 | ''') 196 | 197 | 198 | def test_containment(self): 199 | """ 200 | If a defined name is used on either side of a containment test, no 201 | warning is emitted. 202 | """ 203 | self.flakes(''' 204 | x = 10 205 | y = 20 206 | x in y 207 | x not in y 208 | ''') 209 | 210 | 211 | def test_loopControl(self): 212 | """ 213 | break and continue statements are supported. 214 | """ 215 | self.flakes(''' 216 | for x in [1, 2]: 217 | break 218 | ''') 219 | self.flakes(''' 220 | for x in [1, 2]: 221 | continue 222 | ''') 223 | 224 | 225 | def test_ellipsis(self): 226 | """ 227 | Ellipsis in a slice is supported. 228 | """ 229 | self.flakes(''' 230 | [1, 2][...] 231 | ''') 232 | 233 | 234 | def test_extendedSlice(self): 235 | """ 236 | Extended slices are supported. 237 | """ 238 | self.flakes(''' 239 | x = 3 240 | [1, 2][x,:] 241 | ''') 242 | 243 | 244 | 245 | class TestUnusedAssignment(harness.Test): 246 | """ 247 | Tests for warning about unused assignments. 248 | """ 249 | 250 | def test_unusedVariable(self): 251 | """ 252 | Warn when a variable in a function is assigned a value that's never 253 | used. 254 | """ 255 | self.flakes(''' 256 | def a(): 257 | b = 1 258 | ''', m.UnusedVariable) 259 | 260 | 261 | def test_unusedVariable_asLocals(self): 262 | """ 263 | Using locals() it is perfectly valid to have unused variables 264 | """ 265 | self.flakes(''' 266 | def a(): 267 | b = 1 268 | return locals() 269 | ''') 270 | 271 | def test_unusedVariable_noLocals(self): 272 | """ 273 | Using locals() in wrong scope should not matter 274 | """ 275 | self.flakes(''' 276 | def a(): 277 | locals() 278 | def a(): 279 | b = 1 280 | return 281 | ''', m.UnusedVariable) 282 | 283 | 284 | def test_assignToGlobal(self): 285 | """ 286 | Assigning to a global and then not using that global is perfectly 287 | acceptable. Do not mistake it for an unused local variable. 288 | """ 289 | self.flakes(''' 290 | b = 0 291 | def a(): 292 | global b 293 | b = 1 294 | ''') 295 | 296 | 297 | def test_assignToMember(self): 298 | """ 299 | Assigning to a member of another object and then not using that member 300 | variable is perfectly acceptable. Do not mistake it for an unused 301 | local variable. 302 | """ 303 | # XXX: Adding this test didn't generate a failure. Maybe not 304 | # necessary? 305 | self.flakes(''' 306 | class b: 307 | pass 308 | def a(): 309 | b.foo = 1 310 | ''') 311 | 312 | 313 | def test_assignInForLoop(self): 314 | """ 315 | Don't warn when a variable in a for loop is assigned to but not used. 316 | """ 317 | self.flakes(''' 318 | def f(): 319 | for i in range(10): 320 | pass 321 | ''') 322 | 323 | 324 | def test_assignInListComprehension(self): 325 | """ 326 | Don't warn when a variable in a list comprehension is assigned to but 327 | not used. 328 | """ 329 | self.flakes(''' 330 | def f(): 331 | [None for i in range(10)] 332 | ''') 333 | 334 | 335 | def test_generatorExpression(self): 336 | """ 337 | Don't warn when a variable in a generator expression is assigned to but not used. 338 | """ 339 | self.flakes(''' 340 | def f(): 341 | (None for i in range(10)) 342 | ''') 343 | 344 | 345 | def test_assignmentInsideLoop(self): 346 | """ 347 | Don't warn when a variable assignment occurs lexically after its use. 348 | """ 349 | self.flakes(''' 350 | def f(): 351 | x = None 352 | for i in range(10): 353 | if i > 2: 354 | return x 355 | x = i * 2 356 | ''') 357 | 358 | 359 | def test_tupleUnpacking(self): 360 | """ 361 | Don't warn when a variable included in tuple unpacking is unused. It's 362 | very common for variables in a tuple unpacking assignment to be unused 363 | in good Python code, so warning will only create false positives. 364 | """ 365 | self.flakes(''' 366 | def f(): 367 | (x, y) = 1, 2 368 | ''') 369 | 370 | 371 | def test_listUnpacking(self): 372 | """ 373 | Don't warn when a variable included in list unpacking is unused. 374 | """ 375 | self.flakes(''' 376 | def f(): 377 | [x, y] = [1, 2] 378 | ''') 379 | 380 | 381 | def test_closedOver(self): 382 | """ 383 | Don't warn when the assignment is used in an inner function. 384 | """ 385 | self.flakes(''' 386 | def barMaker(): 387 | foo = 5 388 | def bar(): 389 | return foo 390 | return bar 391 | ''') 392 | 393 | 394 | def test_doubleClosedOver(self): 395 | """ 396 | Don't warn when the assignment is used in an inner function, even if 397 | that inner function itself is in an inner function. 398 | """ 399 | self.flakes(''' 400 | def barMaker(): 401 | foo = 5 402 | def bar(): 403 | def baz(): 404 | return foo 405 | return bar 406 | ''') 407 | 408 | 409 | 410 | class Python25Test(harness.Test): 411 | """ 412 | Tests for checking of syntax only available in Python 2.5 and newer. 413 | """ 414 | if version_info < (2, 5): 415 | skip = "Python 2.5 required for if-else and with tests" 416 | 417 | def test_ifexp(self): 418 | """ 419 | Test C{foo if bar else baz} statements. 420 | """ 421 | self.flakes("a = 'moo' if True else 'oink'") 422 | self.flakes("a = foo if True else 'oink'", m.UndefinedName) 423 | self.flakes("a = 'moo' if True else bar", m.UndefinedName) 424 | 425 | 426 | def test_withStatementNoNames(self): 427 | """ 428 | No warnings are emitted for using inside or after a nameless C{with} 429 | statement a name defined beforehand. 430 | """ 431 | self.flakes(''' 432 | from __future__ import with_statement 433 | bar = None 434 | with open("foo"): 435 | bar 436 | bar 437 | ''') 438 | 439 | def test_withStatementSingleName(self): 440 | """ 441 | No warnings are emitted for using a name defined by a C{with} statement 442 | within the suite or afterwards. 443 | """ 444 | self.flakes(''' 445 | from __future__ import with_statement 446 | with open('foo') as bar: 447 | bar 448 | bar 449 | ''') 450 | 451 | 452 | def test_withStatementAttributeName(self): 453 | """ 454 | No warnings are emitted for using an attribute as the target of a 455 | C{with} statement. 456 | """ 457 | self.flakes(''' 458 | from __future__ import with_statement 459 | import foo 460 | with open('foo') as foo.bar: 461 | pass 462 | ''') 463 | 464 | 465 | def test_withStatementSubscript(self): 466 | """ 467 | No warnings are emitted for using a subscript as the target of a 468 | C{with} statement. 469 | """ 470 | self.flakes(''' 471 | from __future__ import with_statement 472 | import foo 473 | with open('foo') as foo[0]: 474 | pass 475 | ''') 476 | 477 | 478 | def test_withStatementSubscriptUndefined(self): 479 | """ 480 | An undefined name warning is emitted if the subscript used as the 481 | target of a C{with} statement is not defined. 482 | """ 483 | self.flakes(''' 484 | from __future__ import with_statement 485 | import foo 486 | with open('foo') as foo[bar]: 487 | pass 488 | ''', m.UndefinedName) 489 | 490 | 491 | def test_withStatementTupleNames(self): 492 | """ 493 | No warnings are emitted for using any of the tuple of names defined by 494 | a C{with} statement within the suite or afterwards. 495 | """ 496 | self.flakes(''' 497 | from __future__ import with_statement 498 | with open('foo') as (bar, baz): 499 | bar, baz 500 | bar, baz 501 | ''') 502 | 503 | 504 | def test_withStatementListNames(self): 505 | """ 506 | No warnings are emitted for using any of the list of names defined by a 507 | C{with} statement within the suite or afterwards. 508 | """ 509 | self.flakes(''' 510 | from __future__ import with_statement 511 | with open('foo') as [bar, baz]: 512 | bar, baz 513 | bar, baz 514 | ''') 515 | 516 | 517 | def test_withStatementComplicatedTarget(self): 518 | """ 519 | If the target of a C{with} statement uses any or all of the valid forms 520 | for that part of the grammar (See 521 | U{http://docs.python.org/reference/compound_stmts.html#the-with-statement}), 522 | the names involved are checked both for definedness and any bindings 523 | created are respected in the suite of the statement and afterwards. 524 | """ 525 | self.flakes(''' 526 | from __future__ import with_statement 527 | c = d = e = g = h = i = None 528 | with open('foo') as [(a, b), c[d], e.f, g[h:i]]: 529 | a, b, c, d, e, g, h, i 530 | a, b, c, d, e, g, h, i 531 | ''') 532 | 533 | 534 | def test_withStatementSingleNameUndefined(self): 535 | """ 536 | An undefined name warning is emitted if the name first defined by a 537 | C{with} statement is used before the C{with} statement. 538 | """ 539 | self.flakes(''' 540 | from __future__ import with_statement 541 | bar 542 | with open('foo') as bar: 543 | pass 544 | ''', m.UndefinedName) 545 | 546 | 547 | def test_withStatementTupleNamesUndefined(self): 548 | """ 549 | An undefined name warning is emitted if a name first defined by a the 550 | tuple-unpacking form of the C{with} statement is used before the 551 | C{with} statement. 552 | """ 553 | self.flakes(''' 554 | from __future__ import with_statement 555 | baz 556 | with open('foo') as (bar, baz): 557 | pass 558 | ''', m.UndefinedName) 559 | 560 | 561 | def test_withStatementSingleNameRedefined(self): 562 | """ 563 | A redefined name warning is emitted if a name bound by an import is 564 | rebound by the name defined by a C{with} statement. 565 | """ 566 | self.flakes(''' 567 | from __future__ import with_statement 568 | import bar 569 | with open('foo') as bar: 570 | pass 571 | ''', m.RedefinedWhileUnused) 572 | 573 | 574 | def test_withStatementTupleNamesRedefined(self): 575 | """ 576 | A redefined name warning is emitted if a name bound by an import is 577 | rebound by one of the names defined by the tuple-unpacking form of a 578 | C{with} statement. 579 | """ 580 | self.flakes(''' 581 | from __future__ import with_statement 582 | import bar 583 | with open('foo') as (bar, baz): 584 | pass 585 | ''', m.RedefinedWhileUnused) 586 | 587 | 588 | def test_withStatementUndefinedInside(self): 589 | """ 590 | An undefined name warning is emitted if a name is used inside the 591 | body of a C{with} statement without first being bound. 592 | """ 593 | self.flakes(''' 594 | from __future__ import with_statement 595 | with open('foo') as bar: 596 | baz 597 | ''', m.UndefinedName) 598 | 599 | 600 | def test_withStatementNameDefinedInBody(self): 601 | """ 602 | A name defined in the body of a C{with} statement can be used after 603 | the body ends without warning. 604 | """ 605 | self.flakes(''' 606 | from __future__ import with_statement 607 | with open('foo') as bar: 608 | baz = 10 609 | baz 610 | ''') 611 | 612 | 613 | def test_withStatementUndefinedInExpression(self): 614 | """ 615 | An undefined name warning is emitted if a name in the I{test} 616 | expression of a C{with} statement is undefined. 617 | """ 618 | self.flakes(''' 619 | from __future__ import with_statement 620 | with bar as baz: 621 | pass 622 | ''', m.UndefinedName) 623 | 624 | self.flakes(''' 625 | from __future__ import with_statement 626 | with bar as bar: 627 | pass 628 | ''', m.UndefinedName) 629 | 630 | 631 | 632 | class Python27Test(harness.Test): 633 | """ 634 | Tests for checking of syntax only available in Python 2.7 and newer. 635 | """ 636 | if version_info < (2, 7): 637 | skip = "Python 2.7 required for dict/set comprehension tests" 638 | 639 | def test_dictComprehension(self): 640 | """ 641 | Dict comprehensions are properly handled. 642 | """ 643 | self.flakes(''' 644 | a = {1: x for x in range(10)} 645 | ''') 646 | 647 | def test_setComprehensionAndLiteral(self): 648 | """ 649 | Set comprehensions are properly handled. 650 | """ 651 | self.flakes(''' 652 | a = {1, 2, 3} 653 | b = {x for x in range(10)} 654 | ''') 655 | -------------------------------------------------------------------------------- /pyflakes/test/test_imports.py: -------------------------------------------------------------------------------- 1 | 2 | from sys import version_info 3 | 4 | from pyflakes import messages as m 5 | from pyflakes.test import harness 6 | 7 | class Test(harness.Test): 8 | 9 | def test_unusedImport(self): 10 | self.flakes('import fu, bar', m.UnusedImport, m.UnusedImport) 11 | self.flakes('from baz import fu, bar', m.UnusedImport, m.UnusedImport) 12 | 13 | def test_aliasedImport(self): 14 | self.flakes('import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport) 15 | self.flakes('from moo import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport) 16 | 17 | def test_usedImport(self): 18 | self.flakes('import fu; print fu') 19 | self.flakes('from baz import fu; print fu') 20 | 21 | def test_redefinedWhileUnused(self): 22 | self.flakes('import fu; fu = 3', m.RedefinedWhileUnused) 23 | self.flakes('import fu; del fu', m.RedefinedWhileUnused) 24 | self.flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused) 25 | self.flakes('import fu; [fu, bar] = 3', m.RedefinedWhileUnused) 26 | 27 | def test_redefinedIf(self): 28 | """ 29 | Test that importing a module twice within an if 30 | block does raise a warning. 31 | 32 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 33 | """ 34 | self.flakes(''' 35 | i = 2 36 | if i==1: 37 | import os 38 | import os 39 | os.path''', m.RedefinedWhileUnused) 40 | 41 | def test_redefinedIfElse(self): 42 | """ 43 | Test that importing a module twice in if 44 | and else blocks does not raise a warning. 45 | 46 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 47 | """ 48 | self.flakes(''' 49 | i = 2 50 | if i==1: 51 | import os 52 | else: 53 | import os 54 | os.path''') 55 | 56 | def test_redefinedTry(self): 57 | """ 58 | Test that importing a module twice in an try block 59 | does raise a warning. 60 | 61 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 62 | """ 63 | self.flakes(''' 64 | try: 65 | import os 66 | import os 67 | except: 68 | pass 69 | os.path''', m.RedefinedWhileUnused) 70 | 71 | def test_redefinedTryExcept(self): 72 | """ 73 | Test that importing a module twice in an try 74 | and except block does not raise a warning. 75 | 76 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 77 | """ 78 | self.flakes(''' 79 | try: 80 | import os 81 | except: 82 | import os 83 | os.path''') 84 | 85 | def test_redefinedTryNested(self): 86 | """ 87 | Test that importing a module twice using a nested 88 | try/except and if blocks does not issue a warning. 89 | 90 | Issue #13: https://github.com/kevinw/pyflakes/issues/13 91 | """ 92 | self.flakes(''' 93 | try: 94 | if True: 95 | if True: 96 | import os 97 | except: 98 | import os 99 | os.path''') 100 | 101 | def test_redefinedByFunction(self): 102 | self.flakes(''' 103 | import fu 104 | def fu(): 105 | pass 106 | ''', m.RedefinedWhileUnused) 107 | 108 | def test_redefinedInNestedFunction(self): 109 | """ 110 | Test that shadowing a global name with a nested function definition 111 | generates a warning. 112 | """ 113 | self.flakes(''' 114 | import fu 115 | def bar(): 116 | def baz(): 117 | def fu(): 118 | pass 119 | ''', m.RedefinedWhileUnused, m.UnusedImport) 120 | 121 | def test_redefinedByClass(self): 122 | self.flakes(''' 123 | import fu 124 | class fu: 125 | pass 126 | ''', m.RedefinedWhileUnused) 127 | 128 | 129 | def test_redefinedBySubclass(self): 130 | """ 131 | If an imported name is redefined by a class statement which also uses 132 | that name in the bases list, no warning is emitted. 133 | """ 134 | self.flakes(''' 135 | from fu import bar 136 | class bar(bar): 137 | pass 138 | ''') 139 | 140 | 141 | def test_redefinedInClass(self): 142 | """ 143 | Test that shadowing a global with a class attribute does not produce a 144 | warning. 145 | """ 146 | self.flakes(''' 147 | import fu 148 | class bar: 149 | fu = 1 150 | print fu 151 | ''') 152 | 153 | def test_usedInFunction(self): 154 | self.flakes(''' 155 | import fu 156 | def fun(): 157 | print fu 158 | ''') 159 | 160 | def test_shadowedByParameter(self): 161 | self.flakes(''' 162 | import fu 163 | def fun(fu): 164 | print fu 165 | ''', m.UnusedImport) 166 | 167 | self.flakes(''' 168 | import fu 169 | def fun(fu): 170 | print fu 171 | print fu 172 | ''') 173 | 174 | def test_newAssignment(self): 175 | self.flakes('fu = None') 176 | 177 | def test_usedInGetattr(self): 178 | self.flakes('import fu; fu.bar.baz') 179 | self.flakes('import fu; "bar".fu.baz', m.UnusedImport) 180 | 181 | def test_usedInSlice(self): 182 | self.flakes('import fu; print fu.bar[1:]') 183 | 184 | def test_usedInIfBody(self): 185 | self.flakes(''' 186 | import fu 187 | if True: print fu 188 | ''') 189 | 190 | def test_usedInIfConditional(self): 191 | self.flakes(''' 192 | import fu 193 | if fu: pass 194 | ''') 195 | 196 | def test_usedInElifConditional(self): 197 | self.flakes(''' 198 | import fu 199 | if False: pass 200 | elif fu: pass 201 | ''') 202 | 203 | def test_usedInElse(self): 204 | self.flakes(''' 205 | import fu 206 | if False: pass 207 | else: print fu 208 | ''') 209 | 210 | def test_usedInCall(self): 211 | self.flakes('import fu; fu.bar()') 212 | 213 | def test_usedInClass(self): 214 | self.flakes(''' 215 | import fu 216 | class bar: 217 | bar = fu 218 | ''') 219 | 220 | def test_usedInClassBase(self): 221 | self.flakes(''' 222 | import fu 223 | class bar(object, fu.baz): 224 | pass 225 | ''') 226 | 227 | def test_notUsedInNestedScope(self): 228 | self.flakes(''' 229 | import fu 230 | def bleh(): 231 | pass 232 | print fu 233 | ''') 234 | 235 | def test_usedInFor(self): 236 | self.flakes(''' 237 | import fu 238 | for bar in range(9): 239 | print fu 240 | ''') 241 | 242 | def test_usedInForElse(self): 243 | self.flakes(''' 244 | import fu 245 | for bar in range(10): 246 | pass 247 | else: 248 | print fu 249 | ''') 250 | 251 | def test_redefinedByFor(self): 252 | self.flakes(''' 253 | import fu 254 | for fu in range(2): 255 | pass 256 | ''', m.RedefinedWhileUnused) 257 | 258 | def test_shadowedByFor(self): 259 | """ 260 | Test that shadowing a global name with a for loop variable generates a 261 | warning. 262 | """ 263 | self.flakes(''' 264 | import fu 265 | fu.bar() 266 | for fu in (): 267 | pass 268 | ''', m.ImportShadowedByLoopVar) 269 | 270 | def test_shadowedByForDeep(self): 271 | """ 272 | Test that shadowing a global name with a for loop variable nested in a 273 | tuple unpack generates a warning. 274 | """ 275 | self.flakes(''' 276 | import fu 277 | fu.bar() 278 | for (x, y, z, (a, b, c, (fu,))) in (): 279 | pass 280 | ''', m.ImportShadowedByLoopVar) 281 | 282 | def test_usedInReturn(self): 283 | self.flakes(''' 284 | import fu 285 | def fun(): 286 | return fu 287 | ''') 288 | 289 | def test_usedInOperators(self): 290 | self.flakes('import fu; 3 + fu.bar') 291 | self.flakes('import fu; 3 % fu.bar') 292 | self.flakes('import fu; 3 - fu.bar') 293 | self.flakes('import fu; 3 * fu.bar') 294 | self.flakes('import fu; 3 ** fu.bar') 295 | self.flakes('import fu; 3 / fu.bar') 296 | self.flakes('import fu; 3 // fu.bar') 297 | self.flakes('import fu; -fu.bar') 298 | self.flakes('import fu; ~fu.bar') 299 | self.flakes('import fu; 1 == fu.bar') 300 | self.flakes('import fu; 1 | fu.bar') 301 | self.flakes('import fu; 1 & fu.bar') 302 | self.flakes('import fu; 1 ^ fu.bar') 303 | self.flakes('import fu; 1 >> fu.bar') 304 | self.flakes('import fu; 1 << fu.bar') 305 | 306 | def test_usedInAssert(self): 307 | self.flakes('import fu; assert fu.bar') 308 | 309 | def test_usedInSubscript(self): 310 | self.flakes('import fu; fu.bar[1]') 311 | 312 | def test_usedInLogic(self): 313 | self.flakes('import fu; fu and False') 314 | self.flakes('import fu; fu or False') 315 | self.flakes('import fu; not fu.bar') 316 | 317 | def test_usedInList(self): 318 | self.flakes('import fu; [fu]') 319 | 320 | def test_usedInTuple(self): 321 | self.flakes('import fu; (fu,)') 322 | 323 | def test_usedInTry(self): 324 | self.flakes(''' 325 | import fu 326 | try: fu 327 | except: pass 328 | ''') 329 | 330 | def test_usedInExcept(self): 331 | self.flakes(''' 332 | import fu 333 | try: fu 334 | except: pass 335 | ''') 336 | 337 | def test_redefinedByExcept(self): 338 | self.flakes(''' 339 | import fu 340 | try: pass 341 | except Exception, fu: pass 342 | ''', m.RedefinedWhileUnused) 343 | 344 | def test_usedInRaise(self): 345 | self.flakes(''' 346 | import fu 347 | raise fu.bar 348 | ''') 349 | 350 | def test_usedInYield(self): 351 | self.flakes(''' 352 | import fu 353 | def gen(): 354 | yield fu 355 | ''') 356 | 357 | def test_usedInDict(self): 358 | self.flakes('import fu; {fu:None}') 359 | self.flakes('import fu; {1:fu}') 360 | 361 | def test_usedInParameterDefault(self): 362 | self.flakes(''' 363 | import fu 364 | def f(bar=fu): 365 | pass 366 | ''') 367 | 368 | def test_usedInAttributeAssign(self): 369 | self.flakes('import fu; fu.bar = 1') 370 | 371 | def test_usedInKeywordArg(self): 372 | self.flakes('import fu; fu.bar(stuff=fu)') 373 | 374 | def test_usedInAssignment(self): 375 | self.flakes('import fu; bar=fu') 376 | self.flakes('import fu; n=0; n+=fu') 377 | 378 | def test_usedInListComp(self): 379 | self.flakes('import fu; [fu for _ in range(1)]') 380 | self.flakes('import fu; [1 for _ in range(1) if fu]') 381 | 382 | def test_redefinedByListComp(self): 383 | self.flakes('import fu; [1 for fu in range(1)]', m.RedefinedWhileUnused) 384 | 385 | 386 | def test_usedInTryFinally(self): 387 | self.flakes(''' 388 | import fu 389 | try: pass 390 | finally: fu 391 | ''') 392 | 393 | self.flakes(''' 394 | import fu 395 | try: fu 396 | finally: pass 397 | ''') 398 | 399 | def test_usedInWhile(self): 400 | self.flakes(''' 401 | import fu 402 | while 0: 403 | fu 404 | ''') 405 | 406 | self.flakes(''' 407 | import fu 408 | while fu: pass 409 | ''') 410 | 411 | def test_usedInGlobal(self): 412 | self.flakes(''' 413 | import fu 414 | def f(): global fu 415 | ''', m.UnusedImport) 416 | 417 | def test_usedInBackquote(self): 418 | self.flakes('import fu; `fu`') 419 | 420 | def test_usedInExec(self): 421 | self.flakes('import fu; exec "print 1" in fu.bar') 422 | 423 | def test_usedInLambda(self): 424 | self.flakes('import fu; lambda: fu') 425 | 426 | def test_shadowedByLambda(self): 427 | self.flakes('import fu; lambda fu: fu', m.UnusedImport) 428 | 429 | def test_usedInSliceObj(self): 430 | self.flakes('import fu; "meow"[::fu]') 431 | 432 | def test_unusedInNestedScope(self): 433 | self.flakes(''' 434 | def bar(): 435 | import fu 436 | fu 437 | ''', m.UnusedImport, m.UndefinedName) 438 | 439 | def test_methodsDontUseClassScope(self): 440 | self.flakes(''' 441 | class bar: 442 | import fu 443 | def fun(self): 444 | fu 445 | ''', m.UnusedImport, m.UndefinedName) 446 | 447 | def test_nestedFunctionsNestScope(self): 448 | self.flakes(''' 449 | def a(): 450 | def b(): 451 | fu 452 | import fu 453 | ''') 454 | 455 | def test_nestedClassAndFunctionScope(self): 456 | self.flakes(''' 457 | def a(): 458 | import fu 459 | class b: 460 | def c(self): 461 | print fu 462 | ''') 463 | 464 | def test_importStar(self): 465 | self.flakes('from fu import *', m.ImportStarUsed) 466 | 467 | 468 | def test_packageImport(self): 469 | """ 470 | If a dotted name is imported and used, no warning is reported. 471 | """ 472 | self.flakes(''' 473 | import fu.bar 474 | fu.bar 475 | ''') 476 | 477 | 478 | def test_unusedPackageImport(self): 479 | """ 480 | If a dotted name is imported and not used, an unused import warning is 481 | reported. 482 | """ 483 | self.flakes('import fu.bar', m.UnusedImport) 484 | 485 | 486 | def test_duplicateSubmoduleImport(self): 487 | """ 488 | If a submodule of a package is imported twice, an unused import warning 489 | and a redefined while unused warning are reported. 490 | """ 491 | self.flakes(''' 492 | import fu.bar, fu.bar 493 | fu.bar 494 | ''', m.RedefinedWhileUnused) 495 | self.flakes(''' 496 | import fu.bar 497 | import fu.bar 498 | fu.bar 499 | ''', m.RedefinedWhileUnused) 500 | 501 | 502 | def test_differentSubmoduleImport(self): 503 | """ 504 | If two different submodules of a package are imported, no duplicate 505 | import warning is reported for the package. 506 | """ 507 | self.flakes(''' 508 | import fu.bar, fu.baz 509 | fu.bar, fu.baz 510 | ''') 511 | self.flakes(''' 512 | import fu.bar 513 | import fu.baz 514 | fu.bar, fu.baz 515 | ''') 516 | 517 | def test_assignRHSFirst(self): 518 | self.flakes('import fu; fu = fu') 519 | self.flakes('import fu; fu, bar = fu') 520 | self.flakes('import fu; [fu, bar] = fu') 521 | self.flakes('import fu; fu += fu') 522 | 523 | def test_tryingMultipleImports(self): 524 | self.flakes(''' 525 | try: 526 | import fu 527 | except ImportError: 528 | import bar as fu 529 | ''') 530 | test_tryingMultipleImports.todo = '' 531 | 532 | def test_nonGlobalDoesNotRedefine(self): 533 | self.flakes(''' 534 | import fu 535 | def a(): 536 | fu = 3 537 | return fu 538 | fu 539 | ''') 540 | 541 | def test_functionsRunLater(self): 542 | self.flakes(''' 543 | def a(): 544 | fu 545 | import fu 546 | ''') 547 | 548 | def test_functionNamesAreBoundNow(self): 549 | self.flakes(''' 550 | import fu 551 | def fu(): 552 | fu 553 | fu 554 | ''', m.RedefinedWhileUnused) 555 | 556 | def test_ignoreNonImportRedefinitions(self): 557 | self.flakes('a = 1; a = 2') 558 | 559 | def test_importingForImportError(self): 560 | self.flakes(''' 561 | try: 562 | import fu 563 | except ImportError: 564 | pass 565 | ''') 566 | test_importingForImportError.todo = '' 567 | 568 | def test_importedInClass(self): 569 | '''Imports in class scope can be used through self''' 570 | self.flakes(''' 571 | class c: 572 | import i 573 | def __init__(self): 574 | self.i 575 | ''') 576 | test_importedInClass.todo = 'requires evaluating attribute access' 577 | 578 | def test_futureImport(self): 579 | '''__future__ is special''' 580 | self.flakes('from __future__ import division') 581 | self.flakes(''' 582 | "docstring is allowed before future import" 583 | from __future__ import division 584 | ''') 585 | 586 | def test_futureImportFirst(self): 587 | """ 588 | __future__ imports must come before anything else. 589 | """ 590 | self.flakes(''' 591 | x = 5 592 | from __future__ import division 593 | ''', m.LateFutureImport) 594 | self.flakes(''' 595 | from foo import bar 596 | from __future__ import division 597 | bar 598 | ''', m.LateFutureImport) 599 | 600 | 601 | 602 | class TestSpecialAll(harness.Test): 603 | """ 604 | Tests for suppression of unused import warnings by C{__all__}. 605 | """ 606 | def test_ignoredInFunction(self): 607 | """ 608 | An C{__all__} definition does not suppress unused import warnings in a 609 | function scope. 610 | """ 611 | self.flakes(''' 612 | def foo(): 613 | import bar 614 | __all__ = ["bar"] 615 | ''', m.UnusedImport, m.UnusedVariable) 616 | 617 | 618 | def test_ignoredInClass(self): 619 | """ 620 | An C{__all__} definition does not suppress unused import warnings in a 621 | class scope. 622 | """ 623 | self.flakes(''' 624 | class foo: 625 | import bar 626 | __all__ = ["bar"] 627 | ''', m.UnusedImport) 628 | 629 | 630 | def test_warningSuppressed(self): 631 | """ 632 | If a name is imported and unused but is named in C{__all__}, no warning 633 | is reported. 634 | """ 635 | self.flakes(''' 636 | import foo 637 | __all__ = ["foo"] 638 | ''') 639 | 640 | 641 | def test_unrecognizable(self): 642 | """ 643 | If C{__all__} is defined in a way that can't be recognized statically, 644 | it is ignored. 645 | """ 646 | self.flakes(''' 647 | import foo 648 | __all__ = ["f" + "oo"] 649 | ''', m.UnusedImport) 650 | self.flakes(''' 651 | import foo 652 | __all__ = [] + ["foo"] 653 | ''', m.UnusedImport) 654 | 655 | 656 | def test_unboundExported(self): 657 | """ 658 | If C{__all__} includes a name which is not bound, a warning is emitted. 659 | """ 660 | self.flakes(''' 661 | __all__ = ["foo"] 662 | ''', m.UndefinedExport) 663 | 664 | # Skip this in __init__.py though, since the rules there are a little 665 | # different. 666 | for filename in ["foo/__init__.py", "__init__.py"]: 667 | self.flakes(''' 668 | __all__ = ["foo"] 669 | ''', filename=filename) 670 | 671 | 672 | def test_usedInGenExp(self): 673 | """ 674 | Using a global in a generator expression results in no warnings. 675 | """ 676 | self.flakes('import fu; (fu for _ in range(1))') 677 | self.flakes('import fu; (1 for _ in range(1) if fu)') 678 | 679 | 680 | def test_redefinedByGenExp(self): 681 | """ 682 | Re-using a global name as the loop variable for a generator 683 | expression results in a redefinition warning. 684 | """ 685 | self.flakes('import fu; (1 for fu in range(1))', m.RedefinedWhileUnused) 686 | 687 | 688 | def test_usedAsDecorator(self): 689 | """ 690 | Using a global name in a decorator statement results in no warnings, 691 | but using an undefined name in a decorator statement results in an 692 | undefined name warning. 693 | """ 694 | self.flakes(''' 695 | from interior import decorate 696 | @decorate 697 | def f(): 698 | return "hello" 699 | ''') 700 | 701 | self.flakes(''' 702 | from interior import decorate 703 | @decorate('value') 704 | def f(): 705 | return "hello" 706 | ''') 707 | 708 | self.flakes(''' 709 | @decorate 710 | def f(): 711 | return "hello" 712 | ''', m.UndefinedName) 713 | 714 | 715 | class Python26Tests(harness.Test): 716 | """ 717 | Tests for checking of syntax which is valid in PYthon 2.6 and newer. 718 | """ 719 | if version_info < (2, 6): 720 | skip = "Python 2.6 required for class decorator tests." 721 | 722 | 723 | def test_usedAsClassDecorator(self): 724 | """ 725 | Using an imported name as a class decorator results in no warnings, 726 | but using an undefined name as a class decorator results in an 727 | undefined name warning. 728 | """ 729 | self.flakes(''' 730 | from interior import decorate 731 | @decorate 732 | class foo: 733 | pass 734 | ''') 735 | 736 | self.flakes(''' 737 | from interior import decorate 738 | @decorate("foo") 739 | class bar: 740 | pass 741 | ''') 742 | 743 | self.flakes(''' 744 | @decorate 745 | class foo: 746 | pass 747 | ''', m.UndefinedName) 748 | -------------------------------------------------------------------------------- /pyflakes/checker.py: -------------------------------------------------------------------------------- 1 | # -*- test-case-name: pyflakes -*- 2 | # (c) 2005-2010 Divmod, Inc. 3 | # See LICENSE file for details 4 | 5 | import __builtin__ 6 | import os.path 7 | import _ast 8 | 9 | from pyflakes import messages 10 | 11 | 12 | # utility function to iterate over an AST node's children, adapted 13 | # from Python 2.6's standard ast module 14 | try: 15 | import ast 16 | iter_child_nodes = ast.iter_child_nodes 17 | except (ImportError, AttributeError): 18 | def iter_child_nodes(node, astcls=_ast.AST): 19 | """ 20 | Yield all direct child nodes of *node*, that is, all fields that are nodes 21 | and all items of fields that are lists of nodes. 22 | """ 23 | for name in node._fields: 24 | field = getattr(node, name, None) 25 | if isinstance(field, astcls): 26 | yield field 27 | elif isinstance(field, list): 28 | for item in field: 29 | yield item 30 | 31 | 32 | class Binding(object): 33 | """ 34 | Represents the binding of a value to a name. 35 | 36 | The checker uses this to keep track of which names have been bound and 37 | which names have not. See L{Assignment} for a special type of binding that 38 | is checked with stricter rules. 39 | 40 | @ivar used: pair of (L{Scope}, line-number) indicating the scope and 41 | line number that this binding was last used 42 | """ 43 | 44 | def __init__(self, name, source): 45 | self.name = name 46 | self.source = source 47 | self.used = False 48 | 49 | 50 | def __str__(self): 51 | return self.name 52 | 53 | 54 | def __repr__(self): 55 | return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__, 56 | self.name, 57 | self.source.lineno, 58 | id(self)) 59 | 60 | 61 | 62 | class UnBinding(Binding): 63 | '''Created by the 'del' operator.''' 64 | 65 | 66 | 67 | class Importation(Binding): 68 | """ 69 | A binding created by an import statement. 70 | 71 | @ivar fullName: The complete name given to the import statement, 72 | possibly including multiple dotted components. 73 | @type fullName: C{str} 74 | """ 75 | def __init__(self, name, source): 76 | self.fullName = name 77 | name = name.split('.')[0] 78 | super(Importation, self).__init__(name, source) 79 | 80 | 81 | 82 | class Argument(Binding): 83 | """ 84 | Represents binding a name as an argument. 85 | """ 86 | 87 | 88 | 89 | class Assignment(Binding): 90 | """ 91 | Represents binding a name with an explicit assignment. 92 | 93 | The checker will raise warnings for any Assignment that isn't used. Also, 94 | the checker does not consider assignments in tuple/list unpacking to be 95 | Assignments, rather it treats them as simple Bindings. 96 | """ 97 | 98 | 99 | 100 | class FunctionDefinition(Binding): 101 | _property_decorator = False 102 | 103 | 104 | 105 | class ExportBinding(Binding): 106 | """ 107 | A binding created by an C{__all__} assignment. If the names in the list 108 | can be determined statically, they will be treated as names for export and 109 | additional checking applied to them. 110 | 111 | The only C{__all__} assignment that can be recognized is one which takes 112 | the value of a literal list containing literal strings. For example:: 113 | 114 | __all__ = ["foo", "bar"] 115 | 116 | Names which are imported and not otherwise used but appear in the value of 117 | C{__all__} will not have an unused import warning reported for them. 118 | """ 119 | def names(self): 120 | """ 121 | Return a list of the names referenced by this binding. 122 | """ 123 | names = [] 124 | if isinstance(self.source, _ast.List): 125 | for node in self.source.elts: 126 | if isinstance(node, _ast.Str): 127 | names.append(node.s) 128 | return names 129 | 130 | 131 | 132 | class Scope(dict): 133 | importStarred = False # set to True when import * is found 134 | usesLocals = False 135 | 136 | 137 | def __repr__(self): 138 | return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) 139 | 140 | 141 | def __init__(self): 142 | super(Scope, self).__init__() 143 | 144 | 145 | 146 | class ClassScope(Scope): 147 | pass 148 | 149 | 150 | 151 | class FunctionScope(Scope): 152 | """ 153 | I represent a name scope for a function. 154 | 155 | @ivar globals: Names declared 'global' in this function. 156 | """ 157 | def __init__(self): 158 | super(FunctionScope, self).__init__() 159 | self.globals = {} 160 | 161 | 162 | 163 | class ModuleScope(Scope): 164 | pass 165 | 166 | 167 | # Globally defined names which are not attributes of the __builtin__ module. 168 | _MAGIC_GLOBALS = ['__file__', '__builtins__'] 169 | 170 | 171 | 172 | class Checker(object): 173 | """ 174 | I check the cleanliness and sanity of Python code. 175 | 176 | @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements 177 | of the list are two-tuples. The first element is the callable passed 178 | to L{deferFunction}. The second element is a copy of the scope stack 179 | at the time L{deferFunction} was called. 180 | 181 | @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for 182 | callables which are deferred assignment checks. 183 | """ 184 | 185 | nodeDepth = 0 186 | traceTree = False 187 | 188 | def __init__(self, tree, filename=None): 189 | if filename is None: 190 | filename = '(none)' 191 | self._deferredFunctions = [] 192 | self._deferredAssignments = [] 193 | self.dead_scopes = [] 194 | self.messages = [] 195 | self.filename = filename 196 | self.scopeStack = [ModuleScope()] 197 | self.futuresAllowed = True 198 | self.root = tree 199 | self.handleChildren(tree) 200 | self._runDeferred(self._deferredFunctions) 201 | # Set _deferredFunctions to None so that deferFunction will fail 202 | # noisily if called after we've run through the deferred functions. 203 | self._deferredFunctions = None 204 | self._runDeferred(self._deferredAssignments) 205 | # Set _deferredAssignments to None so that deferAssignment will fail 206 | # noisly if called after we've run through the deferred assignments. 207 | self._deferredAssignments = None 208 | del self.scopeStack[1:] 209 | self.popScope() 210 | self.check_dead_scopes() 211 | 212 | 213 | def deferFunction(self, callable): 214 | ''' 215 | Schedule a function handler to be called just before completion. 216 | 217 | This is used for handling function bodies, which must be deferred 218 | because code later in the file might modify the global scope. When 219 | `callable` is called, the scope at the time this is called will be 220 | restored, however it will contain any new bindings added to it. 221 | ''' 222 | self._deferredFunctions.append((callable, self.scopeStack[:])) 223 | 224 | 225 | def deferAssignment(self, callable): 226 | """ 227 | Schedule an assignment handler to be called just after deferred 228 | function handlers. 229 | """ 230 | self._deferredAssignments.append((callable, self.scopeStack[:])) 231 | 232 | 233 | def _runDeferred(self, deferred): 234 | """ 235 | Run the callables in C{deferred} using their associated scope stack. 236 | """ 237 | for handler, scope in deferred: 238 | self.scopeStack = scope 239 | handler() 240 | 241 | 242 | def scope(self): 243 | return self.scopeStack[-1] 244 | scope = property(scope) 245 | 246 | def popScope(self): 247 | self.dead_scopes.append(self.scopeStack.pop()) 248 | 249 | 250 | def check_dead_scopes(self): 251 | """ 252 | Look at scopes which have been fully examined and report names in them 253 | which were imported but unused. 254 | """ 255 | for scope in self.dead_scopes: 256 | export = isinstance(scope.get('__all__'), ExportBinding) 257 | if export: 258 | all = scope['__all__'].names() 259 | if os.path.split(self.filename)[1] != '__init__.py': 260 | # Look for possible mistakes in the export list 261 | undefined = set(all) - set(scope) 262 | for name in undefined: 263 | self.report( 264 | messages.UndefinedExport, 265 | scope['__all__'].source, 266 | name) 267 | else: 268 | all = [] 269 | 270 | # Look for imported names that aren't used. 271 | for importation in scope.itervalues(): 272 | if isinstance(importation, Importation): 273 | if not importation.used and importation.name not in all: 274 | self.report( 275 | messages.UnusedImport, 276 | importation.source, 277 | importation.name) 278 | 279 | 280 | def pushFunctionScope(self): 281 | self.scopeStack.append(FunctionScope()) 282 | 283 | def pushClassScope(self): 284 | self.scopeStack.append(ClassScope()) 285 | 286 | def report(self, messageClass, *args, **kwargs): 287 | self.messages.append(messageClass(self.filename, *args, **kwargs)) 288 | 289 | def lowestCommonAncestor(self, lnode, rnode, stop=None): 290 | if not stop: 291 | stop = self.root 292 | if lnode is stop: 293 | return lnode 294 | if rnode is stop: 295 | return rnode 296 | 297 | if lnode is rnode: 298 | return lnode 299 | if (lnode.level > rnode.level): 300 | return self.lowestCommonAncestor(lnode.parent, rnode, stop) 301 | if (rnode.level > lnode.level): 302 | return self.lowestCommonAncestor(lnode, rnode.parent, stop) 303 | if lnode.parent is rnode.parent: 304 | return lnode.parent 305 | else: 306 | return self.lowestCommonAncestor(lnode.parent, rnode.parent, stop) 307 | 308 | def descendantOf(self, node, ancestors, stop=None): 309 | for a in ancestors: 310 | try: 311 | p = self.lowestCommonAncestor(node, a, stop) 312 | if not p is stop: 313 | return True 314 | except AttributeError: 315 | # Skip some bogus objects like <_ast.Pass> 316 | pass 317 | return False 318 | 319 | def onFork(self, parent, lnode, rnode, items): 320 | return int(self.descendantOf(lnode, items, parent)) + \ 321 | int(self.descendantOf(rnode, items, parent)) 322 | 323 | def differentForks(self, lnode, rnode): 324 | "True, if lnode and rnode are located on different forks of IF/TRY" 325 | ancestor = self.lowestCommonAncestor(lnode, rnode) 326 | if isinstance(ancestor, _ast.If): 327 | for fork in (ancestor.body, ancestor.orelse): 328 | if self.onFork(ancestor, lnode, rnode, fork) == 1: 329 | return True 330 | if isinstance(ancestor, _ast.TryExcept): 331 | for fork in (ancestor.body, ancestor.handlers, ancestor.orelse): 332 | if self.onFork(ancestor, lnode, rnode, fork) == 1: 333 | return True 334 | return False 335 | 336 | def handleChildren(self, tree): 337 | for node in iter_child_nodes(tree): 338 | self.handleNode(node, tree) 339 | 340 | def isDocstring(self, node): 341 | """ 342 | Determine if the given node is a docstring, as long as it is at the 343 | correct place in the node tree. 344 | """ 345 | return isinstance(node, _ast.Str) or \ 346 | (isinstance(node, _ast.Expr) and 347 | isinstance(node.value, _ast.Str)) 348 | 349 | def handleNode(self, node, parent): 350 | node.parent = parent 351 | if self.traceTree: 352 | print ' ' * self.nodeDepth + node.__class__.__name__ 353 | self.nodeDepth += 1 354 | if self.futuresAllowed and not \ 355 | (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)): 356 | self.futuresAllowed = False 357 | nodeType = node.__class__.__name__.upper() 358 | node.level = self.nodeDepth 359 | try: 360 | handler = getattr(self, nodeType) 361 | handler(node) 362 | finally: 363 | self.nodeDepth -= 1 364 | if self.traceTree: 365 | print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ 366 | 367 | def ignore(self, node): 368 | pass 369 | 370 | # "stmt" type nodes 371 | RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \ 372 | TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren 373 | 374 | CONTINUE = BREAK = PASS = ignore 375 | 376 | # "expr" type nodes 377 | BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \ 378 | CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren 379 | 380 | NUM = STR = ELLIPSIS = ignore 381 | 382 | # "slice" type nodes 383 | SLICE = EXTSLICE = INDEX = handleChildren 384 | 385 | # expression contexts are node instances too, though being constants 386 | LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore 387 | 388 | # same for operators 389 | AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ 390 | BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ 391 | EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore 392 | 393 | # additional node types 394 | COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren 395 | 396 | def addBinding(self, loc, value, reportRedef=True): 397 | '''Called when a binding is altered. 398 | 399 | - `loc` is the location (an object with lineno and optionally 400 | col_offset attributes) of the statement responsible for the change 401 | - `value` is the optional new value, a Binding instance, associated 402 | with the binding; if None, the binding is deleted if it exists. 403 | - if `reportRedef` is True (default), rebinding while unused will be 404 | reported. 405 | ''' 406 | if (isinstance(self.scope.get(value.name), FunctionDefinition) 407 | and isinstance(value, FunctionDefinition)): 408 | if not value._property_decorator: 409 | if not self.differentForks(loc, self.scope[value.name].source): 410 | self.report(messages.RedefinedFunction, 411 | loc, value.name, self.scope[value.name].source) 412 | 413 | if not isinstance(self.scope, ClassScope): 414 | for scope in self.scopeStack[::-1]: 415 | existing = scope.get(value.name) 416 | if (isinstance(existing, Importation) 417 | and not existing.used 418 | and (not isinstance(value, Importation) or value.fullName == existing.fullName) 419 | and reportRedef): 420 | 421 | if not self.differentForks(loc, existing.source): 422 | self.report(messages.RedefinedWhileUnused, 423 | loc, value.name, existing.source) 424 | 425 | if isinstance(value, UnBinding): 426 | try: 427 | del self.scope[value.name] 428 | except KeyError: 429 | self.report(messages.UndefinedName, loc, value.name) 430 | else: 431 | self.scope[value.name] = value 432 | 433 | def GLOBAL(self, node): 434 | """ 435 | Keep track of globals declarations. 436 | """ 437 | if isinstance(self.scope, FunctionScope): 438 | self.scope.globals.update(dict.fromkeys(node.names)) 439 | 440 | def LISTCOMP(self, node): 441 | # handle generators before element 442 | for gen in node.generators: 443 | self.handleNode(gen, node) 444 | self.handleNode(node.elt, node) 445 | 446 | GENERATOREXP = SETCOMP = LISTCOMP 447 | 448 | # dictionary comprehensions; introduced in Python 2.7 449 | def DICTCOMP(self, node): 450 | for gen in node.generators: 451 | self.handleNode(gen, node) 452 | self.handleNode(node.key, node) 453 | self.handleNode(node.value, node) 454 | 455 | def FOR(self, node): 456 | """ 457 | Process bindings for loop variables. 458 | """ 459 | vars = [] 460 | def collectLoopVars(n): 461 | if isinstance(n, _ast.Name): 462 | vars.append(n.id) 463 | elif isinstance(n, _ast.expr_context): 464 | return 465 | else: 466 | for c in iter_child_nodes(n): 467 | collectLoopVars(c) 468 | 469 | collectLoopVars(node.target) 470 | for varn in vars: 471 | if (isinstance(self.scope.get(varn), Importation) 472 | # unused ones will get an unused import warning 473 | and self.scope[varn].used): 474 | self.report(messages.ImportShadowedByLoopVar, 475 | node, varn, self.scope[varn].source) 476 | 477 | self.handleChildren(node) 478 | 479 | def NAME(self, node): 480 | """ 481 | Handle occurrence of Name (which can be a load/store/delete access.) 482 | """ 483 | if node.id == 'locals' and isinstance(node.parent, _ast.Call): 484 | # we are doing locals() call in current scope 485 | self.scope.usesLocals = True 486 | 487 | # Locate the name in locals / function / globals scopes. 488 | if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)): 489 | # try local scope 490 | importStarred = self.scope.importStarred 491 | try: 492 | self.scope[node.id].used = (self.scope, node) 493 | except KeyError: 494 | pass 495 | else: 496 | return 497 | 498 | # try enclosing function scopes 499 | 500 | for scope in self.scopeStack[-2:0:-1]: 501 | importStarred = importStarred or scope.importStarred 502 | if not isinstance(scope, FunctionScope): 503 | continue 504 | try: 505 | scope[node.id].used = (self.scope, node) 506 | except KeyError: 507 | pass 508 | else: 509 | return 510 | 511 | # try global scope 512 | 513 | importStarred = importStarred or self.scopeStack[0].importStarred 514 | try: 515 | self.scopeStack[0][node.id].used = (self.scope, node) 516 | except KeyError: 517 | if ((not hasattr(__builtin__, node.id)) 518 | and node.id not in _MAGIC_GLOBALS 519 | and not importStarred): 520 | if (os.path.basename(self.filename) == '__init__.py' and 521 | node.id == '__path__'): 522 | # the special name __path__ is valid only in packages 523 | pass 524 | else: 525 | self.report(messages.UndefinedName, node, node.id) 526 | elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)): 527 | # if the name hasn't already been defined in the current scope 528 | if isinstance(self.scope, FunctionScope) and node.id not in self.scope: 529 | # for each function or module scope above us 530 | for scope in self.scopeStack[:-1]: 531 | if not isinstance(scope, (FunctionScope, ModuleScope)): 532 | continue 533 | # if the name was defined in that scope, and the name has 534 | # been accessed already in the current scope, and hasn't 535 | # been declared global 536 | if (node.id in scope 537 | and scope[node.id].used 538 | and scope[node.id].used[0] is self.scope 539 | and node.id not in self.scope.globals): 540 | # then it's probably a mistake 541 | self.report(messages.UndefinedLocal, 542 | scope[node.id].used[1], 543 | node.id, 544 | scope[node.id].source) 545 | break 546 | 547 | if isinstance(node.parent, 548 | (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)): 549 | binding = Binding(node.id, node) 550 | elif (node.id == '__all__' and 551 | isinstance(self.scope, ModuleScope)): 552 | binding = ExportBinding(node.id, node.parent.value) 553 | else: 554 | binding = Assignment(node.id, node) 555 | if node.id in self.scope: 556 | binding.used = self.scope[node.id].used 557 | self.addBinding(node, binding) 558 | elif isinstance(node.ctx, _ast.Del): 559 | if isinstance(self.scope, FunctionScope) and \ 560 | node.id in self.scope.globals: 561 | del self.scope.globals[node.id] 562 | else: 563 | self.addBinding(node, UnBinding(node.id, node)) 564 | else: 565 | # must be a Param context -- this only happens for names in function 566 | # arguments, but these aren't dispatched through here 567 | raise RuntimeError( 568 | "Got impossible expression context: %r" % (node.ctx,)) 569 | 570 | 571 | def FUNCTIONDEF(self, node): 572 | # the decorators attribute is called decorator_list as of Python 2.6 573 | if hasattr(node, 'decorators'): 574 | for deco in node.decorators: 575 | self.handleNode(deco, node) 576 | else: 577 | for deco in node.decorator_list: 578 | self.handleNode(deco, node) 579 | 580 | # Check for property decorator 581 | func_def = FunctionDefinition(node.name, node) 582 | 583 | if hasattr(node, 'decorators'): 584 | for decorator in node.decorators: 585 | if getattr(decorator, 'attr', None) in ('setter', 'deleter'): 586 | func_def._property_decorator = True 587 | else: 588 | for decorator in node.decorator_list: 589 | if getattr(decorator, 'attr', None) in ('setter', 'deleter'): 590 | func_def._property_decorator = True 591 | 592 | self.addBinding(node, func_def) 593 | self.LAMBDA(node) 594 | 595 | def LAMBDA(self, node): 596 | for default in node.args.defaults: 597 | self.handleNode(default, node) 598 | 599 | def runFunction(): 600 | args = [] 601 | 602 | def addArgs(arglist): 603 | for arg in arglist: 604 | if isinstance(arg, _ast.Tuple): 605 | addArgs(arg.elts) 606 | else: 607 | if arg.id in args: 608 | self.report(messages.DuplicateArgument, 609 | node, arg.id) 610 | args.append(arg.id) 611 | 612 | self.pushFunctionScope() 613 | addArgs(node.args.args) 614 | # vararg/kwarg identifiers are not Name nodes 615 | if node.args.vararg: 616 | args.append(node.args.vararg) 617 | if node.args.kwarg: 618 | args.append(node.args.kwarg) 619 | for name in args: 620 | self.addBinding(node, Argument(name, node), reportRedef=False) 621 | if isinstance(node.body, list): 622 | # case for FunctionDefs 623 | for stmt in node.body: 624 | self.handleNode(stmt, node) 625 | else: 626 | # case for Lambdas 627 | self.handleNode(node.body, node) 628 | def checkUnusedAssignments(): 629 | """ 630 | Check to see if any assignments have not been used. 631 | """ 632 | for name, binding in self.scope.iteritems(): 633 | if (not binding.used and not name in self.scope.globals 634 | and not self.scope.usesLocals 635 | and isinstance(binding, Assignment)): 636 | self.report(messages.UnusedVariable, 637 | binding.source, name) 638 | self.deferAssignment(checkUnusedAssignments) 639 | self.popScope() 640 | 641 | self.deferFunction(runFunction) 642 | 643 | 644 | def CLASSDEF(self, node): 645 | """ 646 | Check names used in a class definition, including its decorators, base 647 | classes, and the body of its definition. Additionally, add its name to 648 | the current scope. 649 | """ 650 | # decorator_list is present as of Python 2.6 651 | for deco in getattr(node, 'decorator_list', []): 652 | self.handleNode(deco, node) 653 | for baseNode in node.bases: 654 | self.handleNode(baseNode, node) 655 | self.pushClassScope() 656 | for stmt in node.body: 657 | self.handleNode(stmt, node) 658 | self.popScope() 659 | self.addBinding(node, Binding(node.name, node)) 660 | 661 | def ASSIGN(self, node): 662 | self.handleNode(node.value, node) 663 | for target in node.targets: 664 | self.handleNode(target, node) 665 | 666 | def AUGASSIGN(self, node): 667 | # AugAssign is awkward: must set the context explicitly and visit twice, 668 | # once with AugLoad context, once with AugStore context 669 | node.target.ctx = _ast.AugLoad() 670 | self.handleNode(node.target, node) 671 | self.handleNode(node.value, node) 672 | node.target.ctx = _ast.AugStore() 673 | self.handleNode(node.target, node) 674 | 675 | def IMPORT(self, node): 676 | for alias in node.names: 677 | name = alias.asname or alias.name 678 | importation = Importation(name, node) 679 | self.addBinding(node, importation) 680 | 681 | def IMPORTFROM(self, node): 682 | if node.module == '__future__': 683 | if not self.futuresAllowed: 684 | self.report(messages.LateFutureImport, node, 685 | [n.name for n in node.names]) 686 | else: 687 | self.futuresAllowed = False 688 | 689 | for alias in node.names: 690 | if alias.name == '*': 691 | self.scope.importStarred = True 692 | self.report(messages.ImportStarUsed, node, node.module) 693 | continue 694 | name = alias.asname or alias.name 695 | importation = Importation(name, node) 696 | if node.module == '__future__': 697 | importation.used = (self.scope, node) 698 | self.addBinding(node, importation) 699 | --------------------------------------------------------------------------------