Merge lp:~florent.x/pyflakes/989203-python3 into lp:pyflakes

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
Reviewer Review Type Date Requested Status
Pyflakes Dev Pending
Review via email: mp+144668@code.launchpad.net

Description of the change

This branch brings the minimal changes to achieve Python 3 support.

Tests are green: https://travis-ci.org/florentx/pyflakes/builds/4250803

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 ])

Subscribers

People subscribed via source and target branches

to all changes: