From d57287b89b95d437a590d817c41c72f7a5bcca96 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Fri, 19 Apr 2024 01:45:45 +0900 Subject: [PATCH] Fix OSError.winerror and errno/winerror converions --- Cargo.lock | 1 + Cargo.toml | 1 + Lib/test/test_tempfile.py | 1 - common/Cargo.toml | 4 + common/src/crt_fd.rs | 25 +----- common/src/os.rs | 178 ++++++++++++++++++++++++++++++++++++-- stdlib/Cargo.toml | 2 +- stdlib/src/socket.rs | 16 ++-- stdlib/src/ssl.rs | 2 +- stdlib/src/termios.rs | 3 +- vm/Cargo.toml | 2 +- vm/src/exceptions.rs | 126 +++------------------------ vm/src/stdlib/io.rs | 34 ++++++-- vm/src/stdlib/nt.rs | 4 +- vm/src/stdlib/os.rs | 4 +- 15 files changed, 234 insertions(+), 169 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a1d67ef..d96db01cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2063,6 +2063,7 @@ dependencies = [ "siphasher", "volatile", "widestring", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e58a3da91..fa53396d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ thiserror = "1.0" thread_local = "1.1.4" unicode_names2 = "1.1.0" widestring = "1.1.0" +windows-sys = "0.52.0" [features] default = ["threading", "stdlib", "zlib", "importlib"] diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 1a175fa65..913897de4 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1418,7 +1418,6 @@ class TestTemporaryDirectory(BaseTestCase): with open(os.path.join(path, "test%d.txt" % i), "wb") as f: f.write(b"Hello world!") - @unittest.expectedFailureIfWindows("TODO: RUSTPYTHON") def test_mkdtemp_failure(self): # Check no additional exception if mkdtemp fails # Previously would raise AttributeError instead diff --git a/common/Cargo.toml b/common/Cargo.toml index 2d5ba72ec..28f6c4098 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -35,3 +35,7 @@ volatile = "0.3" [target.'cfg(windows)'.dependencies] widestring = { workspace = true } +windows-sys = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Networking_WinSock", +] } diff --git a/common/src/crt_fd.rs b/common/src/crt_fd.rs index 8eef8d4df..14f61b805 100644 --- a/common/src/crt_fd.rs +++ b/common/src/crt_fd.rs @@ -23,7 +23,7 @@ pub type Offset = libc::c_longlong; #[inline] fn cvt(ret: I, f: impl FnOnce(I) -> T) -> io::Result { if ret < I::zero() { - Err(crate::os::errno()) + Err(crate::os::last_os_error()) } else { Ok(f(ret)) } @@ -86,7 +86,7 @@ impl Fd { } } -impl io::Write for &Fd { +impl io::Write for Fd { fn write(&mut self, buf: &[u8]) -> io::Result { let count = cmp::min(buf.len(), MAX_RW); cvt( @@ -101,19 +101,7 @@ impl io::Write for &Fd { } } -impl io::Write for Fd { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (&*self).write(buf) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (&*self).flush() - } -} - -impl io::Read for &Fd { +impl io::Read for Fd { fn read(&mut self, buf: &mut [u8]) -> io::Result { let count = cmp::min(buf.len(), MAX_RW); cvt( @@ -122,10 +110,3 @@ impl io::Read for &Fd { ) } } - -impl io::Read for Fd { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (&*self).read(buf) - } -} diff --git a/common/src/os.rs b/common/src/os.rs index c583647ce..c16ec014c 100644 --- a/common/src/os.rs +++ b/common/src/os.rs @@ -2,26 +2,64 @@ use std::{io, str::Utf8Error}; +pub trait ErrorExt { + fn posix_errno(&self) -> i32; +} + +impl ErrorExt for io::Error { + #[cfg(not(windows))] + fn posix_errno(&self) -> i32 { + self.raw_os_error().unwrap_or(0) + } + #[cfg(windows)] + fn posix_errno(&self) -> i32 { + let winerror = self.raw_os_error().unwrap_or(0); + winerror_to_errno(winerror) + } +} + #[cfg(windows)] -pub fn errno() -> io::Error { +pub fn last_os_error() -> io::Error { let err = io::Error::last_os_error(); // FIXME: probably not ideal, we need a bigger dichotomy between GetLastError and errno if err.raw_os_error() == Some(0) { extern "C" { fn _get_errno(pValue: *mut i32) -> i32; } - let mut e = 0; - unsafe { suppress_iph!(_get_errno(&mut e)) }; - io::Error::from_raw_os_error(e) + let mut errno = 0; + unsafe { suppress_iph!(_get_errno(&mut errno)) }; + let errno = errno_to_winerror(errno); + io::Error::from_raw_os_error(errno) } else { err } } + #[cfg(not(windows))] -pub fn errno() -> io::Error { +pub fn last_os_error() -> io::Error { io::Error::last_os_error() } +#[cfg(windows)] +pub fn last_posix_errno() -> i32 { + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(0) { + extern "C" { + fn _get_errno(pValue: *mut i32) -> i32; + } + let mut errno = 0; + unsafe { suppress_iph!(_get_errno(&mut errno)) }; + errno + } else { + err.posix_errno() + } +} + +#[cfg(not(windows))] +pub fn last_posix_errno() -> i32 { + last_os_error().posix_errno() +} + #[cfg(unix)] pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> { use std::os::unix::ffi::OsStrExt; @@ -37,3 +75,133 @@ pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> { pub use std::os::unix::ffi; #[cfg(target_os = "wasi")] pub use std::os::wasi::ffi; + +#[cfg(windows)] +pub fn errno_to_winerror(errno: i32) -> i32 { + use libc::*; + use windows_sys::Win32::Foundation::*; + let winerror = match errno { + ENOENT => ERROR_FILE_NOT_FOUND, + E2BIG => ERROR_BAD_ENVIRONMENT, + ENOEXEC => ERROR_BAD_FORMAT, + EBADF => ERROR_INVALID_HANDLE, + ECHILD => ERROR_WAIT_NO_CHILDREN, + EAGAIN => ERROR_NO_PROC_SLOTS, + ENOMEM => ERROR_NOT_ENOUGH_MEMORY, + EACCES => ERROR_ACCESS_DENIED, + EEXIST => ERROR_FILE_EXISTS, + EXDEV => ERROR_NOT_SAME_DEVICE, + ENOTDIR => ERROR_DIRECTORY, + EMFILE => ERROR_TOO_MANY_OPEN_FILES, + ENOSPC => ERROR_DISK_FULL, + EPIPE => ERROR_BROKEN_PIPE, + ENOTEMPTY => ERROR_DIR_NOT_EMPTY, + EILSEQ => ERROR_NO_UNICODE_TRANSLATION, + EINVAL => ERROR_INVALID_FUNCTION, + _ => ERROR_INVALID_FUNCTION, + }; + winerror as _ +} + +// winerror: https://learn.microsoft.com/windows/win32/debug/system-error-codes--0-499- +// errno: https://learn.microsoft.com/cpp/c-runtime-library/errno-constants?view=msvc-170 +#[cfg(windows)] +pub fn winerror_to_errno(winerror: i32) -> i32 { + use libc::*; + use windows_sys::Win32::{ + Foundation::*, + Networking::WinSock::{WSAEACCES, WSAEBADF, WSAEFAULT, WSAEINTR, WSAEINVAL, WSAEMFILE}, + }; + // Unwrap FACILITY_WIN32 HRESULT errors. + // if ((winerror & 0xFFFF0000) == 0x80070000) { + // winerror &= 0x0000FFFF; + // } + + // Winsock error codes (10000-11999) are errno values. + if (10000..12000).contains(&winerror) { + match winerror { + WSAEINTR | WSAEBADF | WSAEACCES | WSAEFAULT | WSAEINVAL | WSAEMFILE => { + // Winsock definitions of errno values. See WinSock2.h + return winerror - 10000; + } + _ => return winerror as _, + } + } + + #[allow(non_upper_case_globals)] + match winerror as u32 { + ERROR_FILE_NOT_FOUND + | ERROR_PATH_NOT_FOUND + | ERROR_INVALID_DRIVE + | ERROR_NO_MORE_FILES + | ERROR_BAD_NETPATH + | ERROR_BAD_NET_NAME + | ERROR_BAD_PATHNAME + | ERROR_FILENAME_EXCED_RANGE => ENOENT, + ERROR_BAD_ENVIRONMENT => E2BIG, + ERROR_BAD_FORMAT + | ERROR_INVALID_STARTING_CODESEG + | ERROR_INVALID_STACKSEG + | ERROR_INVALID_MODULETYPE + | ERROR_INVALID_EXE_SIGNATURE + | ERROR_EXE_MARKED_INVALID + | ERROR_BAD_EXE_FORMAT + | ERROR_ITERATED_DATA_EXCEEDS_64k + | ERROR_INVALID_MINALLOCSIZE + | ERROR_DYNLINK_FROM_INVALID_RING + | ERROR_IOPL_NOT_ENABLED + | ERROR_INVALID_SEGDPL + | ERROR_AUTODATASEG_EXCEEDS_64k + | ERROR_RING2SEG_MUST_BE_MOVABLE + | ERROR_RELOC_CHAIN_XEEDS_SEGLIM + | ERROR_INFLOOP_IN_RELOC_CHAIN => ENOEXEC, + ERROR_INVALID_HANDLE | ERROR_INVALID_TARGET_HANDLE | ERROR_DIRECT_ACCESS_HANDLE => EBADF, + ERROR_WAIT_NO_CHILDREN | ERROR_CHILD_NOT_COMPLETE => ECHILD, + ERROR_NO_PROC_SLOTS | ERROR_MAX_THRDS_REACHED | ERROR_NESTING_NOT_ALLOWED => EAGAIN, + ERROR_ARENA_TRASHED + | ERROR_NOT_ENOUGH_MEMORY + | ERROR_INVALID_BLOCK + | ERROR_NOT_ENOUGH_QUOTA => ENOMEM, + ERROR_ACCESS_DENIED + | ERROR_CURRENT_DIRECTORY + | ERROR_WRITE_PROTECT + | ERROR_BAD_UNIT + | ERROR_NOT_READY + | ERROR_BAD_COMMAND + | ERROR_CRC + | ERROR_BAD_LENGTH + | ERROR_SEEK + | ERROR_NOT_DOS_DISK + | ERROR_SECTOR_NOT_FOUND + | ERROR_OUT_OF_PAPER + | ERROR_WRITE_FAULT + | ERROR_READ_FAULT + | ERROR_GEN_FAILURE + | ERROR_SHARING_VIOLATION + | ERROR_LOCK_VIOLATION + | ERROR_WRONG_DISK + | ERROR_SHARING_BUFFER_EXCEEDED + | ERROR_NETWORK_ACCESS_DENIED + | ERROR_CANNOT_MAKE + | ERROR_FAIL_I24 + | ERROR_DRIVE_LOCKED + | ERROR_SEEK_ON_DEVICE + | ERROR_NOT_LOCKED + | ERROR_LOCK_FAILED + | 35 => EACCES, + ERROR_FILE_EXISTS | ERROR_ALREADY_EXISTS => EEXIST, + ERROR_NOT_SAME_DEVICE => EXDEV, + ERROR_DIRECTORY => ENOTDIR, + ERROR_TOO_MANY_OPEN_FILES => EMFILE, + ERROR_DISK_FULL => ENOSPC, + ERROR_BROKEN_PIPE | ERROR_NO_DATA => EPIPE, + ERROR_DIR_NOT_EMPTY => ENOTEMPTY, + ERROR_NO_UNICODE_TRANSLATION => EILSEQ, + ERROR_INVALID_FUNCTION + | ERROR_INVALID_ACCESS + | ERROR_INVALID_DATA + | ERROR_INVALID_PARAMETER + | ERROR_NEGATIVE_SEEK => EINVAL, + _ => EINVAL, + } +} diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml index ad36d109e..052a583eb 100644 --- a/stdlib/Cargo.toml +++ b/stdlib/Cargo.toml @@ -117,7 +117,7 @@ features = [ ] [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.52.0" +workspace = true features = [ "Win32_Networking_WinSock", "Win32_NetworkManagement_IpHelper", diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 12231bac9..ad823201e 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -13,6 +13,7 @@ mod _socket { use crate::common::lock::{PyMappedRwLockReadGuard, PyRwLock, PyRwLockReadGuard}; use crate::vm::{ builtins::{PyBaseExceptionRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef}, + common::os::ErrorExt, convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject}, function::{ArgBytesLike, ArgMemoryBuffer, Either, FsPath, OptionalArg, OptionalOption}, types::{DefaultConstructor, Initializer, Representable}, @@ -1018,7 +1019,7 @@ mod _socket { let sock = self.sock()?; let err = sock.take_error()?; match err { - Some(e) if e.raw_os_error() == Some(libc::EISCONN) => Ok(()), + Some(e) if e.posix_errno() == libc::EISCONN => Ok(()), Some(e) => Err(e), // TODO: is this accurate? None => Ok(()), @@ -1408,7 +1409,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::errno().into()); + return Err(crate::common::os::last_os_error().into()); } Ok(vm.ctx.new_int(flag).into()) } else { @@ -1429,7 +1430,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::errno().into()); + return Err(crate::common::os::last_os_error().into()); } buf.truncate(buflen as usize); Ok(vm.ctx.new_bytes(buf).into()) @@ -1470,7 +1471,7 @@ mod _socket { } }; if ret < 0 { - Err(crate::common::os::errno().into()) + Err(crate::common::os::last_os_error().into()) } else { Ok(()) } @@ -1694,10 +1695,7 @@ mod _socket { fn errno(self) -> PyResult { match self { Self::Timeout => Ok(errcode!(EWOULDBLOCK)), - Self::Io(err) => { - // TODO: just unwrap()? - Ok(err.raw_os_error().unwrap_or(1)) - } + Self::Io(err) => Ok(err.posix_errno()), Self::Py(exc) => Err(exc), } } @@ -2354,7 +2352,7 @@ mod _socket { use windows_sys::Win32::Networking::WinSock::closesocket as close; let ret = unsafe { close(x as _) }; if ret < 0 { - let err = crate::common::os::errno(); + let err = crate::common::os::last_os_error(); if err.raw_os_error() != Some(errcode!(ECONNRESET)) { return Err(err); } diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index cf3a80203..51b22e366 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -680,7 +680,7 @@ mod _ssl { ) }; if ret != 1 { - let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); + let errno = crate::common::os::last_posix_errno(); let err = if errno != 0 { crate::vm::stdlib::os::errno_err(vm) } else { diff --git a/stdlib/src/termios.rs b/stdlib/src/termios.rs index 637e9185a..84c12b2b6 100644 --- a/stdlib/src/termios.rs +++ b/stdlib/src/termios.rs @@ -4,6 +4,7 @@ pub(crate) use self::termios::make_module; mod termios { use crate::vm::{ builtins::{PyBaseExceptionRef, PyBytes, PyInt, PyListRef, PyTypeRef}, + common::os::ErrorExt, convert::ToPyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, }; @@ -259,7 +260,7 @@ mod termios { vm.new_exception( error_type(vm), vec![ - err.raw_os_error().to_pyobject(vm), + err.posix_errno().to_pyobject(vm), vm.ctx.new_str(err.to_string()).into(), ], ) diff --git a/vm/Cargo.toml b/vm/Cargo.toml index fee7dcbb9..e31472371 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -119,7 +119,7 @@ features = [ ] [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.52.0" +workspace = true features = [ "Win32_Foundation", "Win32_Networking_WinSock", diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3e498f127..5f4993afe 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -800,10 +800,8 @@ impl ExceptionZoo { // second exception filename "filename2" => ctx.none(), }); - // TODO: this isn't really accurate #[cfg(windows)] - excs.os_error - .set_str_attr("winerror", errno_getter.clone(), ctx); + excs.os_error.set_str_attr("winerror", ctx.none(), ctx); extend_exception!(PyBlockingIOError, ctx, excs.blocking_io_error); extend_exception!(PyChildProcessError, ctx, excs.child_process_error); @@ -1013,14 +1011,9 @@ impl ToPyException for widestring::error::ContainsNul { } #[cfg(any(unix, windows, target_os = "wasi"))] -pub(crate) fn raw_os_error_to_exc_type( - errno: i32, - vm: &VirtualMachine, -) -> Option<&'static Py> { +pub(crate) fn errno_to_exc_type(errno: i32, vm: &VirtualMachine) -> Option<&'static Py> { use crate::stdlib::errno::errors; let excs = &vm.ctx.exceptions; - #[cfg(windows)] - let errno = win_error_to_errno(errno as u32); match errno { errors::EWOULDBLOCK => Some(excs.blocking_io_error), errors::EALREADY => Some(excs.blocking_io_error), @@ -1045,111 +1038,8 @@ pub(crate) fn raw_os_error_to_exc_type( } } -#[cfg(windows)] -pub(crate) fn win_error_to_errno(winerror: u32) -> i32 { - use crate::stdlib::errno::errors; - use windows_sys::Win32::Foundation::*; - // Unwrap FACILITY_WIN32 HRESULT errors. - // if ((winerror & 0xFFFF0000) == 0x80070000) { - // winerror &= 0x0000FFFF; - // } - - // Winsock error codes (10000-11999) are errno values. - // if winerror >= 10000 && winerror < 12000 { - // match winerror { - // WSAEINTR | WSAEBADF | WSAEACCES | WSAEFAULT | WSAEINVAL | WSAEMFILE => { - // // Winsock definitions of errno values. See WinSock2.h - // return winerror - 10000; - // }, - // _ => return winerror, - // } - // } - - #[allow(non_upper_case_globals)] - match winerror { - ERROR_FILE_NOT_FOUND - | ERROR_PATH_NOT_FOUND - | ERROR_INVALID_DRIVE - | ERROR_NO_MORE_FILES - | ERROR_BAD_NETPATH - | ERROR_BAD_NET_NAME - | ERROR_BAD_PATHNAME - | ERROR_FILENAME_EXCED_RANGE => errors::ENOENT, - ERROR_BAD_ENVIRONMENT => errors::E2BIG, - ERROR_BAD_FORMAT - | ERROR_INVALID_STARTING_CODESEG - | ERROR_INVALID_STACKSEG - | ERROR_INVALID_MODULETYPE - | ERROR_INVALID_EXE_SIGNATURE - | ERROR_EXE_MARKED_INVALID - | ERROR_BAD_EXE_FORMAT - | ERROR_ITERATED_DATA_EXCEEDS_64k - | ERROR_INVALID_MINALLOCSIZE - | ERROR_DYNLINK_FROM_INVALID_RING - | ERROR_IOPL_NOT_ENABLED - | ERROR_INVALID_SEGDPL - | ERROR_AUTODATASEG_EXCEEDS_64k - | ERROR_RING2SEG_MUST_BE_MOVABLE - | ERROR_RELOC_CHAIN_XEEDS_SEGLIM - | ERROR_INFLOOP_IN_RELOC_CHAIN => errors::ENOEXEC, - ERROR_INVALID_HANDLE | ERROR_INVALID_TARGET_HANDLE | ERROR_DIRECT_ACCESS_HANDLE => { - errors::EBADF - } - ERROR_WAIT_NO_CHILDREN | ERROR_CHILD_NOT_COMPLETE => errors::ECHILD, - ERROR_NO_PROC_SLOTS | ERROR_MAX_THRDS_REACHED | ERROR_NESTING_NOT_ALLOWED => errors::EAGAIN, - ERROR_ARENA_TRASHED - | ERROR_NOT_ENOUGH_MEMORY - | ERROR_INVALID_BLOCK - | ERROR_NOT_ENOUGH_QUOTA => errors::ENOMEM, - ERROR_ACCESS_DENIED - | ERROR_CURRENT_DIRECTORY - | ERROR_WRITE_PROTECT - | ERROR_BAD_UNIT - | ERROR_NOT_READY - | ERROR_BAD_COMMAND - | ERROR_CRC - | ERROR_BAD_LENGTH - | ERROR_SEEK - | ERROR_NOT_DOS_DISK - | ERROR_SECTOR_NOT_FOUND - | ERROR_OUT_OF_PAPER - | ERROR_WRITE_FAULT - | ERROR_READ_FAULT - | ERROR_GEN_FAILURE - | ERROR_SHARING_VIOLATION - | ERROR_LOCK_VIOLATION - | ERROR_WRONG_DISK - | ERROR_SHARING_BUFFER_EXCEEDED - | ERROR_NETWORK_ACCESS_DENIED - | ERROR_CANNOT_MAKE - | ERROR_FAIL_I24 - | ERROR_DRIVE_LOCKED - | ERROR_SEEK_ON_DEVICE - | ERROR_NOT_LOCKED - | ERROR_LOCK_FAILED - | 35 => errors::EACCES, - ERROR_FILE_EXISTS | ERROR_ALREADY_EXISTS => errors::EEXIST, - ERROR_NOT_SAME_DEVICE => errors::EXDEV, - ERROR_DIRECTORY => errors::ENOTDIR, - ERROR_TOO_MANY_OPEN_FILES => errors::EMFILE, - ERROR_DISK_FULL => errors::ENOSPC, - ERROR_BROKEN_PIPE | ERROR_NO_DATA => errors::EPIPE, - ERROR_DIR_NOT_EMPTY => errors::ENOTEMPTY, - ERROR_NO_UNICODE_TRANSLATION => errors::EILSEQ, - ERROR_INVALID_FUNCTION - | ERROR_INVALID_ACCESS - | ERROR_INVALID_DATA - | ERROR_INVALID_PARAMETER - | ERROR_NEGATIVE_SEEK => errors::EINVAL, - _ => errors::EINVAL, - } -} - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] -pub(crate) fn raw_os_error_to_exc_type( - _errno: i32, - _vm: &VirtualMachine, -) -> Option<&'static Py> { +pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'static Py> { None } @@ -1359,7 +1249,7 @@ pub(super) mod types { errno .payload_if_subclass::(vm) .and_then(|errno| errno.try_to_primitive::(vm).ok()) - .and_then(|errno| super::raw_os_error_to_exc_type(errno, vm)) + .and_then(|errno| super::errno_to_exc_type(errno, vm)) .and_then(|typ| vm.invoke_exception(typ.to_owned(), args.to_vec()).ok()) } else { None @@ -1392,8 +1282,12 @@ pub(super) mod types { let mut new_args = args; if (3..=5).contains(&len) { zelf.set_attr("filename", new_args.args[2].clone(), vm)?; - if len == 5 { - zelf.set_attr("filename2", new_args.args[4].clone(), vm)?; + #[cfg(windows)] + if let Some(winerror) = new_args.args.get(3) { + zelf.set_attr("winerror", winerror.clone(), vm)?; + } + if let Some(filename2) = new_args.args.get(4) { + zelf.set_attr("filename2", filename2.clone(), vm)?; } new_args.args.truncate(2); diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index edebde137..deea3eafb 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -10,9 +10,9 @@ cfg_if::cfg_if! { } use crate::{ - builtins::PyBaseExceptionRef, - builtins::PyModule, - convert::{IntoPyException, ToPyException, ToPyObject}, + builtins::{PyBaseExceptionRef, PyModule}, + common::os::ErrorExt, + convert::{IntoPyException, ToPyException}, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine, }; pub use _io::io_open as open; @@ -28,14 +28,32 @@ impl ToPyException for std::io::Error { ErrorKind::PermissionDenied => excs.permission_error, ErrorKind::AlreadyExists => excs.file_exists_error, ErrorKind::WouldBlock => excs.blocking_io_error, - _ => self - .raw_os_error() - .and_then(|errno| crate::exceptions::raw_os_error_to_exc_type(errno, vm)) + _ => crate::exceptions::errno_to_exc_type(self.posix_errno(), vm) .unwrap_or(excs.os_error), }; - let errno = self.raw_os_error().to_pyobject(vm); + + let errno = self.raw_os_error().unwrap_or(0); + let errno_obj = vm.new_pyobj(errno); + #[cfg(windows)] + let (winerror_obj, errno_obj) = { + let winerror = errno; + let winerror_obj = errno_obj.clone(); + let errno = crate::common::os::winerror_to_errno(winerror); + (winerror_obj, vm.new_pyobj(errno)) + }; + let msg = vm.ctx.new_str(self.to_string()).into(); - vm.new_exception(exc_type.to_owned(), vec![errno, msg]) + #[allow(clippy::let_and_return)] + let exc = vm.new_exception(exc_type.to_owned(), vec![errno_obj, msg]); + #[cfg(windows)] + { + // FIXME: manual setup winerror due to lack of OSError.__init__ support + use crate::object::AsObject; + exc.as_object() + .set_attr("winerror", vm.new_pyobj(winerror_obj), vm) + .unwrap(); + } + exc } } diff --git a/vm/src/stdlib/nt.rs b/vm/src/stdlib/nt.rs index 206faa82e..2da8f5e0d 100644 --- a/vm/src/stdlib/nt.rs +++ b/vm/src/stdlib/nt.rs @@ -14,7 +14,7 @@ pub(crate) mod module { use crate::builtins::PyListRef; use crate::{ builtins::{PyDictRef, PyStrRef, PyTupleRef}, - common::{crt_fd::Fd, os::errno, suppress_iph}, + common::{crt_fd::Fd, os::last_os_error, suppress_iph}, convert::ToPyException, function::Either, function::OptionalArg, @@ -385,7 +385,7 @@ pub(crate) mod module { Foundation::SetHandleInformation(handle as _, Foundation::HANDLE_FLAG_INHERIT, flags) }; if res == 0 { - Err(errno()) + Err(last_os_error()) } else { Ok(()) } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 092c42f6b..3121b7944 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,7 +1,7 @@ use crate::{ builtins::{PyBaseExceptionRef, PyModule, PySet}, common::crt_fd::Fd, - convert::IntoPyException, + convert::{IntoPyException, ToPyException}, function::{ArgumentError, FromArgs, FsPath, FuncArgs}, AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; @@ -205,7 +205,7 @@ impl IntoPyException for IOErrorBuilder { /// Convert the error stored in the `errno` variable into an Exception #[inline] pub fn errno_err(vm: &VirtualMachine) -> PyBaseExceptionRef { - crate::common::os::errno().into_pyexception(vm) + crate::common::os::last_os_error().to_pyexception(vm) } #[allow(dead_code)]