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