From 98d4fa33ccacf74c62c5a17cc35ce572fd35b223 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 27 Nov 2022 12:38:53 -0800 Subject: pyflakes: python3.8+ (#752) --- .github/workflows/test.yml | 6 ++-- pyflakes/checker.py | 59 +++++++++------------------------- pyflakes/messages.py | 7 ---- pyflakes/reporter.py | 2 -- pyflakes/test/test_api.py | 29 ++++------------- pyflakes/test/test_doctests.py | 13 ++------ pyflakes/test/test_other.py | 46 -------------------------- pyflakes/test/test_type_annotations.py | 3 -- setup.py | 7 +--- tox.ini | 3 +- 10 files changed, 31 insertions(+), 144 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74c2759..d023ba1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,12 +12,12 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11.0-beta - 3.11.999", "pypy-3.9"] + python-version: ["3.8", "3.9", "3.10", "3.11", "pypy-3.9"] os: [ubuntu-latest] # Include minimum py3 + maximum py3 + pypy3 on Windows include: - - { os: "windows-latest" , python-version: "3.6" } - - { os: "windows-latest" , python-version: "3.10" } + - { os: "windows-latest" , python-version: "3.8" } + - { os: "windows-latest" , python-version: "3.11" } - { os: "windows-latest" , python-version: "pypy-3.9" } steps: diff --git a/pyflakes/checker.py b/pyflakes/checker.py index a79de56..13d2452 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -18,7 +18,6 @@ import warnings from pyflakes import messages -PY38_PLUS = sys.version_info >= (3, 8) PYPY = hasattr(sys, 'pypy_version_info') builtin_vars = dir(builtins) @@ -35,15 +34,12 @@ def getAlternatives(n): FOR_TYPES = (ast.For, ast.AsyncFor) -if PY38_PLUS: - def _is_singleton(node): # type: (ast.AST) -> bool - return ( - isinstance(node, ast.Constant) and - isinstance(node.value, (bool, type(Ellipsis), type(None))) - ) -else: - def _is_singleton(node): # type: (ast.AST) -> bool - return isinstance(node, (ast.NameConstant, ast.Ellipsis)) + +def _is_singleton(node): # type: (ast.AST) -> bool + return ( + isinstance(node, ast.Constant) and + isinstance(node.value, (bool, type(Ellipsis), type(None))) + ) def _is_tuple_constant(node): # type: (ast.AST) -> bool @@ -53,16 +49,8 @@ def _is_tuple_constant(node): # type: (ast.AST) -> bool ) -if PY38_PLUS: - def _is_constant(node): - return isinstance(node, ast.Constant) or _is_tuple_constant(node) -else: - def _is_constant(node): - return ( - isinstance(node, (ast.Str, ast.Num, ast.Bytes)) or - _is_singleton(node) or - _is_tuple_constant(node) - ) +def _is_constant(node): + return isinstance(node, ast.Constant) or _is_tuple_constant(node) def _is_const_non_singleton(node): # type: (ast.AST) -> bool @@ -1176,7 +1164,7 @@ class Checker: ) ): binding = ExportBinding(name, node._pyflakes_parent, self.scope) - elif PY38_PLUS and isinstance(parent_stmt, ast.NamedExpr): + elif isinstance(parent_stmt, ast.NamedExpr): binding = NamedExprAssignment(name, node) else: binding = Assignment(name, node) @@ -1252,13 +1240,7 @@ class Checker: if not isinstance(node, ast.Str): return (None, None) - if PYPY or PY38_PLUS: - 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) + return (node.s, node.lineno - 1) def handleNode(self, node, parent): if node is None: @@ -1729,12 +1711,9 @@ class Checker: else: self.deferFunction(fn) - if PY38_PLUS: - def CONSTANT(self, node): - if isinstance(node.value, str): - return self.STR(node) - else: - NUM = BYTES = ELLIPSIS = CONSTANT = ignore + def CONSTANT(self, node): + if isinstance(node.value, str): + return self.STR(node) # "slice" type nodes SLICE = EXTSLICE = INDEX = handleChildren @@ -1900,11 +1879,6 @@ class Checker: return if isinstance(n, (ast.FunctionDef, ast.ClassDef)): break - # Handle Try/TryFinally difference in Python < and >= 3.3 - if hasattr(n, 'finalbody') and isinstance(node, ast.Continue): - if n_child in n.finalbody and not PY38_PLUS: - self.report(messages.ContinueInFinally, node) - return if isinstance(node, ast.Continue): self.report(messages.ContinueOutsideLoop, node) else: # ast.Break @@ -1952,10 +1926,9 @@ class Checker: args = [] annotations = [] - if PY38_PLUS: - for arg in node.args.posonlyargs: - args.append(arg.arg) - annotations.append(arg.annotation) + for arg in node.args.posonlyargs: + args.append(arg.arg) + annotations.append(arg.annotation) for arg in node.args.args + node.args.kwonlyargs: args.append(arg.arg) annotations.append(arg.annotation) diff --git a/pyflakes/messages.py b/pyflakes/messages.py index f45fd46..86cf6c7 100644 --- a/pyflakes/messages.py +++ b/pyflakes/messages.py @@ -198,13 +198,6 @@ class BreakOutsideLoop(Message): message = '\'break\' outside loop' -class ContinueInFinally(Message): - """ - Indicates a continue statement in a finally block in a while or for loop. - """ - message = '\'continue\' not supported inside \'finally\' clause' - - class DefaultExceptNotLast(Message): """ Indicates an except: block as not the last exception handler. diff --git a/pyflakes/reporter.py b/pyflakes/reporter.py index 1babbb4..af834d1 100644 --- a/pyflakes/reporter.py +++ b/pyflakes/reporter.py @@ -60,8 +60,6 @@ class Reporter: lineno = max(lineno, 1) if offset is not None: - if sys.version_info < (3, 8) and text is not None: - offset = offset - (len(text) - len(line)) + 1 # some versions of python emit an offset of -1 for certain encoding errors offset = max(offset, 1) self._stderr.write('%s:%d:%d: %s\n' % diff --git a/pyflakes/test/test_api.py b/pyflakes/test/test_api.py index 535e701..5c1879c 100644 --- a/pyflakes/test/test_api.py +++ b/pyflakes/test/test_api.py @@ -233,9 +233,7 @@ class TestReporter(TestCase): """ err = io.StringIO() reporter = Reporter(None, err) - reporter.syntaxError('foo.py', 'a problem', 3, - 8 if sys.version_info >= (3, 8) else 7, - 'bad line of source') + reporter.syntaxError('foo.py', 'a problem', 3, 8, 'bad line of source') self.assertEqual( ("foo.py:3:8: a problem\n" "bad line of source\n" @@ -281,11 +279,10 @@ class TestReporter(TestCase): reporter = Reporter(None, err) reporter.syntaxError('foo.py', 'a problem', 3, len(lines[0]) + 7, '\n'.join(lines)) - column = 25 if sys.version_info >= (3, 8) else 7 self.assertEqual( - ("foo.py:3:%d: a problem\n" % column + + ("foo.py:3:25: a problem\n" + lines[-1] + "\n" + - " " * (column - 1) + "^\n"), + " " * 24 + "^\n"), err.getvalue()) def test_unexpectedError(self): @@ -417,10 +414,8 @@ def baz(): if PYPY or sys.version_info >= (3, 10): column = 12 - elif sys.version_info >= (3, 8): - column = 8 else: - column = 11 + column = 8 self.assertHasErrors( sourcePath, ["""\ @@ -487,10 +482,8 @@ def foo(bar=baz, bax): column = 18 elif sys.version_info >= (3, 9): column = 21 - elif sys.version_info >= (3, 8): - column = 9 else: - column = 8 + column = 9 last_line = ' ' * (column - 1) + '^\n' columnstr = '%d:' % column self.assertHasErrors( @@ -512,7 +505,7 @@ foo(bar=baz, bax) with self.makeTempFile(source) as sourcePath: if sys.version_info >= (3, 9): column = 17 - elif not PYPY and sys.version_info >= (3, 8): + elif not PYPY: column = 14 else: column = 13 @@ -679,18 +672,10 @@ x = "%s" "max(1 for i in range(10), key=lambda x: x+1)", " ^", ] - elif sys.version_info >= (3, 8): + else: expected_error = [ ":1:5: Generator expression must be parenthesized", ] - elif sys.version_info >= (3, 7): - expected_error = [ - ":1:4: Generator expression must be parenthesized", - ] - elif sys.version_info >= (3, 6): - expected_error = [ - ":1:4: Generator expression must be parenthesized if not sole argument", # noqa: E501 - ] self.assertEqual(errlines, expected_error) diff --git a/pyflakes/test/test_doctests.py b/pyflakes/test/test_doctests.py index 6c8e69e..63cea4d 100644 --- a/pyflakes/test/test_doctests.py +++ b/pyflakes/test/test_doctests.py @@ -1,4 +1,3 @@ -import sys import textwrap from pyflakes import messages as m @@ -323,7 +322,7 @@ class Test(TestCase): m.DoctestSyntaxError).messages exc = exceptions[0] self.assertEqual(exc.lineno, 4) - if not PYPY and sys.version_info >= (3, 8): + if not PYPY: self.assertEqual(exc.col, 18) else: self.assertEqual(exc.col, 26) @@ -339,10 +338,7 @@ class Test(TestCase): self.assertEqual(exc.col, 16) exc = exceptions[2] self.assertEqual(exc.lineno, 6) - if PYPY or sys.version_info >= (3, 8): - self.assertEqual(exc.col, 13) - else: - self.assertEqual(exc.col, 18) + self.assertEqual(exc.col, 13) def test_indentationErrorInDoctest(self): exc = self.flakes(''' @@ -353,10 +349,7 @@ class Test(TestCase): """ ''', m.DoctestSyntaxError).messages[0] self.assertEqual(exc.lineno, 5) - if PYPY or sys.version_info >= (3, 8): - self.assertEqual(exc.col, 13) - else: - self.assertEqual(exc.col, 16) + self.assertEqual(exc.col, 13) def test_offsetWithMultiLineArgs(self): (exc1, exc2) = self.flakes( diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py index ce742a5..42e99ae 100644 --- a/pyflakes/test/test_other.py +++ b/pyflakes/test/test_other.py @@ -445,36 +445,6 @@ class Test(TestCase): continue ''') - @skipIf(version_info > (3, 8), "Python <= 3.8 only") - def test_continueInFinally(self): - # 'continue' inside 'finally' is a special syntax error - # that is removed in 3.8 - self.flakes(''' - while True: - try: - pass - finally: - continue - ''', m.ContinueInFinally) - - self.flakes(''' - while True: - try: - pass - finally: - if 1: - if 2: - continue - ''', m.ContinueInFinally) - - # Even when not in a loop, this is the error Python gives - self.flakes(''' - try: - pass - finally: - continue - ''', m.ContinueInFinally) - def test_breakOutsideLoop(self): self.flakes(''' break @@ -1716,7 +1686,6 @@ class TestUnusedAssignment(TestCase): print(f'\x7b4*baz\N{RIGHT CURLY BRACKET}') ''') - @skipIf(version_info < (3, 8), 'new in Python 3.8') def test_assign_expr(self): """Test PEP 572 assignment expressions are treated as usage / write.""" self.flakes(''' @@ -1725,7 +1694,6 @@ class TestUnusedAssignment(TestCase): print(x) ''') - @skipIf(version_info < (3, 8), 'new in Python 3.8') def test_assign_expr_generator_scope(self): """Test assignment expressions in generator expressions.""" self.flakes(''' @@ -1733,7 +1701,6 @@ class TestUnusedAssignment(TestCase): print(y) ''') - @skipIf(version_info < (3, 8), 'new in Python 3.8') def test_assign_expr_nested(self): """Test assignment expressions in nested expressions.""" self.flakes(''' @@ -1972,19 +1939,6 @@ class TestAsyncStatements(TestCase): return output ''', m.BreakOutsideLoop) - @skipIf(version_info > (3, 8), "Python <= 3.8 only") - def test_continueInAsyncForFinally(self): - self.flakes(''' - async def read_data(db): - output = [] - async for row in db.cursor(): - try: - output.append(row) - finally: - continue - return output - ''', m.ContinueInFinally) - def test_asyncWith(self): self.flakes(''' async def commit(session, data): diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py index 885302c..2f27b06 100644 --- a/pyflakes/test/test_type_annotations.py +++ b/pyflakes/test/test_type_annotations.py @@ -379,7 +379,6 @@ class TestTypeAnnotations(TestCase): async def func(c: c) -> None: pass ''') - @skipIf(version_info < (3, 7), 'new in Python 3.7') def test_postponed_annotations(self): self.flakes(''' from __future__ import annotations @@ -434,7 +433,6 @@ class TestTypeAnnotations(TestCase): return Y """, m.UndefinedName) - @skipIf(version_info < (3, 8), 'new in Python 3.8') def test_positional_only_argument_annotations(self): self.flakes(""" from x import C @@ -584,7 +582,6 @@ class TestTypeAnnotations(TestCase): return None """) - @skipIf(version_info < (3, 7), 'new in Python 3.7') def test_partial_string_annotations_with_future_annotations(self): self.flakes(""" from __future__ import annotations diff --git a/setup.py b/setup.py index 673a76d..3cc2fbd 100755 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( author_email="code-quality@python.org", url="https://github.com/PyCQA/pyflakes", packages=["pyflakes", "pyflakes.scripts", "pyflakes.test"], - python_requires='>=3.6', + python_requires='>=3.8', classifiers=[ "Development Status :: 6 - Mature", "Environment :: Console", @@ -50,11 +50,6 @@ setup( "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", diff --git a/tox.ini b/tox.ini index 9e9a9e9..84d5b71 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] skip_missing_interpreters = True -envlist = - py36,py37,py38,py39,py310,pypy3 +envlist = py,pypy3 [testenv] deps = flake8==4.0.1 -- cgit v1.2.1