Merge lp:~pyflakes-dev/pyflakes/0.6-col-offset into lp:pyflakes

Proposed by Florent
Status: Merged
Merged at revision: 86
Proposed branch: lp:~pyflakes-dev/pyflakes/0.6-col-offset
Merge into: lp:pyflakes
Diff against target: 467 lines (+97/-61)
8 files modified
NEWS.txt (+3/-0)
pyflakes/__init__.py (+7/-1)
pyflakes/__main__.py (+4/-0)
pyflakes/api.py (+5/-3)
pyflakes/checker.py (+19/-19)
pyflakes/messages.py (+32/-31)
pyflakes/test/test_api.py (+15/-6)
setup.py (+12/-1)
To merge this branch: bzr merge lp:~pyflakes-dev/pyflakes/0.6-col-offset
Reviewer Review Type Date Requested Status
Pyflakes Dev Pending
Review via email: mp+156231@code.launchpad.net

Description of the change

Adds the column number

To post a comment you must log in.
77. By Florent

Tests: rename the mock AST node class.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS.txt'
2--- NEWS.txt 2013-03-06 22:39:32 +0000
3+++ NEWS.txt 2013-03-30 16:06:22 +0000
4@@ -1,4 +1,7 @@
5 0.6.x (unreleased):
6+ - Add --version and --help options.
7+ - Support `python -m pyflakes`.
8+ - Add attribute `Message.col` to report column offset.
9 - Do not report redefinition of variable for a variable used in a list
10 comprehension in a conditional.
11 - Do not report redefinition of variable for generator expressions and
12
13=== modified file 'pyflakes/__init__.py'
14--- pyflakes/__init__.py 2013-01-29 15:39:57 +0000
15+++ pyflakes/__init__.py 2013-03-30 16:06:22 +0000
16@@ -1,2 +1,8 @@
17
18-__version__ = '0.6.1'
19+__version__ = '0.6.2a0'
20+
21+
22+# Python 2.5 support for: python -m pyflakes
23+if __name__ == '__main__':
24+ from pyflakes.api import main
25+ main(prog='pyflakes')
26
27=== added file 'pyflakes/__main__.py'
28--- pyflakes/__main__.py 1970-01-01 00:00:00 +0000
29+++ pyflakes/__main__.py 2013-03-30 16:06:22 +0000
30@@ -0,0 +1,4 @@
31+from pyflakes.api import main
32+
33+if __name__ == '__main__':
34+ main(prog='pyflakes')
35
36=== modified file 'pyflakes/api.py'
37--- pyflakes/api.py 2013-01-25 12:56:41 +0000
38+++ pyflakes/api.py 2013-03-30 16:06:22 +0000
39@@ -5,8 +5,9 @@
40 import sys
41 import os
42 import _ast
43+from optparse import OptionParser
44
45-from pyflakes import checker
46+from pyflakes import checker, __version__
47 from pyflakes import reporter as modReporter
48
49 __all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
50@@ -120,8 +121,9 @@
51 return warnings
52
53
54-def main():
55- args = sys.argv[1:]
56+def main(prog=None):
57+ parser = OptionParser(prog=prog, version=__version__)
58+ __, args = parser.parse_args()
59 reporter = modReporter._makeDefaultReporter()
60 if args:
61 warnings = checkRecursive(args, reporter)
62
63=== modified file 'pyflakes/checker.py'
64--- pyflakes/checker.py 2013-03-06 22:54:03 +0000
65+++ pyflakes/checker.py 2013-03-30 16:06:22 +0000
66@@ -273,7 +273,7 @@
67 undefined = set(all) - set(scope)
68 for name in undefined:
69 self.report(messages.UndefinedExport,
70- scope['__all__'].source.lineno, name)
71+ scope['__all__'].source, name)
72 else:
73 all = []
74
75@@ -282,7 +282,7 @@
76 if isinstance(importation, Importation):
77 if not importation.used and importation.name not in all:
78 self.report(messages.UnusedImport,
79- importation.source.lineno, importation.name)
80+ importation.source, importation.name)
81
82 def pushFunctionScope(self):
83 self.scopeStack.append(FunctionScope())
84@@ -363,7 +363,7 @@
85 and not self.differentForks(node, existing.source)):
86 redefinedWhileUnused = True
87 self.report(messages.RedefinedWhileUnused,
88- node.lineno, value.name, existing.source.lineno)
89+ node, value.name, existing.source)
90
91 existing = self.scope.get(value.name)
92 if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
93@@ -371,18 +371,18 @@
94 and not self.hasParent(existing.source, (ast.For, ast.ListComp))
95 and not self.differentForks(node, existing.source)):
96 self.report(messages.RedefinedInListComp,
97- node.lineno, value.name, existing.source.lineno)
98+ node, value.name, existing.source)
99
100 if isinstance(value, UnBinding):
101 try:
102 del self.scope[value.name]
103 except KeyError:
104- self.report(messages.UndefinedName, node.lineno, value.name)
105+ self.report(messages.UndefinedName, node, value.name)
106 elif (isinstance(existing, Definition)
107 and not existing.used
108 and not self.differentForks(node, existing.source)):
109 self.report(messages.RedefinedWhileUnused,
110- node.lineno, value.name, existing.source.lineno)
111+ node, value.name, existing.source)
112 else:
113 self.scope[value.name] = value
114
115@@ -393,7 +393,7 @@
116 # try local scope
117 importStarred = self.scope.importStarred
118 try:
119- self.scope[name].used = (self.scope, node.lineno)
120+ self.scope[name].used = (self.scope, node)
121 except KeyError:
122 pass
123 else:
124@@ -405,7 +405,7 @@
125 if not isinstance(scope, FunctionScope):
126 continue
127 try:
128- scope[name].used = (self.scope, node.lineno)
129+ scope[name].used = (self.scope, node)
130 except KeyError:
131 pass
132 else:
133@@ -414,14 +414,14 @@
134 # try global scope
135 importStarred = importStarred or self.scopeStack[0].importStarred
136 try:
137- self.scopeStack[0][name].used = (self.scope, node.lineno)
138+ self.scopeStack[0][name].used = (self.scope, node)
139 except KeyError:
140 if not importStarred and name not in self.builtIns:
141 if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'):
142 # the special name __path__ is valid only in packages
143 pass
144 else:
145- self.report(messages.UndefinedName, node.lineno, name)
146+ self.report(messages.UndefinedName, node, name)
147
148 def handleNodeStore(self, node):
149 name = getNodeName(node)
150@@ -440,7 +440,7 @@
151 and name not in self.scope.globals):
152 # then it's probably a mistake
153 self.report(messages.UndefinedLocal,
154- scope[name].used[1], name, scope[name].source.lineno)
155+ scope[name].used[1], name, scope[name].source)
156 break
157
158 parent = getattr(node, 'parent', None)
159@@ -579,7 +579,7 @@
160 # unused ones will get an unused import warning
161 and self.scope[varn].used):
162 self.report(messages.ImportShadowedByLoopVar,
163- node.lineno, varn, self.scope[varn].source.lineno)
164+ node, varn, self.scope[varn].source)
165
166 self.handleChildren(node)
167
168@@ -619,7 +619,7 @@
169 else:
170 if arg.id in args:
171 self.report(messages.DuplicateArgument,
172- node.lineno, arg.id)
173+ node, arg.id)
174 args.append(arg.id)
175 addArgs(node.args.args)
176 defaults = node.args.defaults
177@@ -627,7 +627,7 @@
178 for arg in node.args.args + node.args.kwonlyargs:
179 if arg.arg in args:
180 self.report(messages.DuplicateArgument,
181- node.lineno, arg.arg)
182+ node, arg.arg)
183 args.append(arg.arg)
184 self.handleNode(arg.annotation, node)
185 if hasattr(node, 'returns'): # Only for FunctionDefs
186@@ -641,7 +641,7 @@
187 if not wildcard:
188 continue
189 if wildcard in args:
190- self.report(messages.DuplicateArgument, node.lineno, wildcard)
191+ self.report(messages.DuplicateArgument, node, wildcard)
192 args.append(wildcard)
193 for default in defaults:
194 self.handleNode(default, node)
195@@ -668,7 +668,7 @@
196 and not self.scope.usesLocals
197 and isinstance(binding, Assignment)):
198 self.report(messages.UnusedVariable,
199- binding.source.lineno, name)
200+ binding.source, name)
201 self.deferAssignment(checkUnusedAssignments)
202 self.popScope()
203
204@@ -713,19 +713,19 @@
205 if node.module == '__future__':
206 if not self.futuresAllowed:
207 self.report(messages.LateFutureImport,
208- node.lineno, [n.name for n in node.names])
209+ node, [n.name for n in node.names])
210 else:
211 self.futuresAllowed = False
212
213 for alias in node.names:
214 if alias.name == '*':
215 self.scope.importStarred = True
216- self.report(messages.ImportStarUsed, node.lineno, node.module)
217+ self.report(messages.ImportStarUsed, node, node.module)
218 continue
219 name = alias.asname or alias.name
220 importation = Importation(name, node)
221 if node.module == '__future__':
222- importation.used = (self.scope, node.lineno)
223+ importation.used = (self.scope, node)
224 self.addBinding(node, importation)
225
226 def EXCEPTHANDLER(self, node):
227
228=== modified file 'pyflakes/messages.py'
229--- pyflakes/messages.py 2013-01-25 11:50:40 +0000
230+++ pyflakes/messages.py 2013-03-30 16:06:22 +0000
231@@ -5,9 +5,10 @@
232 message = ''
233 message_args = ()
234
235- def __init__(self, filename, lineno):
236+ def __init__(self, filename, loc):
237 self.filename = filename
238- self.lineno = lineno
239+ self.lineno = loc.lineno
240+ self.col = getattr(loc, 'col_offset', 0)
241
242 def __str__(self):
243 return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args)
244@@ -16,88 +17,88 @@
245 class UnusedImport(Message):
246 message = '%r imported but unused'
247
248- def __init__(self, filename, lineno, name):
249- Message.__init__(self, filename, lineno)
250+ def __init__(self, filename, loc, name):
251+ Message.__init__(self, filename, loc)
252 self.message_args = (name,)
253
254
255 class RedefinedWhileUnused(Message):
256 message = 'redefinition of unused %r from line %r'
257
258- def __init__(self, filename, lineno, name, orig_lineno):
259- Message.__init__(self, filename, lineno)
260- self.message_args = (name, orig_lineno)
261+ def __init__(self, filename, loc, name, orig_loc):
262+ Message.__init__(self, filename, loc)
263+ self.message_args = (name, orig_loc.lineno)
264
265
266 class RedefinedInListComp(Message):
267 message = 'list comprehension redefines %r from line %r'
268
269- def __init__(self, filename, lineno, name, orig_lineno):
270- Message.__init__(self, filename, lineno)
271- self.message_args = (name, orig_lineno)
272+ def __init__(self, filename, loc, name, orig_loc):
273+ Message.__init__(self, filename, loc)
274+ self.message_args = (name, orig_loc.lineno)
275
276
277 class ImportShadowedByLoopVar(Message):
278 message = 'import %r from line %r shadowed by loop variable'
279
280- def __init__(self, filename, lineno, name, orig_lineno):
281- Message.__init__(self, filename, lineno)
282- self.message_args = (name, orig_lineno)
283+ def __init__(self, filename, loc, name, orig_loc):
284+ Message.__init__(self, filename, loc)
285+ self.message_args = (name, orig_loc.lineno)
286
287
288 class ImportStarUsed(Message):
289 message = "'from %s import *' used; unable to detect undefined names"
290
291- def __init__(self, filename, lineno, modname):
292- Message.__init__(self, filename, lineno)
293+ def __init__(self, filename, loc, modname):
294+ Message.__init__(self, filename, loc)
295 self.message_args = (modname,)
296
297
298 class UndefinedName(Message):
299 message = 'undefined name %r'
300
301- def __init__(self, filename, lineno, name):
302- Message.__init__(self, filename, lineno)
303+ def __init__(self, filename, loc, name):
304+ Message.__init__(self, filename, loc)
305 self.message_args = (name,)
306
307
308 class UndefinedExport(Message):
309 message = 'undefined name %r in __all__'
310
311- def __init__(self, filename, lineno, name):
312- Message.__init__(self, filename, lineno)
313+ def __init__(self, filename, loc, name):
314+ Message.__init__(self, filename, loc)
315 self.message_args = (name,)
316
317
318 class UndefinedLocal(Message):
319 message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment"
320
321- def __init__(self, filename, lineno, name, orig_lineno):
322- Message.__init__(self, filename, lineno)
323- self.message_args = (name, orig_lineno)
324+ def __init__(self, filename, loc, name, orig_loc):
325+ Message.__init__(self, filename, loc)
326+ self.message_args = (name, orig_loc.lineno)
327
328
329 class DuplicateArgument(Message):
330 message = 'duplicate argument %r in function definition'
331
332- def __init__(self, filename, lineno, name):
333- Message.__init__(self, filename, lineno)
334+ def __init__(self, filename, loc, name):
335+ Message.__init__(self, filename, loc)
336 self.message_args = (name,)
337
338
339 class Redefined(Message):
340 message = 'redefinition of %r from line %r'
341
342- def __init__(self, filename, lineno, name, orig_lineno):
343- Message.__init__(self, filename, lineno)
344- self.message_args = (name, orig_lineno)
345+ def __init__(self, filename, loc, name, orig_loc):
346+ Message.__init__(self, filename, loc)
347+ self.message_args = (name, orig_loc.lineno)
348
349
350 class LateFutureImport(Message):
351 message = 'future import(s) %r after other statements'
352
353- def __init__(self, filename, lineno, names):
354- Message.__init__(self, filename, lineno)
355+ def __init__(self, filename, loc, names):
356+ Message.__init__(self, filename, loc)
357 self.message_args = (names,)
358
359
360@@ -108,6 +109,6 @@
361 """
362 message = 'local variable %r is assigned to but never used'
363
364- def __init__(self, filename, lineno, names):
365- Message.__init__(self, filename, lineno)
366+ def __init__(self, filename, loc, names):
367+ Message.__init__(self, filename, loc)
368 self.message_args = (names,)
369
370=== modified file 'pyflakes/test/test_api.py'
371--- pyflakes/test/test_api.py 2013-01-25 12:56:41 +0000
372+++ pyflakes/test/test_api.py 2013-03-30 16:06:22 +0000
373@@ -36,6 +36,15 @@
374 sys.stderr = outer
375
376
377+class Node(object):
378+ """
379+ Mock an AST node.
380+ """
381+ def __init__(self, lineno, col_offset=0):
382+ self.lineno = lineno
383+ self.col_offset = col_offset
384+
385+
386 class LoggingReporter(object):
387 """
388 Implementation of Reporter that just appends any errors to a list.
389@@ -223,7 +232,7 @@
390 """
391 out = StringIO()
392 reporter = Reporter(out, None)
393- message = UnusedImport('foo.py', 42, 'bar')
394+ message = UnusedImport('foo.py', Node(42), 'bar')
395 reporter.flake(message)
396 self.assertEquals(out.getvalue(), "%s\n" % (message,))
397
398@@ -434,7 +443,7 @@
399 count, errors = self.getErrors(sourcePath)
400 self.assertEquals(count, 1)
401 self.assertEquals(
402- errors, [('flake', str(UnusedImport(sourcePath, 1, 'foo')))])
403+ errors, [('flake', str(UnusedImport(sourcePath, Node(1), 'foo')))])
404
405
406 @skipIf(sys.version_info >= (3,), "not relevant")
407@@ -489,9 +498,9 @@
408 self.assertEqual(warnings, 2)
409 self.assertEqual(
410 sorted(log),
411- sorted([('flake', str(UnusedImport(file1, 1, 'baz'))),
412+ sorted([('flake', str(UnusedImport(file1, Node(1), 'baz'))),
413 ('flake',
414- str(UnusedImport(file2, 1, 'contraband')))]))
415+ str(UnusedImport(file2, Node(1), 'contraband')))]))
416
417
418 class IntegrationTests(TestCase):
419@@ -563,7 +572,7 @@
420 fd.write("import contraband\n".encode('ascii'))
421 fd.close()
422 d = self.runPyflakes([self.tempfilepath])
423- self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, 1, 'contraband'), '', 1))
424+ self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, Node(1), 'contraband'), '', 1))
425
426
427 def test_errors(self):
428@@ -581,4 +590,4 @@
429 If no arguments are passed to C{pyflakes} then it reads from stdin.
430 """
431 d = self.runPyflakes([], stdin='import contraband'.encode('ascii'))
432- self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1))
433+ self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', Node(1), 'contraband'), '', 1))
434
435=== modified file 'setup.py'
436--- setup.py 2013-01-29 15:39:57 +0000
437+++ setup.py 2013-03-30 16:06:22 +0000
438@@ -1,6 +1,9 @@
439 #!/usr/bin/env python
440 # Copyright 2005-2011 Divmod, Inc.
441 # Copyright 2013 Florent Xicluna. See LICENSE file for details
442+from __future__ import with_statement
443+
444+import os.path
445 import sys
446
447 try:
448@@ -19,10 +22,18 @@
449 'console_scripts': ['pyflakes = pyflakes.api:main'],
450 }
451
452+
453+def get_version(fname=os.path.join('pyflakes', '__init__.py')):
454+ with open(fname) as f:
455+ for line in f:
456+ if line.startswith('__version__'):
457+ return eval(line.split('=')[-1])
458+
459+
460 setup(
461 name="pyflakes",
462 license="MIT",
463- version="0.6.1",
464+ version=get_version(),
465 description="passive checker of Python programs",
466 author="Phil Frost",
467 author_email="indigo@bitglue.com",

Subscribers

People subscribed via source and target branches

to all changes: