Skip to content

Python 3.9.23 regression for Path.resolve() on Windows with WinError 53 #136755

@krassowski

Description

@krassowski

Bug report

Bug description:

from pathlib import Path

Path("//VBOXSVR/shared-folder").expanduser().resolve()

Raises FileNotFoundError: [WinError 53] The network path was not found: '\\\\VBOXSVR\\shared-folder\\'.

This appears to be a consequence of this backport:

By default the FileNotFoundError should be caught as a descendant of OSError, but since #135035 it is then also re-raised as it is not on the list of ignored Windows errors.

This passes on Python 3.10+ because 53: ERROR_BAD_NETPATH is included on the list since #27574:

cpython/Lib/ntpath.py

Lines 659 to 668 in 28937d3

# 53: ERROR_BAD_NETPATH
# 65: ERROR_NETWORK_ACCESS_DENIED
# 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
# 87: ERROR_INVALID_PARAMETER
# 123: ERROR_INVALID_NAME
# 161: ERROR_BAD_PATHNAME
# 1005: ERROR_UNRECOGNIZED_VOLUME
# 1920: ERROR_CANT_ACCESS_FILE
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1005, 1920, 1921

but it it was not backported to 3.9:

cpython/Lib/ntpath.py

Lines 588 to 596 in 6c97200

# 21: ERROR_NOT_READY (implies drive with no media)
# 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
# 50: ERROR_NOT_SUPPORTED
# 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
# 87: ERROR_INVALID_PARAMETER
# 123: ERROR_INVALID_NAME
# 1920: ERROR_CANT_ACCESS_FILE
# 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1920, 1921

And since this code path is now used for Path.resolve() since #135035 was merged and backported I think that #27574 should be backported to other Python version to preserve old behaviour.

    def normalized_uri(root_dir):
        """Attempt to make an LSP rootUri from a ContentsManager root_dir
    
        Special care must be taken around windows paths: the canonical form of
        windows drives and UNC paths is lower case
        """
>       root_uri = Path(root_dir).expanduser().resolve().as_uri()

C:\Users\runneradmin\miniconda3\envs\test\lib\site-packages\jupyter_lsp\paths.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = WindowsPath('//VBOXSVR/shared-folder/'), strict = False

    def resolve(self, strict=False):
        """
        Make the path absolute, resolving all symlinks on the way and also
        normalizing it (for example turning slashes into backslashes under
        Windows).
        """
    
        def check_eloop(e):
            winerror = getattr(e, 'winerror', 0)
            if e.errno == ELOOP or winerror == _WINERROR_CANT_RESOLVE_FILENAME:
                raise RuntimeError("Symlink loop from %r" % e.filename)
    
        try:
>           s = self._accessor.realpath(self, strict=strict)

C:\Users\runneradmin\miniconda3\envs\test\lib\pathlib.py:1140: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

path = '\\\\VBOXSVR\\shared-folder\\'

    def realpath(path, *, strict=False):
        path = normpath(path)
        if isinstance(path, bytes):
            prefix = b'\\\\?\\'
            unc_prefix = b'\\\\?\\UNC\\'
            new_unc_prefix = b'\\\\'
            cwd = os.getcwdb()
            # bpo-38081: Special case for realpath(b'nul')
            if normcase(path) == normcase(os.fsencode(devnull)):
                return b'\\\\.\\NUL'
        else:
            prefix = '\\\\?\\'
            unc_prefix = '\\\\?\\UNC\\'
            new_unc_prefix = '\\\\'
            cwd = os.getcwd()
            # bpo-38081: Special case for realpath('nul')
            if normcase(path) == normcase(devnull):
                return '\\\\.\\NUL'
        had_prefix = path.startswith(prefix)
    
        if strict is ALLOW_MISSING:
            ignored_error = FileNotFoundError
            strict = True
        elif strict:
            ignored_error = ()
        else:
            ignored_error = OSError
    
        if not had_prefix and not isabs(path):
            path = join(cwd, path)
        try:
            path = _getfinalpathname(path)
            initial_winerror = 0
        except ignored_error as ex:
            initial_winerror = ex.winerror
>           path = _getfinalpathname_nonstrict(path,
                                               ignored_error=ignored_error)

C:\Users\runneradmin\miniconda3\envs\test\lib\ntpath.py:663: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

path = '\\\\VBOXSVR\\shared-folder\\', ignored_error = <class 'OSError'>

    def _getfinalpathname_nonstrict(path, ignored_error=OSError):
        # These error codes indicate that we should stop resolving the path
        # and return the value we currently have.
        # 1: ERROR_INVALID_FUNCTION
        # 2: ERROR_FILE_NOT_FOUND
        # 3: ERROR_DIRECTORY_NOT_FOUND
        # 5: ERROR_ACCESS_DENIED
        # 21: ERROR_NOT_READY (implies drive with no media)
        # 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
        # 50: ERROR_NOT_SUPPORTED
        # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
        # 87: ERROR_INVALID_PARAMETER
        # 123: ERROR_INVALID_NAME
        # 1920: ERROR_CANT_ACCESS_FILE
        # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
        allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1920, 1921
    
        # Non-strict algorithm is to find as much of the target directory
        # as we can and join the rest.
        tail = ''
        while path:
            try:
>               path = _getfinalpathname(path)
E               FileNotFoundError: [WinError 53] The network path was not found: '\\\\VBOXSVR\\shared-folder\\'

CPython versions tested on:

3.9

Operating systems tested on:

Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions