Skip to content

Commit

Permalink
Add attribute Message.col to report the columns offset.
Browse files Browse the repository at this point in the history
  • Loading branch information
florentx committed Mar 30, 2013
1 parent 99df084 commit ef4da24
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 57 deletions.
3 changes: 2 additions & 1 deletion NEWS.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
0.6.x (unreleased):
- Add --version and --help options.
- Support `python -m pyflakes`
- Support `python -m pyflakes`.
- Add attribute `Message.col` to report column offset.
- Do not report redefinition of variable for a variable used in a list
comprehension in a conditional.
- Do not report redefinition of variable for generator expressions and
Expand Down
38 changes: 19 additions & 19 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def checkDeadScopes(self):
undefined = set(all) - set(scope)
for name in undefined:
self.report(messages.UndefinedExport,
scope['__all__'].source.lineno, name)
scope['__all__'].source, name)
else:
all = []

Expand All @@ -282,7 +282,7 @@ def checkDeadScopes(self):
if isinstance(importation, Importation):
if not importation.used and importation.name not in all:
self.report(messages.UnusedImport,
importation.source.lineno, importation.name)
importation.source, importation.name)

def pushFunctionScope(self):
self.scopeStack.append(FunctionScope())
Expand Down Expand Up @@ -363,26 +363,26 @@ def addBinding(self, node, value, reportRedef=True):
and not self.differentForks(node, existing.source)):
redefinedWhileUnused = True
self.report(messages.RedefinedWhileUnused,
node.lineno, value.name, existing.source.lineno)
node, value.name, existing.source)

existing = self.scope.get(value.name)
if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
if (existing and reportRedef
and not self.hasParent(existing.source, (ast.For, ast.ListComp))
and not self.differentForks(node, existing.source)):
self.report(messages.RedefinedInListComp,
node.lineno, value.name, existing.source.lineno)
node, value.name, existing.source)

if isinstance(value, UnBinding):
try:
del self.scope[value.name]
except KeyError:
self.report(messages.UndefinedName, node.lineno, value.name)
self.report(messages.UndefinedName, node, value.name)
elif (isinstance(existing, Definition)
and not existing.used
and not self.differentForks(node, existing.source)):
self.report(messages.RedefinedWhileUnused,
node.lineno, value.name, existing.source.lineno)
node, value.name, existing.source)
else:
self.scope[value.name] = value

Expand All @@ -393,7 +393,7 @@ def handleNodeLoad(self, node):
# try local scope
importStarred = self.scope.importStarred
try:
self.scope[name].used = (self.scope, node.lineno)
self.scope[name].used = (self.scope, node)
except KeyError:
pass
else:
Expand All @@ -405,7 +405,7 @@ def handleNodeLoad(self, node):
if not isinstance(scope, FunctionScope):
continue
try:
scope[name].used = (self.scope, node.lineno)
scope[name].used = (self.scope, node)
except KeyError:
pass
else:
Expand All @@ -414,14 +414,14 @@ def handleNodeLoad(self, node):
# try global scope
importStarred = importStarred or self.scopeStack[0].importStarred
try:
self.scopeStack[0][name].used = (self.scope, node.lineno)
self.scopeStack[0][name].used = (self.scope, node)
except KeyError:
if not importStarred and name not in self.builtIns:
if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'):
# the special name __path__ is valid only in packages
pass
else:
self.report(messages.UndefinedName, node.lineno, name)
self.report(messages.UndefinedName, node, name)

def handleNodeStore(self, node):
name = getNodeName(node)
Expand All @@ -440,7 +440,7 @@ def handleNodeStore(self, node):
and name not in self.scope.globals):
# then it's probably a mistake
self.report(messages.UndefinedLocal,
scope[name].used[1], name, scope[name].source.lineno)
scope[name].used[1], name, scope[name].source)
break

parent = getattr(node, 'parent', None)
Expand Down Expand Up @@ -579,7 +579,7 @@ def collectLoopVars(n):
# unused ones will get an unused import warning
and self.scope[varn].used):
self.report(messages.ImportShadowedByLoopVar,
node.lineno, varn, self.scope[varn].source.lineno)
node, varn, self.scope[varn].source)

self.handleChildren(node)

Expand Down Expand Up @@ -619,15 +619,15 @@ def addArgs(arglist):
else:
if arg.id in args:
self.report(messages.DuplicateArgument,
node.lineno, arg.id)
node, arg.id)
args.append(arg.id)
addArgs(node.args.args)
defaults = node.args.defaults
else:
for arg in node.args.args + node.args.kwonlyargs:
if arg.arg in args:
self.report(messages.DuplicateArgument,
node.lineno, arg.arg)
node, arg.arg)
args.append(arg.arg)
self.handleNode(arg.annotation, node)
if hasattr(node, 'returns'): # Only for FunctionDefs
Expand All @@ -641,7 +641,7 @@ def addArgs(arglist):
if not wildcard:
continue
if wildcard in args:
self.report(messages.DuplicateArgument, node.lineno, wildcard)
self.report(messages.DuplicateArgument, node, wildcard)
args.append(wildcard)
for default in defaults:
self.handleNode(default, node)
Expand All @@ -668,7 +668,7 @@ def checkUnusedAssignments():
and not self.scope.usesLocals
and isinstance(binding, Assignment)):
self.report(messages.UnusedVariable,
binding.source.lineno, name)
binding.source, name)
self.deferAssignment(checkUnusedAssignments)
self.popScope()

Expand Down Expand Up @@ -713,19 +713,19 @@ def IMPORTFROM(self, node):
if node.module == '__future__':
if not self.futuresAllowed:
self.report(messages.LateFutureImport,
node.lineno, [n.name for n in node.names])
node, [n.name for n in node.names])
else:
self.futuresAllowed = False

for alias in node.names:
if alias.name == '*':
self.scope.importStarred = True
self.report(messages.ImportStarUsed, node.lineno, node.module)
self.report(messages.ImportStarUsed, node, node.module)
continue
name = alias.asname or alias.name
importation = Importation(name, node)
if node.module == '__future__':
importation.used = (self.scope, node.lineno)
importation.used = (self.scope, node)
self.addBinding(node, importation)

def EXCEPTHANDLER(self, node):
Expand Down
63 changes: 32 additions & 31 deletions pyflakes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ class Message(object):
message = ''
message_args = ()

def __init__(self, filename, lineno):
def __init__(self, filename, loc):
self.filename = filename
self.lineno = lineno
self.lineno = loc.lineno
self.col = getattr(loc, 'col_offset', 0)

def __str__(self):
return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args)
Expand All @@ -16,88 +17,88 @@ def __str__(self):
class UnusedImport(Message):
message = '%r imported but unused'

def __init__(self, filename, lineno, name):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, name):
Message.__init__(self, filename, loc)
self.message_args = (name,)


class RedefinedWhileUnused(Message):
message = 'redefinition of unused %r from line %r'

def __init__(self, filename, lineno, name, orig_lineno):
Message.__init__(self, filename, lineno)
self.message_args = (name, orig_lineno)
def __init__(self, filename, loc, name, orig_loc):
Message.__init__(self, filename, loc)
self.message_args = (name, orig_loc.lineno)


class RedefinedInListComp(Message):
message = 'list comprehension redefines %r from line %r'

def __init__(self, filename, lineno, name, orig_lineno):
Message.__init__(self, filename, lineno)
self.message_args = (name, orig_lineno)
def __init__(self, filename, loc, name, orig_loc):
Message.__init__(self, filename, loc)
self.message_args = (name, orig_loc.lineno)


class ImportShadowedByLoopVar(Message):
message = 'import %r from line %r shadowed by loop variable'

def __init__(self, filename, lineno, name, orig_lineno):
Message.__init__(self, filename, lineno)
self.message_args = (name, orig_lineno)
def __init__(self, filename, loc, name, orig_loc):
Message.__init__(self, filename, loc)
self.message_args = (name, orig_loc.lineno)


class ImportStarUsed(Message):
message = "'from %s import *' used; unable to detect undefined names"

def __init__(self, filename, lineno, modname):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, modname):
Message.__init__(self, filename, loc)
self.message_args = (modname,)


class UndefinedName(Message):
message = 'undefined name %r'

def __init__(self, filename, lineno, name):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, name):
Message.__init__(self, filename, loc)
self.message_args = (name,)


class UndefinedExport(Message):
message = 'undefined name %r in __all__'

def __init__(self, filename, lineno, name):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, name):
Message.__init__(self, filename, loc)
self.message_args = (name,)


class UndefinedLocal(Message):
message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment"

def __init__(self, filename, lineno, name, orig_lineno):
Message.__init__(self, filename, lineno)
self.message_args = (name, orig_lineno)
def __init__(self, filename, loc, name, orig_loc):
Message.__init__(self, filename, loc)
self.message_args = (name, orig_loc.lineno)


class DuplicateArgument(Message):
message = 'duplicate argument %r in function definition'

def __init__(self, filename, lineno, name):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, name):
Message.__init__(self, filename, loc)
self.message_args = (name,)


class Redefined(Message):
message = 'redefinition of %r from line %r'

def __init__(self, filename, lineno, name, orig_lineno):
Message.__init__(self, filename, lineno)
self.message_args = (name, orig_lineno)
def __init__(self, filename, loc, name, orig_loc):
Message.__init__(self, filename, loc)
self.message_args = (name, orig_loc.lineno)


class LateFutureImport(Message):
message = 'future import(s) %r after other statements'

def __init__(self, filename, lineno, names):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, names):
Message.__init__(self, filename, loc)
self.message_args = (names,)


Expand All @@ -108,6 +109,6 @@ class UnusedVariable(Message):
"""
message = 'local variable %r is assigned to but never used'

def __init__(self, filename, lineno, names):
Message.__init__(self, filename, lineno)
def __init__(self, filename, loc, names):
Message.__init__(self, filename, loc)
self.message_args = (names,)
21 changes: 15 additions & 6 deletions pyflakes/test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ def withStderrTo(stderr, f, *args, **kwargs):
sys.stderr = outer


class Stmt(object):
"""
Mock an AST statement.
"""
def __init__(self, lineno, col_offset=0):
self.lineno = lineno
self.col_offset = col_offset


class LoggingReporter(object):
"""
Implementation of Reporter that just appends any errors to a list.
Expand Down Expand Up @@ -223,7 +232,7 @@ def test_flake(self):
"""
out = StringIO()
reporter = Reporter(out, None)
message = UnusedImport('foo.py', 42, 'bar')
message = UnusedImport('foo.py', Stmt(42), 'bar')
reporter.flake(message)
self.assertEquals(out.getvalue(), "%s\n" % (message,))

Expand Down Expand Up @@ -434,7 +443,7 @@ def test_pyflakesWarning(self):
count, errors = self.getErrors(sourcePath)
self.assertEquals(count, 1)
self.assertEquals(
errors, [('flake', str(UnusedImport(sourcePath, 1, 'foo')))])
errors, [('flake', str(UnusedImport(sourcePath, Stmt(1), 'foo')))])


@skipIf(sys.version_info >= (3,), "not relevant")
Expand Down Expand Up @@ -489,9 +498,9 @@ def test_checkRecursive(self):
self.assertEqual(warnings, 2)
self.assertEqual(
sorted(log),
sorted([('flake', str(UnusedImport(file1, 1, 'baz'))),
sorted([('flake', str(UnusedImport(file1, Stmt(1), 'baz'))),
('flake',
str(UnusedImport(file2, 1, 'contraband')))]))
str(UnusedImport(file2, Stmt(1), 'contraband')))]))


class IntegrationTests(TestCase):
Expand Down Expand Up @@ -563,7 +572,7 @@ def test_fileWithFlakes(self):
fd.write("import contraband\n".encode('ascii'))
fd.close()
d = self.runPyflakes([self.tempfilepath])
self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, 1, 'contraband'), '', 1))
self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, Stmt(1), 'contraband'), '', 1))


def test_errors(self):
Expand All @@ -581,4 +590,4 @@ def test_readFromStdin(self):
If no arguments are passed to C{pyflakes} then it reads from stdin.
"""
d = self.runPyflakes([], stdin='import contraband'.encode('ascii'))
self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1))
self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', Stmt(1), 'contraband'), '', 1))

0 comments on commit ef4da24

Please sign in to comment.