#https://github.com/jayvdb/pyflakes/commit/3088ffbd6256521e0213b361bc2294c1e218e6fb diff --git a/pyflakes/api.py b/pyflakes/api.py #index 3bc2330..2a46a0d 100644 --- a/pyflakes/api.py +++ b/pyflakes/api.py @@ -41,6 +41,18 @@ def check(codeString, filename, reporter=None): (lineno, offset, text) = value.lineno, value.offset, value.text + if checker.PYPY: + if text is None: + lines = codeString.splitlines() + if len(lines) >= lineno: + text = lines[lineno - 1] + if sys.version_info >= (3, ) and isinstance(text, bytes): + try: + text = text.decode('ascii') + except UnicodeDecodeError: + text = None + offset -= 1 + # If there's an encoding problem with the file, the text is None. if text is None: # Avoid using msg, since for the only known case, it contains a diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 753fa9b..f538d3f 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -11,6 +11,12 @@ PY2 = sys.version_info < (3, 0) PY32 = sys.version_info < (3, 3) # Python 2.5 to 3.2 PY33 = sys.version_info < (3, 4) # Python 2.5 to 3.3 +try: + sys.pypy_version_info + PYPY = True +except AttributeError: + PYPY = False + builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins')) try: @@ -594,8 +600,13 @@ def getDocstring(self, node): node = node.value if not isinstance(node, ast.Str): return (None, None) - # Computed incorrectly if the docstring has backslash - doctest_lineno = node.lineno - node.s.count('\n') - 1 + + if PYPY: + doctest_lineno = node.lineno - 1 + else: + # Computed incorrectly if the docstring has backslash + doctest_lineno = node.lineno - node.s.count('\n') - 1 + return (node.s, doctest_lineno) def handleNode(self, node, parent): @@ -642,6 +653,8 @@ def handleDoctests(self, node): tree = compile(example.source, "", "exec", ast.PyCF_ONLY_AST) except SyntaxError: e = sys.exc_info()[1] + if PYPY: + e.offset += 1 position = (node_lineno + example.lineno + e.lineno, example.indent + 4 + (e.offset or 0)) self.report(messages.DoctestSyntaxError, node, position) diff --git a/pyflakes/test/test_api.py b/pyflakes/test/test_api.py index 34a59bc..d2a5036 100644 --- a/pyflakes/test/test_api.py +++ b/pyflakes/test/test_api.py @@ -23,6 +23,14 @@ from io import StringIO unichr = chr +try: + sys.pypy_version_info + PYPY = True +except AttributeError: + PYPY = False + +ERROR_HAS_COL_NUM = ERROR_HAS_LAST_LINE = sys.version_info >= (3, 2) or PYPY + def withStderrTo(stderr, f, *args, **kwargs): """ @@ -312,18 +320,25 @@ def evaluate(source): evaluate(source) except SyntaxError: e = sys.exc_info()[1] - self.assertTrue(e.text.count('\n') > 1) + if not PYPY: + self.assertTrue(e.text.count('\n') > 1) else: self.fail() sourcePath = self.makeTempFile(source) + + if PYPY: + message = 'EOF while scanning triple-quoted string literal' + else: + message = 'invalid syntax' + self.assertHasErrors( sourcePath, ["""\ -%s:8:11: invalid syntax +%s:8:11: %s '''quux''' ^ -""" % (sourcePath,)]) +""" % (sourcePath, message)]) def test_eofSyntaxError(self): """ @@ -331,13 +346,22 @@ def test_eofSyntaxError(self): syntax error reflects the cause for the syntax error. """ sourcePath = self.makeTempFile("def foo(") - self.assertHasErrors( - sourcePath, - ["""\ + if PYPY: + result = """\ +%s:1:7: parenthesis is never closed +def foo( + ^ +""" % (sourcePath,) + else: + result = """\ %s:1:9: unexpected EOF while parsing def foo( ^ -""" % (sourcePath,)]) +""" % (sourcePath,) + + self.assertHasErrors( + sourcePath, + [result]) def test_eofSyntaxErrorWithTab(self): """ @@ -345,13 +369,16 @@ def test_eofSyntaxErrorWithTab(self): syntax error reflects the cause for the syntax error. """ sourcePath = self.makeTempFile("if True:\n\tfoo =") + column = 5 if PYPY else 7 + last_line = '\t ^' if PYPY else '\t ^' + self.assertHasErrors( sourcePath, ["""\ -%s:2:7: invalid syntax +%s:2:%s: invalid syntax \tfoo = -\t ^ -""" % (sourcePath,)]) +%s +""" % (sourcePath, column, last_line)]) def test_nonDefaultFollowsDefaultSyntaxError(self): """ @@ -364,8 +391,8 @@ def foo(bar=baz, bax): pass """ sourcePath = self.makeTempFile(source) - last_line = ' ^\n' if sys.version_info >= (3, 2) else '' - column = '8:' if sys.version_info >= (3, 2) else '' + last_line = ' ^\n' if ERROR_HAS_LAST_LINE else '' + column = '8:' if ERROR_HAS_COL_NUM else '' self.assertHasErrors( sourcePath, ["""\ @@ -383,8 +410,8 @@ def test_nonKeywordAfterKeywordSyntaxError(self): foo(bar=baz, bax) """ sourcePath = self.makeTempFile(source) - last_line = ' ^\n' if sys.version_info >= (3, 2) else '' - column = '13:' if sys.version_info >= (3, 2) else '' + last_line = ' ^\n' if ERROR_HAS_LAST_LINE else '' + column = '13:' if ERROR_HAS_COL_NUM or PYPY else '' if sys.version_info >= (3, 5): message = 'positional argument follows keyword argument' @@ -407,8 +434,15 @@ def test_invalidEscape(self): sourcePath = self.makeTempFile(r"foo = '\xyz'") if ver < (3,): decoding_error = "%s: problem decoding source\n" % (sourcePath,) + elif PYPY: + # pypy3 only + decoding_error = """\ +%s:1:6: %s: ('unicodeescape', b'\\\\xyz', 0, 2, 'truncated \\\\xXX escape') +foo = '\\xyz' + ^ +""" % (sourcePath, 'UnicodeDecodeError') else: - last_line = ' ^\n' if ver >= (3, 2) else '' + last_line = ' ^\n' if ERROR_HAS_LAST_LINE else '' # Column has been "fixed" since 3.2.4 and 3.3.1 col = 1 if ver >= (3, 3, 1) or ((3, 2, 4) <= ver < (3, 3)) else 2 decoding_error = """\ @@ -474,8 +508,21 @@ def test_misencodedFileUTF8(self): x = "%s" """ % SNOWMAN).encode('utf-8') sourcePath = self.makeTempFile(source) + + if PYPY and sys.version_info < (3, ): + message = ('\'ascii\' codec can\'t decode byte 0xe2 ' + 'in position 21: ordinal not in range(128)') + result = """\ +%s:0:0: %s +x = "\xe2\x98\x83" + ^\n""" % (sourcePath, message) + + else: + message = 'problem decoding source' + result = "%s: problem decoding source\n" % (sourcePath,) + self.assertHasErrors( - sourcePath, ["%s: problem decoding source\n" % (sourcePath,)]) + sourcePath, [result]) def test_misencodedFileUTF16(self): """ diff --git a/pyflakes/test/test_doctests.py b/pyflakes/test/test_doctests.py index f15acb8..6793da9 100644 --- a/pyflakes/test/test_doctests.py +++ b/pyflakes/test/test_doctests.py @@ -1,3 +1,4 @@ +import sys import textwrap from pyflakes import messages as m @@ -11,6 +12,12 @@ from pyflakes.test.test_undefined_names import Test as TestUndefinedNames from pyflakes.test.harness import TestCase, skip +try: + sys.pypy_version_info + PYPY = True +except AttributeError: + PYPY = False + class _DoctestMixin(object): @@ -273,12 +280,22 @@ def doctest_stuff(): exc = exceptions[0] self.assertEqual(exc.lineno, 4) self.assertEqual(exc.col, 26) + + # PyPy error column offset is 0, + # for the second and third line of the doctest + # i.e. at the beginning of the line exc = exceptions[1] self.assertEqual(exc.lineno, 5) - self.assertEqual(exc.col, 16) + if PYPY: + self.assertEqual(exc.col, 13) + else: + self.assertEqual(exc.col, 16) exc = exceptions[2] self.assertEqual(exc.lineno, 6) - self.assertEqual(exc.col, 18) + if PYPY: + self.assertEqual(exc.col, 13) + else: + self.assertEqual(exc.col, 18) def test_indentationErrorInDoctest(self): exc = self.flakes(''' @@ -289,7 +306,10 @@ def doctest_stuff(): """ ''', m.DoctestSyntaxError).messages[0] self.assertEqual(exc.lineno, 5) - self.assertEqual(exc.col, 16) + if PYPY: + self.assertEqual(exc.col, 13) + else: + self.assertEqual(exc.col, 16) def test_offsetWithMultiLineArgs(self): (exc1, exc2) = self.flakes(