From b35e1a868b0e5a3e2f7a6896d363ad494c114c6d Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 15 Dec 2025 20:21:03 +0900 Subject: [PATCH 1/5] {IO=>OS}ErrorBuilder --- crates/vm/src/ospath.rs | 8 ++++---- crates/vm/src/stdlib/io.rs | 10 +++++----- crates/vm/src/stdlib/os.rs | 36 +++++++++++++++++------------------ crates/vm/src/stdlib/posix.rs | 20 +++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/crates/vm/src/ospath.rs b/crates/vm/src/ospath.rs index add40f9b20c..7aaafa282e9 100644 --- a/crates/vm/src/ospath.rs +++ b/crates/vm/src/ospath.rs @@ -145,13 +145,13 @@ impl OsPathOrFd<'_> { } // TODO: preserve the input `PyObjectRef` of filename and filename2 (Failing check `self.assertIs(err.filename, name, str(func)`) -pub struct IOErrorBuilder<'a> { +pub struct OSErrorBuilder<'a> { error: &'a std::io::Error, filename: Option>, filename2: Option>, } -impl<'a> IOErrorBuilder<'a> { +impl<'a> OSErrorBuilder<'a> { pub const fn new(error: &'a std::io::Error) -> Self { Self { error, @@ -177,7 +177,7 @@ impl<'a> IOErrorBuilder<'a> { filename: impl Into>, vm: &VirtualMachine, ) -> PyBaseExceptionRef { - let zelf = IOErrorBuilder { + let zelf = OSErrorBuilder { error, filename: Some(filename.into()), filename2: None, @@ -186,7 +186,7 @@ impl<'a> IOErrorBuilder<'a> { } } -impl ToPyException for IOErrorBuilder<'_> { +impl ToPyException for OSErrorBuilder<'_> { fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef { let exc = self.error.to_pyexception(vm); diff --git a/crates/vm/src/stdlib/io.rs b/crates/vm/src/stdlib/io.rs index 3d67591d567..56441c6ba6d 100644 --- a/crates/vm/src/stdlib/io.rs +++ b/crates/vm/src/stdlib/io.rs @@ -4329,7 +4329,7 @@ mod fileio { common::crt_fd, convert::{IntoPyException, ToPyException}, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, - ospath::{IOErrorBuilder, OsPath, OsPathOrFd}, + ospath::{OSErrorBuilder, OsPath, OsPathOrFd}, stdlib::os, types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable}, }; @@ -4526,7 +4526,7 @@ mod fileio { let filename = OsPathOrFd::Path(path); match fd { Ok(fd) => (fd.into_raw(), Some(filename)), - Err(e) => return Err(IOErrorBuilder::with_filename(&e, filename, vm)), + Err(e) => return Err(OSErrorBuilder::with_filename(&e, filename, vm)), } } }; @@ -4541,7 +4541,7 @@ mod fileio { #[cfg(windows)] { if let Err(err) = fd_fstat { - return Err(IOErrorBuilder::with_filename(&err, filename, vm)); + return Err(OSErrorBuilder::with_filename(&err, filename, vm)); } } #[cfg(any(unix, target_os = "wasi"))] @@ -4550,12 +4550,12 @@ mod fileio { Ok(status) => { if (status.st_mode & libc::S_IFMT) == libc::S_IFDIR { let err = std::io::Error::from_raw_os_error(libc::EISDIR); - return Err(IOErrorBuilder::with_filename(&err, filename, vm)); + return Err(OSErrorBuilder::with_filename(&err, filename, vm)); } } Err(err) => { if err.raw_os_error() == Some(libc::EBADF) { - return Err(IOErrorBuilder::with_filename(&err, filename, vm)); + return Err(OSErrorBuilder::with_filename(&err, filename, vm)); } } } diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index f6ffd66759d..7d7b6f47d22 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -162,7 +162,7 @@ pub(super) mod _os { }, convert::{IntoPyException, ToPyObject}, function::{ArgBytesLike, FsPath, FuncArgs, OptionalArg}, - ospath::{IOErrorBuilder, OsPath, OsPathOrFd, OutputMode}, + ospath::{OSErrorBuilder, OsPath, OsPathOrFd, OutputMode}, protocol::PyIterReturn, recursion::ReprGuard, types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter, Unconstructible}, @@ -263,7 +263,7 @@ pub(super) mod _os { crt_fd::open(&name, flags, mode) } }; - fd.map_err(|err| IOErrorBuilder::with_filename(&err, name, vm)) + fd.map_err(|err| OSErrorBuilder::with_filename(&err, name, vm)) } #[pyfunction] @@ -316,7 +316,7 @@ pub(super) mod _os { } else { fs::remove_file(&path) }; - res.map_err(|err| IOErrorBuilder::with_filename(&err, path, vm)) + res.map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } #[cfg(not(windows))] @@ -334,7 +334,7 @@ pub(super) mod _os { let res = unsafe { libc::mkdirat(fd, c_path.as_ptr(), mode as _) }; return if res < 0 { let err = crate::common::os::errno_io_error(); - Err(IOErrorBuilder::with_filename(&err, path, vm)) + Err(OSErrorBuilder::with_filename(&err, path, vm)) } else { Ok(()) }; @@ -344,7 +344,7 @@ pub(super) mod _os { let res = unsafe { libc::mkdir(c_path.as_ptr(), mode as _) }; if res < 0 { let err = crate::common::os::errno_io_error(); - return Err(IOErrorBuilder::with_filename(&err, path, vm)); + return Err(OSErrorBuilder::with_filename(&err, path, vm)); } Ok(()) } @@ -357,7 +357,7 @@ pub(super) mod _os { #[pyfunction] fn rmdir(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; - fs::remove_dir(&path).map_err(|err| IOErrorBuilder::with_filename(&err, path, vm)) + fs::remove_dir(&path).map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } const LISTDIR_FD: bool = cfg!(all(unix, not(target_os = "redox"))); @@ -373,13 +373,13 @@ pub(super) mod _os { let dir_iter = match fs::read_dir(&path) { Ok(iter) => iter, Err(err) => { - return Err(IOErrorBuilder::with_filename(&err, path, vm)); + return Err(OSErrorBuilder::with_filename(&err, path, vm)); } }; dir_iter .map(|entry| match entry { Ok(entry_path) => Ok(path.mode.process_path(entry_path.file_name(), vm)), - Err(err) => Err(IOErrorBuilder::with_filename(&err, path.clone(), vm)), + Err(err) => Err(OSErrorBuilder::with_filename(&err, path.clone(), vm)), }) .collect::>()? } @@ -546,7 +546,7 @@ pub(super) mod _os { let mode = path.mode; let [] = dir_fd.0; let path = - fs::read_link(&path).map_err(|err| IOErrorBuilder::with_filename(&err, path, vm))?; + fs::read_link(&path).map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))?; Ok(mode.process_path(path, vm)) } @@ -859,7 +859,7 @@ pub(super) mod _os { fn scandir(path: OptionalArg, vm: &VirtualMachine) -> PyResult { let path = path.unwrap_or_else(|| OsPath::new_str(".")); let entries = fs::read_dir(&path.path) - .map_err(|err| IOErrorBuilder::with_filename(&err, path.clone(), vm))?; + .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; Ok(ScandirIterator { entries: PyRwLock::new(Some(entries)), mode: path.mode, @@ -1084,7 +1084,7 @@ pub(super) mod _os { vm: &VirtualMachine, ) -> PyResult { let stat = stat_inner(file.clone(), dir_fd, follow_symlinks) - .map_err(|err| IOErrorBuilder::with_filename(&err, file, vm))? + .map_err(|err| OSErrorBuilder::with_filename(&err, file, vm))? .ok_or_else(|| crate::exceptions::cstring_error(vm))?; Ok(StatResultData::from_stat(&stat, vm).to_pyobject(vm)) } @@ -1115,7 +1115,7 @@ pub(super) mod _os { #[pyfunction] fn chdir(path: OsPath, vm: &VirtualMachine) -> PyResult<()> { env::set_current_dir(&path.path) - .map_err(|err| IOErrorBuilder::with_filename(&err, path, vm)) + .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)) } #[pyfunction] @@ -1127,7 +1127,7 @@ pub(super) mod _os { #[pyfunction(name = "replace")] fn rename(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> { fs::rename(&src.path, &dst.path).map_err(|err| { - IOErrorBuilder::new(&err) + OSErrorBuilder::new(&err) .filename(src) .filename2(dst) .into_pyexception(vm) @@ -1219,7 +1219,7 @@ pub(super) mod _os { #[pyfunction] fn link(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> { fs::hard_link(&src.path, &dst.path).map_err(|err| { - IOErrorBuilder::new(&err) + OSErrorBuilder::new(&err) .filename(src) .filename2(dst) .into_pyexception(vm) @@ -1334,7 +1334,7 @@ pub(super) mod _os { ) }; if ret < 0 { - Err(IOErrorBuilder::with_filename( + Err(OSErrorBuilder::with_filename( &io::Error::last_os_error(), path_for_err, vm, @@ -1385,14 +1385,14 @@ pub(super) mod _os { .write(true) .custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS) .open(&path) - .map_err(|err| IOErrorBuilder::with_filename(&err, path.clone(), vm))?; + .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; let ret = unsafe { FileSystem::SetFileTime(f.as_raw_handle() as _, std::ptr::null(), &acc, &modif) }; if ret == 0 { - Err(IOErrorBuilder::with_filename( + Err(OSErrorBuilder::with_filename( &io::Error::last_os_error(), path, vm, @@ -1565,7 +1565,7 @@ pub(super) mod _os { error: std::io::Error, path: OsPath, ) -> crate::builtins::PyBaseExceptionRef { - IOErrorBuilder::with_filename(&error, path, vm) + OSErrorBuilder::with_filename(&error, path, vm) } let path = OsPath::try_from_object(vm, path)?; diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 680e9914a03..9d5a9789076 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -27,7 +27,7 @@ pub mod module { builtins::{PyDictRef, PyInt, PyListRef, PyStrRef, PyTupleRef, PyType, PyUtf8StrRef}, convert::{IntoPyException, ToPyObject, TryFromObject}, function::{Either, KwArgs, OptionalArg}, - ospath::{IOErrorBuilder, OsPath, OsPathOrFd}, + ospath::{OSErrorBuilder, OsPath, OsPathOrFd}, stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata}, types::{Constructor, Representable}, utils::ToCString, @@ -412,7 +412,7 @@ pub mod module { } let metadata = - metadata.map_err(|err| IOErrorBuilder::with_filename(&err, path.clone(), vm))?; + metadata.map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))?; let user_id = metadata.uid(); let group_id = metadata.gid(); @@ -482,12 +482,12 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] fn chroot(path: OsPath, vm: &VirtualMachine) -> PyResult<()> { - use crate::ospath::IOErrorBuilder; + use crate::ospath::OSErrorBuilder; nix::unistd::chroot(&*path.path).map_err(|err| { // Use `From for io::Error` when it is available let err = io::Error::from_raw_os_error(err as i32); - IOErrorBuilder::with_filename(&err, path, vm) + OSErrorBuilder::with_filename(&err, path, vm) }) } @@ -533,7 +533,7 @@ pub mod module { .map_err(|err| { // Use `From for io::Error` when it is available let err = io::Error::from_raw_os_error(err as i32); - IOErrorBuilder::with_filename(&err, path, vm) + OSErrorBuilder::with_filename(&err, path, vm) }) } @@ -1031,7 +1031,7 @@ pub mod module { permissions.set_mode(mode); fs::set_permissions(&path, permissions) }; - body().map_err(|err| IOErrorBuilder::with_filename(&err, err_path, vm)) + body().map_err(|err| OSErrorBuilder::with_filename(&err, err_path, vm)) } #[cfg(not(target_os = "redox"))] @@ -1093,7 +1093,7 @@ pub mod module { Ok(()) } else { let err = std::io::Error::last_os_error(); - Err(IOErrorBuilder::with_filename(&err, path, vm)) + Err(OSErrorBuilder::with_filename(&err, path, vm)) } } @@ -1554,7 +1554,7 @@ pub mod module { }; if let Err(err) = ret { let err = err.into(); - return Err(IOErrorBuilder::with_filename(&err, self.path, vm)); + return Err(OSErrorBuilder::with_filename(&err, self.path, vm)); } } } @@ -1653,7 +1653,7 @@ pub mod module { nix::spawn::posix_spawn(&*path, &file_actions, &attrp, &args, &env) }; ret.map(Into::into) - .map_err(|err| IOErrorBuilder::with_filename(&err.into(), self.path, vm)) + .map_err(|err| OSErrorBuilder::with_filename(&err.into(), self.path, vm)) } } @@ -2126,7 +2126,7 @@ pub mod module { if Errno::last_raw() == 0 { Ok(None) } else { - Err(IOErrorBuilder::with_filename( + Err(OSErrorBuilder::with_filename( &io::Error::from(Errno::last()), path, vm, From d7357f8bdcc2e6679938fb6196325a1e1c48be10 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 15 Dec 2025 20:40:07 +0900 Subject: [PATCH 2/5] Integrate OSError creations into OSErrorBuilder --- crates/vm/src/exceptions.rs | 110 ++++++++++++++++++++++++++++++++++ crates/vm/src/ospath.rs | 67 ++++----------------- crates/vm/src/stdlib/io.rs | 38 ++++++------ crates/vm/src/stdlib/os.rs | 20 ++++--- crates/vm/src/stdlib/posix.rs | 5 +- crates/vm/src/vm/vm_new.rs | 24 +------- 6 files changed, 157 insertions(+), 107 deletions(-) diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 04dd78fb448..600d80df1b1 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -1193,6 +1193,8 @@ pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'s None } +pub(crate) use types::{OSErrorBuilder, ToOSErrorBuilder}; + pub(super) mod types { use crate::common::lock::PyRwLock; use crate::object::{MaybeTraverse, Traverse, TraverseFn}; @@ -1204,6 +1206,7 @@ pub(super) mod types { PyInt, PyStrRef, PyTupleRef, PyType, PyTypeRef, traceback::PyTracebackRef, tuple::IntoPyTuple, }, + convert::ToPyObject, convert::ToPyResult, function::{ArgBytesLike, FuncArgs}, types::{Constructor, Initializer}, @@ -1212,6 +1215,113 @@ pub(super) mod types { use itertools::Itertools; use rustpython_common::str::UnicodeEscapeCodepoint; + pub(crate) trait ToOSErrorBuilder { + fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder; + } + + pub struct OSErrorBuilder { + exc_type: PyTypeRef, + errno: Option, + strerror: Option, + filename: Option, + #[cfg(windows)] + winerror: Option, + filename2: Option, + } + + impl OSErrorBuilder { + #[must_use] + pub fn with_subtype( + exc_type: PyTypeRef, + errno: Option, + strerror: impl ToPyObject, + vm: &VirtualMachine, + ) -> Self { + let strerror = strerror.to_pyobject(vm); + Self { + exc_type, + errno, + strerror: Some(strerror.to_pyobject(vm)), + filename: None, + #[cfg(windows)] + winerror: None, + filename2: None, + } + } + #[must_use] + pub fn with_errno(errno: i32, strerror: impl ToPyObject, vm: &VirtualMachine) -> Self { + let exc_type = crate::exceptions::errno_to_exc_type(errno, vm) + .unwrap_or(vm.ctx.exceptions.os_error) + .to_owned(); + Self::with_subtype(exc_type, Some(errno), strerror, vm) + } + + // #[must_use] + // pub(crate) fn errno(mut self, errno: i32) -> Self { + // self.errno.replace(errno); + // self + // } + + #[must_use] + pub(crate) fn filename(mut self, filename: PyObjectRef) -> Self { + self.filename.replace(filename); + self + } + + #[must_use] + pub(crate) fn filename2(mut self, filename: PyObjectRef) -> Self { + self.filename2.replace(filename); + self + } + + #[must_use] + #[cfg(windows)] + pub(crate) fn winerror(mut self, winerror: PyObjectRef) -> Self { + self.winerror.replace(winerror); + self + } + + pub fn build(self, vm: &VirtualMachine) -> PyRef { + let OSErrorBuilder { + exc_type, + errno, + strerror, + filename, + #[cfg(windows)] + winerror, + filename2, + } = self; + + #[cfg(windows)] + let winerror = winerror.to_pyobject(vm); + #[cfg(not(windows))] + let winerror = vm.ctx.none(); + + let args = vec![ + errno.to_pyobject(vm), + strerror.to_pyobject(vm), + filename.to_pyobject(vm), + winerror, + filename2.to_pyobject(vm), + ]; + + let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm) + .expect("new_os_error usage error"); + let os_error = payload + .into_ref_with_type(vm, exc_type) + .expect("new_os_error usage error"); + PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm) + .expect("new_os_error usage error"); + os_error + } + } + + impl crate::convert::IntoPyException for OSErrorBuilder { + fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef { + self.build(vm).upcast() + } + } + // Re-export exception group types from dedicated module pub use crate::exception_group::types::PyBaseExceptionGroup; diff --git a/crates/vm/src/ospath.rs b/crates/vm/src/ospath.rs index 7aaafa282e9..9fca53d869c 100644 --- a/crates/vm/src/ospath.rs +++ b/crates/vm/src/ospath.rs @@ -2,10 +2,8 @@ use rustpython_common::crt_fd; use crate::{ PyObjectRef, PyResult, VirtualMachine, - builtins::PyBaseExceptionRef, convert::{IntoPyException, ToPyException, ToPyObject, TryFromObject}, function::FsPath, - object::AsObject, }; use std::path::{Path, PathBuf}; @@ -144,62 +142,17 @@ impl OsPathOrFd<'_> { } } -// TODO: preserve the input `PyObjectRef` of filename and filename2 (Failing check `self.assertIs(err.filename, name, str(func)`) -pub struct OSErrorBuilder<'a> { - error: &'a std::io::Error, - filename: Option>, - filename2: Option>, -} - -impl<'a> OSErrorBuilder<'a> { - pub const fn new(error: &'a std::io::Error) -> Self { - Self { - error, - filename: None, - filename2: None, - } - } - - pub(crate) fn filename(mut self, filename: impl Into>) -> Self { - let filename = filename.into(); - self.filename.replace(filename); - self - } - - pub(crate) fn filename2(mut self, filename: impl Into>) -> Self { - let filename = filename.into(); - self.filename2.replace(filename); - self - } - - pub(crate) fn with_filename( - error: &'a std::io::Error, +impl crate::exceptions::OSErrorBuilder { + #[must_use] + pub(crate) fn with_filename<'a>( + error: &std::io::Error, filename: impl Into>, vm: &VirtualMachine, - ) -> PyBaseExceptionRef { - let zelf = OSErrorBuilder { - error, - filename: Some(filename.into()), - filename2: None, - }; - zelf.to_pyexception(vm) - } -} - -impl ToPyException for OSErrorBuilder<'_> { - fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef { - let exc = self.error.to_pyexception(vm); - - if let Some(filename) = &self.filename { - exc.as_object() - .set_attr("filename", filename.filename(vm), vm) - .unwrap(); - } - if let Some(filename2) = &self.filename2 { - exc.as_object() - .set_attr("filename2", filename2.filename(vm), vm) - .unwrap(); - } - exc + ) -> crate::builtins::PyBaseExceptionRef { + // TODO: return type to PyRef + use crate::exceptions::ToOSErrorBuilder; + let builder = error.to_os_error_builder(vm); + let builder = builder.filename(filename.into().filename(vm)); + builder.build(vm).upcast() } } diff --git a/crates/vm/src/stdlib/io.rs b/crates/vm/src/stdlib/io.rs index 56441c6ba6d..56bcbefddeb 100644 --- a/crates/vm/src/stdlib/io.rs +++ b/crates/vm/src/stdlib/io.rs @@ -14,11 +14,12 @@ use crate::{ builtins::{PyBaseExceptionRef, PyModule}, common::os::ErrorExt, convert::{IntoPyException, ToPyException}, + exceptions::{OSErrorBuilder, ToOSErrorBuilder}, }; pub use _io::{OpenArgs, io_open as open}; -impl ToPyException for std::io::Error { - fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef { +impl ToOSErrorBuilder for std::io::Error { + fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder { let errno = self.posix_errno(); #[cfg(windows)] let msg = 'msg: { @@ -53,23 +54,23 @@ impl ToPyException for std::io::Error { #[cfg(not(any(windows, unix)))] let msg = self.to_string(); - #[allow(clippy::let_and_return)] - let exc = vm.new_errno_error(errno, msg); - #[cfg(windows)] - { - use crate::object::AsObject; - let winerror = if let Some(winerror) = self.raw_os_error() { - vm.new_pyobj(winerror) - } else { - vm.ctx.none() - }; + #[allow(unused_mut)] + let mut builder = OSErrorBuilder::with_errno(errno, msg, vm); - // FIXME: manual setup winerror due to lack of OSError.__init__ support - exc.as_object() - .set_attr("winerror", vm.new_pyobj(winerror), vm) - .unwrap(); + #[cfg(windows)] + if let Some(winerror) = self.raw_os_error() { + use crate::convert::ToPyObject; + builder = builder.winerror(winerror.to_pyobject(vm)); } - exc.upcast() + + builder + } +} + +impl ToPyException for std::io::Error { + fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef { + let builder = self.to_os_error_builder(vm); + builder.into_pyexception(vm) } } @@ -4328,8 +4329,9 @@ mod fileio { builtins::{PyBaseExceptionRef, PyUtf8Str, PyUtf8StrRef}, common::crt_fd, convert::{IntoPyException, ToPyException}, + exceptions::OSErrorBuilder, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, - ospath::{OSErrorBuilder, OsPath, OsPathOrFd}, + ospath::{OsPath, OsPathOrFd}, stdlib::os, types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable}, }; diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 7d7b6f47d22..a698cae059c 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -153,6 +153,7 @@ pub(super) mod _os { AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, builtins::{ PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef, + ToOSErrorBuilder, }, common::{ crt_fd, @@ -161,8 +162,9 @@ pub(super) mod _os { suppress_iph, }, convert::{IntoPyException, ToPyObject}, + exceptions::OSErrorBuilder, function::{ArgBytesLike, FsPath, FuncArgs, OptionalArg}, - ospath::{OSErrorBuilder, OsPath, OsPathOrFd, OutputMode}, + ospath::{OsPath, OsPathOrFd, OutputMode}, protocol::PyIterReturn, recursion::ReprGuard, types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter, Unconstructible}, @@ -1127,10 +1129,10 @@ pub(super) mod _os { #[pyfunction(name = "replace")] fn rename(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> { fs::rename(&src.path, &dst.path).map_err(|err| { - OSErrorBuilder::new(&err) - .filename(src) - .filename2(dst) - .into_pyexception(vm) + let builder = err.to_os_error_builder(vm); + let builder = builder.filename(src.filename(vm)); + let builder = builder.filename2(dst.filename(vm)); + builder.build(vm).upcast() }) } @@ -1219,10 +1221,10 @@ pub(super) mod _os { #[pyfunction] fn link(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> { fs::hard_link(&src.path, &dst.path).map_err(|err| { - OSErrorBuilder::new(&err) - .filename(src) - .filename2(dst) - .into_pyexception(vm) + let builder = err.to_os_error_builder(vm); + let builder = builder.filename(src.filename(vm)); + let builder = builder.filename2(dst.filename(vm)); + builder.build(vm).upcast() }) } diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index 9d5a9789076..f9a2728187d 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -26,8 +26,9 @@ pub mod module { AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, builtins::{PyDictRef, PyInt, PyListRef, PyStrRef, PyTupleRef, PyType, PyUtf8StrRef}, convert::{IntoPyException, ToPyObject, TryFromObject}, + exceptions::OSErrorBuilder, function::{Either, KwArgs, OptionalArg}, - ospath::{OSErrorBuilder, OsPath, OsPathOrFd}, + ospath::{OsPath, OsPathOrFd}, stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata}, types::{Constructor, Representable}, utils::ToCString, @@ -482,7 +483,7 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] fn chroot(path: OsPath, vm: &VirtualMachine) -> PyResult<()> { - use crate::ospath::OSErrorBuilder; + use crate::exceptions::OSErrorBuilder; nix::unistd::chroot(&*path.path).map_err(|err| { // Use `From for io::Error` when it is available diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index 36481e5dbe3..6d0e983c844 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -1,5 +1,5 @@ use crate::{ - AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, + AsObject, Py, PyObject, PyObjectRef, PyRef, builtins::{ PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError, PyStrRef, PyType, PyTypeRef, @@ -8,9 +8,9 @@ use crate::{ tuple::{IntoPyTuple, PyTupleRef}, }, convert::{ToPyException, ToPyObject}, + exceptions::OSErrorBuilder, function::{IntoPyNativeFn, PyMethodFlags}, scope::Scope, - types::Constructor, vm::VirtualMachine, }; use rustpython_compiler_core::SourceLocation; @@ -119,26 +119,8 @@ impl VirtualMachine { msg: impl ToPyObject, ) -> PyRef { debug_assert_eq!(exc_type.slots.basicsize, std::mem::size_of::()); - let msg = msg.to_pyobject(self); - - fn new_os_subtype_error_impl( - vm: &VirtualMachine, - exc_type: PyTypeRef, - errno: Option, - msg: PyObjectRef, - ) -> PyRef { - let args = match errno { - Some(e) => vec![vm.new_pyobj(e), msg], - None => vec![msg], - }; - let payload = - PyOSError::py_new(&exc_type, args.into(), vm).expect("new_os_error usage error"); - payload - .into_ref_with_type(vm, exc_type) - .expect("new_os_error usage error") - } - new_os_subtype_error_impl(self, exc_type, errno, msg) + OSErrorBuilder::with_subtype(exc_type, errno, msg, self).build(self) } /// Instantiate an exception with no arguments. From 51b634738aab81bfd2ee26adc888028d826e8cd5 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 16 Dec 2025 19:50:52 +0900 Subject: [PATCH 3/5] fix macos --- crates/vm/src/exceptions.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 600d80df1b1..fb5b26de5cc 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -1292,18 +1292,22 @@ pub(super) mod types { filename2, } = self; - #[cfg(windows)] - let winerror = winerror.to_pyobject(vm); - #[cfg(not(windows))] - let winerror = vm.ctx.none(); - - let args = vec![ - errno.to_pyobject(vm), - strerror.to_pyobject(vm), - filename.to_pyobject(vm), - winerror, - filename2.to_pyobject(vm), - ]; + let args = if let Some(errno) = errno { + #[cfg(windows)] + let winerror = winerror.to_pyobject(vm); + #[cfg(not(windows))] + let winerror = vm.ctx.none(); + + vec![ + errno.to_pyobject(vm), + strerror.to_pyobject(vm), + filename.to_pyobject(vm), + winerror, + filename2.to_pyobject(vm), + ] + } else { + vec![strerror.to_pyobject(vm)] + }; let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm) .expect("new_os_error usage error"); From 6fbd7b5b30c9dc84748daaad08e04d355dfddf80 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 16 Dec 2025 21:02:11 +0900 Subject: [PATCH 4/5] apply review --- crates/vm/src/exceptions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index fb5b26de5cc..2b725085bec 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -1241,7 +1241,7 @@ pub(super) mod types { Self { exc_type, errno, - strerror: Some(strerror.to_pyobject(vm)), + strerror: Some(strerror), filename: None, #[cfg(windows)] winerror: None, From ccc153e65e65284281fc580cf302064b9748a773 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 16 Dec 2025 21:02:46 +0900 Subject: [PATCH 5/5] try review suggestion --- crates/vm/src/stdlib/posix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index f9a2728187d..cfe605733d3 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -487,8 +487,8 @@ pub mod module { nix::unistd::chroot(&*path.path).map_err(|err| { // Use `From for io::Error` when it is available - let err = io::Error::from_raw_os_error(err as i32); - OSErrorBuilder::with_filename(&err, path, vm) + let io_err: io::Error = err.into(); + OSErrorBuilder::with_filename(&io_err, path, vm) }) }