Eryk Sun <eryk...@gmail.com> added the comment:

> (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()
    '/etc'
    >>> os.fspath(Path('nonexistent').resolve())
    '/etc/nonexistent'

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():
        try:
            open('D:\\test.txt')
        except:
            winerror, ntstatus = doserrno(), get_last_nt_status()
            print('Windows error:', winerror)
            print('NT status:', format(ntstatus, '#010x'))
            raise

    >>> 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.

[1]: 
https://docs.microsoft.com/en-us/cpp/c-runtime-library/errno-doserrno-sys-errlist-and-sys-nerr?view=vs-2017

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue35306>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to