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 <jay...@gmail.com>
+
+- 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",

Reply via email to