Merge lp:~florent.x/pyflakes/989203-python3 into lp:pyflakes
- 989203-python3
- Merge into master
Proposed by
Florent
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 53 | ||||
Proposed branch: | lp:~florent.x/pyflakes/989203-python3 | ||||
Merge into: | lp:pyflakes | ||||
Diff against target: |
814 lines (+215/-149) 9 files modified
.travis.yml (+5/-1) pyflakes/checker.py (+128/-96) pyflakes/reporter.py (+11/-7) pyflakes/scripts/pyflakes.py (+7/-5) pyflakes/test/harness.py (+2/-2) pyflakes/test/test_imports.py (+23/-17) pyflakes/test/test_script.py (+28/-15) pyflakes/test/test_undefined_names.py (+9/-6) setup.py (+2/-0) |
||||
To merge this branch: | bzr merge lp:~florent.x/pyflakes/989203-python3 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Pyflakes Dev | Pending | ||
Review via email: mp+144668@code.launchpad.net |
Commit message
Description of the change
This branch brings the minimal changes to achieve Python 3 support.
Tests are green: https:/
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 '.travis.yml' |
2 | --- .travis.yml 2013-01-19 00:26:52 +0000 |
3 | +++ .travis.yml 2013-01-24 10:27:21 +0000 |
4 | @@ -3,10 +3,14 @@ |
5 | - 2.5 |
6 | - 2.6 |
7 | - 2.7 |
8 | + - 3.2 |
9 | + - 3.3 |
10 | - pypy |
11 | +before_install: |
12 | + - if [ "${TRAVIS_PYTHON_VERSION::1}" == "3" ]; then export TEST_PKG=unittest2py3k; else export TEST_PKG=unittest2; fi |
13 | install: |
14 | - python setup.py install |
15 | - - pip install unittest2 |
16 | + - pip install $TEST_PKG |
17 | script: |
18 | - unit2 discover |
19 | matrix: |
20 | |
21 | === modified file 'pyflakes/checker.py' |
22 | --- pyflakes/checker.py 2012-01-10 19:09:22 +0000 |
23 | +++ pyflakes/checker.py 2013-01-24 10:27:21 +0000 |
24 | @@ -2,9 +2,14 @@ |
25 | # (c) 2005-2010 Divmod, Inc. |
26 | # See LICENSE file for details |
27 | |
28 | -import __builtin__ |
29 | import os.path |
30 | import _ast |
31 | +try: |
32 | + import builtins |
33 | + PY2 = False |
34 | +except ImportError: |
35 | + import __builtin__ as builtins |
36 | + PY2 = True |
37 | |
38 | from pyflakes import messages |
39 | |
40 | @@ -173,11 +178,18 @@ |
41 | pass |
42 | |
43 | |
44 | -# Globally defined names which are not attributes of the __builtin__ module, or |
45 | +# Globally defined names which are not attributes of the builtins module, or |
46 | # are only present on some platforms. |
47 | _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError'] |
48 | |
49 | |
50 | +def getNodeName(node): |
51 | + # Returns node.id, or node.name, or None |
52 | + if hasattr(node, 'id'): # One of the many nodes with an id |
53 | + return node.id |
54 | + if hasattr(node, 'name'): # a ExceptHandler node |
55 | + return node.name |
56 | + |
57 | |
58 | class Checker(object): |
59 | """ |
60 | @@ -275,7 +287,7 @@ |
61 | all = [] |
62 | |
63 | # Look for imported names that aren't used. |
64 | - for importation in scope.itervalues(): |
65 | + for importation in scope.values(): |
66 | if isinstance(importation, Importation): |
67 | if not importation.used and importation.name not in all: |
68 | self.report( |
69 | @@ -293,6 +305,83 @@ |
70 | def report(self, messageClass, *args, **kwargs): |
71 | self.messages.append(messageClass(self.filename, *args, **kwargs)) |
72 | |
73 | + def handleNodeLoad(self, node): |
74 | + name = getNodeName(node) |
75 | + if not name: |
76 | + return |
77 | + # try local scope |
78 | + importStarred = self.scope.importStarred |
79 | + try: |
80 | + self.scope[name].used = (self.scope, node.lineno) |
81 | + except KeyError: |
82 | + pass |
83 | + else: |
84 | + return |
85 | + |
86 | + # try enclosing function scopes |
87 | + for scope in self.scopeStack[-2:0:-1]: |
88 | + importStarred = importStarred or scope.importStarred |
89 | + if not isinstance(scope, FunctionScope): |
90 | + continue |
91 | + try: |
92 | + scope[name].used = (self.scope, node.lineno) |
93 | + except KeyError: |
94 | + pass |
95 | + else: |
96 | + return |
97 | + |
98 | + # try global scope |
99 | + importStarred = importStarred or self.scopeStack[0].importStarred |
100 | + try: |
101 | + self.scopeStack[0][name].used = (self.scope, node.lineno) |
102 | + except KeyError: |
103 | + if ((not hasattr(builtins, name)) and name not in _MAGIC_GLOBALS and not importStarred): |
104 | + if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'): |
105 | + # the special name __path__ is valid only in packages |
106 | + pass |
107 | + else: |
108 | + self.report(messages.UndefinedName, node.lineno, name) |
109 | + |
110 | + def handleNodeStore(self, node): |
111 | + name = getNodeName(node) |
112 | + if not name: |
113 | + return |
114 | + # if the name hasn't already been defined in the current scope |
115 | + if isinstance(self.scope, FunctionScope) and name not in self.scope: |
116 | + # for each function or module scope above us |
117 | + for scope in self.scopeStack[:-1]: |
118 | + if not isinstance(scope, (FunctionScope, ModuleScope)): |
119 | + continue |
120 | + # if the name was defined in that scope, and the name has |
121 | + # been accessed already in the current scope, and hasn't |
122 | + # been declared global |
123 | + if (name in scope and scope[name].used and scope[name].used[0] is self.scope |
124 | + and name not in self.scope.globals): |
125 | + # then it's probably a mistake |
126 | + self.report(messages.UndefinedLocal, scope[name].used[1], name, |
127 | + scope[name].source.lineno) |
128 | + break |
129 | + |
130 | + parent = getattr(node, 'parent', None) |
131 | + if isinstance(parent, (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)): |
132 | + binding = Binding(name, node) |
133 | + elif parent is not None and name == '__all__' and isinstance(self.scope, ModuleScope): |
134 | + binding = ExportBinding(name, parent.value) |
135 | + else: |
136 | + binding = Assignment(name, node) |
137 | + if name in self.scope: |
138 | + binding.used = self.scope[name].used |
139 | + self.addBinding(node.lineno, binding) |
140 | + |
141 | + def handleNodeDelete(self, node): |
142 | + name = getNodeName(node) |
143 | + if not name: |
144 | + return |
145 | + if isinstance(self.scope, FunctionScope) and name in self.scope.globals: |
146 | + del self.scope.globals[name] |
147 | + else: |
148 | + self.addBinding(node.lineno, UnBinding(name, node)) |
149 | + |
150 | def handleChildren(self, tree): |
151 | for node in iter_child_nodes(tree): |
152 | self.handleNode(node, tree) |
153 | @@ -309,7 +398,7 @@ |
154 | def handleNode(self, node, parent): |
155 | node.parent = parent |
156 | if self.traceTree: |
157 | - print ' ' * self.nodeDepth + node.__class__.__name__ |
158 | + print(' ' * self.nodeDepth + node.__class__.__name__) |
159 | self.nodeDepth += 1 |
160 | if self.futuresAllowed and not \ |
161 | (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)): |
162 | @@ -321,14 +410,14 @@ |
163 | finally: |
164 | self.nodeDepth -= 1 |
165 | if self.traceTree: |
166 | - print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ |
167 | + print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__) |
168 | |
169 | def ignore(self, node): |
170 | pass |
171 | |
172 | # "stmt" type nodes |
173 | - RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \ |
174 | - TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren |
175 | + RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \ |
176 | + TRYEXCEPT = TRYFINALLY = TRY = ASSERT = EXEC = EXPR = handleChildren |
177 | |
178 | CONTINUE = BREAK = PASS = ignore |
179 | |
180 | @@ -350,7 +439,7 @@ |
181 | EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore |
182 | |
183 | # additional node types |
184 | - COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren |
185 | + COMPREHENSION = KEYWORD = handleChildren |
186 | |
187 | def addBinding(self, lineno, value, reportRedef=True): |
188 | '''Called when a binding is altered. |
189 | @@ -436,81 +525,11 @@ |
190 | """ |
191 | # Locate the name in locals / function / globals scopes. |
192 | if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)): |
193 | - # try local scope |
194 | - importStarred = self.scope.importStarred |
195 | - try: |
196 | - self.scope[node.id].used = (self.scope, node.lineno) |
197 | - except KeyError: |
198 | - pass |
199 | - else: |
200 | - return |
201 | - |
202 | - # try enclosing function scopes |
203 | - |
204 | - for scope in self.scopeStack[-2:0:-1]: |
205 | - importStarred = importStarred or scope.importStarred |
206 | - if not isinstance(scope, FunctionScope): |
207 | - continue |
208 | - try: |
209 | - scope[node.id].used = (self.scope, node.lineno) |
210 | - except KeyError: |
211 | - pass |
212 | - else: |
213 | - return |
214 | - |
215 | - # try global scope |
216 | - |
217 | - importStarred = importStarred or self.scopeStack[0].importStarred |
218 | - try: |
219 | - self.scopeStack[0][node.id].used = (self.scope, node.lineno) |
220 | - except KeyError: |
221 | - if ((not hasattr(__builtin__, node.id)) |
222 | - and node.id not in _MAGIC_GLOBALS |
223 | - and not importStarred): |
224 | - if (os.path.basename(self.filename) == '__init__.py' and |
225 | - node.id == '__path__'): |
226 | - # the special name __path__ is valid only in packages |
227 | - pass |
228 | - else: |
229 | - self.report(messages.UndefinedName, node.lineno, node.id) |
230 | + self.handleNodeLoad(node) |
231 | elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)): |
232 | - # if the name hasn't already been defined in the current scope |
233 | - if isinstance(self.scope, FunctionScope) and node.id not in self.scope: |
234 | - # for each function or module scope above us |
235 | - for scope in self.scopeStack[:-1]: |
236 | - if not isinstance(scope, (FunctionScope, ModuleScope)): |
237 | - continue |
238 | - # if the name was defined in that scope, and the name has |
239 | - # been accessed already in the current scope, and hasn't |
240 | - # been declared global |
241 | - if (node.id in scope |
242 | - and scope[node.id].used |
243 | - and scope[node.id].used[0] is self.scope |
244 | - and node.id not in self.scope.globals): |
245 | - # then it's probably a mistake |
246 | - self.report(messages.UndefinedLocal, |
247 | - scope[node.id].used[1], |
248 | - node.id, |
249 | - scope[node.id].source.lineno) |
250 | - break |
251 | - |
252 | - if isinstance(node.parent, |
253 | - (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)): |
254 | - binding = Binding(node.id, node) |
255 | - elif (node.id == '__all__' and |
256 | - isinstance(self.scope, ModuleScope)): |
257 | - binding = ExportBinding(node.id, node.parent.value) |
258 | - else: |
259 | - binding = Assignment(node.id, node) |
260 | - if node.id in self.scope: |
261 | - binding.used = self.scope[node.id].used |
262 | - self.addBinding(node.lineno, binding) |
263 | + self.handleNodeStore(node) |
264 | elif isinstance(node.ctx, _ast.Del): |
265 | - if isinstance(self.scope, FunctionScope) and \ |
266 | - node.id in self.scope.globals: |
267 | - del self.scope.globals[node.id] |
268 | - else: |
269 | - self.addBinding(node.lineno, UnBinding(node.id, node)) |
270 | + self.handleNodeDelete(node) |
271 | else: |
272 | # must be a Param context -- this only happens for names in function |
273 | # arguments, but these aren't dispatched through here |
274 | @@ -536,18 +555,28 @@ |
275 | def runFunction(): |
276 | args = [] |
277 | |
278 | - def addArgs(arglist): |
279 | - for arg in arglist: |
280 | - if isinstance(arg, _ast.Tuple): |
281 | - addArgs(arg.elts) |
282 | - else: |
283 | - if arg.id in args: |
284 | + if PY2: |
285 | + def addArgs(arglist): |
286 | + for arg in arglist: |
287 | + if isinstance(arg, _ast.Tuple): |
288 | + addArgs(arg.elts) |
289 | + else: |
290 | + if arg.id in args: |
291 | + self.report(messages.DuplicateArgument, |
292 | + node.lineno, arg.id) |
293 | + args.append(arg.id) |
294 | + else: |
295 | + def addArgs(arglist): |
296 | + for arg in arglist: |
297 | + if arg.arg in args: |
298 | self.report(messages.DuplicateArgument, |
299 | - node.lineno, arg.id) |
300 | - args.append(arg.id) |
301 | + node.lineno, arg.arg) |
302 | + args.append(arg.arg) |
303 | |
304 | self.pushFunctionScope() |
305 | addArgs(node.args.args) |
306 | + if not PY2: |
307 | + addArgs(node.args.kwonlyargs) |
308 | # vararg/kwarg identifiers are not Name nodes |
309 | if node.args.vararg: |
310 | args.append(node.args.vararg) |
311 | @@ -566,7 +595,7 @@ |
312 | """ |
313 | Check to see if any assignments have not been used. |
314 | """ |
315 | - for name, binding in self.scope.iteritems(): |
316 | + for name, binding in self.scope.items(): |
317 | if (not binding.used and not name in self.scope.globals |
318 | and isinstance(binding, Assignment)): |
319 | self.report(messages.UnusedVariable, |
320 | @@ -600,13 +629,9 @@ |
321 | self.handleNode(target, node) |
322 | |
323 | def AUGASSIGN(self, node): |
324 | - # AugAssign is awkward: must set the context explicitly and visit twice, |
325 | - # once with AugLoad context, once with AugStore context |
326 | - node.target.ctx = _ast.AugLoad() |
327 | - self.handleNode(node.target, node) |
328 | + self.handleNodeLoad(node.target) |
329 | self.handleNode(node.value, node) |
330 | - node.target.ctx = _ast.AugStore() |
331 | - self.handleNode(node.target, node) |
332 | + self.handleNodeStore(node.target) |
333 | |
334 | def IMPORT(self, node): |
335 | for alias in node.names: |
336 | @@ -632,3 +657,10 @@ |
337 | if node.module == '__future__': |
338 | importation.used = (self.scope, node.lineno) |
339 | self.addBinding(node.lineno, importation) |
340 | + |
341 | + def EXCEPTHANDLER(self, node): |
342 | + # in addition to handling children, we must handle the name of the exception, which is not |
343 | + # a Name node, but a simple string. |
344 | + if node.name: |
345 | + self.handleNodeStore(node) |
346 | + self.handleChildren(node) |
347 | |
348 | === modified file 'pyflakes/reporter.py' |
349 | --- pyflakes/reporter.py 2012-10-23 13:07:35 +0000 |
350 | +++ pyflakes/reporter.py 2013-01-24 10:27:21 +0000 |
351 | @@ -2,6 +2,10 @@ |
352 | # See LICENSE file for details |
353 | |
354 | import sys |
355 | +try: |
356 | + u = unicode |
357 | +except NameError: |
358 | + u = str |
359 | |
360 | |
361 | class Reporter(object): |
362 | @@ -33,7 +37,7 @@ |
363 | @param msg: A message explaining the problem. |
364 | @ptype msg: C{unicode} |
365 | """ |
366 | - self._stderr.write(u"%s: %s\n" % (filename, msg)) |
367 | + self._stderr.write(u("%s: %s\n") % (filename, msg)) |
368 | |
369 | |
370 | def syntaxError(self, filename, msg, lineno, offset, text): |
371 | @@ -54,11 +58,11 @@ |
372 | line = text.splitlines()[-1] |
373 | if offset is not None: |
374 | offset = offset - (len(text) - len(line)) |
375 | - self._stderr.write(u'%s:%d: %s\n' % (filename, lineno, msg)) |
376 | - self._stderr.write(line) |
377 | - self._stderr.write(u'\n') |
378 | + self._stderr.write(u('%s:%d: %s\n') % (filename, lineno, msg)) |
379 | + self._stderr.write(u(line)) |
380 | + self._stderr.write(u('\n')) |
381 | if offset is not None: |
382 | - self._stderr.write(u" " * (offset + 1) + u"^\n") |
383 | + self._stderr.write(u(" " * (offset + 1) + "^\n")) |
384 | |
385 | |
386 | def flake(self, message): |
387 | @@ -67,8 +71,8 @@ |
388 | |
389 | @param: A L{pyflakes.messages.Message}. |
390 | """ |
391 | - self._stdout.write(unicode(message)) |
392 | - self._stdout.write(u'\n') |
393 | + self._stdout.write(u(message)) |
394 | + self._stdout.write(u('\n')) |
395 | |
396 | |
397 | |
398 | |
399 | === modified file 'pyflakes/scripts/pyflakes.py' |
400 | --- pyflakes/scripts/pyflakes.py 2012-10-23 11:48:54 +0000 |
401 | +++ pyflakes/scripts/pyflakes.py 2013-01-24 10:27:21 +0000 |
402 | @@ -34,7 +34,8 @@ |
403 | # First, compile into an AST and handle syntax errors. |
404 | try: |
405 | tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST) |
406 | - except SyntaxError, value: |
407 | + except SyntaxError: |
408 | + value = sys.exc_info()[1] |
409 | msg = value.args[0] |
410 | |
411 | (lineno, offset, text) = value.lineno, value.offset, value.text |
412 | @@ -44,14 +45,14 @@ |
413 | # Avoid using msg, since for the only known case, it contains a |
414 | # bogus message that claims the encoding the file declared was |
415 | # unknown. |
416 | - reporter.unexpectedError(filename, u'problem decoding source') |
417 | + reporter.unexpectedError(filename, 'problem decoding source') |
418 | else: |
419 | reporter.syntaxError(filename, msg, lineno, offset, text) |
420 | return 1 |
421 | else: |
422 | # Okay, it's syntactically valid. Now check it. |
423 | w = checker.Checker(tree, filename) |
424 | - w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) |
425 | + w.messages.sort(key=lambda m: m.lineno) |
426 | for warning in w.messages: |
427 | reporter.flake(warning) |
428 | return len(w.messages) |
429 | @@ -69,8 +70,9 @@ |
430 | if reporter is None: |
431 | reporter = modReporter._makeDefaultReporter() |
432 | try: |
433 | - return check(file(filename, 'U').read() + '\n', filename, reporter) |
434 | - except IOError, msg: |
435 | + return check(open(filename, 'U').read() + '\n', filename, reporter) |
436 | + except IOError: |
437 | + msg = sys.exc_info()[1] |
438 | reporter.unexpectedError(filename, msg.args[1]) |
439 | return 1 |
440 | |
441 | |
442 | === modified file 'pyflakes/test/harness.py' |
443 | --- pyflakes/test/harness.py 2013-01-08 16:34:11 +0000 |
444 | +++ pyflakes/test/harness.py 2013-01-24 10:27:21 +0000 |
445 | @@ -15,8 +15,8 @@ |
446 | w = checker.Checker(ast, **kw) |
447 | outputs = [type(o) for o in w.messages] |
448 | expectedOutputs = list(expectedOutputs) |
449 | - outputs.sort() |
450 | - expectedOutputs.sort() |
451 | + outputs.sort(key=lambda t: t.__name__) |
452 | + expectedOutputs.sort(key=lambda t: t.__name__) |
453 | self.assertEqual(outputs, expectedOutputs, '''\ |
454 | for input: |
455 | %s |
456 | |
457 | === modified file 'pyflakes/test/test_imports.py' |
458 | --- pyflakes/test/test_imports.py 2013-01-08 16:34:11 +0000 |
459 | +++ pyflakes/test/test_imports.py 2013-01-24 10:27:21 +0000 |
460 | @@ -16,8 +16,8 @@ |
461 | self.flakes('from moo import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport) |
462 | |
463 | def test_usedImport(self): |
464 | - self.flakes('import fu; print fu') |
465 | - self.flakes('from baz import fu; print fu') |
466 | + self.flakes('import fu; print(fu)') |
467 | + self.flakes('from baz import fu; print(fu)') |
468 | |
469 | def test_redefinedWhileUnused(self): |
470 | self.flakes('import fu; fu = 3', m.RedefinedWhileUnused) |
471 | @@ -74,28 +74,28 @@ |
472 | import fu |
473 | class bar: |
474 | fu = 1 |
475 | - print fu |
476 | + print(fu) |
477 | ''') |
478 | |
479 | def test_usedInFunction(self): |
480 | self.flakes(''' |
481 | import fu |
482 | def fun(): |
483 | - print fu |
484 | + print(fu) |
485 | ''') |
486 | |
487 | def test_shadowedByParameter(self): |
488 | self.flakes(''' |
489 | import fu |
490 | def fun(fu): |
491 | - print fu |
492 | + print(fu) |
493 | ''', m.UnusedImport) |
494 | |
495 | self.flakes(''' |
496 | import fu |
497 | def fun(fu): |
498 | - print fu |
499 | - print fu |
500 | + print(fu) |
501 | + print(fu) |
502 | ''') |
503 | |
504 | def test_newAssignment(self): |
505 | @@ -106,12 +106,12 @@ |
506 | self.flakes('import fu; "bar".fu.baz', m.UnusedImport) |
507 | |
508 | def test_usedInSlice(self): |
509 | - self.flakes('import fu; print fu.bar[1:]') |
510 | + self.flakes('import fu; print(fu.bar[1:])') |
511 | |
512 | def test_usedInIfBody(self): |
513 | self.flakes(''' |
514 | import fu |
515 | - if True: print fu |
516 | + if True: print(fu) |
517 | ''') |
518 | |
519 | def test_usedInIfConditional(self): |
520 | @@ -131,7 +131,7 @@ |
521 | self.flakes(''' |
522 | import fu |
523 | if False: pass |
524 | - else: print fu |
525 | + else: print(fu) |
526 | ''') |
527 | |
528 | def test_usedInCall(self): |
529 | @@ -156,14 +156,14 @@ |
530 | import fu |
531 | def bleh(): |
532 | pass |
533 | - print fu |
534 | + print(fu) |
535 | ''') |
536 | |
537 | def test_usedInFor(self): |
538 | self.flakes(''' |
539 | import fu |
540 | for bar in range(9): |
541 | - print fu |
542 | + print(fu) |
543 | ''') |
544 | |
545 | def test_usedInForElse(self): |
546 | @@ -172,7 +172,7 @@ |
547 | for bar in range(10): |
548 | pass |
549 | else: |
550 | - print fu |
551 | + print(fu) |
552 | ''') |
553 | |
554 | def test_redefinedByFor(self): |
555 | @@ -262,11 +262,12 @@ |
556 | ''') |
557 | |
558 | def test_redefinedByExcept(self): |
559 | + as_exc = ', ' if version_info < (2, 6) else ' as ' |
560 | self.flakes(''' |
561 | import fu |
562 | try: pass |
563 | - except Exception, fu: pass |
564 | - ''', m.RedefinedWhileUnused) |
565 | + except Exception%sfu: pass |
566 | + ''' % as_exc, m.RedefinedWhileUnused) |
567 | |
568 | def test_usedInRaise(self): |
569 | self.flakes(''' |
570 | @@ -341,11 +342,16 @@ |
571 | def f(): global fu |
572 | ''', m.UnusedImport) |
573 | |
574 | + @skipIf(version_info >= (3,), 'deprecated syntax') |
575 | def test_usedInBackquote(self): |
576 | self.flakes('import fu; `fu`') |
577 | |
578 | def test_usedInExec(self): |
579 | - self.flakes('import fu; exec "print 1" in fu.bar') |
580 | + if version_info < (3,): |
581 | + exec_stmt = 'exec "print 1" in fu.bar' |
582 | + else: |
583 | + exec_stmt = 'exec("print(1)", fu.bar)' |
584 | + self.flakes('import fu; %s' % exec_stmt) |
585 | |
586 | def test_usedInLambda(self): |
587 | self.flakes('import fu; lambda: fu') |
588 | @@ -385,7 +391,7 @@ |
589 | import fu |
590 | class b: |
591 | def c(self): |
592 | - print fu |
593 | + print(fu) |
594 | ''') |
595 | |
596 | def test_importStar(self): |
597 | |
598 | === modified file 'pyflakes/test/test_script.py' |
599 | --- pyflakes/test/test_script.py 2013-01-08 16:34:11 +0000 |
600 | +++ pyflakes/test/test_script.py 2013-01-24 10:27:21 +0000 |
601 | @@ -7,9 +7,12 @@ |
602 | import shutil |
603 | import subprocess |
604 | import tempfile |
605 | -from StringIO import StringIO |
606 | +try: |
607 | + from io import StringIO |
608 | +except ImportError: |
609 | + from StringIO import StringIO |
610 | |
611 | -from unittest2 import TestCase |
612 | +from unittest2 import skipIf, TestCase |
613 | |
614 | from pyflakes.messages import UnusedImport |
615 | from pyflakes.reporter import Reporter |
616 | @@ -207,8 +210,8 @@ |
617 | """ |
618 | err = StringIO() |
619 | reporter = Reporter(None, err) |
620 | - reporter.unexpectedError(u'source.py', u'error message') |
621 | - self.assertEquals(u'source.py: error message\n', err.getvalue()) |
622 | + reporter.unexpectedError('source.py', 'error message') |
623 | + self.assertEquals('source.py: error message\n', err.getvalue()) |
624 | |
625 | |
626 | def test_flake(self): |
627 | @@ -234,6 +237,8 @@ |
628 | Make a temporary file containing C{content} and return a path to it. |
629 | """ |
630 | _, fpath = tempfile.mkstemp() |
631 | + if not hasattr(content, 'decode'): |
632 | + content = content.encode('ascii') |
633 | fd = open(fpath, 'wb') |
634 | fd.write(content) |
635 | fd.close() |
636 | @@ -309,10 +314,11 @@ |
637 | # Sanity check - SyntaxError.text should be multiple lines, if it |
638 | # isn't, something this test was unprepared for has happened. |
639 | def evaluate(source): |
640 | - exec source |
641 | + exec(source) |
642 | try: |
643 | evaluate(source) |
644 | - except SyntaxError, e: |
645 | + except SyntaxError: |
646 | + e = sys.exc_info()[1] |
647 | self.assertTrue(e.text.count('\n') > 1) |
648 | else: |
649 | self.fail() |
650 | @@ -353,12 +359,13 @@ |
651 | pass |
652 | """ |
653 | sourcePath = self.makeTempFile(source) |
654 | + last_line = ' ^\n' if sys.version_info >= (3, 2) else '' |
655 | self.assertHasErrors( |
656 | sourcePath, |
657 | ["""\ |
658 | %s:1: non-default argument follows default argument |
659 | def foo(bar=baz, bax): |
660 | -""" % (sourcePath,)]) |
661 | +%s""" % (sourcePath, last_line)]) |
662 | |
663 | |
664 | def test_nonKeywordAfterKeywordSyntaxError(self): |
665 | @@ -371,12 +378,13 @@ |
666 | foo(bar=baz, bax) |
667 | """ |
668 | sourcePath = self.makeTempFile(source) |
669 | + last_line = ' ^\n' if sys.version_info >= (3, 2) else '' |
670 | self.assertHasErrors( |
671 | sourcePath, |
672 | ["""\ |
673 | %s:1: non-keyword arg after keyword arg |
674 | foo(bar=baz, bax) |
675 | -""" % (sourcePath,)]) |
676 | +%s""" % (sourcePath, last_line)]) |
677 | |
678 | |
679 | def test_permissionDenied(self): |
680 | @@ -405,15 +413,17 @@ |
681 | errors, [('flake', str(UnusedImport(sourcePath, 1, 'foo')))]) |
682 | |
683 | |
684 | + @skipIf(sys.version_info >= (3,), "need adaptation for Python 3") |
685 | def test_misencodedFile(self): |
686 | """ |
687 | If a source file contains bytes which cannot be decoded, this is |
688 | reported on stderr. |
689 | """ |
690 | - source = u"""\ |
691 | + SNOWMAN = unichr(0x2603) |
692 | + source = ("""\ |
693 | # coding: ascii |
694 | -x = "\N{SNOWMAN}" |
695 | -""".encode('utf-8') |
696 | +x = "%s" |
697 | +""" % SNOWMAN).encode('utf-8') |
698 | sourcePath = self.makeTempFile(source) |
699 | self.assertHasErrors( |
700 | sourcePath, ["%s: problem decoding source\n" % (sourcePath,)]) |
701 | @@ -428,11 +438,11 @@ |
702 | os.mkdir(os.path.join(tempdir, 'foo')) |
703 | file1 = os.path.join(tempdir, 'foo', 'bar.py') |
704 | fd = open(file1, 'wb') |
705 | - fd.write("import baz\n") |
706 | + fd.write("import baz\n".encode('ascii')) |
707 | fd.close() |
708 | file2 = os.path.join(tempdir, 'baz.py') |
709 | fd = open(file2, 'wb') |
710 | - fd.write("import contraband") |
711 | + fd.write("import contraband".encode('ascii')) |
712 | fd.close() |
713 | log = [] |
714 | reporter = LoggingReporter(log) |
715 | @@ -488,6 +498,9 @@ |
716 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
717 | (stdout, stderr) = p.communicate() |
718 | rv = p.wait() |
719 | + if sys.version_info >= (3,): |
720 | + stdout = stdout.decode('utf-8') |
721 | + stderr = stderr.decode('utf-8') |
722 | return (stdout, stderr, rv) |
723 | |
724 | |
725 | @@ -508,7 +521,7 @@ |
726 | and the warnings are printed to stdout. |
727 | """ |
728 | fd = open(self.tempfilepath, 'wb') |
729 | - fd.write("import contraband\n") |
730 | + fd.write("import contraband\n".encode('ascii')) |
731 | fd.close() |
732 | d = self.runPyflakes([self.tempfilepath]) |
733 | self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, 1, 'contraband'), '', 1)) |
734 | @@ -528,5 +541,5 @@ |
735 | """ |
736 | If no arguments are passed to C{pyflakes} then it reads from stdin. |
737 | """ |
738 | - d = self.runPyflakes([], stdin='import contraband') |
739 | + d = self.runPyflakes([], stdin='import contraband'.encode('ascii')) |
740 | self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1)) |
741 | |
742 | === modified file 'pyflakes/test/test_undefined_names.py' |
743 | --- pyflakes/test/test_undefined_names.py 2013-01-08 16:34:11 +0000 |
744 | +++ pyflakes/test/test_undefined_names.py 2013-01-24 10:27:21 +0000 |
745 | @@ -1,7 +1,8 @@ |
746 | |
747 | from _ast import PyCF_ONLY_AST |
748 | +from sys import version_info |
749 | |
750 | -from unittest2 import skip, TestCase |
751 | +from unittest2 import skip, skipIf, TestCase |
752 | |
753 | from pyflakes import messages as m, checker |
754 | from pyflakes.test import harness |
755 | @@ -80,6 +81,7 @@ |
756 | bar |
757 | ''', m.ImportStarUsed, m.UndefinedName) |
758 | |
759 | + @skipIf(version_info >= (3,), 'obsolete syntax') |
760 | def test_unpackedParameter(self): |
761 | '''Unpacked function parameters create bindings''' |
762 | self.flakes(''' |
763 | @@ -102,7 +104,7 @@ |
764 | self.flakes(''' |
765 | global x |
766 | def foo(): |
767 | - print x |
768 | + print(x) |
769 | ''', m.UndefinedName) |
770 | |
771 | def test_del(self): |
772 | @@ -176,8 +178,8 @@ |
773 | def h(self): |
774 | a = x |
775 | x = None |
776 | - print x, a |
777 | - print x |
778 | + print(x, a) |
779 | + print(x) |
780 | ''', m.UndefinedLocal) |
781 | |
782 | |
783 | @@ -246,7 +248,7 @@ |
784 | '''star and double-star arg names are defined''' |
785 | self.flakes(''' |
786 | def f(a, *b, **c): |
787 | - print a, b, c |
788 | + print(a, b, c) |
789 | ''') |
790 | |
791 | def test_definedInGenExp(self): |
792 | @@ -254,7 +256,8 @@ |
793 | Using the loop variable of a generator expression results in no |
794 | warnings. |
795 | """ |
796 | - self.flakes('(a for a in xrange(10) if a)') |
797 | + self.flakes('(a for a in %srange(10) if a)' % |
798 | + ('x' if version_info < (3,) else '')) |
799 | |
800 | |
801 | |
802 | |
803 | === modified file 'setup.py' |
804 | --- setup.py 2011-09-03 16:31:04 +0000 |
805 | +++ setup.py 2013-01-24 10:27:21 +0000 |
806 | @@ -24,6 +24,8 @@ |
807 | "Intended Audience :: Developers", |
808 | "License :: OSI Approved :: MIT License", |
809 | "Programming Language :: Python", |
810 | + "Programming Language :: Python :: 2", |
811 | + "Programming Language :: Python :: 3", |
812 | "Topic :: Software Development", |
813 | "Topic :: Utilities", |
814 | ]) |