Skip to content

Commit aa1af71

Browse files
authored
Merge pull request #1598 from palaviv/fix-exc-panic
Separate BaseException to __init__ and __new__
2 parents 72e12b4 + 637d678 commit aa1af71

File tree

2 files changed

+134
-32
lines changed

2 files changed

+134
-32
lines changed

tests/snippets/exceptions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,15 @@ def __eq__(self, other):
5151
assert exc.path == 'path'
5252
assert exc.msg == 'hello'
5353
assert exc.args == ('hello',)
54+
55+
56+
class NewException(Exception):
57+
58+
def __init__(self, value):
59+
self.value = value
60+
61+
62+
try:
63+
raise NewException("test")
64+
except NewException as e:
65+
assert e.value == "test"

vm/src/exceptions.rs

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,131 @@ use crate::obj::objtraceback::PyTracebackRef;
33
use crate::obj::objtuple::{PyTuple, PyTupleRef};
44
use crate::obj::objtype;
55
use crate::obj::objtype::PyClassRef;
6-
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol};
6+
use crate::pyobject::{
7+
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
8+
};
79
use crate::types::create_type;
810
use crate::vm::VirtualMachine;
911
use itertools::Itertools;
12+
use std::cell::{Cell, RefCell};
13+
use std::fmt;
1014
use std::fs::File;
1115
use std::io::{self, BufRead, BufReader, Write};
1216

13-
fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
14-
let exc_self = args.args[0].clone();
15-
let exc_args = vm.ctx.new_tuple(args.args[1..].to_vec());
16-
vm.set_attr(&exc_self, "args", exc_args)?;
17-
18-
// TODO: have an actual `traceback` object for __traceback__
19-
vm.set_attr(&exc_self, "__traceback__", vm.get_none())?;
20-
vm.set_attr(&exc_self, "__cause__", vm.get_none())?;
21-
vm.set_attr(&exc_self, "__context__", vm.get_none())?;
22-
vm.set_attr(&exc_self, "__suppress_context__", vm.new_bool(false))?;
23-
Ok(vm.get_none())
17+
#[pyclass]
18+
pub struct PyBaseException {
19+
traceback: RefCell<Option<PyTracebackRef>>,
20+
cause: RefCell<Option<PyObjectRef>>,
21+
context: RefCell<Option<PyObjectRef>>,
22+
suppress_context: Cell<bool>,
23+
args: RefCell<PyObjectRef>,
24+
}
25+
26+
impl fmt::Debug for PyBaseException {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
// TODO: implement more detailed, non-recursive Debug formatter
29+
f.write_str("PyBaseException")
30+
}
31+
}
32+
33+
pub type PyBaseExceptionRef = PyRef<PyBaseException>;
34+
35+
impl PyValue for PyBaseException {
36+
const HAVE_DICT: bool = true;
37+
38+
fn class(vm: &VirtualMachine) -> PyClassRef {
39+
vm.ctx.exceptions.base_exception_type.clone()
40+
}
41+
}
42+
43+
#[pyimpl]
44+
impl PyBaseException {
45+
#[pyslot(new)]
46+
fn tp_new(
47+
cls: PyClassRef,
48+
_args: PyFuncArgs,
49+
vm: &VirtualMachine,
50+
) -> PyResult<PyBaseExceptionRef> {
51+
PyBaseException {
52+
traceback: RefCell::new(None),
53+
cause: RefCell::new(None),
54+
context: RefCell::new(None),
55+
suppress_context: Cell::new(false),
56+
args: RefCell::new(vm.ctx.new_tuple(vec![])),
57+
}
58+
.into_ref_with_type(vm, cls)
59+
}
60+
61+
#[pymethod(name = "__init__")]
62+
fn init(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> {
63+
self.args.replace(vm.ctx.new_tuple(args.args.to_vec()));
64+
Ok(())
65+
}
66+
67+
#[pyproperty]
68+
fn args(&self, _vm: &VirtualMachine) -> PyObjectRef {
69+
self.args.borrow().clone()
70+
}
71+
72+
#[pyproperty(setter)]
73+
fn set_args(&self, args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
74+
self.args.replace(args);
75+
Ok(vm.get_none())
76+
}
77+
78+
#[pyproperty(name = "__traceback__")]
79+
fn get_traceback(&self, _vm: &VirtualMachine) -> Option<PyTracebackRef> {
80+
self.traceback.borrow().clone()
81+
}
82+
83+
#[pyproperty(name = "__traceback__", setter)]
84+
fn set_traceback(&self, traceback: Option<PyTracebackRef>, vm: &VirtualMachine) -> PyResult {
85+
self.traceback.replace(traceback);
86+
Ok(vm.get_none())
87+
}
88+
89+
#[pyproperty(name = "__cause__")]
90+
fn get_cause(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
91+
self.cause.borrow().clone()
92+
}
93+
94+
#[pyproperty(name = "__cause__", setter)]
95+
fn set_cause(&self, cause: Option<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
96+
self.cause.replace(cause);
97+
Ok(vm.get_none())
98+
}
99+
100+
#[pyproperty(name = "__context__")]
101+
fn get_context(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
102+
self.context.borrow().clone()
103+
}
104+
105+
#[pyproperty(name = "__context__", setter)]
106+
fn set_context(&self, context: Option<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
107+
self.context.replace(context);
108+
Ok(vm.get_none())
109+
}
110+
111+
#[pyproperty(name = "__suppress_context__")]
112+
fn get_suppress_context(&self, _vm: &VirtualMachine) -> bool {
113+
self.suppress_context.get()
114+
}
115+
116+
#[pyproperty(name = "__suppress_context__", setter)]
117+
fn set_suppress_context(&self, suppress_context: bool, vm: &VirtualMachine) -> PyResult {
118+
self.suppress_context.set(suppress_context);
119+
Ok(vm.get_none())
120+
}
121+
122+
#[pymethod]
123+
fn with_traceback(
124+
zelf: PyRef<Self>,
125+
tb: Option<PyTracebackRef>,
126+
_vm: &VirtualMachine,
127+
) -> PyResult {
128+
zelf.traceback.replace(tb);
129+
Ok(zelf.as_object().clone())
130+
}
24131
}
25132

26133
/// Print exception chain
@@ -208,19 +315,6 @@ fn exception_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
208315
Ok(vm.new_str(joined_str))
209316
}
210317

211-
fn exception_with_traceback(
212-
zelf: PyObjectRef,
213-
tb: Option<PyTracebackRef>,
214-
vm: &VirtualMachine,
215-
) -> PyResult {
216-
vm.set_attr(
217-
&zelf,
218-
"__traceback__",
219-
tb.map_or(vm.get_none(), |tb| tb.into_object()),
220-
)?;
221-
Ok(zelf)
222-
}
223-
224318
#[derive(Debug)]
225319
pub struct ExceptionZoo {
226320
pub arithmetic_error: PyClassRef,
@@ -416,10 +510,9 @@ impl ExceptionZoo {
416510
}
417511

418512
fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
419-
// TODO: call super().__init__(*args) instead
420-
exception_init(vm, args.clone())?;
421-
422513
let exc_self = args.args[0].clone();
514+
515+
vm.set_attr(&exc_self, "args", vm.ctx.new_tuple(args.args[1..].to_vec()))?;
423516
vm.set_attr(
424517
&exc_self,
425518
"name",
@@ -446,10 +539,7 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
446539

447540
pub fn init(context: &PyContext) {
448541
let base_exception_type = &context.exceptions.base_exception_type;
449-
extend_class!(context, base_exception_type, {
450-
"__init__" => context.new_rustfunc(exception_init),
451-
"with_traceback" => context.new_rustfunc(exception_with_traceback)
452-
});
542+
PyBaseException::extend_class(context, base_exception_type);
453543

454544
let exception_type = &context.exceptions.exception_type;
455545
extend_class!(context, exception_type, {

0 commit comments

Comments
 (0)