divmod-dev team mailing list archive
-
divmod-dev team
-
Mailing list archive
-
Message #00397
[Merge] lp:~florent.x/pyflakes/989203-python3 into lp:pyflakes
Florent has proposed merging lp:~florent.x/pyflakes/989203-python3 into lp:pyflakes.
Requested reviews:
Divmod-dev (divmod-dev)
Related bugs:
Bug #989203 in Pyflakes: "pyflakes native support for Python 3"
https://bugs.launchpad.net/pyflakes/+bug/989203
For more details, see:
https://code.launchpad.net/~florent.x/pyflakes/989203-python3/+merge/143992
This branch brings the minimal changes to achieve Python 3 support.
Tests are green: https://travis-ci.org/florentx/pyflakes/builds/4250803
--
https://code.launchpad.net/~florent.x/pyflakes/989203-python3/+merge/143992
Your team Divmod-dev is requested to review the proposed merge of lp:~florent.x/pyflakes/989203-python3 into lp:pyflakes.
=== modified file '.travis.yml'
--- .travis.yml 2013-01-19 00:26:52 +0000
+++ .travis.yml 2013-01-19 17:22:21 +0000
@@ -3,10 +3,14 @@
- 2.5
- 2.6
- 2.7
+ - 3.2
+ - 3.3
- pypy
+before_install:
+ - if [ "${TRAVIS_PYTHON_VERSION::1}" == "3" ]; then export TEST_PKG=unittest2py3k; else export TEST_PKG=unittest2; fi
install:
- python setup.py install
- - pip install unittest2
+ - pip install $TEST_PKG
script:
- unit2 discover
matrix:
=== modified file 'pyflakes/checker.py'
--- pyflakes/checker.py 2012-01-10 19:09:22 +0000
+++ pyflakes/checker.py 2013-01-19 17:22:21 +0000
@@ -2,9 +2,14 @@
# (c) 2005-2010 Divmod, Inc.
# See LICENSE file for details
-import __builtin__
import os.path
import _ast
+try:
+ import builtins
+ PY2 = False
+except ImportError:
+ import __builtin__ as builtins
+ PY2 = True
from pyflakes import messages
@@ -173,11 +178,18 @@
pass
-# Globally defined names which are not attributes of the __builtin__ module, or
+# Globally defined names which are not attributes of the builtins module, or
# are only present on some platforms.
_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
+def getNodeName(node):
+ # Returns node.id, or node.name, or None
+ if hasattr(node, 'id'): # One of the many nodes with an id
+ return node.id
+ if hasattr(node, 'name'): # a ExceptHandler node
+ return node.name
+
class Checker(object):
"""
@@ -275,7 +287,7 @@
all = []
# Look for imported names that aren't used.
- for importation in scope.itervalues():
+ for importation in scope.values():
if isinstance(importation, Importation):
if not importation.used and importation.name not in all:
self.report(
@@ -293,6 +305,83 @@
def report(self, messageClass, *args, **kwargs):
self.messages.append(messageClass(self.filename, *args, **kwargs))
+ def handleNodeLoad(self, node):
+ name = getNodeName(node)
+ if not name:
+ return
+ # try local scope
+ importStarred = self.scope.importStarred
+ try:
+ self.scope[name].used = (self.scope, node.lineno)
+ except KeyError:
+ pass
+ else:
+ return
+
+ # try enclosing function scopes
+ for scope in self.scopeStack[-2:0:-1]:
+ importStarred = importStarred or scope.importStarred
+ if not isinstance(scope, FunctionScope):
+ continue
+ try:
+ scope[name].used = (self.scope, node.lineno)
+ except KeyError:
+ pass
+ else:
+ return
+
+ # try global scope
+ importStarred = importStarred or self.scopeStack[0].importStarred
+ try:
+ self.scopeStack[0][name].used = (self.scope, node.lineno)
+ except KeyError:
+ if ((not hasattr(builtins, name)) and name not in _MAGIC_GLOBALS and not importStarred):
+ 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)
+
+ def handleNodeStore(self, node):
+ name = getNodeName(node)
+ if not name:
+ return
+ # if the name hasn't already been defined in the current scope
+ if isinstance(self.scope, FunctionScope) and name not in self.scope:
+ # for each function or module scope above us
+ for scope in self.scopeStack[:-1]:
+ if not isinstance(scope, (FunctionScope, ModuleScope)):
+ continue
+ # if the name was defined in that scope, and the name has
+ # been accessed already in the current scope, and hasn't
+ # been declared global
+ if (name in scope and scope[name].used and scope[name].used[0] is self.scope
+ 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)
+ break
+
+ parent = getattr(node, 'parent', None)
+ if isinstance(parent, (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)):
+ binding = Binding(name, node)
+ elif parent is not None and name == '__all__' and isinstance(self.scope, ModuleScope):
+ binding = ExportBinding(name, parent.value)
+ else:
+ binding = Assignment(name, node)
+ if name in self.scope:
+ binding.used = self.scope[name].used
+ self.addBinding(node.lineno, binding)
+
+ def handleNodeDelete(self, node):
+ name = getNodeName(node)
+ if not name:
+ return
+ if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
+ del self.scope.globals[name]
+ else:
+ self.addBinding(node.lineno, UnBinding(name, node))
+
def handleChildren(self, tree):
for node in iter_child_nodes(tree):
self.handleNode(node, tree)
@@ -309,7 +398,7 @@
def handleNode(self, node, parent):
node.parent = parent
if self.traceTree:
- print ' ' * self.nodeDepth + node.__class__.__name__
+ print(' ' * self.nodeDepth + node.__class__.__name__)
self.nodeDepth += 1
if self.futuresAllowed and not \
(isinstance(node, _ast.ImportFrom) or self.isDocstring(node)):
@@ -321,14 +410,14 @@
finally:
self.nodeDepth -= 1
if self.traceTree:
- print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__
+ print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__)
def ignore(self, node):
pass
# "stmt" type nodes
- RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
- TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
+ RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
+ TRYEXCEPT = TRYFINALLY = TRY = ASSERT = EXEC = EXPR = handleChildren
CONTINUE = BREAK = PASS = ignore
@@ -350,7 +439,7 @@
EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
# additional node types
- COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
+ COMPREHENSION = KEYWORD = handleChildren
def addBinding(self, lineno, value, reportRedef=True):
'''Called when a binding is altered.
@@ -436,81 +525,11 @@
"""
# Locate the name in locals / function / globals scopes.
if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)):
- # try local scope
- importStarred = self.scope.importStarred
- try:
- self.scope[node.id].used = (self.scope, node.lineno)
- except KeyError:
- pass
- else:
- return
-
- # try enclosing function scopes
-
- for scope in self.scopeStack[-2:0:-1]:
- importStarred = importStarred or scope.importStarred
- if not isinstance(scope, FunctionScope):
- continue
- try:
- scope[node.id].used = (self.scope, node.lineno)
- except KeyError:
- pass
- else:
- return
-
- # try global scope
-
- importStarred = importStarred or self.scopeStack[0].importStarred
- try:
- self.scopeStack[0][node.id].used = (self.scope, node.lineno)
- except KeyError:
- if ((not hasattr(__builtin__, node.id))
- and node.id not in _MAGIC_GLOBALS
- and not importStarred):
- if (os.path.basename(self.filename) == '__init__.py' and
- node.id == '__path__'):
- # the special name __path__ is valid only in packages
- pass
- else:
- self.report(messages.UndefinedName, node.lineno, node.id)
+ self.handleNodeLoad(node)
elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)):
- # if the name hasn't already been defined in the current scope
- if isinstance(self.scope, FunctionScope) and node.id not in self.scope:
- # for each function or module scope above us
- for scope in self.scopeStack[:-1]:
- if not isinstance(scope, (FunctionScope, ModuleScope)):
- continue
- # if the name was defined in that scope, and the name has
- # been accessed already in the current scope, and hasn't
- # been declared global
- if (node.id in scope
- and scope[node.id].used
- and scope[node.id].used[0] is self.scope
- and node.id not in self.scope.globals):
- # then it's probably a mistake
- self.report(messages.UndefinedLocal,
- scope[node.id].used[1],
- node.id,
- scope[node.id].source.lineno)
- break
-
- if isinstance(node.parent,
- (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)):
- binding = Binding(node.id, node)
- elif (node.id == '__all__' and
- isinstance(self.scope, ModuleScope)):
- binding = ExportBinding(node.id, node.parent.value)
- else:
- binding = Assignment(node.id, node)
- if node.id in self.scope:
- binding.used = self.scope[node.id].used
- self.addBinding(node.lineno, binding)
+ self.handleNodeStore(node)
elif isinstance(node.ctx, _ast.Del):
- if isinstance(self.scope, FunctionScope) and \
- node.id in self.scope.globals:
- del self.scope.globals[node.id]
- else:
- self.addBinding(node.lineno, UnBinding(node.id, node))
+ self.handleNodeDelete(node)
else:
# must be a Param context -- this only happens for names in function
# arguments, but these aren't dispatched through here
@@ -536,18 +555,28 @@
def runFunction():
args = []
- def addArgs(arglist):
- for arg in arglist:
- if isinstance(arg, _ast.Tuple):
- addArgs(arg.elts)
- else:
- if arg.id in args:
+ if PY2:
+ def addArgs(arglist):
+ for arg in arglist:
+ if isinstance(arg, _ast.Tuple):
+ addArgs(arg.elts)
+ else:
+ if arg.id in args:
+ self.report(messages.DuplicateArgument,
+ node.lineno, arg.id)
+ args.append(arg.id)
+ else:
+ def addArgs(arglist):
+ for arg in arglist:
+ if arg.arg in args:
self.report(messages.DuplicateArgument,
- node.lineno, arg.id)
- args.append(arg.id)
+ node.lineno, arg.arg)
+ args.append(arg.arg)
self.pushFunctionScope()
addArgs(node.args.args)
+ if not PY2:
+ addArgs(node.args.kwonlyargs)
# vararg/kwarg identifiers are not Name nodes
if node.args.vararg:
args.append(node.args.vararg)
@@ -566,7 +595,7 @@
"""
Check to see if any assignments have not been used.
"""
- for name, binding in self.scope.iteritems():
+ for name, binding in self.scope.items():
if (not binding.used and not name in self.scope.globals
and isinstance(binding, Assignment)):
self.report(messages.UnusedVariable,
@@ -600,13 +629,9 @@
self.handleNode(target, node)
def AUGASSIGN(self, node):
- # AugAssign is awkward: must set the context explicitly and visit twice,
- # once with AugLoad context, once with AugStore context
- node.target.ctx = _ast.AugLoad()
- self.handleNode(node.target, node)
+ self.handleNodeLoad(node.target)
self.handleNode(node.value, node)
- node.target.ctx = _ast.AugStore()
- self.handleNode(node.target, node)
+ self.handleNodeStore(node.target)
def IMPORT(self, node):
for alias in node.names:
@@ -632,3 +657,10 @@
if node.module == '__future__':
importation.used = (self.scope, node.lineno)
self.addBinding(node.lineno, importation)
+
+ def EXCEPTHANDLER(self, node):
+ # in addition to handling children, we must handle the name of the exception, which is not
+ # a Name node, but a simple string.
+ if node.name:
+ self.handleNodeStore(node)
+ self.handleChildren(node)
=== modified file 'pyflakes/reporter.py'
--- pyflakes/reporter.py 2012-10-23 13:07:35 +0000
+++ pyflakes/reporter.py 2013-01-19 17:22:21 +0000
@@ -2,6 +2,10 @@
# See LICENSE file for details
import sys
+try:
+ u = unicode
+except NameError:
+ u = str
class Reporter(object):
@@ -33,7 +37,7 @@
@param msg: A message explaining the problem.
@ptype msg: C{unicode}
"""
- self._stderr.write(u"%s: %s\n" % (filename, msg))
+ self._stderr.write(u("%s: %s\n") % (filename, msg))
def syntaxError(self, filename, msg, lineno, offset, text):
@@ -54,11 +58,11 @@
line = text.splitlines()[-1]
if offset is not None:
offset = offset - (len(text) - len(line))
- self._stderr.write(u'%s:%d: %s\n' % (filename, lineno, msg))
- self._stderr.write(line)
- self._stderr.write(u'\n')
+ self._stderr.write(u('%s:%d: %s\n') % (filename, lineno, msg))
+ self._stderr.write(u(line))
+ self._stderr.write(u('\n'))
if offset is not None:
- self._stderr.write(u" " * (offset + 1) + u"^\n")
+ self._stderr.write(u(" " * (offset + 1) + "^\n"))
def flake(self, message):
@@ -67,8 +71,8 @@
@param: A L{pyflakes.messages.Message}.
"""
- self._stdout.write(unicode(message))
- self._stdout.write(u'\n')
+ self._stdout.write(u(message))
+ self._stdout.write(u('\n'))
=== modified file 'pyflakes/scripts/pyflakes.py'
--- pyflakes/scripts/pyflakes.py 2012-10-23 11:48:54 +0000
+++ pyflakes/scripts/pyflakes.py 2013-01-19 17:22:21 +0000
@@ -34,7 +34,8 @@
# First, compile into an AST and handle syntax errors.
try:
tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
- except SyntaxError, value:
+ except SyntaxError:
+ value = sys.exc_info()[1]
msg = value.args[0]
(lineno, offset, text) = value.lineno, value.offset, value.text
@@ -44,14 +45,14 @@
# Avoid using msg, since for the only known case, it contains a
# bogus message that claims the encoding the file declared was
# unknown.
- reporter.unexpectedError(filename, u'problem decoding source')
+ reporter.unexpectedError(filename, 'problem decoding source')
else:
reporter.syntaxError(filename, msg, lineno, offset, text)
return 1
else:
# Okay, it's syntactically valid. Now check it.
w = checker.Checker(tree, filename)
- w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
+ w.messages.sort(key=lambda m: m.lineno)
for warning in w.messages:
reporter.flake(warning)
return len(w.messages)
@@ -69,8 +70,9 @@
if reporter is None:
reporter = modReporter._makeDefaultReporter()
try:
- return check(file(filename, 'U').read() + '\n', filename, reporter)
- except IOError, msg:
+ return check(open(filename, 'U').read() + '\n', filename, reporter)
+ except IOError:
+ msg = sys.exc_info()[1]
reporter.unexpectedError(filename, msg.args[1])
return 1
=== modified file 'pyflakes/test/harness.py'
--- pyflakes/test/harness.py 2013-01-08 16:34:11 +0000
+++ pyflakes/test/harness.py 2013-01-19 17:22:21 +0000
@@ -15,8 +15,8 @@
w = checker.Checker(ast, **kw)
outputs = [type(o) for o in w.messages]
expectedOutputs = list(expectedOutputs)
- outputs.sort()
- expectedOutputs.sort()
+ outputs.sort(key=lambda t: t.__name__)
+ expectedOutputs.sort(key=lambda t: t.__name__)
self.assertEqual(outputs, expectedOutputs, '''\
for input:
%s
=== modified file 'pyflakes/test/test_imports.py'
--- pyflakes/test/test_imports.py 2013-01-08 16:34:11 +0000
+++ pyflakes/test/test_imports.py 2013-01-19 17:22:21 +0000
@@ -16,8 +16,8 @@
self.flakes('from moo import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport)
def test_usedImport(self):
- self.flakes('import fu; print fu')
- self.flakes('from baz import fu; print fu')
+ self.flakes('import fu; print(fu)')
+ self.flakes('from baz import fu; print(fu)')
def test_redefinedWhileUnused(self):
self.flakes('import fu; fu = 3', m.RedefinedWhileUnused)
@@ -74,28 +74,28 @@
import fu
class bar:
fu = 1
- print fu
+ print(fu)
''')
def test_usedInFunction(self):
self.flakes('''
import fu
def fun():
- print fu
+ print(fu)
''')
def test_shadowedByParameter(self):
self.flakes('''
import fu
def fun(fu):
- print fu
+ print(fu)
''', m.UnusedImport)
self.flakes('''
import fu
def fun(fu):
- print fu
- print fu
+ print(fu)
+ print(fu)
''')
def test_newAssignment(self):
@@ -106,12 +106,12 @@
self.flakes('import fu; "bar".fu.baz', m.UnusedImport)
def test_usedInSlice(self):
- self.flakes('import fu; print fu.bar[1:]')
+ self.flakes('import fu; print(fu.bar[1:])')
def test_usedInIfBody(self):
self.flakes('''
import fu
- if True: print fu
+ if True: print(fu)
''')
def test_usedInIfConditional(self):
@@ -131,7 +131,7 @@
self.flakes('''
import fu
if False: pass
- else: print fu
+ else: print(fu)
''')
def test_usedInCall(self):
@@ -156,14 +156,14 @@
import fu
def bleh():
pass
- print fu
+ print(fu)
''')
def test_usedInFor(self):
self.flakes('''
import fu
for bar in range(9):
- print fu
+ print(fu)
''')
def test_usedInForElse(self):
@@ -172,7 +172,7 @@
for bar in range(10):
pass
else:
- print fu
+ print(fu)
''')
def test_redefinedByFor(self):
@@ -262,11 +262,12 @@
''')
def test_redefinedByExcept(self):
+ as_exc = ', ' if version_info < (2, 6) else ' as '
self.flakes('''
import fu
try: pass
- except Exception, fu: pass
- ''', m.RedefinedWhileUnused)
+ except Exception%sfu: pass
+ ''' % as_exc, m.RedefinedWhileUnused)
def test_usedInRaise(self):
self.flakes('''
@@ -341,11 +342,16 @@
def f(): global fu
''', m.UnusedImport)
+ @skipIf(version_info >= (3,), 'deprecated syntax')
def test_usedInBackquote(self):
self.flakes('import fu; `fu`')
def test_usedInExec(self):
- self.flakes('import fu; exec "print 1" in fu.bar')
+ if version_info < (3,):
+ exec_stmt = 'exec "print 1" in fu.bar'
+ else:
+ exec_stmt = 'exec("print(1)", fu.bar)'
+ self.flakes('import fu; %s' % exec_stmt)
def test_usedInLambda(self):
self.flakes('import fu; lambda: fu')
@@ -385,7 +391,7 @@
import fu
class b:
def c(self):
- print fu
+ print(fu)
''')
def test_importStar(self):
=== modified file 'pyflakes/test/test_script.py'
--- pyflakes/test/test_script.py 2013-01-08 16:34:11 +0000
+++ pyflakes/test/test_script.py 2013-01-19 17:22:21 +0000
@@ -7,9 +7,12 @@
import shutil
import subprocess
import tempfile
-from StringIO import StringIO
+try:
+ from io import StringIO
+except ImportError:
+ from StringIO import StringIO
-from unittest2 import TestCase
+from unittest2 import skipIf, TestCase
from pyflakes.messages import UnusedImport
from pyflakes.reporter import Reporter
@@ -207,8 +210,8 @@
"""
err = StringIO()
reporter = Reporter(None, err)
- reporter.unexpectedError(u'source.py', u'error message')
- self.assertEquals(u'source.py: error message\n', err.getvalue())
+ reporter.unexpectedError('source.py', 'error message')
+ self.assertEquals('source.py: error message\n', err.getvalue())
def test_flake(self):
@@ -234,6 +237,8 @@
Make a temporary file containing C{content} and return a path to it.
"""
_, fpath = tempfile.mkstemp()
+ if not hasattr(content, 'decode'):
+ content = content.encode('ascii')
fd = open(fpath, 'wb')
fd.write(content)
fd.close()
@@ -309,10 +314,11 @@
# Sanity check - SyntaxError.text should be multiple lines, if it
# isn't, something this test was unprepared for has happened.
def evaluate(source):
- exec source
+ exec(source)
try:
evaluate(source)
- except SyntaxError, e:
+ except SyntaxError:
+ e = sys.exc_info()[1]
self.assertTrue(e.text.count('\n') > 1)
else:
self.fail()
@@ -353,12 +359,13 @@
pass
"""
sourcePath = self.makeTempFile(source)
+ last_line = ' ^\n' if sys.version_info >= (3, 2) else ''
self.assertHasErrors(
sourcePath,
["""\
%s:1: non-default argument follows default argument
def foo(bar=baz, bax):
-""" % (sourcePath,)])
+%s""" % (sourcePath, last_line)])
def test_nonKeywordAfterKeywordSyntaxError(self):
@@ -371,12 +378,13 @@
foo(bar=baz, bax)
"""
sourcePath = self.makeTempFile(source)
+ last_line = ' ^\n' if sys.version_info >= (3, 2) else ''
self.assertHasErrors(
sourcePath,
["""\
%s:1: non-keyword arg after keyword arg
foo(bar=baz, bax)
-""" % (sourcePath,)])
+%s""" % (sourcePath, last_line)])
def test_permissionDenied(self):
@@ -405,15 +413,17 @@
errors, [('flake', str(UnusedImport(sourcePath, 1, 'foo')))])
+ @skipIf(sys.version_info >= (3,), "need adaptation for Python 3")
def test_misencodedFile(self):
"""
If a source file contains bytes which cannot be decoded, this is
reported on stderr.
"""
- source = u"""\
+ SNOWMAN = unichr(0x2603)
+ source = ("""\
# coding: ascii
-x = "\N{SNOWMAN}"
-""".encode('utf-8')
+x = "%s"
+""" % SNOWMAN).encode('utf-8')
sourcePath = self.makeTempFile(source)
self.assertHasErrors(
sourcePath, ["%s: problem decoding source\n" % (sourcePath,)])
@@ -428,11 +438,11 @@
os.mkdir(os.path.join(tempdir, 'foo'))
file1 = os.path.join(tempdir, 'foo', 'bar.py')
fd = open(file1, 'wb')
- fd.write("import baz\n")
+ fd.write("import baz\n".encode('ascii'))
fd.close()
file2 = os.path.join(tempdir, 'baz.py')
fd = open(file2, 'wb')
- fd.write("import contraband")
+ fd.write("import contraband".encode('ascii'))
fd.close()
log = []
reporter = LoggingReporter(log)
@@ -488,6 +498,9 @@
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdout, stderr) = p.communicate()
rv = p.wait()
+ if sys.version_info >= (3,):
+ stdout = stdout.decode('utf-8')
+ stderr = stderr.decode('utf-8')
return (stdout, stderr, rv)
@@ -508,7 +521,7 @@
and the warnings are printed to stdout.
"""
fd = open(self.tempfilepath, 'wb')
- fd.write("import contraband\n")
+ 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))
@@ -528,5 +541,5 @@
"""
If no arguments are passed to C{pyflakes} then it reads from stdin.
"""
- d = self.runPyflakes([], stdin='import contraband')
+ d = self.runPyflakes([], stdin='import contraband'.encode('ascii'))
self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1))
=== modified file 'pyflakes/test/test_undefined_names.py'
--- pyflakes/test/test_undefined_names.py 2013-01-08 16:34:11 +0000
+++ pyflakes/test/test_undefined_names.py 2013-01-19 17:22:21 +0000
@@ -1,7 +1,8 @@
from _ast import PyCF_ONLY_AST
+from sys import version_info
-from unittest2 import skip, TestCase
+from unittest2 import skip, skipIf, TestCase
from pyflakes import messages as m, checker
from pyflakes.test import harness
@@ -80,6 +81,7 @@
bar
''', m.ImportStarUsed, m.UndefinedName)
+ @skipIf(version_info >= (3,), 'obsolete syntax')
def test_unpackedParameter(self):
'''Unpacked function parameters create bindings'''
self.flakes('''
@@ -102,7 +104,7 @@
self.flakes('''
global x
def foo():
- print x
+ print(x)
''', m.UndefinedName)
def test_del(self):
@@ -176,8 +178,8 @@
def h(self):
a = x
x = None
- print x, a
- print x
+ print(x, a)
+ print(x)
''', m.UndefinedLocal)
@@ -246,7 +248,7 @@
'''star and double-star arg names are defined'''
self.flakes('''
def f(a, *b, **c):
- print a, b, c
+ print(a, b, c)
''')
def test_definedInGenExp(self):
@@ -254,7 +256,8 @@
Using the loop variable of a generator expression results in no
warnings.
"""
- self.flakes('(a for a in xrange(10) if a)')
+ self.flakes('(a for a in %srange(10) if a)' %
+ ('x' if version_info < (3,) else ''))
=== modified file 'setup.py'
--- setup.py 2011-09-03 16:31:04 +0000
+++ setup.py 2013-01-19 17:22:21 +0000
@@ -24,6 +24,8 @@
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 3",
"Topic :: Software Development",
"Topic :: Utilities",
])