Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pyflakes for openSUSE:Factory
checked in at 2021-03-21 23:19:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyflakes (Old)
and /work/SRC/openSUSE:Factory/.python-pyflakes.new.2401 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyflakes"
Sun Mar 21 23:19:16 2021 rev:26 rq:879832 version:2.3.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyflakes/python-pyflakes.changes
2020-05-28 09:06:39.183579162 +0200
+++
/work/SRC/openSUSE:Factory/.python-pyflakes.new.2401/python-pyflakes.changes
2021-03-21 23:19:18.812717469 +0100
@@ -1,0 +2,15 @@
+Tue Mar 16 23:13:47 UTC 2021 - John Vandenberg <[email protected]>
+
+- Update to v2.3.0
+ * Recognize tuple concatenation in ``__all__`` export definitions
+ * Better support use of annotation-only assignments when using
+ ``from __future__ import annotations``
+ * Recognize special-case typing for ``Annotated``
+ * Fix undefined name ``__qualname__`` in class scope
+ * Recognize special-cased typing for ``TypeVar``
+ * Errors for undefined exports in ``__all__`` are shown in a
+ deterministic order
+ * Fix false positives in certain typing constructs (TypeVar,
+ NamedTuple, TypedDict, cast)
+
+-------------------------------------------------------------------
Old:
----
pyflakes-2.2.0.tar.gz
New:
----
pyflakes-2.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pyflakes.spec ++++++
--- /var/tmp/diff_new_pack.wE9NZR/_old 2021-03-21 23:19:19.276717629 +0100
+++ /var/tmp/diff_new_pack.wE9NZR/_new 2021-03-21 23:19:19.280717630 +0100
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%bcond_without test
Name: python-pyflakes
-Version: 2.2.0
+Version: 2.3.0
Release: 0
Summary: Passive checker of Python programs
License: MIT
++++++ pyflakes-2.2.0.tar.gz -> pyflakes-2.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/NEWS.rst new/pyflakes-2.3.0/NEWS.rst
--- old/pyflakes-2.2.0/NEWS.rst 2020-04-10 05:49:52.000000000 +0200
+++ new/pyflakes-2.3.0/NEWS.rst 2021-03-14 17:32:52.000000000 +0100
@@ -1,3 +1,15 @@
+2.3.0 (2021-03-14)
+
+- Recognize tuple concatenation in ``__all__`` export definitions
+- Better support use of annotation-only assignments when using
+ ``from __future__ import annotations``
+- Recognize special-case typing for ``Annotated``
+- Fix undefined name ``__qualname__`` in class scope
+- Recognize special-cased typing for ``TypeVar``
+- Errors for undefined exports in ``__all__`` are shown in a deterministic
order
+- Fix false positives in certain typing constructs (``TypeVar``,
+ ``NamedTuple``, ``TypedDict``, ``cast``)
+
2.2.0 (2020-04-08)
- Include column information in error messages
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/PKG-INFO new/pyflakes-2.3.0/PKG-INFO
--- old/pyflakes-2.2.0/PKG-INFO 2020-04-10 05:51:46.000000000 +0200
+++ new/pyflakes-2.3.0/PKG-INFO 2021-03-14 17:33:07.042730300 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: pyflakes
-Version: 2.2.0
+Version: 2.3.0
Summary: passive checker of Python programs
Home-page: https://github.com/PyCQA/pyflakes
Author: A lot of people
@@ -17,7 +17,7 @@
modules with side effects. It's also much faster.
It is `available on PyPI <https://pypi.org/project/pyflakes/>`_
- and it supports all active versions of Python: 2.7 and 3.4 to 3.7.
+ and it supports all active versions of Python: 2.7 and 3.4 to 3.8.
@@ -80,13 +80,13 @@
All changes should include tests and pass flake8_.
- .. image:: https://api.travis-ci.org/PyCQA/pyflakes.svg?branch=master
- :target: https://travis-ci.org/PyCQA/pyflakes
- :alt: Build status
+ .. image:: https://github.com/PyCQA/pyflakes/workflows/Test/badge.svg
+ :target: https://github.com/PyCQA/pyflakes/actions
+ :alt: GitHub Actions build status
- .. _Pylint: http://www.pylint.org/
+ .. _Pylint: https://www.pylint.org/
.. _flake8: https://pypi.org/project/flake8/
- .. _`PEP 8`: http://legacy.python.org/dev/peps/pep-0008/
+ .. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
.. _Pychecker: http://pychecker.sourceforge.net/
.. _`rebase your changes`:
https://git-scm.com/book/en/v2/Git-Branching-Rebasing
.. _`GitHub pull request`: https://github.com/PyCQA/pyflakes/pulls
@@ -109,6 +109,7 @@
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/README.rst
new/pyflakes-2.3.0/README.rst
--- old/pyflakes-2.2.0/README.rst 2020-04-10 05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/README.rst 2021-03-14 17:31:42.000000000 +0100
@@ -9,7 +9,7 @@
modules with side effects. It's also much faster.
It is `available on PyPI <https://pypi.org/project/pyflakes/>`_
-and it supports all active versions of Python: 2.7 and 3.4 to 3.7.
+and it supports all active versions of Python: 2.7 and 3.4 to 3.8.
@@ -72,13 +72,13 @@
All changes should include tests and pass flake8_.
-.. image:: https://api.travis-ci.org/PyCQA/pyflakes.svg?branch=master
- :target: https://travis-ci.org/PyCQA/pyflakes
- :alt: Build status
+.. image:: https://github.com/PyCQA/pyflakes/workflows/Test/badge.svg
+ :target: https://github.com/PyCQA/pyflakes/actions
+ :alt: GitHub Actions build status
-.. _Pylint: http://www.pylint.org/
+.. _Pylint: https://www.pylint.org/
.. _flake8: https://pypi.org/project/flake8/
-.. _`PEP 8`: http://legacy.python.org/dev/peps/pep-0008/
+.. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
.. _Pychecker: http://pychecker.sourceforge.net/
.. _`rebase your changes`:
https://git-scm.com/book/en/v2/Git-Branching-Rebasing
.. _`GitHub pull request`: https://github.com/PyCQA/pyflakes/pulls
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes/__init__.py
new/pyflakes-2.3.0/pyflakes/__init__.py
--- old/pyflakes-2.2.0/pyflakes/__init__.py 2020-04-10 05:50:00.000000000
+0200
+++ new/pyflakes-2.3.0/pyflakes/__init__.py 2021-03-14 17:32:52.000000000
+0100
@@ -1 +1 @@
-__version__ = '2.2.0'
+__version__ = '2.3.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes/checker.py
new/pyflakes-2.3.0/pyflakes/checker.py
--- old/pyflakes-2.2.0/pyflakes/checker.py 2020-04-10 05:48:16.000000000
+0200
+++ new/pyflakes-2.3.0/pyflakes/checker.py 2021-03-14 17:31:42.000000000
+0100
@@ -79,6 +79,10 @@
LOOP_TYPES = (ast.While, ast.For)
FUNCTION_TYPES = (ast.FunctionDef,)
+if PY36_PLUS:
+ ANNASSIGN_TYPES = (ast.AnnAssign,)
+else:
+ ANNASSIGN_TYPES = ()
if PY38_PLUS:
def _is_singleton(node): # type: (ast.AST) -> bool
@@ -124,6 +128,13 @@
return _is_constant(node) and not _is_singleton(node)
+def _is_name_or_attr(node, name): # type: (ast.Ast, str) -> bool
+ return (
+ (isinstance(node, ast.Name) and node.id == name) or
+ (isinstance(node, ast.Attribute) and node.attr == name)
+ )
+
+
#
https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104
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
@@ -528,6 +539,16 @@
"""
+class Annotation(Binding):
+ """
+ Represents binding a name to a type without an associated value.
+
+ As long as this name is not assigned a value in another binding, it is
considered
+ undefined for most purposes. One notable exception is using the name as a
type
+ annotation.
+ """
+
+
class FunctionDefinition(Definition):
pass
@@ -542,7 +563,7 @@
can be determined statically, they will be treated as names for export and
additional checking applied to them.
- The only recognized C{__all__} assignment via list concatenation is in the
+ The only recognized C{__all__} assignment via list/tuple concatenation is
in the
following format:
__all__ = ['a'] + ['b'] + ['c']
@@ -564,10 +585,10 @@
if isinstance(source.value, (ast.List, ast.Tuple)):
_add_to_names(source.value)
- # If concatenating lists
+ # If concatenating lists or tuples
elif isinstance(source.value, ast.BinOp):
currentValue = source.value
- while isinstance(currentValue.right, ast.List):
+ while isinstance(currentValue.right, (ast.List, ast.Tuple)):
left = currentValue.left
right = currentValue.right
_add_to_names(right)
@@ -575,7 +596,7 @@
if isinstance(left, ast.BinOp):
currentValue = left
# If just two lists are being added
- elif isinstance(left, ast.List):
+ elif isinstance(left, (ast.List, ast.Tuple)):
_add_to_names(left)
# All lists accounted for - done
break
@@ -648,6 +669,10 @@
self.col_offset = col_offset
+class DetectClassScopedMagic:
+ names = dir()
+
+
# Globally defined names which are not attributes of the builtins module, or
# are only present on some platforms.
_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
@@ -732,6 +757,12 @@
)
+class AnnotationState:
+ NONE = 0
+ STRING = 1
+ BARE = 2
+
+
def in_annotation(func):
@functools.wraps(func)
def in_annotation_func(self, *args, **kwargs):
@@ -740,6 +771,14 @@
return in_annotation_func
+def in_string_annotation(func):
+ @functools.wraps(func)
+ def in_annotation_func(self, *args, **kwargs):
+ with self._enter_annotation(AnnotationState.STRING):
+ return func(self, *args, **kwargs)
+ return in_annotation_func
+
+
def make_tokens(code):
# PY3: tokenize.tokenize requires readline of bytes
if not isinstance(code, bytes):
@@ -826,8 +865,7 @@
nodeDepth = 0
offset = None
traceTree = False
- _in_annotation = False
- _in_typing_literal = False
+ _in_annotation = AnnotationState.NONE
_in_deferred = False
builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
@@ -954,7 +992,10 @@
if all_binding:
all_names = set(all_binding.names)
- undefined = all_names.difference(scope)
+ undefined = [
+ name for name in all_binding.names
+ if name not in scope
+ ]
else:
all_names = undefined = []
@@ -1099,7 +1140,10 @@
# then assume the rebound name is used as a global or within a loop
value.used = self.scope[value.name].used
- self.scope[value.name] = value
+ # don't treat annotations as assignments if there is an existing value
+ # in scope
+ if value.name not in self.scope or not isinstance(value, Annotation):
+ self.scope[value.name] = value
def _unknown_handler(self, node):
# this environment variable configures whether to error on unknown
@@ -1146,8 +1190,11 @@
# iteration
continue
- if (name == 'print' and
- isinstance(scope.get(name, None), Builtin)):
+ binding = scope.get(name, None)
+ if isinstance(binding, Annotation) and not
self._in_postponed_annotation:
+ continue
+
+ if name == 'print' and isinstance(binding, Builtin):
parent = self.getParent(node)
if (isinstance(parent, ast.BinOp) and
isinstance(parent.op, ast.RShift)):
@@ -1194,7 +1241,7 @@
# the special name __path__ is valid only in packages
return
- if name == '__module__' and isinstance(self.scope, ClassScope):
+ if name in DetectClassScopedMagic.names and isinstance(self.scope,
ClassScope):
return
# protected with a NameError handler?
@@ -1222,7 +1269,9 @@
break
parent_stmt = self.getParent(node)
- if isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or (
+ if isinstance(parent_stmt, ANNASSIGN_TYPES) 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
not self.isLiteralTupleUnpacking(parent_stmt)):
binding = Binding(name, node)
@@ -1265,13 +1314,20 @@
self.report(messages.UndefinedName, node, name)
@contextlib.contextmanager
- def _enter_annotation(self):
- orig, self._in_annotation = self._in_annotation, True
+ def _enter_annotation(self, ann_type=AnnotationState.BARE):
+ orig, self._in_annotation = self._in_annotation, ann_type
try:
yield
finally:
self._in_annotation = orig
+ @property
+ def _in_postponed_annotation(self):
+ return (
+ self._in_annotation == AnnotationState.STRING or
+ self.annotationsFutureEnabled
+ )
+
def _handle_type_comments(self, node):
for (lineno, col_offset), comment in self._type_comments.get(node, ()):
comment = comment.split(':', 1)[1].strip()
@@ -1399,7 +1455,7 @@
self.popScope()
self.scopeStack = saved_stack
- @in_annotation
+ @in_string_annotation
def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
try:
tree = ast.parse(s)
@@ -1457,20 +1513,36 @@
STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren
def SUBSCRIPT(self, node):
- if (
- (
- isinstance(node.value, ast.Name) and
- node.value.id == 'Literal'
- ) or (
- isinstance(node.value, ast.Attribute) and
- node.value.attr == 'Literal'
- )
- ):
- orig, self._in_typing_literal = self._in_typing_literal, True
- try:
+ if _is_name_or_attr(node.value, 'Literal'):
+ with self._enter_annotation(AnnotationState.NONE):
self.handleChildren(node)
- finally:
- self._in_typing_literal = orig
+ elif _is_name_or_attr(node.value, 'Annotated'):
+ self.handleNode(node.value, node)
+
+ # py39+
+ if isinstance(node.slice, ast.Tuple):
+ slice_tuple = node.slice
+ # <py39
+ elif (
+ isinstance(node.slice, ast.Index) and
+ isinstance(node.slice.value, ast.Tuple)
+ ):
+ slice_tuple = node.slice.value
+ else:
+ slice_tuple = None
+
+ # not a multi-arg `Annotated`
+ if slice_tuple is None or len(slice_tuple.elts) < 2:
+ self.handleNode(node.slice, node)
+ else:
+ # the first argument is the type
+ self.handleNode(slice_tuple.elts[0], node)
+ # the rest of the arguments are not
+ with self._enter_annotation(AnnotationState.NONE):
+ for arg in slice_tuple.elts[1:]:
+ self.handleNode(arg, node)
+
+ self.handleNode(node.ctx, node)
else:
if _is_any_typing_member(node.value, self.scopeStack):
with self._enter_annotation():
@@ -1606,15 +1678,79 @@
):
self._handle_string_dot_format(node)
+ omit = []
+ annotated = []
+ not_annotated = []
+
if (
_is_typing(node.func, 'cast', self.scopeStack) and
- len(node.args) >= 1 and
- isinstance(node.args[0], ast.Str)
+ len(node.args) >= 1
):
with self._enter_annotation():
self.handleNode(node.args[0], node)
- self.handleChildren(node)
+ elif _is_typing(node.func, 'TypeVar', self.scopeStack):
+
+ # TypeVar("T", "int", "str")
+ omit += ["args"]
+ annotated += [arg for arg in node.args[1:]]
+
+ # TypeVar("T", bound="str")
+ omit += ["keywords"]
+ annotated += [k.value for k in node.keywords if k.arg == "bound"]
+ not_annotated += [
+ (k, ["value"] if k.arg == "bound" else None)
+ for k in node.keywords
+ ]
+
+ elif _is_typing(node.func, "TypedDict", self.scopeStack):
+ # TypedDict("a", {"a": int})
+ if len(node.args) > 1 and isinstance(node.args[1], ast.Dict):
+ omit += ["args"]
+ annotated += node.args[1].values
+ not_annotated += [
+ (arg, ["values"] if i == 1 else None)
+ for i, arg in enumerate(node.args)
+ ]
+
+ # TypedDict("a", a=int)
+ omit += ["keywords"]
+ annotated += [k.value for k in node.keywords]
+ not_annotated += [(k, ["value"]) for k in node.keywords]
+
+ elif _is_typing(node.func, "NamedTuple", self.scopeStack):
+ # NamedTuple("a", [("a", int)])
+ if (
+ len(node.args) > 1 and
+ isinstance(node.args[1], (ast.Tuple, ast.List)) and
+ all(isinstance(x, (ast.Tuple, ast.List)) and
+ len(x.elts) == 2 for x in node.args[1].elts)
+ ):
+ omit += ["args"]
+ annotated += [elt.elts[1] for elt in node.args[1].elts]
+ not_annotated += [(elt.elts[0], None) for elt in
node.args[1].elts]
+ not_annotated += [
+ (arg, ["elts"] if i == 1 else None)
+ for i, arg in enumerate(node.args)
+ ]
+ not_annotated += [(elt, "elts") for elt in node.args[1].elts]
+
+ # NamedTuple("a", a=int)
+ omit += ["keywords"]
+ annotated += [k.value for k in node.keywords]
+ not_annotated += [(k, ["value"]) for k in node.keywords]
+
+ if omit:
+ with self._enter_annotation(AnnotationState.NONE):
+ for na_node, na_omit in not_annotated:
+ self.handleChildren(na_node, omit=na_omit)
+ self.handleChildren(node, omit=omit)
+
+ with self._enter_annotation():
+ for annotated_node in annotated:
+ self.handleNode(annotated_node, node)
+ else:
+ self.handleChildren(node)
def _handle_percent_format(self, node):
try:
@@ -1728,7 +1864,7 @@
self.handleChildren(node)
def STR(self, node):
- if self._in_annotation and not self._in_typing_literal:
+ if self._in_annotation:
fn = functools.partial(
self.handleStringAnnotation,
node.s,
@@ -2224,11 +2360,7 @@
self.scope[node.name] = prev_definition
def ANNASSIGN(self, node):
- if node.value:
- # Only bind the *targets* if the assignment has a value.
- # Otherwise it's not really ast.Store and shouldn't silence
- # UndefinedLocal warnings.
- self.handleNode(node.target, node)
+ self.handleNode(node.target, node)
self.handleAnnotation(node.annotation, node)
if node.value:
# If the assignment has value, handle the *value* now.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes/test/test_api.py
new/pyflakes-2.3.0/pyflakes/test/test_api.py
--- old/pyflakes-2.2.0/pyflakes/test/test_api.py 2020-04-10
05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/pyflakes/test/test_api.py 2021-03-14
17:31:42.000000000 +0100
@@ -513,8 +513,10 @@
"""
with self.makeTempFile(source) as sourcePath:
if ERROR_HAS_LAST_LINE:
- if PYPY and sys.version_info >= (3,):
+ if PYPY:
column = 7
+ elif sys.version_info >= (3, 9):
+ column = 21
elif sys.version_info >= (3, 8):
column = 9
else:
@@ -541,8 +543,10 @@
"""
with self.makeTempFile(source) as sourcePath:
if ERROR_HAS_LAST_LINE:
- if PYPY and sys.version_info >= (3,):
+ if PYPY:
column = 12
+ elif sys.version_info >= (3, 9):
+ column = 17
elif sys.version_info >= (3, 8):
column = 14
else:
@@ -576,7 +580,9 @@
else:
position_end = 1
if PYPY:
- column = 6
+ column = 5
+ elif ver >= (3, 9):
+ column = 13
else:
column = 7
# Column has been "fixed" since 3.2.4 and 3.3.1
@@ -715,13 +721,6 @@
"""
Tests of the pyflakes script that actually spawn the script.
"""
-
- #
https://bitbucket.org/pypy/pypy/issues/3069/pypy36-on-windows-incorrect-line-separator
- if PYPY and sys.version_info >= (3,) and WIN:
- LINESEP = '\n'
- else:
- LINESEP = os.linesep
-
def setUp(self):
self.tempdir = tempfile.mkdtemp()
self.tempfilepath = os.path.join(self.tempdir, 'temp')
@@ -782,7 +781,7 @@
fd.write("import contraband\n".encode('ascii'))
d = self.runPyflakes([self.tempfilepath])
expected = UnusedImport(self.tempfilepath, Node(1), 'contraband')
- self.assertEqual(d, ("%s%s" % (expected, self.LINESEP), '', 1))
+ self.assertEqual(d, ("%s%s" % (expected, os.linesep), '', 1))
def test_errors_io(self):
"""
@@ -792,7 +791,7 @@
"""
d = self.runPyflakes([self.tempfilepath])
error_msg = '%s: No such file or directory%s' % (self.tempfilepath,
- self.LINESEP)
+ os.linesep)
self.assertEqual(d, ('', error_msg, 1))
def test_errors_syntax(self):
@@ -805,7 +804,7 @@
fd.write("import".encode('ascii'))
d = self.runPyflakes([self.tempfilepath])
error_msg = '{0}:1:{2}: invalid syntax{1}import{1} {3}^{1}'.format(
- self.tempfilepath, self.LINESEP, 6 if PYPY else 7, '' if PYPY else
' ')
+ self.tempfilepath, os.linesep, 6 if PYPY else 7, '' if PYPY else '
')
self.assertEqual(d, ('', error_msg, 1))
def test_readFromStdin(self):
@@ -814,15 +813,13 @@
"""
d = self.runPyflakes([], stdin='import contraband')
expected = UnusedImport('<stdin>', Node(1), 'contraband')
- self.assertEqual(d, ("%s%s" % (expected, self.LINESEP), '', 1))
+ self.assertEqual(d, ("%s%s" % (expected, os.linesep), '', 1))
class TestMain(IntegrationTests):
"""
Tests of the pyflakes main function.
"""
- LINESEP = os.linesep
-
def runPyflakes(self, paths, stdin=None):
try:
with SysStreamCapturing(stdin) as capture:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes/test/test_imports.py
new/pyflakes-2.3.0/pyflakes/test/test_imports.py
--- old/pyflakes-2.2.0/pyflakes/test/test_imports.py 2020-04-10
05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/pyflakes/test/test_imports.py 2021-03-14
17:31:42.000000000 +0100
@@ -1084,7 +1084,7 @@
__all__ += ['c', 'd']
''', m.UndefinedExport, m.UndefinedExport)
- def test_concatenationAssignment(self):
+ def test_list_concatenation_assignment(self):
"""
The C{__all__} variable is defined through list concatenation.
"""
@@ -1093,6 +1093,15 @@
__all__ = ['a'] + ['b'] + ['c']
''', m.UndefinedExport, m.UndefinedExport, m.UndefinedExport,
m.UnusedImport)
+ def test_tuple_concatenation_assignment(self):
+ """
+ The C{__all__} variable is defined through tuple concatenation.
+ """
+ self.flakes('''
+ import sys
+ __all__ = ('a',) + ('b',) + ('c',)
+ ''', m.UndefinedExport, m.UndefinedExport, m.UndefinedExport,
m.UnusedImport)
+
def test_all_with_attributes(self):
self.flakes('''
from foo import bar
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pyflakes-2.2.0/pyflakes/test/test_type_annotations.py
new/pyflakes-2.3.0/pyflakes/test/test_type_annotations.py
--- old/pyflakes-2.2.0/pyflakes/test/test_type_annotations.py 2020-04-10
05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/pyflakes/test/test_type_annotations.py 2021-03-14
17:31:42.000000000 +0100
@@ -263,6 +263,14 @@
class A: pass
''')
self.flakes('''
+ T: object
+ def f(t: T): pass
+ ''', m.UndefinedName)
+ self.flakes('''
+ T: object
+ def g(t: 'T'): pass
+ ''')
+ self.flakes('''
a: 'A B'
''', m.ForwardAnnotationSyntaxError)
self.flakes('''
@@ -275,6 +283,26 @@
a: 'a: "A"'
''', m.ForwardAnnotationSyntaxError)
+ @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('''
+ x: int
+ class Cls:
+ y: int
+ ''')
+ # TODO: this should print a UnusedVariable message
+ self.flakes('''
+ def f():
+ x: int
+ ''')
+ # This should only print one UnusedVariable message
+ self.flakes('''
+ def f():
+ x: int
+ x = 3
+ ''', m.UnusedVariable)
+
@skipIf(version_info < (3, 5), 'new in Python 3.5')
def test_annotated_async_def(self):
self.flakes('''
@@ -300,6 +328,26 @@
class B: pass
''', m.UndefinedName)
+ self.flakes('''
+ from __future__ import annotations
+ T: object
+ def f(t: T): pass
+ 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
+
+ from y import z
+
+ if not TYPE_CHECKING:
+ __all__ = ("z",)
+ else:
+ __all__: List[str]
+ ''')
+
def test_typeCommentsMarkImportsAsUsed(self):
self.flakes("""
from mod import A, B, C, D, E, F, G
@@ -489,6 +537,21 @@
maybe_int = tsac('Maybe[int]', 42)
""")
+ def test_quoted_TypeVar_constraints(self):
+ self.flakes("""
+ from typing import TypeVar, Optional
+
+ T = TypeVar('T', 'str', 'Optional[int]', bytes)
+ """)
+
+ def test_quoted_TypeVar_bound(self):
+ self.flakes("""
+ from typing import TypeVar, Optional, List
+
+ T = TypeVar('T', bound='Optional[int]')
+ S = TypeVar('S', int, bound='List[int]')
+ """)
+
@skipIf(version_info < (3,), 'new in Python 3')
def test_literal_type_typing(self):
self.flakes("""
@@ -508,6 +571,42 @@
""")
@skipIf(version_info < (3,), 'new in Python 3')
+ def test_annotated_type_typing_missing_forward_type(self):
+ self.flakes("""
+ from typing import Annotated
+
+ def f(x: Annotated['integer']) -> None:
+ 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
+
+ def f(x: Annotated['integer', 1]) -> None:
+ 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
+
+ def f(x: Annotated[int, '> 0']) -> None:
+ 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
+
+ def f(x: Union[Annotated['int', '>0'], 'integer']) -> None:
+ 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("""
@@ -552,3 +651,88 @@
def f() -> Optional['Queue[str]']:
return None
""")
+
+ def test_idomiatic_typing_guards(self):
+ # typing.TYPE_CHECKING: python3.5.3+
+ self.flakes("""
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from t import T
+
+ def f(): # type: () -> T
+ pass
+ """)
+ # False: the old, more-compatible approach
+ self.flakes("""
+ if False:
+ from t import T
+
+ def f(): # type: () -> T
+ pass
+ """)
+ # some choose to assign a constant and do it that way
+ self.flakes("""
+ MYPY = False
+
+ if MYPY:
+ from t import T
+
+ def f(): # type: () -> T
+ pass
+ """)
+
+ def test_typing_guard_for_protocol(self):
+ self.flakes("""
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from typing import Protocol
+ else:
+ Protocol = object
+
+ class C(Protocol):
+ def f(): # type: () -> int
+ pass
+ """)
+
+ def test_typednames_correct_forward_ref(self):
+ self.flakes("""
+ from typing import TypedDict, List, NamedTuple
+
+ List[TypedDict("x", {})]
+ List[TypedDict("x", x=int)]
+ List[NamedTuple("a", a=int)]
+ List[NamedTuple("a", [("a", int)])]
+ """)
+ self.flakes("""
+ from typing import TypedDict, List, NamedTuple, TypeVar
+
+ List[TypedDict("x", {"x": "Y"})]
+ List[TypedDict("x", x="Y")]
+ List[NamedTuple("a", [("a", "Y")])]
+ List[NamedTuple("a", a="Y")]
+ List[TypedDict("x", {"x": List["a"]})]
+ List[TypeVar("A", bound="C")]
+ List[TypeVar("A", List["C"])]
+ """, *[m.UndefinedName]*7)
+ self.flakes("""
+ from typing import NamedTuple, TypeVar, cast
+ from t import A, B, C, D, E
+
+ NamedTuple("A", [("a", A["C"])])
+ TypeVar("A", bound=A["B"])
+ TypeVar("A", A["D"])
+ 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
+ class X(TypedDict):
+ y: TypedDict("z", {"zz":int})
+
+ class Y(NamedTuple):
+ y: NamedTuple("v", [("vv", int)])
+ """)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes/test/test_undefined_names.py
new/pyflakes-2.3.0/pyflakes/test/test_undefined_names.py
--- old/pyflakes-2.2.0/pyflakes/test/test_undefined_names.py 2020-04-10
05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/pyflakes/test/test_undefined_names.py 2021-03-14
17:31:42.000000000 +0100
@@ -279,6 +279,23 @@
__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
+ name warning if used in class scope.
+ """
+ self.flakes('__qualname__', m.UndefinedName)
+ self.flakes('''
+ class Foo:
+ __qualname__
+ ''')
+ self.flakes('''
+ class Foo:
+ def bar(self):
+ __qualname__
+ ''', m.UndefinedName)
+
def test_globalImportStar(self):
"""Can't find undefined names with import *."""
self.flakes('from fu import *; bar',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/pyflakes.egg-info/PKG-INFO
new/pyflakes-2.3.0/pyflakes.egg-info/PKG-INFO
--- old/pyflakes-2.2.0/pyflakes.egg-info/PKG-INFO 2020-04-10
05:51:46.000000000 +0200
+++ new/pyflakes-2.3.0/pyflakes.egg-info/PKG-INFO 2021-03-14
17:33:06.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: pyflakes
-Version: 2.2.0
+Version: 2.3.0
Summary: passive checker of Python programs
Home-page: https://github.com/PyCQA/pyflakes
Author: A lot of people
@@ -17,7 +17,7 @@
modules with side effects. It's also much faster.
It is `available on PyPI <https://pypi.org/project/pyflakes/>`_
- and it supports all active versions of Python: 2.7 and 3.4 to 3.7.
+ and it supports all active versions of Python: 2.7 and 3.4 to 3.8.
@@ -80,13 +80,13 @@
All changes should include tests and pass flake8_.
- .. image:: https://api.travis-ci.org/PyCQA/pyflakes.svg?branch=master
- :target: https://travis-ci.org/PyCQA/pyflakes
- :alt: Build status
+ .. image:: https://github.com/PyCQA/pyflakes/workflows/Test/badge.svg
+ :target: https://github.com/PyCQA/pyflakes/actions
+ :alt: GitHub Actions build status
- .. _Pylint: http://www.pylint.org/
+ .. _Pylint: https://www.pylint.org/
.. _flake8: https://pypi.org/project/flake8/
- .. _`PEP 8`: http://legacy.python.org/dev/peps/pep-0008/
+ .. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/
.. _Pychecker: http://pychecker.sourceforge.net/
.. _`rebase your changes`:
https://git-scm.com/book/en/v2/Git-Branching-Rebasing
.. _`GitHub pull request`: https://github.com/PyCQA/pyflakes/pulls
@@ -109,6 +109,7 @@
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyflakes-2.2.0/setup.py new/pyflakes-2.3.0/setup.py
--- old/pyflakes-2.2.0/setup.py 2020-04-10 05:48:16.000000000 +0200
+++ new/pyflakes-2.3.0/setup.py 2021-03-14 17:31:42.000000000 +0100
@@ -58,6 +58,7 @@
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development",