Skip to content

Commit 65bdfc3

Browse files
authored
Integrate OSError creations into OSErrorBuilder (#6443)
1 parent 272b36d commit 65bdfc3

File tree

6 files changed

+189
-135
lines changed

6 files changed

+189
-135
lines changed

crates/vm/src/exceptions.rs

Lines changed: 114 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,117 @@ 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),
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+
let args = if let Some(errno) = errno {
1296+
#[cfg(windows)]
1297+
let winerror = winerror.to_pyobject(vm);
1298+
#[cfg(not(windows))]
1299+
let winerror = vm.ctx.none();
1300+
1301+
vec![
1302+
errno.to_pyobject(vm),
1303+
strerror.to_pyobject(vm),
1304+
filename.to_pyobject(vm),
1305+
winerror,
1306+
filename2.to_pyobject(vm),
1307+
]
1308+
} else {
1309+
vec![strerror.to_pyobject(vm)]
1310+
};
1311+
1312+
let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm)
1313+
.expect("new_os_error usage error");
1314+
let os_error = payload
1315+
.into_ref_with_type(vm, exc_type)
1316+
.expect("new_os_error usage error");
1317+
PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm)
1318+
.expect("new_os_error usage error");
1319+
os_error
1320+
}
1321+
}
1322+
1323+
impl crate::convert::IntoPyException for OSErrorBuilder {
1324+
fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef {
1325+
self.build(vm).upcast()
1326+
}
1327+
}
1328+
12151329
// Re-export exception group types from dedicated module
12161330
pub use crate::exception_group::types::PyBaseExceptionGroup;
12171331

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 IOErrorBuilder<'a> {
149-
error: &'a std::io::Error,
150-
filename: Option<OsPathOrFd<'a>>,
151-
filename2: Option<OsPathOrFd<'a>>,
152-
}
153-
154-
impl<'a> IOErrorBuilder<'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 = IOErrorBuilder {
181-
error,
182-
filename: Some(filename.into()),
183-
filename2: None,
184-
};
185-
zelf.to_pyexception(vm)
186-
}
187-
}
188-
189-
impl ToPyException for IOErrorBuilder<'_> {
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: 24 additions & 22 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::{IOErrorBuilder, OsPath, OsPathOrFd},
4334+
ospath::{OsPath, OsPathOrFd},
43334335
stdlib::os,
43344336
types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable},
43354337
};
@@ -4526,7 +4528,7 @@ mod fileio {
45264528
let filename = OsPathOrFd::Path(path);
45274529
match fd {
45284530
Ok(fd) => (fd.into_raw(), Some(filename)),
4529-
Err(e) => return Err(IOErrorBuilder::with_filename(&e, filename, vm)),
4531+
Err(e) => return Err(OSErrorBuilder::with_filename(&e, filename, vm)),
45304532
}
45314533
}
45324534
};
@@ -4541,7 +4543,7 @@ mod fileio {
45414543
#[cfg(windows)]
45424544
{
45434545
if let Err(err) = fd_fstat {
4544-
return Err(IOErrorBuilder::with_filename(&err, filename, vm));
4546+
return Err(OSErrorBuilder::with_filename(&err, filename, vm));
45454547
}
45464548
}
45474549
#[cfg(any(unix, target_os = "wasi"))]
@@ -4550,12 +4552,12 @@ mod fileio {
45504552
Ok(status) => {
45514553
if (status.st_mode & libc::S_IFMT) == libc::S_IFDIR {
45524554
let err = std::io::Error::from_raw_os_error(libc::EISDIR);
4553-
return Err(IOErrorBuilder::with_filename(&err, filename, vm));
4555+
return Err(OSErrorBuilder::with_filename(&err, filename, vm));
45544556
}
45554557
}
45564558
Err(err) => {
45574559
if err.raw_os_error() == Some(libc::EBADF) {
4558-
return Err(IOErrorBuilder::with_filename(&err, filename, vm));
4560+
return Err(OSErrorBuilder::with_filename(&err, filename, vm));
45594561
}
45604562
}
45614563
}

0 commit comments

Comments
 (0)