Skip to content

Commit d7357f8

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

File tree

6 files changed

+157
-107
lines changed

6 files changed

+157
-107
lines changed

crates/vm/src/exceptions.rs

Lines changed: 110 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,6 +1206,7 @@ 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},
12091212
types::{Constructor, Initializer},
@@ -1212,6 +1215,113 @@ pub(super) mod types {
12121215
use itertools::Itertools;
12131216
use rustpython_common::str::UnicodeEscapeCodepoint;
12141217

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

crates/vm/src/ospath.rs

Lines changed: 10 additions & 57 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

@@ -144,62 +142,17 @@ impl OsPathOrFd<'_> {
144142
}
145143
}
146144

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,
145+
impl crate::exceptions::OSErrorBuilder {
146+
#[must_use]
147+
pub(crate) fn with_filename<'a>(
148+
error: &std::io::Error,
177149
filename: impl Into<OsPathOrFd<'a>>,
178150
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
151+
) -> crate::builtins::PyBaseExceptionRef {
152+
// TODO: return type to PyRef<PyOSError>
153+
use crate::exceptions::ToOSErrorBuilder;
154+
let builder = error.to_os_error_builder(vm);
155+
let builder = builder.filename(filename.into().filename(vm));
156+
builder.build(vm).upcast()
204157
}
205158
}

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)