Merge lp:~pyflakes-dev/pyflakes/0.6-col-offset into lp:pyflakes
- 0.6-col-offset
- Merge into master
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Pyflakes Dev | Pending | ||
Review via email: mp+156231@code.launchpad.net |
Commit message
Description of the change
Adds the column number
To post a comment you must log in.
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", |