Skip to content

Commit 9cef05b

Browse files
committed
Integrate OSError creations into OSErrorBuilder
1 parent b35e1a8 commit 9cef05b

File tree

6 files changed

+160
-112
lines changed

6 files changed

+160
-112
lines changed

crates/vm/src/exceptions.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,8 @@ pub(crate) fn errno_to_exc_type(_errno: i32, _vm: &VirtualMachine) -> Option<&'s
11931193
None
11941194
}
11951195

1196+
pub(crate) use types::{OSErrorBuilder, ToOSErrorBuilder};
1197+
11961198
pub(super) mod types {
11971199
use crate::common::lock::PyRwLock;
11981200
use crate::object::{MaybeTraverse, Traverse, TraverseFn};
@@ -1204,14 +1206,135 @@ pub(super) mod types {
12041206
PyInt, PyStrRef, PyTupleRef, PyType, PyTypeRef, traceback::PyTracebackRef,
12051207
tuple::IntoPyTuple,
12061208
},
1209+
convert::ToPyObject,
12071210
convert::ToPyResult,
12081211
function::{ArgBytesLike, FuncArgs},
1212+
ospath::OsPathOrFd,
12091213
types::{Constructor, Initializer},
12101214
};
12111215
use crossbeam_utils::atomic::AtomicCell;
12121216
use itertools::Itertools;
12131217
use rustpython_common::str::UnicodeEscapeCodepoint;
12141218

1219+
pub(crate) trait ToOSErrorBuilder {
1220+
fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder;
1221+
}
1222+
1223+
pub struct OSErrorBuilder {
1224+
exc_type: PyTypeRef,
1225+
errno: Option<i32>,
1226+
strerror: Option<PyObjectRef>,
1227+
filename: Option<PyObjectRef>,
1228+
#[cfg(windows)]
1229+
winerror: Option<PyObjectRef>,
1230+
filename2: Option<PyObjectRef>,
1231+
}
1232+
1233+
impl OSErrorBuilder {
1234+
#[must_use]
1235+
pub fn with_subtype(
1236+
exc_type: PyTypeRef,
1237+
errno: Option<i32>,
1238+
strerror: impl ToPyObject,
1239+
vm: &VirtualMachine,
1240+
) -> Self {
1241+
let strerror = strerror.to_pyobject(vm);
1242+
Self {
1243+
exc_type,
1244+
errno,
1245+
strerror: Some(strerror.to_pyobject(vm)),
1246+
filename: None,
1247+
#[cfg(windows)]
1248+
winerror: None,
1249+
filename2: None,
1250+
}
1251+
}
1252+
#[must_use]
1253+
pub fn with_errno(errno: i32, strerror: impl ToPyObject, vm: &VirtualMachine) -> Self {
1254+
let exc_type = crate::exceptions::errno_to_exc_type(errno, vm)
1255+
.unwrap_or(vm.ctx.exceptions.os_error)
1256+
.to_owned();
1257+
Self::with_subtype(exc_type, Some(errno), strerror, vm)
1258+
}
1259+
1260+
// #[must_use]
1261+
// pub(crate) fn errno(mut self, errno: i32) -> Self {
1262+
// self.errno.replace(errno);
1263+
// self
1264+
// }
1265+
1266+
#[must_use]
1267+
pub(crate) fn filename(mut self, filename: PyObjectRef) -> Self {
1268+
self.filename.replace(filename);
1269+
self
1270+
}
1271+
1272+
#[must_use]
1273+
pub(crate) fn filename2(mut self, filename: PyObjectRef) -> Self {
1274+
self.filename2.replace(filename);
1275+
self
1276+
}
1277+
1278+
#[must_use]
1279+
#[cfg(windows)]
1280+
pub(crate) fn winerror(mut self, winerror: PyObjectRef) -> Self {
1281+
self.winerror.replace(winerror);
1282+
self
1283+
}
1284+
1285+
#[must_use]
1286+
pub(crate) fn with_filename<'a>(
1287+
error: &std::io::Error,
1288+
filename: impl Into<OsPathOrFd<'a>>,
1289+
vm: &VirtualMachine,
1290+
) -> PyBaseExceptionRef {
1291+
// TODO: return type to PyRef<PyOSError>
1292+
let builder = error.to_os_error_builder(vm);
1293+
let builder = builder.filename(filename.into().filename(vm));
1294+
builder.build(vm).upcast()
1295+
}
1296+
1297+
pub fn build(self, vm: &VirtualMachine) -> PyRef<PyOSError> {
1298+
let OSErrorBuilder {
1299+
exc_type,
1300+
errno,
1301+
strerror,
1302+
filename,
1303+
#[cfg(windows)]
1304+
winerror,
1305+
filename2,
1306+
} = self;
1307+
1308+
#[cfg(windows)]
1309+
let winerror = winerror.to_pyobject(vm);
1310+
#[cfg(not(windows))]
1311+
let winerror = vm.ctx.none();
1312+
1313+
let args = vec![
1314+
errno.to_pyobject(vm),
1315+
strerror.to_pyobject(vm),
1316+
filename.to_pyobject(vm),
1317+
winerror,
1318+
filename2.to_pyobject(vm),
1319+
];
1320+
1321+
let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm)
1322+
.expect("new_os_error usage error");
1323+
let os_error = payload
1324+
.into_ref_with_type(vm, exc_type)
1325+
.expect("new_os_error usage error");
1326+
PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm)
1327+
.expect("new_os_error usage error");
1328+
os_error
1329+
}
1330+
}
1331+
1332+
impl crate::convert::IntoPyException for OSErrorBuilder {
1333+
fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1334+
self.build(vm).upcast()
1335+
}
1336+
}
1337+
12151338
// Re-export exception group types from dedicated module
12161339
pub use crate::exception_group::types::PyBaseExceptionGroup;
12171340

crates/vm/src/ospath.rs

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ use rustpython_common::crt_fd;
22

33
use crate::{
44
PyObjectRef, PyResult, VirtualMachine,
5-
builtins::PyBaseExceptionRef,
65
convert::{IntoPyException, ToPyException, ToPyObject, TryFromObject},
76
function::FsPath,
8-
object::AsObject,
97
};
108
use std::path::{Path, PathBuf};
119

@@ -143,63 +141,3 @@ impl OsPathOrFd<'_> {
143141
}
144142
}
145143
}
146-
147-
// TODO: preserve the input `PyObjectRef` of filename and filename2 (Failing check `self.assertIs(err.filename, name, str(func)`)
148-
pub struct OSErrorBuilder<'a> {
149-
error: &'a std::io::Error,
150-
filename: Option<OsPathOrFd<'a>>,
151-
filename2: Option<OsPathOrFd<'a>>,
152-
}
153-
154-
impl<'a> OSErrorBuilder<'a> {
155-
pub const fn new(error: &'a std::io::Error) -> Self {
156-
Self {
157-
error,
158-
filename: None,
159-
filename2: None,
160-
}
161-
}
162-
163-
pub(crate) fn filename(mut self, filename: impl Into<OsPathOrFd<'a>>) -> Self {
164-
let filename = filename.into();
165-
self.filename.replace(filename);
166-
self
167-
}
168-
169-
pub(crate) fn filename2(mut self, filename: impl Into<OsPathOrFd<'a>>) -> Self {
170-
let filename = filename.into();
171-
self.filename2.replace(filename);
172-
self
173-
}
174-
175-
pub(crate) fn with_filename(
176-
error: &'a std::io::Error,
177-
filename: impl Into<OsPathOrFd<'a>>,
178-
vm: &VirtualMachine,
179-
) -> PyBaseExceptionRef {
180-
let zelf = OSErrorBuilder {
181-
error,
182-
filename: Some(filename.into()),
183-
filename2: None,
184-
};
185-
zelf.to_pyexception(vm)
186-
}
187-
}
188-
189-
impl ToPyException for OSErrorBuilder<'_> {
190-
fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
191-
let exc = self.error.to_pyexception(vm);
192-
193-
if let Some(filename) = &self.filename {
194-
exc.as_object()
195-
.set_attr("filename", filename.filename(vm), vm)
196-
.unwrap();
197-
}
198-
if let Some(filename2) = &self.filename2 {
199-
exc.as_object()
200-
.set_attr("filename2", filename2.filename(vm), vm)
201-
.unwrap();
202-
}
203-
exc
204-
}
205-
}

crates/vm/src/stdlib/io.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ use crate::{
1414
builtins::{PyBaseExceptionRef, PyModule},
1515
common::os::ErrorExt,
1616
convert::{IntoPyException, ToPyException},
17+
exceptions::{OSErrorBuilder, ToOSErrorBuilder},
1718
};
1819
pub use _io::{OpenArgs, io_open as open};
1920

20-
impl ToPyException for std::io::Error {
21-
fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
21+
impl ToOSErrorBuilder for std::io::Error {
22+
fn to_os_error_builder(&self, vm: &VirtualMachine) -> OSErrorBuilder {
2223
let errno = self.posix_errno();
2324
#[cfg(windows)]
2425
let msg = 'msg: {
@@ -53,23 +54,23 @@ impl ToPyException for std::io::Error {
5354
#[cfg(not(any(windows, unix)))]
5455
let msg = self.to_string();
5556

56-
#[allow(clippy::let_and_return)]
57-
let exc = vm.new_errno_error(errno, msg);
58-
#[cfg(windows)]
59-
{
60-
use crate::object::AsObject;
61-
let winerror = if let Some(winerror) = self.raw_os_error() {
62-
vm.new_pyobj(winerror)
63-
} else {
64-
vm.ctx.none()
65-
};
57+
#[allow(unused_mut)]
58+
let mut builder = OSErrorBuilder::with_errno(errno, msg, vm);
6659

67-
// FIXME: manual setup winerror due to lack of OSError.__init__ support
68-
exc.as_object()
69-
.set_attr("winerror", vm.new_pyobj(winerror), vm)
70-
.unwrap();
60+
#[cfg(windows)]
61+
if let Some(winerror) = self.raw_os_error() {
62+
use crate::convert::ToPyObject;
63+
builder = builder.winerror(winerror.to_pyobject(vm));
7164
}
72-
exc.upcast()
65+
66+
builder
67+
}
68+
}
69+
70+
impl ToPyException for std::io::Error {
71+
fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
72+
let builder = self.to_os_error_builder(vm);
73+
builder.into_pyexception(vm)
7374
}
7475
}
7576

@@ -4328,8 +4329,9 @@ mod fileio {
43284329
builtins::{PyBaseExceptionRef, PyUtf8Str, PyUtf8StrRef},
43294330
common::crt_fd,
43304331
convert::{IntoPyException, ToPyException},
4332+
exceptions::OSErrorBuilder,
43314333
function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption},
4332-
ospath::{OSErrorBuilder, OsPath, OsPathOrFd},
4334+
ospath::{OsPath, OsPathOrFd},
43334335
stdlib::os,
43344336
types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable},
43354337
};

crates/vm/src/stdlib/os.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ pub(super) mod _os {
153153
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
154154
builtins::{
155155
PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef,
156+
ToOSErrorBuilder,
156157
},
157158
common::{
158159
crt_fd,
@@ -161,8 +162,9 @@ pub(super) mod _os {
161162
suppress_iph,
162163
},
163164
convert::{IntoPyException, ToPyObject},
165+
exceptions::OSErrorBuilder,
164166
function::{ArgBytesLike, FsPath, FuncArgs, OptionalArg},
165-
ospath::{OSErrorBuilder, OsPath, OsPathOrFd, OutputMode},
167+
ospath::{OsPath, OsPathOrFd, OutputMode},
166168
protocol::PyIterReturn,
167169
recursion::ReprGuard,
168170
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter, Unconstructible},
@@ -1127,10 +1129,10 @@ pub(super) mod _os {
11271129
#[pyfunction(name = "replace")]
11281130
fn rename(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> {
11291131
fs::rename(&src.path, &dst.path).map_err(|err| {
1130-
OSErrorBuilder::new(&err)
1131-
.filename(src)
1132-
.filename2(dst)
1133-
.into_pyexception(vm)
1132+
let builder = err.to_os_error_builder(vm);
1133+
let builder = builder.filename(src.filename(vm));
1134+
let builder = builder.filename2(dst.filename(vm));
1135+
builder.build(vm).upcast()
11341136
})
11351137
}
11361138

@@ -1219,10 +1221,10 @@ pub(super) mod _os {
12191221
#[pyfunction]
12201222
fn link(src: OsPath, dst: OsPath, vm: &VirtualMachine) -> PyResult<()> {
12211223
fs::hard_link(&src.path, &dst.path).map_err(|err| {
1222-
OSErrorBuilder::new(&err)
1223-
.filename(src)
1224-
.filename2(dst)
1225-
.into_pyexception(vm)
1224+
let builder = err.to_os_error_builder(vm);
1225+
let builder = builder.filename(src.filename(vm));
1226+
let builder = builder.filename2(dst.filename(vm));
1227+
builder.build(vm).upcast()
12261228
})
12271229
}
12281230

crates/vm/src/stdlib/posix.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ pub mod module {
2626
AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
2727
builtins::{PyDictRef, PyInt, PyListRef, PyStrRef, PyTupleRef, PyType, PyUtf8StrRef},
2828
convert::{IntoPyException, ToPyObject, TryFromObject},
29+
exceptions::OSErrorBuilder,
2930
function::{Either, KwArgs, OptionalArg},
30-
ospath::{OSErrorBuilder, OsPath, OsPathOrFd},
31+
ospath::{OsPath, OsPathOrFd},
3132
stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata},
3233
types::{Constructor, Representable},
3334
utils::ToCString,
@@ -482,7 +483,7 @@ pub mod module {
482483
#[cfg(not(target_os = "redox"))]
483484
#[pyfunction]
484485
fn chroot(path: OsPath, vm: &VirtualMachine) -> PyResult<()> {
485-
use crate::ospath::OSErrorBuilder;
486+
use crate::exceptions::OSErrorBuilder;
486487

487488
nix::unistd::chroot(&*path.path).map_err(|err| {
488489
// Use `From<nix::Error> for io::Error` when it is available

crates/vm/src/vm/vm_new.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef,
2+
AsObject, Py, PyObject, PyObjectRef, PyRef,
33
builtins::{
44
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError, PyStrRef,
55
PyType, PyTypeRef,
@@ -8,9 +8,9 @@ use crate::{
88
tuple::{IntoPyTuple, PyTupleRef},
99
},
1010
convert::{ToPyException, ToPyObject},
11+
exceptions::OSErrorBuilder,
1112
function::{IntoPyNativeFn, PyMethodFlags},
1213
scope::Scope,
13-
types::Constructor,
1414
vm::VirtualMachine,
1515
};
1616
use rustpython_compiler_core::SourceLocation;
@@ -119,26 +119,8 @@ impl VirtualMachine {
119119
msg: impl ToPyObject,
120120
) -> PyRef<PyOSError> {
121121
debug_assert_eq!(exc_type.slots.basicsize, std::mem::size_of::<PyOSError>());
122-
let msg = msg.to_pyobject(self);
123-
124-
fn new_os_subtype_error_impl(
125-
vm: &VirtualMachine,
126-
exc_type: PyTypeRef,
127-
errno: Option<i32>,
128-
msg: PyObjectRef,
129-
) -> PyRef<PyOSError> {
130-
let args = match errno {
131-
Some(e) => vec![vm.new_pyobj(e), msg],
132-
None => vec![msg],
133-
};
134-
let payload =
135-
PyOSError::py_new(&exc_type, args.into(), vm).expect("new_os_error usage error");
136-
payload
137-
.into_ref_with_type(vm, exc_type)
138-
.expect("new_os_error usage error")
139-
}
140122

141-
new_os_subtype_error_impl(self, exc_type, errno, msg)
123+
OSErrorBuilder::with_subtype(exc_type, errno, msg, self).build(self)
142124
}
143125

144126
/// Instantiate an exception with no arguments.

0 commit comments

Comments
 (0)