Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
59 changes: 16 additions & 43 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from pyflakes import messages

PY38_PLUS = sys.version_info >= (3, 8)
PYPY = hasattr(sys, 'pypy_version_info')

builtin_vars = dir(builtins)
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -1181,7 +1169,7 @@ def handleNodeStore(self, node):
)
):
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)
Expand Down Expand Up @@ -1257,13 +1245,7 @@ def getDocstring(self, node):
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:
Expand Down Expand Up @@ -1734,12 +1716,9 @@ def STR(self, node):
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
Expand Down Expand Up @@ -1905,11 +1884,6 @@ def CONTINUE(self, node):
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
Expand Down Expand Up @@ -1957,10 +1931,9 @@ def LAMBDA(self, node):
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)
Expand Down
7 changes: 0 additions & 7 deletions pyflakes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 0 additions & 2 deletions pyflakes/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ def syntaxError(self, filename, msg, lineno, offset, text):
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' %
Expand Down
29 changes: 7 additions & 22 deletions pyflakes/test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,7 @@ def test_syntaxError(self):
"""
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"
Expand Down Expand Up @@ -281,11 +279,10 @@ def test_multiLineSyntaxError(self):
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):
Expand Down Expand Up @@ -417,10 +414,8 @@ def evaluate(source):

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,
["""\
Expand Down Expand Up @@ -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(
Expand All @@ -512,7 +505,7 @@ def test_nonKeywordAfterKeywordSyntaxError(self):
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
Expand Down Expand Up @@ -679,18 +672,10 @@ def test_stdinReportsErrors(self):
"max(1 for i in range(10), key=lambda x: x+1)",
" ^",
]
elif sys.version_info >= (3, 8):
else:
expected_error = [
"<stdin>:1:5: Generator expression must be parenthesized",
]
elif sys.version_info >= (3, 7):
expected_error = [
"<stdin>:1:4: Generator expression must be parenthesized",
]
elif sys.version_info >= (3, 6):
expected_error = [
"<stdin>:1:4: Generator expression must be parenthesized if not sole argument", # noqa: E501
]

self.assertEqual(errlines, expected_error)

Expand Down
13 changes: 3 additions & 10 deletions pyflakes/test/test_doctests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys
import textwrap

from pyflakes import messages as m
Expand Down Expand Up @@ -323,7 +322,7 @@ def doctest_stuff():
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)
Expand All @@ -339,10 +338,7 @@ def doctest_stuff():
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('''
Expand All @@ -353,10 +349,7 @@ def doctest_stuff():
"""
''', 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(
Expand Down
46 changes: 0 additions & 46 deletions pyflakes/test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,36 +445,6 @@ def test_continueInsideLoop(self):
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
Expand Down Expand Up @@ -1716,7 +1686,6 @@ def test_f_string(self):
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('''
Expand All @@ -1725,15 +1694,13 @@ def test_assign_expr(self):
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('''
if (any((y := x[0]) for x in [[True]])):
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('''
Expand Down Expand Up @@ -1972,19 +1939,6 @@ async def read_data(db):
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):
Expand Down
3 changes: 0 additions & 3 deletions pyflakes/test/test_type_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ class c: pass
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
Expand Down Expand Up @@ -434,7 +433,6 @@ def t(self) -> Y:
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
Expand Down Expand Up @@ -584,7 +582,6 @@ def f() -> "Optional['Queue[str]']":
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
Expand Down
7 changes: 1 addition & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,14 @@ def get_long_description():
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",
"Intended Audience :: Developers",
"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",
Expand Down
Loading