Repository: PyCQA/pyflakes
Pull request #466 (2019-08-30 21:27:28)

Support typing_extensions.overload in addition to typing.overload
They are synonyms and typing_extensions.overloads works on 3.5.1.


Patch:
diff --git a/.appveyor.yml b/.appveyor.yml
index 0e05ec3..1738253 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -2,25 +2,17 @@
 install:
   - python -m pip install --upgrade tox virtualenv
 
-  # Fetch the three main PyPy releases
-  - ps: (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy-2.6.1-win32.zip', "$env:appveyor_build_folder\pypy-2.6.1-win32.zip")
-  - ps: 7z x pypy-2.6.1-win32.zip | Out-Null
-  - move pypy-2.6.1-win32 C:\
+  - ps: (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.1-win32.zip', "$env:appveyor_build_folder\pypy2.7-v7.1.1-win32.zip")
+  - ps: 7z x pypy2.7-v7.1.1-win32.zip | Out-Null
+  - move pypy2.7-v7.1.1-win32 C:\
+  - 'SET PATH=C:\pypy2.7-v7.1.1-win32\;%PATH%'
 
-  - ps: (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.3.1-win32.zip', "$env:appveyor_build_folder\pypy2-v5.3.1-win32.zip")
-  - ps: 7z x pypy2-v5.3.1-win32.zip | Out-Null
-  - move pypy2-v5.3.1-win32 C:\
-
-  # TODO: pypy 6.0.0 offsets are different
-  #- ps: (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-win32.zip', "$env:appveyor_build_folder\pypy3-v6.0.0-win32.zip")
-  #- ps: 7z x pypy3-v6.0.0-win32.zip | Out-Null
-  #- move pypy3-v6.0.0-win32 C:\
+  - ps: (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.1.1-win32.zip', "$env:appveyor_build_folder\pypy3.6-v7.1.1-win32.zip")
+  - ps: 7z x pypy3.6-v7.1.1-win32.zip | Out-Null
+  - move pypy3.6-v7.1.1-win32 C:\
+  - 'SET PATH=C:\pypy3.6-v7.1.1-win32\;%PATH%'
 
 build: off
 
 test_script:
   - python -m tox
-  - C:\pypy-2.6.1-win32\pypy -m unittest discover pyflakes
-  - C:\pypy2-v5.3.1-win32\pypy -m unittest discover pyflakes
-  # TODO: pypy 6.0.0 offsets are different
-  #- C:\pypy3-v6.0.0-win32\pypy3 -m unittest discover pyflakes
diff --git a/.travis.yml b/.travis.yml
index 20e195d..ae08482 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,7 +7,6 @@ matrix:
     - python: 3.5
     - python: 3.6
     - python: pypy
-    - python: pypy-5.3
     - python: pypy3
     - python: 3.7
       dist: xenial
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index 8196fa4..44c6b25 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -622,7 +622,9 @@ def is_typing_overload(value, scope_stack):
             if name in scope:
                 return (
                     isinstance(scope[name], ImportationFrom) and
-                    scope[name].fullName == 'typing.overload'
+                    scope[name].fullName in (
+                        'typing.overload', 'typing_extensions.overload',
+                    )
                 )
 
         return False


Test patch:
diff --git a/pyflakes/test/test_api.py b/pyflakes/test/test_api.py
index 93b3886..128aa69 100644
--- a/pyflakes/test/test_api.py
+++ b/pyflakes/test/test_api.py
@@ -9,7 +9,6 @@ import shutil
 import subprocess
 import tempfile
 
-from pyflakes.checker import PY2
 from pyflakes.messages import UnusedImport
 from pyflakes.reporter import Reporter
 from pyflakes.api import (
@@ -449,7 +448,7 @@ def baz():
 
         with self.makeTempFile(source) as sourcePath:
             if PYPY:
-                message = 'EOF while scanning triple-quoted string literal'
+                message = 'end of file (EOF) while scanning triple-quoted string literal'
             else:
                 message = 'invalid syntax'
 
@@ -491,8 +490,8 @@ def foo(
         syntax error reflects the cause for the syntax error.
         """
         with self.makeTempFile("if True:\n\tfoo =") as sourcePath:
-            column = 5 if PYPY else 7
-            last_line = '\t   ^' if PYPY else '\t     ^'
+            column = 6 if PYPY else 7
+            last_line = '\t    ^' if PYPY else '\t     ^'
 
             self.assertHasErrors(
                 sourcePath,
@@ -514,7 +513,12 @@ def foo(bar=baz, bax):
 """
         with self.makeTempFile(source) as sourcePath:
             if ERROR_HAS_LAST_LINE:
-                column = 9 if sys.version_info >= (3, 8) else 8
+                if PYPY and sys.version_info >= (3,):
+                    column = 7
+                elif sys.version_info >= (3, 8):
+                    column = 9
+                else:
+                    column = 8
                 last_line = ' ' * (column - 1) + '^\n'
                 columnstr = '%d:' % column
             else:
@@ -537,7 +541,12 @@ foo(bar=baz, bax)
 """
         with self.makeTempFile(source) as sourcePath:
             if ERROR_HAS_LAST_LINE:
-                column = 14 if sys.version_info >= (3, 8) else 13
+                if PYPY and sys.version_info >= (3,):
+                    column = 12
+                elif sys.version_info >= (3, 8):
+                    column = 14
+                else:
+                    column = 13
                 last_line = ' ' * (column - 1) + '^\n'
                 columnstr = '%d:' % column
             else:
@@ -707,6 +716,12 @@ class IntegrationTests(TestCase):
     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')
@@ -747,9 +762,6 @@ class IntegrationTests(TestCase):
         if sys.version_info >= (3,):
             stdout = stdout.decode('utf-8')
             stderr = stderr.decode('utf-8')
-        # Workaround https://bitbucket.org/pypy/pypy/issues/2350
-        if PYPY and PY2 and WIN:
-            stderr = stderr.replace('\r\r\n', '\r\n')
         return (stdout, stderr, rv)
 
     def test_goodFile(self):
@@ -770,7 +782,7 @@ class IntegrationTests(TestCase):
             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, os.linesep), '', 1))
+        self.assertEqual(d, ("%s%s" % (expected, self.LINESEP), '', 1))
 
     def test_errors_io(self):
         """
@@ -780,7 +792,7 @@ class IntegrationTests(TestCase):
         """
         d = self.runPyflakes([self.tempfilepath])
         error_msg = '%s: No such file or directory%s' % (self.tempfilepath,
-                                                         os.linesep)
+                                                         self.LINESEP)
         self.assertEqual(d, ('', error_msg, 1))
 
     def test_errors_syntax(self):
@@ -792,8 +804,8 @@ class IntegrationTests(TestCase):
         with open(self.tempfilepath, 'wb') as fd:
             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, os.linesep, 5 if PYPY else 7, '' if PYPY else '  ')
+        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.assertEqual(d, ('', error_msg, 1))
 
     def test_readFromStdin(self):
@@ -802,13 +814,14 @@ 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, ("%s%s" % (expected, self.LINESEP), '', 1))
 
 
 class TestMain(IntegrationTests):
     """
     Tests of the pyflakes main function.
     """
+    LINESEP = os.linesep
 
     def runPyflakes(self, paths, stdin=None):
         try:
diff --git a/pyflakes/test/test_doctests.py b/pyflakes/test/test_doctests.py
index 5565226..836b248 100644
--- a/pyflakes/test/test_doctests.py
+++ b/pyflakes/test/test_doctests.py
@@ -328,7 +328,9 @@ class Test(TestCase):
             m.DoctestSyntaxError).messages
         exc = exceptions[0]
         self.assertEqual(exc.lineno, 4)
-        if sys.version_info >= (3, 8):
+        if PYPY:
+            self.assertEqual(exc.col, 27)
+        elif sys.version_info >= (3, 8):
             self.assertEqual(exc.col, 18)
         else:
             self.assertEqual(exc.col, 26)
@@ -339,12 +341,14 @@ class Test(TestCase):
         exc = exceptions[1]
         self.assertEqual(exc.lineno, 5)
         if PYPY:
-            self.assertEqual(exc.col, 13)
+            self.assertEqual(exc.col, 14)
         else:
             self.assertEqual(exc.col, 16)
         exc = exceptions[2]
         self.assertEqual(exc.lineno, 6)
-        if PYPY or sys.version_info >= (3, 8):
+        if PYPY:
+            self.assertEqual(exc.col, 14)
+        elif sys.version_info >= (3, 8):
             self.assertEqual(exc.col, 13)
         else:
             self.assertEqual(exc.col, 18)
@@ -358,7 +362,9 @@ class Test(TestCase):
             """
         ''', m.DoctestSyntaxError).messages[0]
         self.assertEqual(exc.lineno, 5)
-        if PYPY or sys.version_info >= (3, 8):
+        if PYPY:
+            self.assertEqual(exc.col, 14)
+        elif sys.version_info >= (3, 8):
             self.assertEqual(exc.col, 13)
         else:
             self.assertEqual(exc.col, 16)
@@ -377,7 +383,10 @@ class Test(TestCase):
             m.DoctestSyntaxError,
             m.UndefinedName).messages
         self.assertEqual(exc1.lineno, 6)
-        self.assertEqual(exc1.col, 19)
+        if PYPY:
+            self.assertEqual(exc1.col, 20)
+        else:
+            self.assertEqual(exc1.col, 19)
         self.assertEqual(exc2.lineno, 7)
         self.assertEqual(exc2.col, 12)
 
diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py
index bebdef7..676a4a5 100644
--- a/pyflakes/test/test_type_annotations.py
+++ b/pyflakes/test/test_type_annotations.py
@@ -39,6 +39,23 @@ class TestTypeAnnotations(TestCase):
             return s
         """)
 
+    def test_typingExtensionsOverload(self):
+        """Allow intentional redefinitions via @typing_extensions.overload"""
+        self.flakes("""
+        from typing_extensions import overload
+
+        @overload
+        def f(s):  # type: (None) -> None
+            pass
+
+        @overload
+        def f(s):  # type: (int) -> int
+            pass
+
+        def f(s):
+            return s
+        """)
+
     def test_overload_with_multiple_decorators(self):
         self.flakes("""
             from typing import overload