This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, jimbo1qaz_, paul.moore, pitrou, serhiy.storchaka, steve.dower, tim.golden, zach.ware
Date 2019-01-16.01:14:21
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
> (sidenote: what os.path operation does Path.resolve() match? 
> Path('nonexistent').resolve() returns a relative path on Python 
> 3.7.1, whereas Path().resolve() returns an absolute path.)

pathlib should resolve 'nonexistent' in Windows. It works as expected in Unix:

    >>> os.getcwd()
    >>> os.fspath(Path('nonexistent').resolve())

A PR to implement ntpath.realpath is in development for issue 14094. The proposed implementation calls ntpath.abspath at the start, unless it's an extended path (i.e. prefixed by \\?\). Unlike Unix, Windows normalizes a path in user mode as a text operation before passing it to the kernel and file system. This means there's no problem if abspath removes a reparse point (e.g. symlink or mountpoint) when it resolves a ".." component.

> The code paths should be audited to check that EINVAL can't mean something else.

We'd have to use the Windows error code (e.g. ERROR_INVALID_NAME) if it has to be specific. EINVAL is the default errno value. In particular, EINVAL includes some low-level device failures such as ERROR_IO_DEVICE and errors for operations that a device doesn't implement, which are commonly ERROR_INVALID_PARAMETER, ERROR_INVALID_FUNCTION, and ERROR_NOT_SUPPORTED. 

Also, a few device and files-system errors are mapped to EACCES (e.g. ERROR_NOT_READY and ERROR_SECTOR_NOT_FOUND). If we include EACCES, then files that exist but are inaccessible (e.g. the user isn't allowed to list the parent  directory) will be reported as not existing instead of raising an error. It's what os.path.exists does, but I guess pathlib wants to be more nuanced.

When using C runtime I/O (e.g. open, read, write), it can help to get the last Windows error code, _doserrno [1]. Its value gets set when errno is set by mapping an OS error. The last NT status value may also help in some cases. It gets set whenever an NT status code is mapped to a Windows error via RtlNtStatusToDosError (usually followed immediately by RtlSetLastWin32Error). It would be nice if OSError always included these two values, maybe as "last_winerror" (differentiated from "winerror") and "last_ntstatus".

For example, here's a case of trying to open a file on a CD drive that has no disk in it.

    import ctypes

    doserrno = ctypes.WinDLL('ucrtbase').__doserrno
    doserrno.restype = ctypes.POINTER(ctypes.c_ulong)
    doserrno.errcheck = lambda r, f, a: r[0]

    get_last_nt_status = ctypes.WinDLL('ntdll').RtlGetLastNtStatus
    get_last_nt_status.restype = ctypes.c_ulong

    def test():
            winerror, ntstatus = doserrno(), get_last_nt_status()
            print('Windows error:', winerror)
            print('NT status:', format(ntstatus, '#010x'))

    >>> test()
    Windows error: 21
    NT status: 0xc0000013
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in test
    PermissionError: [Errno 13] Permission denied: 'D:\\test.txt'

Windows error 21 is ERROR_NOT_READY, so we're already much better informed than EACCES (13). NT status 0xC0000013 is STATUS_NO_MEDIA_IN_DEVICE.

Date User Action Args
2021-03-27 05:08:43eryksununlinkissue35306 messages
2019-01-16 01:14:23eryksunsetrecipients: + eryksun, paul.moore, pitrou, tim.golden, zach.ware, serhiy.storchaka, steve.dower, jimbo1qaz_
2019-01-16 01:14:21eryksunsetmessageid: <>
2019-01-16 01:14:21eryksunlinkissue35306 messages
2019-01-16 01:14:21eryksuncreate