python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.55k stars 2.84k forks source link

[ci] Try to fix Windows CI failures #18145

Closed sterliakov closed 1 week ago

sterliakov commented 2 weeks ago

I often encounter test failures on Windows (failing tests are always related to mypy daemon).

https://github.com/python/mypy/actions/runs/11783610549/job/32821129579 https://github.com/python/mypy/actions/runs/11767558564/job/32776369396 https://github.com/python/mypy/actions/runs/11763771676/job/32768172783

They always manifest as a RecursionError during test teardown, the following two frames are repeated many times:

___________ ERROR at teardown of testDaemonRunIgnoreMissingImports ____________
[gw3] win32 -- Python 3.8.10 D:\a\mypy\mypy\.tox\py38\Scripts\python.EXE

path = 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp'
onerror = <function TemporaryDirectory._rmtree.<locals>.onerror at 0x00000250EE0F94C0>

    def _rmtree_unsafe(path, onerror):
        try:
            with os.scandir(path) as scandir_it:
                entries = list(scandir_it)
        except OSError:
            onerror(os.scandir, path, sys.exc_info())
            entries = []
        for entry in entries:
            fullname = entry.path
            if _rmtree_isdir(entry):
                try:
                    if entry.is_symlink():
                        # This can only happen if someone replaces
                        # a directory with a symlink after the call to
                        # os.scandir or entry.is_dir above.
                        raise OSError("Cannot call rmtree on a symbolic link")
                except OSError:
                    onerror(os.path.islink, fullname, sys.exc_info())
                    continue
                _rmtree_unsafe(fullname, onerror)
            else:
                try:
                    os.unlink(fullname)
                except OSError:
                    onerror(os.unlink, fullname, sys.exc_info())
        try:
>           os.rmdir(path)
E           PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp'

C:\hostedtoolcache\windows\Python\3.8.10\x64\lib\shutil.py:620: PermissionError

During handling of the above exception, another exception occurred:

func = <built-in function rmdir>
path = 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp'
exc_info = (<class 'PermissionError'>, PermissionError(13, 'The process cannot access the file because it is being used by another process'), <traceback object at 0x00000250EEB52A80>)

    def onerror(func, path, exc_info):
        if issubclass(exc_info[0], PermissionError):
            def resetperms(path):
                try:
                    _os.chflags(path, 0)
                except AttributeError:
                    pass
                _os.chmod(path, 0o700)

            try:
                if path != name:
                    resetperms(_os.path.dirname(path))
                resetperms(path)

                try:
>                   _os.unlink(path)
E                   PermissionError: [WinError 5] Access is denied: 'C:\Users\RUNNER~1\AppData\Local\Temp\mypy-test-6t6j6e1l\tmp'

C:\hostedtoolcache\windows\Python\3.8.10\x64\lib\tempfile.py:802: PermissionError

This likely can be solved by TemporaryDirectory.ignore_cleanup_errors but it is only available on 3.10+. I hope this PR does not mask any real deficiency - those failures are extremely annoying.