Skip to content

Commit 3a36e55

Browse files
authored
Merge pull request RustPython#4191 from dvermd/oserror_reduce
Add OSError.__reduce__
2 parents 54d7c34 + 4c6cb00 commit 3a36e55

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

extra_tests/snippets/builtin_exceptions.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import builtins
22
import platform
3+
import pickle
34
import sys
45

56
def exceptions_eq(e1, e2):
@@ -273,6 +274,14 @@ class SubError(MyError):
273274
assert w.filename == None
274275
assert w.filename2 == None
275276
assert str(w) == ""
277+
x = pickle.loads(pickle.dumps(w, 4))
278+
assert type(w) == type(x)
279+
assert x.errno == None
280+
assert not sys.platform.startswith("win") or x.winerror == None
281+
assert x.strerror == None
282+
assert x.filename == None
283+
assert x.filename2 == None
284+
assert str(x) == ""
276285

277286
w = OSError(0)
278287
assert w.errno == None
@@ -281,6 +290,14 @@ class SubError(MyError):
281290
assert w.filename == None
282291
assert w.filename2 == None
283292
assert str(w) == "0"
293+
x = pickle.loads(pickle.dumps(w, 4))
294+
assert type(w) == type(x)
295+
assert x.errno == None
296+
assert not sys.platform.startswith("win") or x.winerror == None
297+
assert x.strerror == None
298+
assert x.filename == None
299+
assert x.filename2 == None
300+
assert str(x) == "0"
284301

285302
w = OSError('foo')
286303
assert w.errno == None
@@ -289,6 +306,14 @@ class SubError(MyError):
289306
assert w.filename == None
290307
assert w.filename2 == None
291308
assert str(w) == "foo"
309+
x = pickle.loads(pickle.dumps(w, 4))
310+
assert type(w) == type(x)
311+
assert x.errno == None
312+
assert not sys.platform.startswith("win") or x.winerror == None
313+
assert x.strerror == None
314+
assert x.filename == None
315+
assert x.filename2 == None
316+
assert str(x) == "foo"
292317

293318
w = OSError('a', 'b', 'c', 'd', 'e', 'f')
294319
assert w.errno == None
@@ -297,6 +322,14 @@ class SubError(MyError):
297322
assert w.filename == None
298323
assert w.filename2 == None
299324
assert str(w) == "('a', 'b', 'c', 'd', 'e', 'f')"
325+
x = pickle.loads(pickle.dumps(w, 4))
326+
assert type(w) == type(x)
327+
assert x.errno == None
328+
assert not sys.platform.startswith("win") or x.winerror == None
329+
assert x.strerror == None
330+
assert x.filename == None
331+
assert x.filename2 == None
332+
assert str(x) == "('a', 'b', 'c', 'd', 'e', 'f')"
300333

301334
# Custom `__new__` and `__init__`:
302335
assert ImportError.__init__.__qualname__ == 'ImportError.__init__'

vm/src/exceptions.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use self::types::{PyBaseException, PyBaseExceptionRef};
2+
use crate::builtins::tuple::IntoPyTuple;
23
use crate::common::lock::PyRwLock;
34
use crate::{
45
builtins::{
@@ -773,6 +774,7 @@ impl ExceptionZoo {
773774
// second exception filename
774775
"filename2" => ctx.none(),
775776
"__str__" => ctx.new_method("__str__", excs.os_error, os_error_str),
777+
"__reduce__" => ctx.new_method("__reduce__", excs.os_error, os_error_reduce),
776778
});
777779
// TODO: this isn't really accurate
778780
#[cfg(windows)]
@@ -907,6 +909,42 @@ fn os_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrR
907909
}
908910
}
909911

912+
fn os_error_reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef {
913+
let args = exc.args();
914+
let obj = exc.as_object().to_owned();
915+
let mut result: Vec<PyObjectRef> = vec![obj.class().clone().into()];
916+
917+
if args.len() >= 2 && args.len() <= 5 {
918+
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
919+
let errno = exc.get_arg(0).unwrap();
920+
let msg = exc.get_arg(1).unwrap();
921+
922+
if let Ok(filename) = obj.get_attr("filename", vm) {
923+
if !vm.is_none(&filename) {
924+
let mut args_reduced: Vec<PyObjectRef> = vec![errno, msg, filename];
925+
926+
if let Ok(filename2) = obj.get_attr("filename2", vm) {
927+
if !vm.is_none(&filename2) {
928+
args_reduced.push(filename2);
929+
}
930+
}
931+
result.push(args_reduced.into_pytuple(vm).into());
932+
} else {
933+
result.push(vm.new_tuple((errno, msg)).into());
934+
}
935+
} else {
936+
result.push(vm.new_tuple((errno, msg)).into());
937+
}
938+
} else {
939+
result.push(args.into());
940+
}
941+
942+
if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) {
943+
result.push(dict.into());
944+
}
945+
result.into_pytuple(vm)
946+
}
947+
910948
fn system_exit_code(exc: PyBaseExceptionRef) -> Option<PyObjectRef> {
911949
exc.args.read().first().map(|code| {
912950
match_class!(match code {

0 commit comments

Comments
 (0)