From 96fdaab0c1e683fcc3053e099d91eddd8c9816da Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sat, 16 Nov 2019 13:45:57 +0200 Subject: [PATCH 1/5] Separate BaseException to __init__ and __new__ --- tests/snippets/exceptions.py | 12 ++++++++++++ vm/src/exceptions.rs | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/tests/snippets/exceptions.py b/tests/snippets/exceptions.py index fed8d32093..60ba5b23a3 100644 --- a/tests/snippets/exceptions.py +++ b/tests/snippets/exceptions.py @@ -51,3 +51,15 @@ assert exc.name == 'name' assert exc.path == 'path' assert exc.msg == 'hello' assert exc.args == ('hello',) + + +class NewException(Exception): + + def __init__(self, value): + self.value = value + + +try: + raise NewException("test") +except NewException as e: + assert e.value == "test" diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index cc2f5cad3f..cf5d46466b 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -3,23 +3,46 @@ use crate::obj::objtraceback::PyTracebackRef; use crate::obj::objtuple::{PyTuple, PyTupleRef}; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::types::create_type; use crate::vm::VirtualMachine; use itertools::Itertools; use std::fs::File; use std::io::{self, BufRead, BufReader, Write}; +#[derive(Debug)] +pub struct PyBaseException {} +pub type PyBaseExceptionRef = PyRef; + +impl PyValue for PyBaseException { + const HAVE_DICT: bool = true; + + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.ctx.exceptions.base_exception_type.clone() + } +} + +impl PyBaseExceptionRef { + fn new( + cls: PyClassRef, + _args: PyFuncArgs, + vm: &VirtualMachine, + ) -> PyResult { + let zelf = PyBaseException {}.into_ref_with_type(vm, cls)?; + let exc = zelf.clone().into_object(); + vm.set_attr(&exc, "__traceback__", vm.get_none())?; + vm.set_attr(&exc, "__cause__", vm.get_none())?; + vm.set_attr(&exc, "__context__", vm.get_none())?; + vm.set_attr(&exc, "__suppress_context__", vm.new_bool(false))?; + Ok(zelf) + } +} + fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let exc_self = args.args[0].clone(); let exc_args = vm.ctx.new_tuple(args.args[1..].to_vec()); vm.set_attr(&exc_self, "args", exc_args)?; - // TODO: have an actual `traceback` object for __traceback__ - vm.set_attr(&exc_self, "__traceback__", vm.get_none())?; - vm.set_attr(&exc_self, "__cause__", vm.get_none())?; - vm.set_attr(&exc_self, "__context__", vm.get_none())?; - vm.set_attr(&exc_self, "__suppress_context__", vm.new_bool(false))?; Ok(vm.get_none()) } @@ -419,6 +442,7 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let base_exception_type = &context.exceptions.base_exception_type; extend_class!(context, base_exception_type, { + "__new__" => context.new_rustfunc(PyBaseExceptionRef::new), "__init__" => context.new_rustfunc(exception_init), "with_traceback" => context.new_rustfunc(exception_with_traceback) }); From 144a4728f0ed9047c8eb13913dab560f6faecd33 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 17 Nov 2019 18:47:25 +0200 Subject: [PATCH 2/5] Move BaseException propeties to PyBaseException --- vm/src/exceptions.rs | 79 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index cf5d46466b..7839bae65f 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -3,15 +3,24 @@ use crate::obj::objtraceback::PyTracebackRef; use crate::obj::objtuple::{PyTuple, PyTupleRef}; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{ + IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, +}; use crate::types::create_type; use crate::vm::VirtualMachine; use itertools::Itertools; +use std::cell::{Cell, RefCell}; use std::fs::File; use std::io::{self, BufRead, BufReader, Write}; +#[pyclass] #[derive(Debug)] -pub struct PyBaseException {} +pub struct PyBaseException { + traceback: RefCell>, + cause: RefCell>, + context: RefCell>, + suppress_context: Cell, +} pub type PyBaseExceptionRef = PyRef; impl PyValue for PyBaseException { @@ -22,19 +31,65 @@ impl PyValue for PyBaseException { } } -impl PyBaseExceptionRef { - fn new( +#[pyimpl] +impl PyBaseException { + #[pyslot(new)] + fn tp_new( cls: PyClassRef, _args: PyFuncArgs, vm: &VirtualMachine, ) -> PyResult { - let zelf = PyBaseException {}.into_ref_with_type(vm, cls)?; - let exc = zelf.clone().into_object(); - vm.set_attr(&exc, "__traceback__", vm.get_none())?; - vm.set_attr(&exc, "__cause__", vm.get_none())?; - vm.set_attr(&exc, "__context__", vm.get_none())?; - vm.set_attr(&exc, "__suppress_context__", vm.new_bool(false))?; - Ok(zelf) + PyBaseException { + traceback: RefCell::new(None), + cause: RefCell::new(None), + context: RefCell::new(None), + suppress_context: Cell::new(false), + } + .into_ref_with_type(vm, cls) + } + + #[pyproperty(name = "__traceback__")] + fn get_traceback(&self, _vm: &VirtualMachine) -> Option { + self.traceback.borrow().clone() + } + + #[pyproperty(name = "__traceback__", setter)] + fn set_traceback(&self, traceback: Option, vm: &VirtualMachine) -> PyResult { + self.traceback.replace(traceback); + Ok(vm.get_none()) + } + + #[pyproperty(name = "__cause__")] + fn get_cause(&self, _vm: &VirtualMachine) -> Option { + self.cause.borrow().clone() + } + + #[pyproperty(name = "__cause__", setter)] + fn set_cause(&self, cause: Option, vm: &VirtualMachine) -> PyResult { + self.cause.replace(cause); + Ok(vm.get_none()) + } + + #[pyproperty(name = "__context__")] + fn get_context(&self, _vm: &VirtualMachine) -> Option { + self.context.borrow().clone() + } + + #[pyproperty(name = "__context__", setter)] + fn set_context(&self, context: Option, vm: &VirtualMachine) -> PyResult { + self.context.replace(context); + Ok(vm.get_none()) + } + + #[pyproperty(name = "__suppress_context__")] + fn get_suppress_context(&self, _vm: &VirtualMachine) -> bool { + self.suppress_context.get() + } + + #[pyproperty(name = "__suppress_context__", setter)] + fn set_suppress_context(&self, suppress_context: bool, vm: &VirtualMachine) -> PyResult { + self.suppress_context.set(suppress_context); + Ok(vm.get_none()) } } @@ -441,8 +496,8 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let base_exception_type = &context.exceptions.base_exception_type; + PyBaseException::extend_class(context, base_exception_type); extend_class!(context, base_exception_type, { - "__new__" => context.new_rustfunc(PyBaseExceptionRef::new), "__init__" => context.new_rustfunc(exception_init), "with_traceback" => context.new_rustfunc(exception_with_traceback) }); From c15140e0fe0d953a1c93d0436cd0c095286fca59 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 17 Nov 2019 18:56:42 +0200 Subject: [PATCH 3/5] Move with_traceback --- vm/src/exceptions.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 7839bae65f..0df709bb13 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -91,6 +91,16 @@ impl PyBaseException { self.suppress_context.set(suppress_context); Ok(vm.get_none()) } + + #[pymethod] + fn with_traceback( + zelf: PyRef, + tb: Option, + _vm: &VirtualMachine, + ) -> PyResult { + zelf.traceback.replace(tb); + Ok(zelf.as_object().clone()) + } } fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -286,19 +296,6 @@ fn exception_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(joined_str)) } -fn exception_with_traceback( - zelf: PyObjectRef, - tb: Option, - vm: &VirtualMachine, -) -> PyResult { - vm.set_attr( - &zelf, - "__traceback__", - tb.map_or(vm.get_none(), |tb| tb.into_object()), - )?; - Ok(zelf) -} - #[derive(Debug)] pub struct ExceptionZoo { pub arithmetic_error: PyClassRef, @@ -499,7 +496,6 @@ pub fn init(context: &PyContext) { PyBaseException::extend_class(context, base_exception_type); extend_class!(context, base_exception_type, { "__init__" => context.new_rustfunc(exception_init), - "with_traceback" => context.new_rustfunc(exception_with_traceback) }); let exception_type = &context.exceptions.exception_type; From f4a8427dc9a565bfbdf5f939473cdaf2f0fab08a Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Sun, 17 Nov 2019 19:14:45 +0200 Subject: [PATCH 4/5] Move args to PyBaseException --- vm/src/exceptions.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 0df709bb13..5f87fbba5f 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -20,6 +20,7 @@ pub struct PyBaseException { cause: RefCell>, context: RefCell>, suppress_context: Cell, + args: RefCell, } pub type PyBaseExceptionRef = PyRef; @@ -44,10 +45,28 @@ impl PyBaseException { cause: RefCell::new(None), context: RefCell::new(None), suppress_context: Cell::new(false), + args: RefCell::new(vm.ctx.new_tuple(vec![])), } .into_ref_with_type(vm, cls) } + #[pymethod(name = "__init__")] + fn init(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> { + self.args.replace(vm.ctx.new_tuple(args.args.to_vec())); + Ok(()) + } + + #[pyproperty] + fn args(&self, _vm: &VirtualMachine) -> PyObjectRef { + self.args.borrow().clone() + } + + #[pyproperty(setter)] + fn set_args(&self, args: PyObjectRef, vm: &VirtualMachine) -> PyResult { + self.args.replace(args); + Ok(vm.get_none()) + } + #[pyproperty(name = "__traceback__")] fn get_traceback(&self, _vm: &VirtualMachine) -> Option { self.traceback.borrow().clone() @@ -103,14 +122,6 @@ impl PyBaseException { } } -fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let exc_self = args.args[0].clone(); - let exc_args = vm.ctx.new_tuple(args.args[1..].to_vec()); - vm.set_attr(&exc_self, "args", exc_args)?; - - Ok(vm.get_none()) -} - /// Print exception chain pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) { let _ = write_exception(io::stdout(), vm, exc); @@ -463,10 +474,9 @@ impl ExceptionZoo { } fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - // TODO: call super().__init__(*args) instead - exception_init(vm, args.clone())?; - let exc_self = args.args[0].clone(); + + vm.set_attr(&exc_self, "args", vm.ctx.new_tuple(args.args[1..].to_vec()))?; vm.set_attr( &exc_self, "name", @@ -494,9 +504,6 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn init(context: &PyContext) { let base_exception_type = &context.exceptions.base_exception_type; PyBaseException::extend_class(context, base_exception_type); - extend_class!(context, base_exception_type, { - "__init__" => context.new_rustfunc(exception_init), - }); let exception_type = &context.exceptions.exception_type; extend_class!(context, exception_type, { From 637d678ad796740e66728ca3ad83d2945a1c1445 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Tue, 19 Nov 2019 18:32:27 +0200 Subject: [PATCH 5/5] Print less in PyBaseException Debug --- vm/src/exceptions.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 5f87fbba5f..80dce97394 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -10,11 +10,11 @@ use crate::types::create_type; use crate::vm::VirtualMachine; use itertools::Itertools; use std::cell::{Cell, RefCell}; +use std::fmt; use std::fs::File; use std::io::{self, BufRead, BufReader, Write}; #[pyclass] -#[derive(Debug)] pub struct PyBaseException { traceback: RefCell>, cause: RefCell>, @@ -22,6 +22,14 @@ pub struct PyBaseException { suppress_context: Cell, args: RefCell, } + +impl fmt::Debug for PyBaseException { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("PyBaseException") + } +} + pub type PyBaseExceptionRef = PyRef; impl PyValue for PyBaseException {