summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Sottile <asottile@umich.edu>2022-06-12 17:07:21 -0400
committerGitHub <noreply@github.com>2022-06-12 17:07:21 -0400
commit2246217295dc8cb30ef4a7b9d8dc449ce32e603a (patch)
tree11a8fb153af9229c058f55bb10d0d454f86a2bee
parentbecbab65bae84e3e19fc388a42dfabcff0c323c8 (diff)
downloadpyflakes-2246217295dc8cb30ef4a7b9d8dc449ce32e603a.tar.gz
burn the bridges with python 2.x (#707)
* pyupgrade --py36-plus * remove handling of PY2 * remove handling of PY35_PLUS * remove handling of PY36_PLUS * remove obsolete version_info checks in pyflakes/ * adjust skips in tests for 3.6+ * is_py3_func -> has_annotations (specifically for lambda) * remove references to py 2 * remove references to unichr * clean up version-specific getattrs * remove unused ReturnWithArgsInsideGenerator * remove unused ast handlers * remove unused RedefinedInListComp
-rw-r--r--pyflakes/api.py8
-rw-r--r--pyflakes/checker.py295
-rw-r--r--pyflakes/messages.py23
-rw-r--r--pyflakes/reporter.py4
-rw-r--r--pyflakes/scripts/pyflakes.py1
-rw-r--r--pyflakes/test/harness.py6
-rw-r--r--pyflakes/test/test_api.py215
-rw-r--r--pyflakes/test/test_builtin.py13
-rw-r--r--pyflakes/test/test_checker.py6
-rw-r--r--pyflakes/test/test_code_segment.py5
-rw-r--r--pyflakes/test/test_dict.py22
-rw-r--r--pyflakes/test/test_doctests.py4
-rw-r--r--pyflakes/test/test_imports.py37
-rw-r--r--pyflakes/test/test_other.py70
-rw-r--r--pyflakes/test/test_return_with_arguments_inside_generator.py34
-rw-r--r--pyflakes/test/test_type_annotations.py24
-rw-r--r--pyflakes/test/test_undefined_names.py75
17 files changed, 203 insertions, 639 deletions
diff --git a/pyflakes/api.py b/pyflakes/api.py
index 14e3347..6cc226b 100644
--- a/pyflakes/api.py
+++ b/pyflakes/api.py
@@ -12,7 +12,7 @@ from pyflakes import reporter as modReporter
__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
-PYTHON_SHEBANG_REGEX = re.compile(br'^#!.*\bpython([23](\.\d+)?|w)?[dmu]?\s')
+PYTHON_SHEBANG_REGEX = re.compile(br'^#!.*\bpython(3(\.\d+)?|w)?[dmu]?\s')
def check(codeString, filename, reporter=None):
@@ -48,7 +48,7 @@ def check(codeString, filename, reporter=None):
lines = codeString.splitlines()
if len(lines) >= lineno:
text = lines[lineno - 1]
- if sys.version_info >= (3, ) and isinstance(text, bytes):
+ if isinstance(text, bytes):
try:
text = text.decode('ascii')
except UnicodeDecodeError:
@@ -90,7 +90,7 @@ def checkPath(filename, reporter=None):
try:
with open(filename, 'rb') as f:
codestr = f.read()
- except IOError:
+ except OSError:
msg = sys.exc_info()[1]
reporter.unexpectedError(filename, msg.args[1])
return 1
@@ -113,7 +113,7 @@ def isPythonFile(filename):
text = f.read(max_bytes)
if not text:
return False
- except IOError:
+ except OSError:
return False
return PYTHON_SHEBANG_REGEX.match(text)
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index 6dd4d89..42e5649 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -5,6 +5,7 @@ Implement the central Checker class.
Also, it models the Bindings and Scopes.
"""
import __future__
+import builtins
import ast
import bisect
import collections
@@ -19,66 +20,22 @@ import tokenize
from pyflakes import messages
-PY2 = sys.version_info < (3, 0)
-PY35_PLUS = sys.version_info >= (3, 5) # Python 3.5 and above
-PY36_PLUS = sys.version_info >= (3, 6) # Python 3.6 and above
PY38_PLUS = sys.version_info >= (3, 8)
PYPY = hasattr(sys, 'pypy_version_info')
-builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins'))
+builtin_vars = dir(builtins)
parse_format_string = string.Formatter().parse
-if PY2:
- tokenize_tokenize = tokenize.generate_tokens
-else:
- tokenize_tokenize = tokenize.tokenize
-
-if PY2:
- def getNodeType(node_class):
- # workaround str.upper() which is locale-dependent
- return str(unicode(node_class.__name__).upper())
- def get_raise_argument(node):
- return node.type
+def getAlternatives(n):
+ if isinstance(n, ast.If):
+ return [n.body]
+ if isinstance(n, ast.Try):
+ return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
-else:
- def getNodeType(node_class):
- return node_class.__name__.upper()
-
- def get_raise_argument(node):
- return node.exc
-
- # Silence `pyflakes` from reporting `undefined name 'unicode'` in Python 3.
- unicode = str
-
-# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
-if PY2:
- def getAlternatives(n):
- if isinstance(n, (ast.If, ast.TryFinally)):
- return [n.body]
- if isinstance(n, ast.TryExcept):
- return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
-else:
- def getAlternatives(n):
- if isinstance(n, ast.If):
- return [n.body]
- if isinstance(n, ast.Try):
- return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
-
-if PY35_PLUS:
- FOR_TYPES = (ast.For, ast.AsyncFor)
- LOOP_TYPES = (ast.While, ast.For, ast.AsyncFor)
- FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef)
-else:
- FOR_TYPES = (ast.For,)
- LOOP_TYPES = (ast.While, ast.For)
- FUNCTION_TYPES = (ast.FunctionDef,)
-if PY36_PLUS:
- ANNASSIGN_TYPES = (ast.AnnAssign,)
-else:
- ANNASSIGN_TYPES = ()
+FOR_TYPES = (ast.For, ast.AsyncFor)
if PY38_PLUS:
def _is_singleton(node): # type: (ast.AST) -> bool
@@ -86,15 +43,9 @@ if PY38_PLUS:
isinstance(node, ast.Constant) and
isinstance(node.value, (bool, type(Ellipsis), type(None)))
)
-elif not PY2:
- def _is_singleton(node): # type: (ast.AST) -> bool
- return isinstance(node, (ast.NameConstant, ast.Ellipsis))
else:
def _is_singleton(node): # type: (ast.AST) -> bool
- return (
- isinstance(node, ast.Name) and
- node.id in {'True', 'False', 'Ellipsis', 'None'}
- )
+ return isinstance(node, (ast.NameConstant, ast.Ellipsis))
def _is_tuple_constant(node): # type: (ast.AST) -> bool
@@ -108,13 +59,9 @@ if PY38_PLUS:
def _is_constant(node):
return isinstance(node, ast.Constant) or _is_tuple_constant(node)
else:
- _const_tps = (ast.Str, ast.Num)
- if not PY2:
- _const_tps += (ast.Bytes,)
-
def _is_constant(node):
return (
- isinstance(node, _const_tps) or
+ isinstance(node, (ast.Str, ast.Num, ast.Bytes)) or
_is_singleton(node) or
_is_tuple_constant(node)
)
@@ -136,7 +83,7 @@ TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*')
# https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413
ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()])
TYPE_IGNORE_RE = re.compile(
- TYPE_COMMENT_RE.pattern + r'ignore([{}]|$)'.format(ASCII_NON_ALNUM))
+ TYPE_COMMENT_RE.pattern + fr'ignore([{ASCII_NON_ALNUM}]|$)')
# https://github.com/python/typed_ast/blob/1.4.0/ast27/Grammar/Grammar#L147
TYPE_FUNC_RE = re.compile(r'^(\(.*?\))\s*->\s*(.*)$')
@@ -293,8 +240,7 @@ def convert_to_value(item):
result.name,
result,
)
- elif (not PY2) and isinstance(item, ast.NameConstant):
- # None, True, False are nameconstants in python3, but names in 2
+ elif isinstance(item, ast.NameConstant):
return item.value
else:
return UnhandledKeyType()
@@ -304,7 +250,7 @@ def is_notimplemented_name_node(node):
return isinstance(node, ast.Name) and getNodeName(node) == 'NotImplemented'
-class Binding(object):
+class Binding:
"""
Represents the binding of a value to a name.
@@ -325,10 +271,12 @@ class Binding(object):
return self.name
def __repr__(self):
- return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
- self.name,
- self.source.lineno,
- id(self))
+ return '<{} object {!r} from line {!r} at 0x{:x}>'.format(
+ self.__class__.__name__,
+ self.name,
+ self.source.lineno,
+ id(self),
+ )
def redefines(self, other):
return isinstance(other, Definition) and self.name == other.name
@@ -344,21 +292,23 @@ class Builtin(Definition):
"""A definition created for all Python builtins."""
def __init__(self, name):
- super(Builtin, self).__init__(name, None)
+ super().__init__(name, None)
def __repr__(self):
- return '<%s object %r at 0x%x>' % (self.__class__.__name__,
- self.name,
- id(self))
+ return '<{} object {!r} at 0x{:x}>'.format(
+ self.__class__.__name__,
+ self.name,
+ id(self)
+ )
-class UnhandledKeyType(object):
+class UnhandledKeyType:
"""
A dictionary key of a type that we cannot or do not check for duplicates.
"""
-class VariableKey(object):
+class VariableKey:
"""
A dictionary key which is a variable.
@@ -389,7 +339,7 @@ class Importation(Definition):
def __init__(self, name, source, full_name=None):
self.fullName = full_name or name
self.redefined = []
- super(Importation, self).__init__(name, source)
+ super().__init__(name, source)
def redefines(self, other):
if isinstance(other, SubmoduleImportation):
@@ -405,7 +355,7 @@ class Importation(Definition):
def source_statement(self):
"""Generate a source statement equivalent to the import."""
if self._has_alias():
- return 'import %s as %s' % (self.fullName, self.name)
+ return f'import {self.fullName} as {self.name}'
else:
return 'import %s' % self.fullName
@@ -438,13 +388,13 @@ class SubmoduleImportation(Importation):
# A dot should only appear in the name when it is a submodule import
assert '.' in name and (not source or isinstance(source, ast.Import))
package_name = name.split('.')[0]
- super(SubmoduleImportation, self).__init__(package_name, source)
+ super().__init__(package_name, source)
self.fullName = name
def redefines(self, other):
if isinstance(other, Importation):
return self.fullName == other.fullName
- return super(SubmoduleImportation, self).redefines(other)
+ return super().redefines(other)
def __str__(self):
return self.fullName
@@ -465,7 +415,7 @@ class ImportationFrom(Importation):
else:
full_name = module + '.' + self.real_name
- super(ImportationFrom, self).__init__(name, source, full_name)
+ super().__init__(name, source, full_name)
def __str__(self):
"""Return import full name with alias."""
@@ -477,18 +427,16 @@ class ImportationFrom(Importation):
@property
def source_statement(self):
if self.real_name != self.name:
- return 'from %s import %s as %s' % (self.module,
- self.real_name,
- self.name)
+ return f'from {self.module} import {self.real_name} as {self.name}'
else:
- return 'from %s import %s' % (self.module, self.name)
+ return f'from {self.module} import {self.name}'
class StarImportation(Importation):
"""A binding created by a 'from x import *' statement."""
def __init__(self, name, source):
- super(StarImportation, self).__init__('*', source)
+ super().__init__('*', source)
# Each star importation needs a unique name, and
# may not be the module name otherwise it will be deemed imported
self.name = name + '.*'
@@ -514,7 +462,7 @@ class FutureImportation(ImportationFrom):
"""
def __init__(self, name, source, scope):
- super(FutureImportation, self).__init__(name, source, '__future__')
+ super().__init__(name, source, '__future__')
self.used = (scope, source)
@@ -608,7 +556,7 @@ class ExportBinding(Binding):
# If not list concatenation
else:
break
- super(ExportBinding, self).__init__(name, source)
+ super().__init__(name, source)
class Scope(dict):
@@ -616,7 +564,7 @@ class Scope(dict):
def __repr__(self):
scope_cls = self.__class__.__name__
- return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
+ return f'<{scope_cls} at 0x{id(self):x} {dict.__repr__(self)}>'
class ClassScope(Scope):
@@ -634,7 +582,7 @@ class FunctionScope(Scope):
'__traceback_supplement__'}
def __init__(self):
- super(FunctionScope, self).__init__()
+ super().__init__()
# Simplify: manage the special locals as globals
self.globals = self.alwaysUsed.copy()
self.returnValue = None # First non-empty return
@@ -667,7 +615,7 @@ class DoctestScope(ModuleScope):
"""Scope for a doctest."""
-class DummyNode(object):
+class DummyNode:
"""Used in place of an `ast.AST` to set error message positions"""
def __init__(self, lineno, col_offset):
self.lineno = lineno
@@ -680,10 +628,7 @@ class DetectClassScopedMagic:
# Globally defined names which are not attributes of the builtins module, or
# are only present on some platforms.
-_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
-# module scope annotation will store in `__annotations__`, see also PEP 526.
-if PY36_PLUS:
- _MAGIC_GLOBALS.append('__annotations__')
+_MAGIC_GLOBALS = ['__file__', '__builtins__', '__annotations__', 'WindowsError']
def getNodeName(node):
@@ -766,7 +711,7 @@ def _is_any_typing_member(node, scope_stack):
def is_typing_overload(value, scope_stack):
return (
- isinstance(value.source, FUNCTION_TYPES) and
+ isinstance(value.source, (ast.FunctionDef, ast.AsyncFunctionDef)) and
any(
_is_typing(dec, 'overload', scope_stack)
for dec in value.source.decorator_list
@@ -802,7 +747,7 @@ def make_tokens(code):
code = code.encode('UTF-8')
lines = iter(code.splitlines(True))
# next(lines, b'') is to prevent an error in pypy3
- return tuple(tokenize_tokenize(lambda: next(lines, b'')))
+ return tuple(tokenize.tokenize(lambda: next(lines, b'')))
class _TypeableVisitor(ast.NodeVisitor):
@@ -853,7 +798,7 @@ def _collect_type_comments(tree, tokens):
return type_comments
-class Checker(object):
+class Checker:
"""
I check the cleanliness and sanity of Python code.
@@ -870,14 +815,13 @@ class Checker(object):
ast.Module: ModuleScope,
ast.ClassDef: ClassScope,
ast.FunctionDef: FunctionScope,
+ ast.AsyncFunctionDef: FunctionScope,
ast.Lambda: FunctionScope,
ast.ListComp: GeneratorScope,
ast.SetComp: GeneratorScope,
ast.GeneratorExp: GeneratorScope,
ast.DictComp: GeneratorScope,
}
- if PY35_PLUS:
- _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope
nodeDepth = 0
offset = None
@@ -1138,16 +1082,13 @@ class Checker(object):
node, value.name, existing.source)
elif scope is self.scope:
- if (isinstance(parent_stmt, ast.comprehension) and
- not isinstance(self.getParent(existing.source),
- (FOR_TYPES, ast.comprehension))):
- self.report(messages.RedefinedInListComp,
+ if (
+ (not existing.used and value.redefines(existing)) and
+ (value.name != '_' or isinstance(existing, Importation)) and
+ not is_typing_overload(existing, self.scopeStack)
+ ):
+ self.report(messages.RedefinedWhileUnused,
node, value.name, existing.source)
- elif not existing.used and value.redefines(existing):
- if value.name != '_' or isinstance(existing, Importation):
- if not is_typing_overload(existing, self.scopeStack):
- self.report(messages.RedefinedWhileUnused,
- node, value.name, existing.source)
elif isinstance(existing, Importation) and value.redefines(existing):
existing.redefined.append(node)
@@ -1180,7 +1121,7 @@ class Checker(object):
# in the pyflakes testsuite (so more specific handling can be added if
# needed).
if os.environ.get('PYFLAKES_ERROR_UNKNOWN'):
- raise NotImplementedError('Unexpected type: {}'.format(type(node)))
+ raise NotImplementedError(f'Unexpected type: {type(node)}')
else:
self.handleChildren(node)
@@ -1188,7 +1129,7 @@ class Checker(object):
try:
return self._nodeHandlers[node_class]
except KeyError:
- nodeType = getNodeType(node_class)
+ nodeType = node_class.__name__.upper()
self._nodeHandlers[node_class] = handler = getattr(
self, nodeType, self._unknown_handler,
)
@@ -1205,7 +1146,7 @@ class Checker(object):
# try enclosing function scopes and global scope
for scope in self.scopeStack[-1::-1]:
if isinstance(scope, ClassScope):
- if not PY2 and name == '__class__':
+ if name == '__class__':
return
elif in_generators is False:
# only generators used in a class scope can access the
@@ -1292,7 +1233,7 @@ class Checker(object):
break
parent_stmt = self.getParent(node)
- if isinstance(parent_stmt, ANNASSIGN_TYPES) and parent_stmt.value is None:
+ if isinstance(parent_stmt, ast.AnnAssign) and parent_stmt.value is None:
binding = Annotation(name, node)
elif isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or (
parent_stmt != node._pyflakes_parent and
@@ -1307,8 +1248,6 @@ class Checker(object):
)
):
binding = ExportBinding(name, node._pyflakes_parent, self.scope)
- elif PY2 and isinstance(getattr(node, 'ctx', None), ast.Param):
- binding = Argument(name, self.getScopeNode(node))
elif PY38_PLUS and isinstance(parent_stmt, ast.NamedExpr):
binding = NamedExprAssignment(name, node)
else:
@@ -1373,8 +1312,6 @@ class Checker(object):
parts = (comment,)
for part in parts:
- if PY2:
- part = part.replace('...', 'Ellipsis')
self.deferFunction(functools.partial(
self.handleStringAnnotation,
part, DummyNode(lineno, col_offset), lineno, col_offset,
@@ -1517,16 +1454,14 @@ class Checker(object):
pass
# "stmt" type nodes
- DELETE = PRINT = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = \
- ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \
+ DELETE = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = ASYNCWITH = \
EXPR = ASSIGN = handleChildren
PASS = ignore
# "expr" type nodes
- BOOLOP = UNARYOP = SET = \
- REPR = ATTRIBUTE = \
- STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren
+ BOOLOP = UNARYOP = SET = ATTRIBUTE = STARRED = NAMECONSTANT = \
+ NAMEDEXPR = handleChildren
def SUBSCRIPT(self, node):
if _is_name_or_attr(node.value, 'Literal'):
@@ -1573,15 +1508,16 @@ class Checker(object):
self.report(messages.StringDotFormatInvalidFormat, node, e)
return
- class state: # py2-compatible `nonlocal`
- auto = None
- next_auto = 0
+ auto = None
+ next_auto = 0
placeholder_positional = set()
placeholder_named = set()
def _add_key(fmtkey):
"""Returns True if there is an error which should early-exit"""
+ nonlocal auto, next_auto
+
if fmtkey is None: # end of string or `{` / `}` escapes
return False
@@ -1594,21 +1530,21 @@ class Checker(object):
except ValueError:
pass
else: # fmtkey was an integer
- if state.auto is True:
+ if auto is True:
self.report(messages.StringDotFormatMixingAutomatic, node)
return True
else:
- state.auto = False
+ auto = False
if fmtkey == '':
- if state.auto is False:
+ if auto is False:
self.report(messages.StringDotFormatMixingAutomatic, node)
return True
else:
- state.auto = True
+ auto = True
- fmtkey = state.next_auto
- state.next_auto += 1
+ fmtkey = next_auto
+ next_auto += 1
if isinstance(fmtkey, int):
placeholder_positional.add(fmtkey)
@@ -1643,15 +1579,9 @@ class Checker(object):
# bail early if there is *args or **kwargs
if (
- # python 2.x *args / **kwargs
- getattr(node, 'starargs', None) or
- getattr(node, 'kwargs', None) or
- # python 3.x *args
- any(
- isinstance(arg, getattr(ast, 'Starred', ()))
- for arg in node.args
- ) or
- # python 3.x **kwargs
+ # *args
+ any(isinstance(arg, ast.Starred) for arg in node.args) or
+ # **kwargs
any(kwd.arg is None for kwd in node.keywords)
):
return
@@ -1832,7 +1762,7 @@ class Checker(object):
isinstance(node.right, (ast.List, ast.Tuple)) and
# does not have any *splats (py35+ feature)
not any(
- isinstance(elt, getattr(ast, 'Starred', ()))
+ isinstance(elt, ast.Starred)
for elt in node.right.elts
)
):
@@ -1916,7 +1846,7 @@ class Checker(object):
def RAISE(self, node):
self.handleChildren(node)
- arg = get_raise_argument(node)
+ arg = node.exc
if isinstance(arg, ast.Call):
if is_notimplemented_name_node(arg.func):
@@ -2031,9 +1961,7 @@ class Checker(object):
self.handleChildren(node)
self.popScope()
- LISTCOMP = handleChildren if PY2 else GENERATOREXP
-
- DICTCOMP = SETCOMP = GENERATOREXP
+ LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP
def NAME(self, node):
"""
@@ -2048,13 +1976,11 @@ class Checker(object):
self.scope.usesLocals = True
elif isinstance(node.ctx, ast.Store):
self.handleNodeStore(node)
- elif PY2 and isinstance(node.ctx, ast.Param):
- self.handleNodeStore(node)
elif isinstance(node.ctx, ast.Del):
self.handleNodeDelete(node)
else:
# Unknown context
- raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
+ raise RuntimeError(f"Got impossible expression context: {node.ctx!r}")
def CONTINUE(self, node):
# Walk the tree up until we see a loop (OK), a function or class
@@ -2063,7 +1989,7 @@ class Checker(object):
n = node
while hasattr(n, '_pyflakes_parent'):
n, n_child = n._pyflakes_parent, n
- if isinstance(n, LOOP_TYPES):
+ if isinstance(n, (ast.While, ast.For, ast.AsyncFor)):
# Doesn't apply unless it's in the loop itself
if n_child not in n.orelse:
return
@@ -2122,41 +2048,26 @@ class Checker(object):
args = []
annotations = []
- if PY2:
- def addArgs(arglist):
- for arg in arglist:
- if isinstance(arg, ast.Tuple):
- addArgs(arg.elts)
- else:
- args.append(arg.id)
- addArgs(node.args.args)
- defaults = node.args.defaults
- else:
- if PY38_PLUS:
- for arg in node.args.posonlyargs:
- args.append(arg.arg)
- annotations.append(arg.annotation)
- for arg in node.args.args + node.args.kwonlyargs:
+ if PY38_PLUS:
+ for arg in node.args.posonlyargs:
args.append(arg.arg)
annotations.append(arg.annotation)
- defaults = node.args.defaults + node.args.kw_defaults
+ for arg in node.args.args + node.args.kwonlyargs:
+ args.append(arg.arg)
+ annotations.append(arg.annotation)
+ defaults = node.args.defaults + node.args.kw_defaults
- # Only for Python3 FunctionDefs
- is_py3_func = hasattr(node, 'returns')
+ has_annotations = not isinstance(node, ast.Lambda)
for arg_name in ('vararg', 'kwarg'):
wildcard = getattr(node.args, arg_name)
if not wildcard:
continue
- args.append(wildcard if PY2 else wildcard.arg)
- if is_py3_func:
- if PY2: # Python 2.7
- argannotation = arg_name + 'annotation'
- annotations.append(getattr(node.args, argannotation))
- else: # Python >= 3.4
- annotations.append(wildcard.annotation)
-
- if is_py3_func:
+ args.append(wildcard.arg)
+ if has_annotations:
+ annotations.append(wildcard.annotation)
+
+ if has_annotations:
annotations.append(node.returns)
if len(set(args)) < len(args):
@@ -2184,28 +2095,12 @@ class Checker(object):
self.report(messages.UnusedVariable, binding.source, name)
self.deferAssignment(checkUnusedAssignments)
- if PY2:
- def checkReturnWithArgumentInsideGenerator():
- """
- Check to see if there is any return statement with
- arguments but the function is a generator.
- """
- if self.scope.isGenerator and self.scope.returnValue:
- self.report(messages.ReturnWithArgsInsideGenerator,
- self.scope.returnValue)
- self.deferAssignment(checkReturnWithArgumentInsideGenerator)
self.popScope()
self.deferFunction(runFunction)
def ARGUMENTS(self, node):
self.handleChildren(node, omit=('defaults', 'kw_defaults'))
- if PY2:
- scope_node = self.getScopeNode(node)
- if node.vararg:
- self.addBinding(node, Argument(node.vararg, scope_node))
- if node.kwarg:
- self.addBinding(node, Argument(node.kwarg, scope_node))
def ARG(self, node):
self.addBinding(node, Argument(node.arg, self.getScopeNode(node)))
@@ -2220,9 +2115,8 @@ class Checker(object):
self.handleNode(deco, node)
for baseNode in node.bases:
self.handleNode(baseNode, node)
- if not PY2:
- for keywordNode in node.keywords:
- self.handleNode(keywordNode, node)
+ for keywordNode in node.keywords:
+ self.handleNode(keywordNode, node)
self.pushScope(ClassScope)
# doctest does not process doctest within a doctest
# classes within classes are processed.
@@ -2241,7 +2135,7 @@ class Checker(object):
self.handleNode(node.target, node)
def TUPLE(self, node):
- if not PY2 and isinstance(node.ctx, ast.Store):
+ if isinstance(node.ctx, ast.Store):
# Python 3 advanced tuple unpacking: a, *b, c = d.
# Only one starred expression is allowed, and no more than 1<<8
# assignments are allowed before a stared expression. There is
@@ -2293,8 +2187,7 @@ class Checker(object):
if alias.name == 'annotations':
self.annotationsFutureEnabled = True
elif alias.name == '*':
- # Only Python 2, local import * is a SyntaxWarning
- if not PY2 and not isinstance(self.scope, ModuleScope):
+ if not isinstance(self.scope, ModuleScope):
self.report(messages.ImportStarNotPermitted,
node, module)
continue
@@ -2327,10 +2220,10 @@ class Checker(object):
# Process the other nodes: "except:", "else:", "finally:"
self.handleChildren(node, omit='body')
- TRYEXCEPT = TRYSTAR = TRY
+ TRYSTAR = TRY
def EXCEPTHANDLER(self, node):
- if PY2 or node.name is None:
+ if node.name is None:
self.handleChildren(node)
return
diff --git a/pyflakes/messages.py b/pyflakes/messages.py
index 5a2f0ce..2d08112 100644
--- a/pyflakes/messages.py
+++ b/pyflakes/messages.py
@@ -3,18 +3,18 @@ Provide the class Message and its subclasses.
"""
-class Message(object):
+class Message:
message = ''
message_args = ()
def __init__(self, filename, loc):
self.filename = filename
self.lineno = loc.lineno
- self.col = getattr(loc, 'col_offset', 0)
+ self.col = loc.col_offset
def __str__(self):
- return '%s:%s:%s: %s' % (self.filename, self.lineno, self.col+1,
- self.message % self.message_args)
+ return '{}:{}:{}: {}'.format(self.filename, self.lineno, self.col+1,
+ self.message % self.message_args)
class UnusedImport(Message):
@@ -33,14 +33,6 @@ class RedefinedWhileUnused(Message):
self.message_args = (name, orig_loc.lineno)
-class RedefinedInListComp(Message):
- message = 'list comprehension redefines %r from line %r'
-
- def __init__(self, filename, loc, name, orig_loc):
- Message.__init__(self, filename, loc)
- self.message_args = (name, orig_loc.lineno)
-
-
class ImportShadowedByLoopVar(Message):
message = 'import %r from line %r shadowed by loop variable'
@@ -168,13 +160,6 @@ class UnusedVariable(Message):
self.message_args = (names,)
-class ReturnWithArgsInsideGenerator(Message):
- """
- Indicates a return statement with arguments inside a generator.
- """
- message = '\'return\' with argument inside generator'
-
-
class ReturnOutsideFunction(Message):
"""
Indicates a return statement outside of a function/method.
diff --git a/pyflakes/reporter.py b/pyflakes/reporter.py
index 0faef65..3555185 100644
--- a/pyflakes/reporter.py
+++ b/pyflakes/reporter.py
@@ -6,7 +6,7 @@ import re
import sys
-class Reporter(object):
+class Reporter:
"""
Formats the results of pyflakes checks to users.
"""
@@ -34,7 +34,7 @@ class Reporter(object):
@param msg: A message explaining the problem.
@ptype msg: C{unicode}
"""
- self._stderr.write("%s: %s\n" % (filename, msg))
+ self._stderr.write(f"{filename}: {msg}\n")
def syntaxError(self, filename, msg, lineno, offset, text):
"""
diff --git a/pyflakes/scripts/pyflakes.py b/pyflakes/scripts/pyflakes.py
index 4a18e79..2d6f6aa 100644
--- a/pyflakes/scripts/pyflakes.py
+++ b/pyflakes/scripts/pyflakes.py
@@ -1,7 +1,6 @@
"""
Implementation of the command-line I{pyflakes} tool.
"""
-from __future__ import absolute_import
# For backward compatibility
__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
diff --git a/pyflakes/test/harness.py b/pyflakes/test/harness.py
index 1cdb809..9bcc0bb 100644
--- a/pyflakes/test/harness.py
+++ b/pyflakes/test/harness.py
@@ -29,9 +29,9 @@ class TestCase(unittest.TestCase):
expectedOutputs.sort(key=lambda t: t.__name__)
self.assertEqual(outputs, expectedOutputs, '''\
for input:
-%s
+{}
expected outputs:
-%r
+{!r}
but got:
-%s''' % (input, expectedOutputs, '\n'.join([str(o) for o in w.messages])))
+{}'''.format(input, expectedOutputs, '\n'.join([str(o) for o in w.messages])))
return w
diff --git a/pyflakes/test/test_api.py b/pyflakes/test/test_api.py
index 3d93591..d0795a0 100644
--- a/pyflakes/test/test_api.py
+++ b/pyflakes/test/test_api.py
@@ -3,6 +3,7 @@ Tests for L{pyflakes.scripts.pyflakes}.
"""
import contextlib
+import io
import os
import sys
import shutil
@@ -20,14 +21,6 @@ from pyflakes.api import (
)
from pyflakes.test.harness import TestCase, skipIf
-if sys.version_info < (3,):
- from cStringIO import StringIO
-else:
- from io import StringIO
- unichr = chr
-
-ERROR_HAS_COL_NUM = ERROR_HAS_LAST_LINE = sys.version_info >= (3, 2) or PYPY
-
def withStderrTo(stderr, f, *args, **kwargs):
"""
@@ -40,7 +33,7 @@ def withStderrTo(stderr, f, *args, **kwargs):
sys.stderr = outer
-class Node(object):
+class Node:
"""
Mock an AST node.
"""
@@ -49,32 +42,14 @@ class Node(object):
self.col_offset = col_offset
-class SysStreamCapturing(object):
-
- """
- Context manager capturing sys.stdin, sys.stdout and sys.stderr.
+class SysStreamCapturing:
+ """Context manager capturing sys.stdin, sys.stdout and sys.stderr.
The file handles are replaced with a StringIO object.
- On environments that support it, the StringIO object uses newlines
- set to os.linesep. Otherwise newlines are converted from \\n to
- os.linesep during __exit__.
"""
- def _create_StringIO(self, buffer=None):
- # Python 3 has a newline argument
- try:
- return StringIO(buffer, newline=os.linesep)
- except TypeError:
- self._newline = True
- # Python 2 creates an input only stream when buffer is not None
- if buffer is None:
- return StringIO()
- else:
- return StringIO(buffer)
-
def __init__(self, stdin):
- self._newline = False
- self._stdin = self._create_StringIO(stdin or '')
+ self._stdin = io.StringIO(stdin or '', newline=os.linesep)
def __enter__(self):
self._orig_stdin = sys.stdin
@@ -82,8 +57,8 @@ class SysStreamCapturing(object):
self._orig_stderr = sys.stderr
sys.stdin = self._stdin
- sys.stdout = self._stdout_stringio = self._create_StringIO()
- sys.stderr = self._stderr_stringio = self._create_StringIO()
+ sys.stdout = self._stdout_stringio = io.StringIO(newline=os.linesep)
+ sys.stderr = self._stderr_stringio = io.StringIO(newline=os.linesep)
return self
@@ -91,16 +66,12 @@ class SysStreamCapturing(object):
self.output = self._stdout_stringio.getvalue()
self.error = self._stderr_stringio.getvalue()
- if self._newline and os.linesep != '\n':
- self.output = self.output.replace('\n', os.linesep)
- self.error = self.error.replace('\n', os.linesep)
-
sys.stdin = self._orig_stdin
sys.stdout = self._orig_stdout
sys.stderr = self._orig_stderr
-class LoggingReporter(object):
+class LoggingReporter:
"""
Implementation of Reporter that just appends any error to a list.
"""
@@ -190,10 +161,6 @@ class TestIterSourceCode(TestCase):
with open(os.path.join(self.tempdir, 'c'), 'w') as fd:
fd.write('hello\nworld\n')
- python2 = os.path.join(self.tempdir, 'd')
- with open(python2, 'w') as fd:
- fd.write('#!/usr/bin/env python2\n')
-
python3 = os.path.join(self.tempdir, 'e')
with open(python3, 'w') as fd:
fd.write('#!/usr/bin/env python3\n')
@@ -206,10 +173,6 @@ class TestIterSourceCode(TestCase):
with open(python3args, 'w') as fd:
fd.write('#!/usr/bin/python3 -u\n')
- python2u = os.path.join(self.tempdir, 'h')
- with open(python2u, 'w') as fd:
- fd.write('#!/usr/bin/python2u\n')
-
python3d = os.path.join(self.tempdir, 'i')
with open(python3d, 'w') as fd:
fd.write('#!/usr/local/bin/python3d\n')
@@ -218,10 +181,6 @@ class TestIterSourceCode(TestCase):
with open(python38m, 'w') as fd:
fd.write('#! /usr/bin/env python3.8m\n')
- python27 = os.path.join(self.tempdir, 'k')
- with open(python27, 'w') as fd:
- fd.write('#!/usr/bin/python2.7 \n')
-
# Should NOT be treated as Python source
notfirst = os.path.join(self.tempdir, 'l')
with open(notfirst, 'w') as fd:
@@ -229,8 +188,10 @@ class TestIterSourceCode(TestCase):
self.assertEqual(
sorted(iterSourceCode([self.tempdir])),
- sorted([python, python2, python3, pythonw, python3args, python2u,
- python3d, python38m, python27]))
+ sorted([
+ python, python3, pythonw, python3args, python3d,
+ python38m,
+ ]))
def test_multipleDirectories(self):
"""
@@ -269,7 +230,7 @@ class TestReporter(TestCase):
number, error message, actual line of source and a caret pointing to
where the error is.
"""
- err = StringIO()
+ err = io.StringIO()
reporter = Reporter(None, err)
reporter.syntaxError('foo.py', 'a problem', 3,
8 if sys.version_info >= (3, 8) else 7,
@@ -285,7 +246,7 @@ class TestReporter(TestCase):
C{syntaxError} doesn't include a caret pointing to the error if
C{offset} is passed as C{None}.
"""
- err = StringIO()
+ err = io.StringIO()
reporter = Reporter(None, err)
reporter.syntaxError('foo.py', 'a problem', 3, None,
'bad line of source')
@@ -300,7 +261,7 @@ class TestReporter(TestCase):
line. The offset is adjusted so that it is relative to the start of
the last line.
"""
- err = StringIO()
+ err = io.StringIO()
lines = [
'bad line of source',
'more bad lines of source',
@@ -319,7 +280,7 @@ class TestReporter(TestCase):
"""
C{unexpectedError} reports an error processing a source file.
"""
- err = StringIO()
+ err = io.StringIO()
reporter = Reporter(None, err)
reporter.unexpectedError('source.py', 'error message')
self.assertEqual('source.py: error message\n', err.getvalue())
@@ -329,11 +290,11 @@ class TestReporter(TestCase):
C{flake} reports a code warning from Pyflakes. It is exactly the
str() of a L{pyflakes.messages.Message}.
"""
- out = StringIO()
+ out = io.StringIO()
reporter = Reporter(out, None)
message = UnusedImport('foo.py', Node(42), 'bar')
reporter.flake(message)
- self.assertEqual(out.getvalue(), "%s\n" % (message,))
+ self.assertEqual(out.getvalue(), f"{message}\n")
class CheckTests(TestCase):
@@ -363,7 +324,7 @@ class CheckTests(TestCase):
@param path: A path to a file to check.
@param errorList: A list of errors expected to be printed to stderr.
"""
- err = StringIO()
+ err = io.StringIO()
count = withStderrTo(err, checkPath, path)
self.assertEqual(
(count, err.getvalue()), (len(errorList), ''.join(errorList)))
@@ -496,10 +457,10 @@ def baz():
self.assertHasErrors(
sourcePath,
["""\
-%s:2:%s: invalid syntax
+{}:2:{}: invalid syntax
\tfoo =
-%s
-""" % (sourcePath, column, last_line)])
+{}
+""".format(sourcePath, column, last_line)])
def test_nonDefaultFollowsDefaultSyntaxError(self):
"""
@@ -512,27 +473,24 @@ def foo(bar=baz, bax):
pass
"""
with self.makeTempFile(source) as sourcePath:
- if ERROR_HAS_LAST_LINE:
- if PYPY:
- column = 7
- elif sys.version_info >= (3, 10):
- column = 18
- elif sys.version_info >= (3, 9):
- column = 21
- elif sys.version_info >= (3, 8):
- column = 9
- else:
- column = 8
- last_line = ' ' * (column - 1) + '^\n'
- columnstr = '%d:' % column
+ if PYPY:
+ column = 7
+ elif sys.version_info >= (3, 10):
+ column = 18
+ elif sys.version_info >= (3, 9):
+ column = 21
+ elif sys.version_info >= (3, 8):
+ column = 9
else:
- last_line = columnstr = ''
+ column = 8
+ last_line = ' ' * (column - 1) + '^\n'
+ columnstr = '%d:' % column
self.assertHasErrors(
sourcePath,
["""\
-%s:1:%s non-default argument follows default argument
+{}:1:{} non-default argument follows default argument
def foo(bar=baz, bax):
-%s""" % (sourcePath, columnstr, last_line)])
+{}""".format(sourcePath, columnstr, last_line)])
def test_nonKeywordAfterKeywordSyntaxError(self):
"""
@@ -544,59 +502,43 @@ def foo(bar=baz, bax):
foo(bar=baz, bax)
"""
with self.makeTempFile(source) as sourcePath:
- if ERROR_HAS_LAST_LINE:
- if PYPY:
- column = 12
- elif sys.version_info >= (3, 9):
- column = 17
- elif sys.version_info >= (3, 8):
- column = 14
- else:
- column = 13
- last_line = ' ' * (column - 1) + '^\n'
- columnstr = '%d:' % column
+ if PYPY:
+ column = 12
+ elif sys.version_info >= (3, 9):
+ column = 17
+ elif sys.version_info >= (3, 8):
+ column = 14
else:
- last_line = columnstr = ''
+ column = 13
+ last_line = ' ' * (column - 1) + '^\n'
+ columnstr = '%d:' % column
- if sys.version_info >= (3, 5):
- message = 'positional argument follows keyword argument'
- else:
- message = 'non-keyword arg after keyword arg'
+ message = 'positional argument follows keyword argument'
self.assertHasErrors(
sourcePath,
["""\
-%s:1:%s %s
+{}:1:{} {}
foo(bar=baz, bax)
-%s""" % (sourcePath, columnstr, message, last_line)])
+{}""".format(sourcePath, columnstr, message, last_line)])
def test_invalidEscape(self):
"""
The invalid escape syntax raises ValueError in Python 2
"""
- ver = sys.version_info
# ValueError: invalid \x escape
with self.makeTempFile(r"foo = '\xyz'") as sourcePath:
- if ver < (3,):
- decoding_error = "%s: problem decoding source\n" % (sourcePath,)
+ position_end = 1
+ if PYPY:
+ column = 5
+ elif sys.version_info >= (3, 9):
+ column = 13
else:
- position_end = 1
- if PYPY:
- column = 5
- elif ver >= (3, 9):
- column = 13
- else:
- column = 7
- # Column has been "fixed" since 3.2.4 and 3.3.1
- if ver < (3, 2, 4) or ver[:3] == (3, 3, 0):
- position_end = 2
-
- if ERROR_HAS_LAST_LINE:
- last_line = '%s^\n' % (' ' * (column - 1))
- else:
- last_line = ''
-
- decoding_error = """\
+ column = 7
+
+ last_line = '%s^\n' % (' ' * (column - 1))
+
+ decoding_error = """\
%s:1:%d: (unicode error) 'unicodeescape' codec can't decode bytes \
in position 0-%d: truncated \\xXX escape
foo = '\\xyz'
@@ -637,7 +579,7 @@ foo = '\\xyz'
"""
If source file declares the correct encoding, no error is reported.
"""
- SNOWMAN = unichr(0x2603)
+ SNOWMAN = chr(0x2603)
source = ("""\
# coding: utf-8
x = "%s"
@@ -657,40 +599,28 @@ x = "%s"
If a source file contains bytes which cannot be decoded, this is
reported on stderr.
"""
- SNOWMAN = unichr(0x2603)
+ SNOWMAN = chr(0x2603)
source = ("""\
# coding: ascii
x = "%s"
""" % SNOWMAN).encode('utf-8')
with self.makeTempFile(source) as sourcePath:
- 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, [result])
+ sourcePath, [f"{sourcePath}: problem decoding source\n"])
def test_misencodedFileUTF16(self):
"""
If a source file contains bytes which cannot be decoded, this is
reported on stderr.
"""
- SNOWMAN = unichr(0x2603)
+ SNOWMAN = chr(0x2603)
source = ("""\
# coding: ascii
x = "%s"
""" % SNOWMAN).encode('utf-16')
with self.makeTempFile(source) as sourcePath:
self.assertHasErrors(
- sourcePath, ["%s: problem decoding source\n" % (sourcePath,)])
+ sourcePath, [f"{sourcePath}: problem decoding source\n"])
def test_checkRecursive(self):
"""
@@ -702,10 +632,10 @@ x = "%s"
os.mkdir(os.path.join(tempdir, 'foo'))
file1 = os.path.join(tempdir, 'foo', 'bar.py')
with open(file1, 'wb') as fd:
- fd.write("import baz\n".encode('ascii'))
+ fd.write(b"import baz\n")
file2 = os.path.join(tempdir, 'baz.py')
with open(file2, 'wb') as fd:
- fd.write("import contraband".encode('ascii'))
+ fd.write(b"import contraband")
log = []
reporter = LoggingReporter(log)
warnings = checkRecursive([tempdir], reporter)
@@ -760,9 +690,8 @@ class IntegrationTests(TestCase):
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')
+ stdout = stdout.decode('utf-8')
+ stderr = stderr.decode('utf-8')
return (stdout, stderr, rv)
def test_goodFile(self):
@@ -780,10 +709,10 @@ class IntegrationTests(TestCase):
and the warnings are printed to stdout.
"""
with open(self.tempfilepath, 'wb') as fd:
- fd.write("import contraband\n".encode('ascii'))
+ fd.write(b"import contraband\n")
d = self.runPyflakes([self.tempfilepath])
expected = UnusedImport(self.tempfilepath, Node(1), 'contraband')
- self.assertEqual(d, ("%s%s" % (expected, os.linesep), '', 1))
+ self.assertEqual(d, (f"{expected}{os.linesep}", '', 1))
def test_errors_io(self):
"""
@@ -792,8 +721,8 @@ class IntegrationTests(TestCase):
printed to stderr.
"""
d = self.runPyflakes([self.tempfilepath])
- error_msg = '%s: No such file or directory%s' % (self.tempfilepath,
- os.linesep)
+ error_msg = '{}: No such file or directory{}'.format(self.tempfilepath,
+ os.linesep)
self.assertEqual(d, ('', error_msg, 1))
def test_errors_syntax(self):
@@ -803,7 +732,7 @@ class IntegrationTests(TestCase):
printed to stderr.
"""
with open(self.tempfilepath, 'wb') as fd:
- fd.write("import".encode('ascii'))
+ fd.write(b"import")
d = self.runPyflakes([self.tempfilepath])
error_msg = '{0}:1:{2}: invalid syntax{1}import{1} {3}^{1}'.format(
self.tempfilepath, os.linesep, 6 if PYPY else 7, '' if PYPY else ' ')
@@ -815,7 +744,7 @@ class IntegrationTests(TestCase):
"""
d = self.runPyflakes([], stdin='import contraband')
expected = UnusedImport('<stdin>', Node(1), 'contraband')
- self.assertEqual(d, ("%s%s" % (expected, os.linesep), '', 1))
+ self.assertEqual(d, (f"{expected}{os.linesep}", '', 1))
class TestMain(IntegrationTests):
diff --git a/pyflakes/test/test_builtin.py b/pyflakes/test/test_builtin.py
index 7150ddb..19fe949 100644
--- a/pyflakes/test/test_builtin.py
+++ b/pyflakes/test/test_builtin.py
@@ -1,10 +1,8 @@
"""
Tests for detecting redefinition of builtins.
"""
-from sys import version_info
-
from pyflakes import messages as m
-from pyflakes.test.harness import TestCase, skipIf
+from pyflakes.test.harness import TestCase
class TestBuiltins(TestCase):
@@ -30,12 +28,3 @@ class TestBuiltins(TestCase):
f()
''')
-
- @skipIf(version_info >= (3,), 'not an UnboundLocalError in Python 3')
- def test_builtin_in_comprehension(self):
- self.flakes('''
- def f():
- [range for range in range(1, 10)]
-
- f()
- ''', m.UndefinedLocal)
diff --git a/pyflakes/test/test_checker.py b/pyflakes/test/test_checker.py
index b527572..66e3501 100644
--- a/pyflakes/test/test_checker.py
+++ b/pyflakes/test/test_checker.py
@@ -1,8 +1,7 @@
import ast
-import sys
from pyflakes import checker
-from pyflakes.test.harness import TestCase, skipIf
+from pyflakes.test.harness import TestCase
class TypeableVisitorTests(TestCase):
@@ -53,7 +52,6 @@ def f():
self.assertIsInstance(visitor.typeable_nodes[1], ast.FunctionDef)
self.assertIsInstance(visitor.typeable_nodes[2], ast.Assign)
- @skipIf(sys.version_info < (3, 5), 'async syntax introduced in py35')
def test_py35_node_types(self):
"""
Test that the PEP 492 node types are collected
@@ -110,7 +108,7 @@ class CollectTypeCommentsTests(TestCase):
"""
Test that the function works for text source
"""
- ret = self._collect(u'x = 1 # type: int')
+ ret = self._collect('x = 1 # type: int')
self.assertEqual(ret, {(ast.Assign, ('# type: int',))})
def test_non_type_comment_ignored(self):
diff --git a/pyflakes/test/test_code_segment.py b/pyflakes/test/test_code_segment.py
index 131a74d..79532b9 100644
--- a/pyflakes/test/test_code_segment.py
+++ b/pyflakes/test/test_code_segment.py
@@ -1,9 +1,7 @@
-from sys import version_info
-
from pyflakes import messages as m
from pyflakes.checker import (FunctionScope, ClassScope, ModuleScope,
Argument, FunctionDefinition, Assignment)
-from pyflakes.test.harness import TestCase, skipIf
+from pyflakes.test.harness import TestCase
class TestCodeSegments(TestCase):
@@ -127,6 +125,5 @@ class TestCodeSegments(TestCase):
self.assertIsInstance(function_scope_bar['h'], Argument)
self.assertIsInstance(function_scope_bar['i'], Argument)
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_scope_async_function(self):
self.flakes('async def foo(): pass', is_segment=True)
diff --git a/pyflakes/test/test_dict.py b/pyflakes/test/test_dict.py
index b9059c2..c8ee484 100644
--- a/pyflakes/test/test_dict.py
+++ b/pyflakes/test/test_dict.py
@@ -2,10 +2,8 @@
Tests for dict duplicate keys Pyflakes behavior.
"""
-from sys import version_info
-
from pyflakes import messages as m
-from pyflakes.test.harness import TestCase, skipIf
+from pyflakes.test.harness import TestCase
class Test(TestCase):
@@ -17,13 +15,9 @@ class Test(TestCase):
m.MultiValueRepeatedKeyLiteral,
)
- @skipIf(version_info < (3,),
- "bytes and strings with same 'value' are not equal in python3")
def test_duplicate_keys_bytes_vs_unicode_py3(self):
self.flakes("{b'a': 1, u'a': 2}")
- @skipIf(version_info < (3,),
- "bytes and strings with same 'value' are not equal in python3")
def test_duplicate_values_bytes_vs_unicode_py3(self):
self.flakes(
"{1: b'a', 1: u'a'}",
@@ -31,20 +25,6 @@ class Test(TestCase):
m.MultiValueRepeatedKeyLiteral,
)
- @skipIf(version_info >= (3,),
- "bytes and strings with same 'value' are equal in python2")
- def test_duplicate_keys_bytes_vs_unicode_py2(self):
- self.flakes(
- "{b'a': 1, u'a': 2}",
- m.MultiValueRepeatedKeyLiteral,
- m.MultiValueRepeatedKeyLiteral,
- )
-
- @skipIf(version_info >= (3,),
- "bytes and strings with same 'value' are equal in python2")
- def test_duplicate_values_bytes_vs_unicode_py2(self):
- self.flakes("{1: b'a', 1: u'a'}")
-
def test_multiple_duplicate_keys(self):
self.flakes(
"{'yes': 1, 'yes': 2, 'no': 2, 'no': 3}",
diff --git a/pyflakes/test/test_doctests.py b/pyflakes/test/test_doctests.py
index de796b9..197e7fb 100644
--- a/pyflakes/test/test_doctests.py
+++ b/pyflakes/test/test_doctests.py
@@ -14,7 +14,7 @@ from pyflakes.test.test_undefined_names import Test as TestUndefinedNames
from pyflakes.test.harness import TestCase, skip
-class _DoctestMixin(object):
+class _DoctestMixin:
withDoctest = True
@@ -43,7 +43,7 @@ class _DoctestMixin(object):
return doctestificator % "\n ".join(lines)
def flakes(self, input, *args, **kw):
- return super(_DoctestMixin, self).flakes(self.doctestify(input), *args, **kw)
+ return super().flakes(self.doctestify(input), *args, **kw)
class Test(TestCase):
diff --git a/pyflakes/test/test_imports.py b/pyflakes/test/test_imports.py
index 07504d9..fb5d2fd 100644
--- a/pyflakes/test/test_imports.py
+++ b/pyflakes/test/test_imports.py
@@ -1,5 +1,3 @@
-from sys import version_info
-
from pyflakes import messages as m
from pyflakes.checker import (
FutureImportation,
@@ -8,7 +6,7 @@ from pyflakes.checker import (
StarImportation,
SubmoduleImportation,
)
-from pyflakes.test.harness import TestCase, skip, skipIf
+from pyflakes.test.harness import TestCase, skip
class TestImportationObject(TestCase):
@@ -577,9 +575,8 @@ class Test(TestCase):
def test_redefinedByExcept(self):
expected = [m.RedefinedWhileUnused]
- if version_info >= (3,):
- # The exc variable is unused inside the exception handler.
- expected.append(m.UnusedVariable)
+ # The exc variable is unused inside the exception handler.
+ expected.append(m.UnusedVariable)
self.flakes('''
import fu
try: pass
@@ -624,12 +621,6 @@ class Test(TestCase):
self.flakes('import fu; [fu for _ in range(1)]')
self.flakes('import fu; [1 for _ in range(1) if fu]')
- @skipIf(version_info >= (3,),
- 'in Python 3 list comprehensions execute in a separate scope')
- def test_redefinedByListComp(self):
- self.flakes('import fu; [1 for fu in range(1)]',
- m.RedefinedInListComp)
-
def test_usedInTryFinally(self):
self.flakes('''
import fu
@@ -686,15 +677,8 @@ class Test(TestCase):
def g(): foo.is_used()
''')
- @skipIf(version_info >= (3,), 'deprecated syntax')
- def test_usedInBackquote(self):
- self.flakes('import fu; `fu`')
-
def test_usedInExec(self):
- if version_info < (3,):
- exec_stmt = 'exec "print 1" in fu.bar'
- else:
- exec_stmt = 'exec("print(1)", fu.bar)'
+ exec_stmt = 'exec("print(1)", fu.bar)'
self.flakes('import fu; %s' % exec_stmt)
def test_usedInLambda(self):
@@ -793,8 +777,6 @@ class Test(TestCase):
assert error.message == '%r imported but unused'
assert error.message_args == ('from .. import *', )
- @skipIf(version_info < (3,),
- 'import * below module level is a warning on Python 2')
def test_localImportStar(self):
"""import * is only allowed at module level."""
self.flakes('''
@@ -814,17 +796,6 @@ class Test(TestCase):
assert error.message == "'from %s import *' only allowed at module level"
assert error.message_args == ('..', )
- @skipIf(version_info > (3,),
- 'import * below module level is an error on Python 3')
- def test_importStarNested(self):
- """All star imports are marked as used by an undefined variable."""
- self.flakes('''
- from fu import *
- def f():
- from bar import *
- x
- ''', m.ImportStarUsed, m.ImportStarUsed, m.ImportStarUsage)
-
def test_packageImport(self):
"""
If a dotted name is imported and used, no warning is reported.
diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py
index 97685ef..b138cf6 100644
--- a/pyflakes/test/test_other.py
+++ b/pyflakes/test/test_other.py
@@ -21,37 +21,6 @@ class Test(TestCase):
f()
''', m.UndefinedLocal, m.UnusedVariable)
- @skipIf(version_info >= (3,),
- 'in Python 3 list comprehensions execute in a separate scope')
- def test_redefinedInListComp(self):
- """
- Test that shadowing a variable in a list comprehension raises
- a warning.
- """
- self.flakes('''
- a = 1
- [1 for a, b in [(1, 2)]]
- ''', m.RedefinedInListComp)
- self.flakes('''
- class A:
- a = 1
- [1 for a, b in [(1, 2)]]
- ''', m.RedefinedInListComp)
- self.flakes('''
- def f():
- a = 1
- [1 for a, b in [(1, 2)]]
- ''', m.RedefinedInListComp)
- self.flakes('''
- [1 for a, b in [(1, 2)]]
- [1 for a, b in [(1, 2)]]
- ''')
- self.flakes('''
- for a, b in [(1, 2)]:
- pass
- [1 for a, b in [(1, 2)]]
- ''')
-
def test_redefinedInGenerator(self):
"""
Test that reusing a variable in a generator does not raise
@@ -239,21 +208,6 @@ class Test(TestCase):
[a for a in '12']
''')
- @skipIf(version_info >= (3,),
- 'in Python 3 list comprehensions execute in a separate scope')
- def test_redefinedElseInListComp(self):
- """
- Test that shadowing a variable in a list comprehension in
- an else (or if) block raises a warning.
- """
- self.flakes('''
- if False:
- pass
- else:
- a = 1
- [a for a in '12']
- ''', m.RedefinedInListComp)
-
def test_functionDecorator(self):
"""
Test that shadowing a function definition with a decorated version of
@@ -394,7 +348,6 @@ class Test(TestCase):
yield
''', m.YieldOutsideFunction)
- @skipIf(version_info < (3, 3), "Python >= 3.3 only")
def test_classWithYieldFrom(self):
"""
If a yield from is used inside a class, a warning is emitted.
@@ -404,7 +357,6 @@ class Test(TestCase):
yield from range(10)
''', m.YieldOutsideFunction)
- @skipIf(version_info < (3, 3), "Python >= 3.3 only")
def test_moduleWithYieldFrom(self):
"""
If a yield from is used at the module level, a warning is emitted.
@@ -903,7 +855,6 @@ class Test(TestCase):
pass
''', m.DefaultExceptNotLast, m.DefaultExceptNotLast)
- @skipIf(version_info < (3,), "Python 3 only")
def test_starredAssignmentNoError(self):
"""
Python 3 extended iterable unpacking
@@ -957,7 +908,6 @@ class Test(TestCase):
", *rest] = range(1<<8)"
self.flakes(s)
- @skipIf(version_info < (3, ), "Python 3 only")
def test_starredAssignmentErrors(self):
"""
SyntaxErrors (not encoded in the ast) surrounding Python 3 extended
@@ -1185,7 +1135,6 @@ class Test(TestCase):
pass
''')
- @skipIf(version_info < (3, 3), "Python >= 3.3 only")
def test_function_arguments_python3(self):
self.flakes('''
def foo(a, b, c=0, *args, d=0, **kwargs):
@@ -1276,7 +1225,6 @@ class TestUnusedAssignment(TestCase):
b = 1
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_assignToNonlocal(self):
"""
Assigning to a nonlocal and then not using that binding is perfectly
@@ -1660,8 +1608,6 @@ class TestUnusedAssignment(TestCase):
except Exception as e: e
''')
- @skipIf(version_info < (3,),
- "In Python 2 exception names stay bound after the exception handler")
def test_exceptionUnusedInExcept(self):
self.flakes('''
try: pass
@@ -1754,7 +1700,6 @@ class TestUnusedAssignment(TestCase):
assert 1
''')
- @skipIf(version_info < (3, 3), 'new in Python 3.3')
def test_yieldFromUndefined(self):
"""
Test C{yield from} statement
@@ -1764,7 +1709,6 @@ class TestUnusedAssignment(TestCase):
yield from foo()
''', m.UndefinedName)
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_f_string(self):
"""Test PEP 498 f-strings are treated as a usage."""
self.flakes('''
@@ -1801,7 +1745,6 @@ class TestUnusedAssignment(TestCase):
class TestStringFormatting(TestCase):
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_f_string_without_placeholders(self):
self.flakes("f'foo'", m.FStringMissingPlaceholders)
self.flakes('''
@@ -1939,7 +1882,6 @@ class TestStringFormatting(TestCase):
'%*.*f' % (5, 2, 3.1234)
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_ok_percent_format_cannot_determine_element_count(self):
self.flakes('''
a = []
@@ -1954,28 +1896,24 @@ class TestStringFormatting(TestCase):
class TestAsyncStatements(TestCase):
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncDef(self):
self.flakes('''
async def bar():
return 42
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncDefAwait(self):
self.flakes('''
async def read_data(db):
await db.fetch('SELECT ...')
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncDefUndefined(self):
self.flakes('''
async def bar():
return foo()
''', m.UndefinedName)
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncFor(self):
self.flakes('''
async def read_data(db):
@@ -1985,7 +1923,6 @@ class TestAsyncStatements(TestCase):
return output
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncForUnderscoreLoopVar(self):
self.flakes('''
async def coro(it):
@@ -1993,7 +1930,6 @@ class TestAsyncStatements(TestCase):
pass
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_loopControlInAsyncFor(self):
self.flakes('''
async def read_data(db):
@@ -2015,7 +1951,6 @@ class TestAsyncStatements(TestCase):
return output
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_loopControlInAsyncForElse(self):
self.flakes('''
async def read_data(db):
@@ -2037,7 +1972,6 @@ class TestAsyncStatements(TestCase):
return output
''', m.BreakOutsideLoop)
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
@skipIf(version_info > (3, 8), "Python <= 3.8 only")
def test_continueInAsyncForFinally(self):
self.flakes('''
@@ -2051,7 +1985,6 @@ class TestAsyncStatements(TestCase):
return output
''', m.ContinueInFinally)
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncWith(self):
self.flakes('''
async def commit(session, data):
@@ -2059,7 +1992,6 @@ class TestAsyncStatements(TestCase):
await session.update(data)
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_asyncWithItem(self):
self.flakes('''
async def commit(session, data):
@@ -2069,14 +2001,12 @@ class TestAsyncStatements(TestCase):
await trans.end()
''')
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_matmul(self):
self.flakes('''
def foo(a, b):
return a @ b
''')
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_formatstring(self):
self.flakes('''
hi = 'hi'
diff --git a/pyflakes/test/test_return_with_arguments_inside_generator.py b/pyflakes/test/test_return_with_arguments_inside_generator.py
deleted file mode 100644
index fc1272a..0000000
--- a/pyflakes/test/test_return_with_arguments_inside_generator.py
+++ /dev/null
@@ -1,34 +0,0 @@
-
-from sys import version_info
-
-from pyflakes import messages as m
-from pyflakes.test.harness import TestCase, skipIf
-
-
-class Test(TestCase):
- @skipIf(version_info >= (3, 3), 'new in Python 3.3')
- def test_return(self):
- self.flakes('''
- class a:
- def b():
- for x in a.c:
- if x:
- yield x
- return a
- ''', m.ReturnWithArgsInsideGenerator)
-
- @skipIf(version_info >= (3, 3), 'new in Python 3.3')
- def test_returnNone(self):
- self.flakes('''
- def a():
- yield 12
- return None
- ''', m.ReturnWithArgsInsideGenerator)
-
- @skipIf(version_info >= (3, 3), 'new in Python 3.3')
- def test_returnYieldExpression(self):
- self.flakes('''
- def a():
- b = yield a
- return b
- ''', m.ReturnWithArgsInsideGenerator)
diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py
index 1caecb4..3775cd3 100644
--- a/pyflakes/test/test_type_annotations.py
+++ b/pyflakes/test/test_type_annotations.py
@@ -68,7 +68,6 @@ class TestTypeAnnotations(TestCase):
return s
""")
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_typingOverloadAsync(self):
"""Allow intentional redefinitions via @typing.overload (async)"""
self.flakes("""
@@ -152,7 +151,6 @@ class TestTypeAnnotations(TestCase):
pass
""", m.RedefinedWhileUnused)
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_variable_annotations(self):
self.flakes('''
name: str
@@ -300,7 +298,6 @@ class TestTypeAnnotations(TestCase):
a: 'a: "A"'
''', m.ForwardAnnotationSyntaxError)
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_TypeAlias_annotations(self):
self.flakes("""
from typing_extensions import TypeAlias
@@ -340,7 +337,6 @@ class TestTypeAnnotations(TestCase):
bar: TypeAlias
""", m.UnusedImport)
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_annotating_an_import(self):
self.flakes('''
from a import b, c
@@ -348,7 +344,6 @@ class TestTypeAnnotations(TestCase):
print(b)
''')
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_unused_annotation(self):
# Unused annotations are fine in module and class scope
self.flakes('''
@@ -368,7 +363,6 @@ class TestTypeAnnotations(TestCase):
x = 3
''', m.UnusedVariable)
- @skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_annotated_async_def(self):
self.flakes('''
class c: pass
@@ -400,7 +394,6 @@ class TestTypeAnnotations(TestCase):
def g(t: 'T'): pass
''')
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_type_annotation_clobbers_all(self):
self.flakes('''\
from typing import TYPE_CHECKING, List
@@ -518,13 +511,10 @@ class TestTypeAnnotations(TestCase):
""", m.UndefinedName)
def test_typeIgnoreBogusUnicode(self):
- error = (m.CommentAnnotationSyntaxError if version_info < (3,)
- else m.UndefinedName)
self.flakes("""
x = 2 # type: ignore\xc3
- """, error)
+ """, m.UndefinedName)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_return_annotation_is_class_scope_variable(self):
self.flakes("""
from typing import TypeVar
@@ -535,7 +525,6 @@ class TestTypeAnnotations(TestCase):
return x
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_return_annotation_is_function_body_variable(self):
self.flakes("""
class Test:
@@ -552,7 +541,6 @@ class TestTypeAnnotations(TestCase):
def f(c: C, /): ...
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_partially_quoted_type_annotation(self):
self.flakes("""
from queue import Queue
@@ -617,7 +605,6 @@ class TestTypeAnnotations(TestCase):
S = TypeVar('S', int, bound='List[int]')
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_literal_type_typing(self):
self.flakes("""
from typing import Literal
@@ -626,7 +613,6 @@ class TestTypeAnnotations(TestCase):
return None
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_literal_type_typing_extensions(self):
self.flakes("""
from typing_extensions import Literal
@@ -635,7 +621,6 @@ class TestTypeAnnotations(TestCase):
return None
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_annotated_type_typing_missing_forward_type(self):
self.flakes("""
from typing import Annotated
@@ -644,7 +629,6 @@ class TestTypeAnnotations(TestCase):
return None
""", m.UndefinedName)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_annotated_type_typing_missing_forward_type_multiple_args(self):
self.flakes("""
from typing import Annotated
@@ -653,7 +637,6 @@ class TestTypeAnnotations(TestCase):
return None
""", m.UndefinedName)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_annotated_type_typing_with_string_args(self):
self.flakes("""
from typing import Annotated
@@ -662,7 +645,6 @@ class TestTypeAnnotations(TestCase):
return None
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_annotated_type_typing_with_string_args_in_union(self):
self.flakes("""
from typing import Annotated, Union
@@ -671,7 +653,6 @@ class TestTypeAnnotations(TestCase):
return None
""", m.UndefinedName)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_literal_type_some_other_module(self):
"""err on the side of false-negatives for types named Literal"""
self.flakes("""
@@ -684,7 +665,6 @@ class TestTypeAnnotations(TestCase):
return None
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_literal_union_type_typing(self):
self.flakes("""
from typing import Literal
@@ -693,7 +673,6 @@ class TestTypeAnnotations(TestCase):
return None
""")
- @skipIf(version_info < (3,), 'new in Python 3')
def test_deferred_twice_annotation(self):
self.flakes("""
from queue import Queue
@@ -791,7 +770,6 @@ class TestTypeAnnotations(TestCase):
cast(A["E"], [])
""")
- @skipIf(version_info < (3, 6), 'new in Python 3.6')
def test_namedtypes_classes(self):
self.flakes("""
from typing import TypedDict, NamedTuple
diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py
index e0e628d..f3b89ea 100644
--- a/pyflakes/test/test_undefined_names.py
+++ b/pyflakes/test/test_undefined_names.py
@@ -1,8 +1,7 @@
import ast
-from sys import version_info
from pyflakes import messages as m, checker
-from pyflakes.test.harness import TestCase, skipIf, skip
+from pyflakes.test.harness import TestCase, skip
class Test(TestCase):
@@ -12,8 +11,6 @@ class Test(TestCase):
def test_definedInListComp(self):
self.flakes('[a for a in range(10) if a]')
- @skipIf(version_info < (3,),
- 'in Python 2 list comprehensions execute in the same scope')
def test_undefinedInListComp(self):
self.flakes('''
[a for a in range(10)]
@@ -21,8 +18,6 @@ class Test(TestCase):
''',
m.UndefinedName)
- @skipIf(version_info < (3,),
- 'in Python 2 exception names stay bound after the except: block')
def test_undefinedExceptionName(self):
"""Exception names can't be used after the except: block.
@@ -65,14 +60,12 @@ class Test(TestCase):
''',
m.UndefinedName)
- @skipIf(version_info < (3,),
- 'in Python 2 exception names stay bound after the except: block')
def test_undefinedExceptionNameObscuringLocalVariable2(self):
"""Exception names are unbound after the `except:` block.
- Last line will raise UnboundLocalError on Python 3 but would print out
- 've' on Python 2. The exc variable is unused inside the exception
- handler."""
+ Last line will raise UnboundLocalError.
+ The exc variable is unused inside the exception handler.
+ """
self.flakes('''
try:
raise ValueError('ve')
@@ -87,8 +80,6 @@ class Test(TestCase):
Last line will never raise UnboundLocalError because it's only
entered if no exception was raised."""
- # The exc variable is unused inside the exception handler.
- expected = [] if version_info < (3,) else [m.UnusedVariable]
self.flakes('''
exc = 'Original value'
try:
@@ -97,7 +88,7 @@ class Test(TestCase):
print('exception logged')
raise
exc
- ''', *expected)
+ ''', m.UnusedVariable)
def test_delExceptionInExcept(self):
"""The exception name can be deleted in the except: block."""
@@ -113,8 +104,6 @@ class Test(TestCase):
Last line will never raise UnboundLocalError because `error` is
only falsy if the `except:` block has not been entered."""
- # The exc variable is unused inside the exception handler.
- expected = [] if version_info < (3,) else [m.UnusedVariable]
self.flakes('''
exc = 'Original value'
error = None
@@ -126,15 +115,15 @@ class Test(TestCase):
print(error)
else:
exc
- ''', *expected)
+ ''', m.UnusedVariable)
@skip('error reporting disabled due to false positives below')
def test_undefinedExceptionNameObscuringGlobalVariable(self):
"""Exception names obscure globals, can't be used after.
- Last line will raise UnboundLocalError on both Python 2 and
- Python 3 because the existence of that exception name creates
- a local scope placeholder for it, obscuring any globals, etc."""
+ Last line will raise UnboundLocalError because the existence of that
+ exception name creates a local scope placeholder for it, obscuring any
+ globals, etc."""
self.flakes('''
exc = 'Original value'
def func():
@@ -172,8 +161,6 @@ class Test(TestCase):
Last line will never raise NameError because it's only entered
if no exception was raised."""
- # The exc variable is unused inside the exception handler.
- expected = [] if version_info < (3,) else [m.UnusedVariable]
self.flakes('''
exc = 'Original value'
def func():
@@ -184,15 +171,13 @@ class Test(TestCase):
print('exception logged')
raise
exc
- ''', *expected)
+ ''', m.UnusedVariable)
def test_undefinedExceptionNameObscuringGlobalVariableFalsePositive2(self):
"""Exception names obscure globals, can't be used after. Unless.
Last line will never raise NameError because `error` is only
falsy if the `except:` block has not been entered."""
- # The exc variable is unused inside the exception handler.
- expected = [] if version_info < (3,) else [m.UnusedVariable]
self.flakes('''
exc = 'Original value'
def func():
@@ -206,7 +191,7 @@ class Test(TestCase):
print(error)
else:
exc
- ''', *expected)
+ ''', m.UnusedVariable)
def test_functionsNeedGlobalScope(self):
self.flakes('''
@@ -226,7 +211,6 @@ class Test(TestCase):
"""
self.flakes('WindowsError')
- @skipIf(version_info < (3, 6), 'new feature in 3.6')
def test_moduleAnnotations(self):
"""
Use of the C{__annotations__} in module scope should not emit
@@ -279,7 +263,6 @@ class Test(TestCase):
__module__
''', m.UndefinedName)
- @skipIf(version_info < (3, 3), "Python >= 3.3 only")
def test_magicQualnameInClassScope(self):
"""
Use of the C{__qualname__} magic builtin should not emit an undefined
@@ -301,26 +284,6 @@ class Test(TestCase):
self.flakes('from fu import *; bar',
m.ImportStarUsed, m.ImportStarUsage)
- @skipIf(version_info >= (3,), 'obsolete syntax')
- def test_localImportStar(self):
- """
- A local import * still allows undefined names to be found
- in upper scopes.
- """
- self.flakes('''
- def a():
- from fu import *
- bar
- ''', m.ImportStarUsed, m.UndefinedName, m.UnusedImport)
-
- @skipIf(version_info >= (3,), 'obsolete syntax')
- def test_unpackedParameter(self):
- """Unpacked function parameters create bindings."""
- self.flakes('''
- def a((bar, baz)):
- bar; baz
- ''')
-
def test_definedByGlobal(self):
"""
"global" can make an otherwise undefined name in another function
@@ -608,7 +571,6 @@ class Test(TestCase):
print(a, b, c)
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_definedAsStarUnpack(self):
"""Star names in unpack are defined."""
self.flakes('''
@@ -624,7 +586,6 @@ class Test(TestCase):
print(a, b, c)
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_usedAsStarUnpack(self):
"""
Star names in unpack are used if RHS is not a tuple/list literal.
@@ -642,7 +603,6 @@ class Test(TestCase):
[a, *b, c] = range(10)
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_unusedAsStarUnpack(self):
"""
Star names in unpack are unused if RHS is a tuple/list literal.
@@ -660,7 +620,6 @@ class Test(TestCase):
[a, *b, c] = 9, 8, 7, 6, 5, 4
''', m.UnusedVariable, m.UnusedVariable, m.UnusedVariable)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_keywordOnlyArgs(self):
"""Keyword-only arg names are defined."""
self.flakes('''
@@ -674,7 +633,6 @@ class Test(TestCase):
print(a, b)
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_keywordOnlyArgsUndefined(self):
"""Typo in kwonly name."""
self.flakes('''
@@ -682,7 +640,6 @@ class Test(TestCase):
print(a, b)
''', m.UndefinedName)
- @skipIf(version_info < (3,), 'new in Python 3')
def test_annotationUndefined(self):
"""Undefined annotations."""
self.flakes('''
@@ -697,7 +654,6 @@ class Test(TestCase):
def func(a: {1, d}) -> (lambda c: e): pass
''')
- @skipIf(version_info < (3,), 'new in Python 3')
def test_metaClassUndefined(self):
self.flakes('''
from abc import ABCMeta
@@ -837,10 +793,6 @@ class Test(TestCase):
''', m.UndefinedName)
def test_dunderClass(self):
- """
- `__class__` is defined in class scope under Python 3, but is not
- in Python 2.
- """
code = '''
class Test(object):
def __init__(self):
@@ -849,10 +801,7 @@ class Test(TestCase):
t = Test()
'''
- if version_info < (3,):
- self.flakes(code, m.UndefinedName)
- else:
- self.flakes(code)
+ self.flakes(code)
class NameTests(TestCase):