From 1ff815555f0ec17b9f043c00a9a4d981195f9eb0 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 7 Dec 2025 23:05:02 +0900 Subject: [PATCH 1/3] new_last_{os,errno}_error --- Lib/test/test_os.py | 4 -- Lib/test/test_support.py | 1 - crates/common/src/crt_fd.rs | 6 ++- crates/common/src/fileutils.rs | 2 +- crates/common/src/os.rs | 30 +++++------- crates/stdlib/src/fcntl.rs | 2 +- crates/stdlib/src/multiprocessing.rs | 8 ++-- crates/stdlib/src/overlapped.rs | 15 +++--- crates/stdlib/src/resource.rs | 1 - crates/stdlib/src/socket.rs | 8 ++-- crates/vm/src/stdlib/nt.rs | 68 ++++++++++++++++------------ crates/vm/src/stdlib/os.rs | 14 ++++-- crates/vm/src/stdlib/time.rs | 5 +- crates/vm/src/stdlib/winapi.rs | 13 +++--- crates/vm/src/vm/vm_new.rs | 20 +++++++- crates/vm/src/windows.rs | 7 ++- 16 files changed, 109 insertions(+), 95 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 25e2c30e4d..4fefc9d88a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2428,12 +2428,10 @@ def test_ftruncate(self): self.check(os.ftruncate, 0) self.check_bool(os.truncate, 0) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)') @unittest.skipUnless(hasattr(os, 'lseek'), 'test needs os.lseek()') def test_lseek(self): self.check(os.lseek, 0, 0) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)') @unittest.skipUnless(hasattr(os, 'read'), 'test needs os.read()') def test_read(self): self.check(os.read, 1) @@ -2447,7 +2445,6 @@ def test_readv(self): def test_tcsetpgrpt(self): self.check(os.tcsetpgrp, 0) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)') @unittest.skipUnless(hasattr(os, 'write'), 'test needs os.write()') def test_write(self): self.check(os.write, b" ") @@ -2456,7 +2453,6 @@ def test_write(self): def test_writev(self): self.check(os.writev, [b'abc']) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms') @support.requires_subprocess() def test_inheritable(self): self.check(os.get_inheritable) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 4381a82b6b..7c8380498e 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -310,7 +310,6 @@ def test_temp_cwd__name_none(self): def test_sortdict(self): self.assertEqual(support.sortdict({3:3, 2:2, 1:1}), "{1: 1, 2: 2, 3: 3}") - @unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; actual c fds on windows") def test_make_bad_fd(self): fd = os_helper.make_bad_fd() with self.assertRaises(OSError) as cm: diff --git a/crates/common/src/crt_fd.rs b/crates/common/src/crt_fd.rs index 4fda2f3dcf..7b8279adbe 100644 --- a/crates/common/src/crt_fd.rs +++ b/crates/common/src/crt_fd.rs @@ -35,7 +35,8 @@ pub type Raw = i32; #[inline] fn cvt(ret: I) -> io::Result { if ret < I::zero() { - Err(crate::os::last_os_error()) + // CRT functions set errno, not GetLastError(), so use errno_io_error + Err(crate::os::errno_io_error()) } else { Ok(ret) } @@ -345,7 +346,8 @@ pub fn as_handle(fd: Borrowed<'_>) -> io::Result> { } let handle = unsafe { suppress_iph!(_get_osfhandle(fd)) }; if handle as HANDLE == INVALID_HANDLE_VALUE { - Err(crate::os::last_os_error()) + // _get_osfhandle is a CRT function that sets errno, not GetLastError() + Err(crate::os::errno_io_error()) } else { Ok(unsafe { BorrowedHandle::borrow_raw(handle as _) }) } diff --git a/crates/common/src/fileutils.rs b/crates/common/src/fileutils.rs index 8e21ef656c..a12c1cd82e 100644 --- a/crates/common/src/fileutils.rs +++ b/crates/common/src/fileutils.rs @@ -13,7 +13,7 @@ pub fn fstat(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result { unsafe { let ret = libc::fstat(fd.as_raw(), stat.as_mut_ptr()); if ret == -1 { - Err(crate::os::last_os_error()) + Err(crate::os::errno_io_error()) } else { Ok(stat.assume_init()) } diff --git a/crates/common/src/os.rs b/crates/common/src/os.rs index 0aef70d582..e61c77e0fd 100644 --- a/crates/common/src/os.rs +++ b/crates/common/src/os.rs @@ -19,30 +19,22 @@ impl ErrorExt for io::Error { } } +/// Get the last error from C runtime library functions (like _dup, _dup2, _fstat, etc.) +/// CRT functions set errno, not GetLastError(), so we need to read errno directly. #[cfg(windows)] -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) { - unsafe extern "C" { - fn _get_errno(pValue: *mut i32) -> i32; - } - 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 - } +pub fn errno_io_error() -> io::Error { + let errno: i32 = get_errno(); + let winerror = errno_to_winerror(errno); + io::Error::from_raw_os_error(winerror) } #[cfg(not(windows))] -pub fn last_os_error() -> io::Error { - io::Error::last_os_error() +pub fn errno_io_error() -> io::Error { + std::io::Error::last_os_error() } #[cfg(windows)] -pub fn last_posix_errno() -> i32 { +pub fn get_errno() -> i32 { unsafe extern "C" { fn _get_errno(pValue: *mut i32) -> i32; } @@ -52,8 +44,8 @@ pub fn last_posix_errno() -> i32 { } #[cfg(not(windows))] -pub fn last_posix_errno() -> i32 { - last_os_error().posix_errno() +pub fn get_errno() -> i32 { + std::io::Error::last_os_error().posix_errno() } #[cfg(unix)] diff --git a/crates/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs index 84b60b43ba..4d5c4e24d2 100644 --- a/crates/stdlib/src/fcntl.rs +++ b/crates/stdlib/src/fcntl.rs @@ -8,7 +8,7 @@ mod fcntl { PyResult, VirtualMachine, builtins::PyIntRef, function::{ArgMemoryBuffer, ArgStrOrBytesLike, Either, OptionalArg}, - stdlib::{io, os}, + stdlib::io, }; // TODO: supply these from (please file an issue/PR upstream): diff --git a/crates/stdlib/src/multiprocessing.rs b/crates/stdlib/src/multiprocessing.rs index 4a98c1afad..280eff7d32 100644 --- a/crates/stdlib/src/multiprocessing.rs +++ b/crates/stdlib/src/multiprocessing.rs @@ -3,14 +3,14 @@ pub(crate) use _multiprocessing::make_module; #[cfg(windows)] #[pymodule] mod _multiprocessing { - use crate::vm::{PyResult, VirtualMachine, function::ArgBytesLike, stdlib::os}; + use crate::vm::{PyResult, VirtualMachine, function::ArgBytesLike}; use windows_sys::Win32::Networking::WinSock::{self, SOCKET}; #[pyfunction] fn closesocket(socket: usize, vm: &VirtualMachine) -> PyResult<()> { let res = unsafe { WinSock::closesocket(socket as SOCKET) }; if res == 0 { - Err(os::errno_err(vm)) + Err(vm.new_last_os_error()) } else { Ok(()) } @@ -22,7 +22,7 @@ mod _multiprocessing { let n_read = unsafe { WinSock::recv(socket as SOCKET, buf.as_mut_ptr() as *mut _, size as i32, 0) }; if n_read < 0 { - Err(os::errno_err(vm)) + Err(vm.new_last_os_error()) } else { Ok(n_read) } @@ -34,7 +34,7 @@ mod _multiprocessing { WinSock::send(socket as SOCKET, b.as_ptr() as *const _, b.len() as i32, 0) }); if ret < 0 { - Err(os::errno_err(vm)) + Err(vm.new_last_os_error()) } else { Ok(ret) } diff --git a/crates/stdlib/src/overlapped.rs b/crates/stdlib/src/overlapped.rs index 9d816a03d4..a5b5fa14fc 100644 --- a/crates/stdlib/src/overlapped.rs +++ b/crates/stdlib/src/overlapped.rs @@ -13,7 +13,6 @@ mod _overlapped { common::lock::PyMutex, convert::{ToPyException, ToPyObject}, protocol::PyBuffer, - stdlib::os::errno_err, types::Constructor, }; use windows_sys::Win32::{ @@ -256,7 +255,7 @@ mod _overlapped { }; // CancelIoEx returns ERROR_NOT_FOUND if the I/O completed in-between if ret == 0 && unsafe { GetLastError() } != Foundation::ERROR_NOT_FOUND { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(()) } @@ -276,7 +275,7 @@ mod _overlapped { ) as isize }; if event == NULL { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } } @@ -318,7 +317,7 @@ mod _overlapped { ) as isize }; if r as usize == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(r) } @@ -346,7 +345,7 @@ mod _overlapped { if err == Foundation::WAIT_TIMEOUT { return Ok(vm.ctx.none()); } else { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } } @@ -387,7 +386,7 @@ mod _overlapped { ) as isize }; if event == NULL { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(event) } @@ -396,7 +395,7 @@ mod _overlapped { fn SetEvent(handle: u64, vm: &VirtualMachine) -> PyResult<()> { let ret = unsafe { windows_sys::Win32::System::Threading::SetEvent(u64_to_handle(handle)) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(()) } @@ -406,7 +405,7 @@ mod _overlapped { let ret = unsafe { windows_sys::Win32::System::Threading::ResetEvent(u64_to_handle(handle)) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(()) } diff --git a/crates/stdlib/src/resource.rs b/crates/stdlib/src/resource.rs index 59205a4fa4..78fffb938e 100644 --- a/crates/stdlib/src/resource.rs +++ b/crates/stdlib/src/resource.rs @@ -7,7 +7,6 @@ mod resource { use crate::vm::{ PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, VirtualMachine, convert::{ToPyException, ToPyObject}, - stdlib::os, types::PyStructSequence, }; use std::{io, mem}; diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 234d94a526..11e1d8527d 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -1547,7 +1547,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::last_os_error().into()); + return Err(crate::common::os::errno_io_error().into()); } Ok(vm.ctx.new_int(flag).into()) } else { @@ -1568,7 +1568,7 @@ mod _socket { ) }; if ret < 0 { - return Err(crate::common::os::last_os_error().into()); + return Err(crate::common::os::errno_io_error().into()); } buf.truncate(buflen as usize); Ok(vm.ctx.new_bytes(buf).into()) @@ -1609,7 +1609,7 @@ mod _socket { } }; if ret < 0 { - Err(crate::common::os::last_os_error().into()) + Err(crate::common::os::errno_io_error().into()) } else { Ok(()) } @@ -2488,7 +2488,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::last_os_error(); + let err = std::io::Error::last_os_error(); if err.raw_os_error() != Some(errcode!(ECONNRESET)) { return Err(err); } diff --git a/crates/vm/src/stdlib/nt.rs b/crates/vm/src/stdlib/nt.rs index 6c42517f5f..785e794a26 100644 --- a/crates/vm/src/stdlib/nt.rs +++ b/crates/vm/src/stdlib/nt.rs @@ -15,12 +15,13 @@ pub(crate) mod module { use crate::{ PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef}, - common::{crt_fd, os::last_os_error, suppress_iph, windows::ToWideString}, + common::{crt_fd, suppress_iph, windows::ToWideString}, convert::ToPyException, function::{Either, OptionalArg}, ospath::OsPath, - stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, errno_err}, + stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory}, }; + use libc::intptr_t; use std::os::windows::io::AsRawHandle; use std::{env, fs, io, mem::MaybeUninit, os::windows::ffi::OsStringExt}; @@ -162,7 +163,7 @@ pub(crate) mod module { let mut status: i32 = 0; let pid = unsafe { suppress_iph!(_cwait(&mut status, pid, opt)) }; if pid == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { // Cast to unsigned to handle large exit codes (like 0xC000013A) // then shift left by 8 to match POSIX waitpid format @@ -184,16 +185,24 @@ pub(crate) mod module { if sig == Console::CTRL_C_EVENT || sig == Console::CTRL_BREAK_EVENT { let ret = unsafe { Console::GenerateConsoleCtrlEvent(sig, pid) }; - let res = if ret == 0 { Err(errno_err(vm)) } else { Ok(()) }; + let res = if ret == 0 { + Err(vm.new_last_os_error()) + } else { + Ok(()) + }; return res; } let h = unsafe { Threading::OpenProcess(Threading::PROCESS_ALL_ACCESS, 0, pid) }; if h.is_null() { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let ret = unsafe { Threading::TerminateProcess(h, sig) }; - let res = if ret == 0 { Err(errno_err(vm)) } else { Ok(()) }; + let res = if ret == 0 { + Err(vm.new_last_os_error()) + } else { + Ok(()) + }; unsafe { Foundation::CloseHandle(h) }; res } @@ -218,7 +227,7 @@ pub(crate) mod module { // In that case, try opening CONOUT$ directly with read access let err = unsafe { Foundation::GetLastError() }; if err != Foundation::ERROR_ACCESS_DENIED { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let conout: Vec = "CONOUT$\0".encode_utf16().collect(); let console_handle = unsafe { @@ -233,13 +242,13 @@ pub(crate) mod module { ) }; if console_handle == INVALID_HANDLE_VALUE { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let ret = unsafe { Console::GetConsoleScreenBufferInfo(console_handle, csbi.as_mut_ptr()) }; unsafe { Foundation::CloseHandle(console_handle) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } } let csbi = unsafe { csbi.assume_init() }; @@ -302,7 +311,7 @@ pub(crate) mod module { let result = unsafe { suppress_iph!(_wspawnv(mode, path.as_ptr(), argv_spawn.as_ptr())) }; if result == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(result) } @@ -379,7 +388,7 @@ pub(crate) mod module { )) }; if result == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(result) } @@ -419,7 +428,7 @@ pub(crate) mod module { .collect(); if (unsafe { suppress_iph!(_wexecv(path.as_ptr(), argv_execv.as_ptr())) } == -1) { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(()) } @@ -489,7 +498,7 @@ pub(crate) mod module { if (unsafe { suppress_iph!(_wexecve(path.as_ptr(), argv_execve.as_ptr(), envp.as_ptr())) } == -1) { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(()) } @@ -517,7 +526,7 @@ pub(crate) mod module { ) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } if ret as usize > buffer.len() { buffer.resize(ret as usize, 0); @@ -530,7 +539,7 @@ pub(crate) mod module { ) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } } let buffer = widestring::WideCString::from_vec_truncate(buffer); @@ -546,7 +555,7 @@ pub(crate) mod module { FileSystem::GetVolumePathNameW(wide.as_ptr(), buffer.as_mut_ptr(), buflen as _) }; if ret == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let buffer = widestring::WideCString::from_vec_truncate(buffer); Ok(path.mode.process_path(buffer.to_os_string(), vm)) @@ -617,7 +626,7 @@ pub(crate) mod module { }; return if ret == 0 { - Err(errno_err(vm)) + Err(err.to_pyexception(vm)) } else { Ok((total, free)) }; @@ -629,10 +638,9 @@ pub(crate) mod module { fn get_handle_inheritable(handle: intptr_t, vm: &VirtualMachine) -> PyResult { let mut flags = 0; if unsafe { Foundation::GetHandleInformation(handle as _, &mut flags) } == 0 { - Err(errno_err(vm)) - } else { - Ok(flags & Foundation::HANDLE_FLAG_INHERIT != 0) + return Err(vm.new_last_os_error()); } + Ok(flags & Foundation::HANDLE_FLAG_INHERIT != 0) } #[pyfunction] @@ -673,7 +681,7 @@ pub(crate) mod module { Foundation::SetHandleInformation(handle as _, Foundation::HANDLE_FLAG_INHERIT, flags) }; if res == 0 { - Err(last_os_error()) + Err(std::io::Error::last_os_error()) } else { Ok(()) } @@ -687,7 +695,7 @@ pub(crate) mod module { let len = unsafe { FileSystem::GetLogicalDriveStringsW(buffer.len() as _, buffer.as_mut_ptr()) }; if len == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } if len as usize >= buffer.len() { return Err(std::io::Error::from_raw_os_error(ERROR_MORE_DATA as _).to_pyexception(vm)); @@ -708,7 +716,7 @@ pub(crate) mod module { let find = unsafe { FileSystem::FindFirstVolumeW(buffer.as_mut_ptr(), buffer.len() as _) }; if find == INVALID_HANDLE_VALUE { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } loop { @@ -832,7 +840,7 @@ pub(crate) mod module { ) }; if convert_result == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let res = unsafe { FileSystem::CreateDirectoryW(wide.as_ptr(), &sec_attr as *const _ as _) }; @@ -843,7 +851,7 @@ pub(crate) mod module { }; if res == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(()) } @@ -863,7 +871,7 @@ pub(crate) mod module { fn umask(mask: i32, vm: &VirtualMachine) -> PyResult { let result = unsafe { _umask(mask) }; if result < 0 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(result) } @@ -883,7 +891,7 @@ pub(crate) mod module { 0, ); if res == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } (read.assume_init(), write.assume_init()) }; @@ -899,7 +907,7 @@ pub(crate) mod module { Foundation::CloseHandle(read_handle as _); Foundation::CloseHandle(write_handle as _); } - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok((read_fd, write_fd)) @@ -980,7 +988,7 @@ pub(crate) mod module { fn dup(fd: i32, vm: &VirtualMachine) -> PyResult { let fd2 = unsafe { suppress_iph!(libc::dup(fd)) }; if fd2 < 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_errno_error()); } let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(fd2) }; let handle = crt_fd::as_handle(borrowed).map_err(|e| close_fd_and_raise(fd2, e, vm))?; @@ -1003,7 +1011,7 @@ pub(crate) mod module { fn dup2(args: Dup2Args, vm: &VirtualMachine) -> PyResult { let result = unsafe { suppress_iph!(libc::dup2(args.fd, args.fd2)) }; if result < 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_errno_error()); } if !args.inheritable { let borrowed = unsafe { crt_fd::Borrowed::borrow_raw(args.fd2) }; diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 6d4a0836a0..6f7ca95ced 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -37,7 +37,7 @@ impl crate::convert::IntoPyException for rustix::io::Errno { /// Convert the error stored in the `errno` variable into an Exception #[inline] pub fn errno_err(vm: &VirtualMachine) -> PyBaseExceptionRef { - crate::common::os::last_os_error().to_pyexception(vm) + crate::common::os::errno_io_error().to_pyexception(vm) } #[allow(dead_code)] @@ -151,7 +151,7 @@ impl ToPyObject for crt_fd::Borrowed<'_> { #[pymodule(sub)] pub(super) mod _os { - use super::{DirFd, FollowSymlinks, SupportFunc, errno_err}; + use super::{DirFd, FollowSymlinks, SupportFunc}; #[cfg(windows)] use crate::common::windows::ToWideString; use crate::{ @@ -338,7 +338,7 @@ pub(super) mod _os { if let Some(fd) = dir_fd.raw_opt() { let res = unsafe { libc::mkdirat(fd, c_path.as_ptr(), mode as _) }; return if res < 0 { - let err = crate::common::os::last_os_error(); + let err = crate::common::os::errno_io_error(); Err(IOErrorBuilder::with_filename(&err, path, vm)) } else { Ok(()) @@ -348,7 +348,7 @@ pub(super) mod _os { let [] = dir_fd.0; let res = unsafe { libc::mkdir(c_path.as_ptr(), mode as _) }; if res < 0 { - let err = crate::common::os::last_os_error(); + let err = crate::common::os::errno_io_error(); return Err(IOErrorBuilder::with_filename(&err, path, vm)); } Ok(()) @@ -1157,7 +1157,11 @@ pub(super) mod _os { std::mem::transmute::<[i32; 2], i64>(distance_to_move) } }; - if res < 0 { Err(errno_err(vm)) } else { Ok(res) } + if res < 0 { + Err(vm.new_last_os_error()) + } else { + Ok(res) + } } #[pyfunction] diff --git a/crates/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs index 212697b7a9..1c6113bad7 100644 --- a/crates/vm/src/stdlib/time.rs +++ b/crates/vm/src/stdlib/time.rs @@ -799,7 +799,6 @@ mod platform { use crate::{ PyRef, PyResult, VirtualMachine, builtins::{PyNamespace, PyStrRef}, - stdlib::os::errno_err, }; use std::time::Duration; use windows_sys::Win32::{ @@ -818,7 +817,7 @@ mod platform { let frequency = unsafe { let mut freq = std::mem::MaybeUninit::uninit(); if QueryPerformanceFrequency(freq.as_mut_ptr()) == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } freq.assume_init() }; @@ -866,7 +865,7 @@ mod platform { _is_time_adjustment_disabled.as_mut_ptr(), ) == 0 { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } time_increment.assume_init() }; diff --git a/crates/vm/src/stdlib/winapi.rs b/crates/vm/src/stdlib/winapi.rs index 6b6e452b38..62505b2b74 100644 --- a/crates/vm/src/stdlib/winapi.rs +++ b/crates/vm/src/stdlib/winapi.rs @@ -11,7 +11,6 @@ mod _winapi { common::windows::ToWideString, convert::{ToPyException, ToPyResult}, function::{ArgMapping, ArgSequence, OptionalArg}, - stdlib::os::errno_err, windows::{WinHandle, WindowsSysResult}, }; use std::ptr::{null, null_mut}; @@ -85,7 +84,7 @@ mod _winapi { ) -> PyResult> { let handle = unsafe { windows_sys::Win32::System::Console::GetStdHandle(std_handle) }; if handle == INVALID_HANDLE_VALUE { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(if handle.is_null() { // NULL handle - return None @@ -162,7 +161,7 @@ mod _winapi { ) -> PyResult { let file_type = unsafe { windows_sys::Win32::Storage::FileSystem::GetFileType(h.0) }; if file_type == 0 && unsafe { windows_sys::Win32::Foundation::GetLastError() } != 0 { - Err(errno_err(vm)) + Err(vm.new_last_os_error()) } else { Ok(file_type) } @@ -305,7 +304,7 @@ mod _winapi { ) }; if handle.is_null() { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(WinHandle(handle)) } @@ -418,7 +417,7 @@ mod _winapi { || unsafe { windows_sys::Win32::Foundation::GetLastError() } != windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } let mut attrlist = vec![0u8; size]; WindowsSysResult(unsafe { @@ -457,7 +456,7 @@ mod _winapi { fn WaitForSingleObject(h: WinHandle, ms: u32, vm: &VirtualMachine) -> PyResult { let ret = unsafe { windows_sys::Win32::System::Threading::WaitForSingleObject(h.0, ms) }; if ret == windows_sys::Win32::Foundation::WAIT_FAILED { - Err(errno_err(vm)) + Err(vm.new_last_os_error()) } else { Ok(ret) } @@ -530,7 +529,7 @@ mod _winapi { ) }; if handle == INVALID_HANDLE_VALUE { - return Err(errno_err(vm)); + return Err(vm.new_last_os_error()); } Ok(handle as _) } diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index 63622b90a2..1054ba9b31 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -7,7 +7,7 @@ use crate::{ descriptor::PyMethodDescriptor, tuple::{IntoPyTuple, PyTupleRef}, }, - convert::ToPyObject, + convert::{ToPyException, ToPyObject}, function::{IntoPyNativeFn, PyMethodFlags}, scope::Scope, vm::VirtualMachine, @@ -202,6 +202,24 @@ impl VirtualMachine { )) } + /// Create a new OSError from the last OS error. + /// + /// On windows, windows-sys errors are expected to be handled by this function. + /// This is identical to `new_last_errno_error` on non-Windows platforms. + pub fn new_last_os_error(&self) -> PyBaseExceptionRef { + let err = std::io::Error::last_os_error(); + err.to_pyexception(self) + } + + /// Create a new OSError from the last POSIX errno. + /// + /// On windows, CRT errno are expected to be handled by this function. + /// This is identical to `new_last_os_error` on non-Windows platforms. + pub fn new_last_errno_error(&self) -> PyBaseExceptionRef { + let err = crate::common::os::errno_io_error(); + err.to_pyexception(self) + } + pub fn new_errno_error(&self, errno: i32, msg: impl Into) -> PyBaseExceptionRef { let vm = self; let exc_type = diff --git a/crates/vm/src/windows.rs b/crates/vm/src/windows.rs index 394714be95..ccf940811b 100644 --- a/crates/vm/src/windows.rs +++ b/crates/vm/src/windows.rs @@ -5,7 +5,6 @@ use crate::common::fileutils::{ use crate::{ PyObjectRef, PyResult, TryFromObject, VirtualMachine, convert::{ToPyObject, ToPyResult}, - stdlib::os::errno_err, }; use rustpython_common::windows::ToWideString; use std::ffi::OsStr; @@ -47,10 +46,10 @@ impl WindowsSysResult { self.0.is_err() } pub fn into_pyresult(self, vm: &VirtualMachine) -> PyResult { - if self.is_err() { - Err(errno_err(vm)) - } else { + if !self.is_err() { Ok(self.0.into_ok()) + } else { + Err(vm.new_last_os_error()) } } } From eab4ba516efc14ca3c18948b8ccf9f0859aa61ed Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Tue, 9 Dec 2025 21:13:29 +0900 Subject: [PATCH 2/3] Remove os::errno_err --- crates/stdlib/src/fcntl.rs | 14 ++++----- crates/stdlib/src/resource.rs | 2 +- crates/stdlib/src/socket.rs | 2 +- crates/vm/src/stdlib/io.rs | 3 +- crates/vm/src/stdlib/msvcrt.rs | 5 ++-- crates/vm/src/stdlib/os.rs | 14 +++------ crates/vm/src/stdlib/posix.rs | 52 +++++++++++++++++++++------------- crates/vm/src/stdlib/signal.rs | 2 +- 8 files changed, 50 insertions(+), 44 deletions(-) diff --git a/crates/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs index 4d5c4e24d2..dc6a0b8171 100644 --- a/crates/stdlib/src/fcntl.rs +++ b/crates/stdlib/src/fcntl.rs @@ -75,7 +75,7 @@ mod fcntl { } let ret = unsafe { libc::fcntl(fd, cmd, buf.as_mut_ptr()) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } return Ok(vm.ctx.new_bytes(buf[..arg_len].to_vec()).into()); } @@ -84,7 +84,7 @@ mod fcntl { }; let ret = unsafe { libc::fcntl(fd, cmd, int as i32) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(vm.new_pyobj(ret)) } @@ -117,7 +117,7 @@ mod fcntl { let ret = unsafe { libc::ioctl(fd, request as _, arg_buf.as_mut_ptr()) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } return Ok(vm.ctx.new_int(ret).into()); } @@ -128,14 +128,14 @@ mod fcntl { }; let ret = unsafe { libc::ioctl(fd, request as _, buf.as_mut_ptr()) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(vm.ctx.new_bytes(buf[..buf_len].to_vec()).into()) } Either::B(i) => { let ret = unsafe { libc::ioctl(fd, request as _, i) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(vm.ctx.new_int(ret).into()) } @@ -149,7 +149,7 @@ mod fcntl { let ret = unsafe { libc::flock(fd, operation) }; // TODO: add support for platforms that don't have a builtin `flock` syscall if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(vm.ctx.new_int(ret).into()) } @@ -209,7 +209,7 @@ mod fcntl { ) }; if ret < 0 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(vm.ctx.new_int(ret).into()) } diff --git a/crates/stdlib/src/resource.rs b/crates/stdlib/src/resource.rs index 78fffb938e..052f45e0ca 100644 --- a/crates/stdlib/src/resource.rs +++ b/crates/stdlib/src/resource.rs @@ -160,7 +160,7 @@ mod resource { let rlimit = unsafe { let mut rlimit = mem::MaybeUninit::::uninit(); if libc::getrlimit(resource as _, rlimit.as_mut_ptr()) == -1 { - return Err(os::errno_err(vm)); + return Err(vm.new_last_errno_error()); } rlimit.assume_init() }; diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs index 11e1d8527d..c00799feab 100644 --- a/crates/stdlib/src/socket.rs +++ b/crates/stdlib/src/socket.rs @@ -2158,7 +2158,7 @@ mod _socket { let mut buf = [0; c::IF_NAMESIZE + 1]; let ret = unsafe { c::if_indextoname(index, buf.as_mut_ptr()) }; if ret.is_null() { - Err(crate::vm::stdlib::os::errno_err(vm)) + Err(vm.new_last_errno_error()) } else { let buf = unsafe { ffi::CStr::from_ptr(buf.as_ptr() as _) }; Ok(buf.to_string_lossy().into_owned()) diff --git a/crates/vm/src/stdlib/io.rs b/crates/vm/src/stdlib/io.rs index 8efd52a29f..223e86503e 100644 --- a/crates/vm/src/stdlib/io.rs +++ b/crates/vm/src/stdlib/io.rs @@ -3991,8 +3991,7 @@ mod _io { // check file descriptor validity #[cfg(unix)] if let Ok(crate::ospath::OsPathOrFd::Fd(fd)) = file.clone().try_into_value(vm) { - nix::fcntl::fcntl(fd, nix::fcntl::F_GETFD) - .map_err(|_| crate::stdlib::os::errno_err(vm))?; + nix::fcntl::fcntl(fd, nix::fcntl::F_GETFD).map_err(|_| vm.new_last_errno_error())?; } // Construct a FileIO (subclass of RawIOBase) diff --git a/crates/vm/src/stdlib/msvcrt.rs b/crates/vm/src/stdlib/msvcrt.rs index 70ac09f8a0..cf0dac2c9d 100644 --- a/crates/vm/src/stdlib/msvcrt.rs +++ b/crates/vm/src/stdlib/msvcrt.rs @@ -9,7 +9,6 @@ mod msvcrt { builtins::{PyBytes, PyStrRef}, common::{crt_fd, suppress_iph}, convert::IntoPyException, - stdlib::os::errno_err, }; use itertools::Itertools; use std::os::windows::io::AsRawHandle; @@ -82,7 +81,7 @@ mod msvcrt { fn setmode(fd: crt_fd::Borrowed<'_>, flags: i32, vm: &VirtualMachine) -> PyResult { let flags = unsafe { suppress_iph!(_setmode(fd, flags)) }; if flags == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(flags) } @@ -92,7 +91,7 @@ mod msvcrt { fn open_osfhandle(handle: isize, flags: i32, vm: &VirtualMachine) -> PyResult { let ret = unsafe { suppress_iph!(libc::open_osfhandle(handle, flags)) }; if ret == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(ret) } diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 6f7ca95ced..8f42d24878 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -2,7 +2,7 @@ use crate::{ AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, - builtins::{PyBaseExceptionRef, PyModule, PySet}, + builtins::{PyModule, PySet}, common::crt_fd, convert::{IntoPyException, ToPyException, ToPyObject}, function::{ArgumentError, FromArgs, FuncArgs}, @@ -22,24 +22,18 @@ pub(crate) fn fs_metadata>( #[cfg(unix)] impl crate::convert::IntoPyException for nix::Error { - fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef { + fn into_pyexception(self, vm: &VirtualMachine) -> crate::builtins::PyBaseExceptionRef { io::Error::from(self).into_pyexception(vm) } } #[cfg(unix)] impl crate::convert::IntoPyException for rustix::io::Errno { - fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef { + fn into_pyexception(self, vm: &VirtualMachine) -> crate::builtins::PyBaseExceptionRef { io::Error::from(self).into_pyexception(vm) } } -/// Convert the error stored in the `errno` variable into an Exception -#[inline] -pub fn errno_err(vm: &VirtualMachine) -> PyBaseExceptionRef { - crate::common::os::errno_io_error().to_pyexception(vm) -} - #[allow(dead_code)] #[derive(FromArgs, Default)] pub struct TargetIsDirectory { @@ -1484,7 +1478,7 @@ pub(super) mod _os { ) }; - usize::try_from(ret).map_err(|_| errno_err(vm)) + usize::try_from(ret).map_err(|_| vm.new_last_errno_error()) } #[pyfunction] diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 7409064668..3fbea58e45 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -28,9 +28,7 @@ pub mod module { convert::{IntoPyException, ToPyObject, TryFromObject}, function::{Either, KwArgs, OptionalArg}, ospath::{IOErrorBuilder, OsPath, OsPathOrFd}, - stdlib::os::{ - _os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, errno_err, fs_metadata, - }, + stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata}, types::{Constructor, Representable}, utils::ToCString, }; @@ -467,7 +465,11 @@ pub mod module { { let [] = args.dir_fd.0; let res = unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }; - if res < 0 { Err(errno_err(vm)) } else { Ok(()) } + if res < 0 { + Err(vm.new_last_errno_error()) + } else { + Ok(()) + } } } @@ -715,14 +717,22 @@ pub mod module { ) }, }; - if ret != 0 { Err(errno_err(vm)) } else { Ok(()) } + if ret != 0 { + Err(vm.new_last_errno_error()) + } else { + Ok(()) + } } #[cfg(target_vendor = "apple")] fn mknod(self, vm: &VirtualMachine) -> PyResult<()> { let [] = self.dir_fd.0; let ret = self._mknod(vm)?; - if ret != 0 { Err(errno_err(vm)) } else { Ok(()) } + if ret != 0 { + Err(vm.new_last_errno_error()) + } else { + Ok(()) + } } } @@ -739,7 +749,7 @@ pub mod module { Errno::clear(); let res = unsafe { libc::nice(increment) }; if res == -1 && Errno::last_raw() != 0 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(res) } @@ -750,7 +760,7 @@ pub mod module { fn sched_get_priority_max(policy: i32, vm: &VirtualMachine) -> PyResult { let max = unsafe { libc::sched_get_priority_max(policy) }; if max == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(max) } @@ -761,7 +771,7 @@ pub mod module { fn sched_get_priority_min(policy: i32, vm: &VirtualMachine) -> PyResult { let min = unsafe { libc::sched_get_priority_min(policy) }; if min == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(min) } @@ -852,7 +862,7 @@ pub mod module { fn sched_getscheduler(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult { let policy = unsafe { libc::sched_getscheduler(pid) }; if policy == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(policy) } @@ -886,7 +896,7 @@ pub mod module { let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?; let policy = unsafe { libc::sched_setscheduler(args.pid, args.policy, &libc_sched_param) }; if policy == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(policy) } @@ -902,7 +912,7 @@ pub mod module { let param = unsafe { let mut param = std::mem::MaybeUninit::uninit(); if -1 == libc::sched_getparam(pid, param.as_mut_ptr()) { - return Err(errno_err(vm)); + return Err(vm.new_last_errno_error()); } param.assume_init() }; @@ -937,7 +947,7 @@ pub mod module { let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?; let ret = unsafe { libc::sched_setparam(args.pid, &libc_sched_param) }; if ret == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(ret) } @@ -1719,7 +1729,7 @@ pub mod module { { let ret = unsafe { libc::kill(pid, sig as i32) }; if ret == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(()) } @@ -1762,7 +1772,11 @@ pub mod module { #[pyfunction] fn _fcopyfile(in_fd: i32, out_fd: i32, flags: i32, vm: &VirtualMachine) -> PyResult<()> { let ret = unsafe { fcopyfile(in_fd, out_fd, std::ptr::null_mut(), flags as u32) }; - if ret < 0 { Err(errno_err(vm)) } else { Ok(()) } + if ret < 0 { + Err(vm.new_last_errno_error()) + } else { + Ok(()) + } } #[pyfunction] @@ -1884,7 +1898,7 @@ pub mod module { Errno::clear(); let retval = unsafe { libc::getpriority(which, who) }; if Errno::last_raw() != 0 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(vm.ctx.new_int(retval).into()) } @@ -1900,7 +1914,7 @@ pub mod module { ) -> PyResult<()> { let retval = unsafe { libc::setpriority(which, who, priority) }; if retval == -1 { - Err(errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(()) } @@ -2325,7 +2339,7 @@ pub mod module { fn sysconf(name: SysconfName, vm: &VirtualMachine) -> PyResult { let r = unsafe { libc::sysconf(name.0) }; if r == -1 { - return Err(errno_err(vm)); + return Err(vm.new_last_errno_error()); } Ok(r) } @@ -2452,7 +2466,7 @@ pub mod module { flags.unwrap_or(0), ) .try_into() - .map_err(|_| errno_err(vm))?; + .map_err(|_| vm.new_last_os_error())?; buf.set_len(len); } Ok(buf) diff --git a/crates/vm/src/stdlib/signal.rs b/crates/vm/src/stdlib/signal.rs index bab45b3415..5c3d8825f7 100644 --- a/crates/vm/src/stdlib/signal.rs +++ b/crates/vm/src/stdlib/signal.rs @@ -296,7 +296,7 @@ pub(crate) mod _signal { signal::assert_in_range(signum, vm)?; let res = unsafe { siginterrupt(signum, flag) }; if res < 0 { - Err(crate::stdlib::os::errno_err(vm)) + Err(vm.new_last_errno_error()) } else { Ok(()) } From 967bff2747fcad549f3fdb72b4049721aff2802d Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Wed, 10 Dec 2025 00:08:54 +0900 Subject: [PATCH 3/3] enable ssl multithread test --- Lib/test/test_ssl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index e40628640a..f073def5bc 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2891,7 +2891,6 @@ def test_echo(self): 'Cannot create a client socket with a PROTOCOL_TLS_SERVER context', str(e.exception)) - @unittest.skip("TODO: RUSTPYTHON; Flaky on windows") @unittest.skipUnless(support.Py_GIL_DISABLED, "test is only useful if the GIL is disabled") def test_ssl_in_multiple_threads(self): # See GH-124984: OpenSSL is not thread safe.