Merge pull request #1644 from RustPython/coolreader18/exc-cleanup

Use PyBaseExceptionRef in places where we expect exceptions
This commit is contained in:
Noah
2019-12-29 17:17:26 -05:00
committed by GitHub
37 changed files with 506 additions and 444 deletions

12
Cargo.lock generated
View File

@@ -598,7 +598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.8.0"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -633,7 +633,7 @@ dependencies = [
"diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lalrpop-util 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1255,7 +1255,7 @@ version = "0.1.1"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython-bytecode 0.1.1",
@@ -1316,7 +1316,7 @@ dependencies = [
"hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1368,8 +1368,6 @@ dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython-compiler 0.1.1",
"rustpython-parser 0.1.1",
"rustpython-vm 0.1.1",
@@ -2226,7 +2224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79296f72d53a89096cbc9a88c9547ee8dfe793388674620e2207593d370550ac"
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79"
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"

View File

@@ -7,9 +7,9 @@ extern crate log;
use clap::{App, AppSettings, Arg, ArgMatches};
use rustpython_compiler::compile;
use rustpython_vm::{
exceptions::print_exception,
match_class,
obj::{objint::PyInt, objtuple::PyTuple, objtype},
print_exception,
obj::{objint::PyInt, objtype},
pyobject::{ItemProtocol, PyResult},
scope::Scope,
util, InitParameter, PySettings, VirtualMachine,
@@ -51,8 +51,7 @@ fn main() {
// See if any exception leaked out:
if let Err(err) = res {
if objtype::isinstance(&err, &vm.ctx.exceptions.system_exit) {
let args = vm.get_attribute(err.clone(), "args").unwrap();
let args = args.downcast::<PyTuple>().expect("'args' must be a tuple");
let args = err.args();
match args.elements.len() {
0 => return,
1 => match_class!(match args.elements[0].clone() {

View File

@@ -5,9 +5,9 @@ mod rustyline_helper;
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
use rustpython_parser::error::ParseErrorType;
use rustpython_vm::{
exceptions::{print_exception, PyBaseExceptionRef},
obj::objtype,
print_exception,
pyobject::{ItemProtocol, PyObjectRef, PyResult},
pyobject::{ItemProtocol, PyResult},
scope::Scope,
VirtualMachine,
};
@@ -16,7 +16,7 @@ use readline::{Readline, ReadlineResult};
enum ShellExecResult {
Ok,
PyErr(PyObjectRef),
PyErr(PyBaseExceptionRef),
Continue,
}
@@ -118,9 +118,8 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
ReadlineResult::Interrupt => {
continuing = false;
full_input.clear();
let keyboard_interrupt = vm
.new_empty_exception(vm.ctx.exceptions.keyboard_interrupt.clone())
.unwrap();
let keyboard_interrupt =
vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.clone());
Err(keyboard_interrupt)
}
ReadlineResult::EOF => {

View File

@@ -250,7 +250,23 @@ try:
except ZeroDivisionError as ex:
raise NameError from ex
except NameError as ex2:
pass
assert isinstance(ex2.__cause__, ZeroDivisionError)
else:
assert False, "no raise"
try:
try:
try:
raise ZeroDivisionError
except ZeroDivisionError as ex:
raise NameError from ex
except NameError:
raise
except NameError as ex2:
assert isinstance(ex2.__cause__, ZeroDivisionError)
else:
assert False, "no raise"
# the else clause requires at least one except clause:

View File

@@ -49,7 +49,7 @@ chrono = { version = "=0.4.9", features = ["wasmbind"] }
unicode-xid = "0.2.0"
lazy_static = "^1.0.1"
lexical = "4"
itertools = "^0.8.0"
itertools = "0.8"
hex = "0.4.0"
hexf-parse = "0.1.0"
indexmap = "1.0.2"

View File

@@ -12,6 +12,7 @@ use num_traits::{Signed, ToPrimitive, Zero};
#[cfg(feature = "rustpython-compiler")]
use rustpython_compiler::compile;
use crate::exceptions::PyBaseExceptionRef;
use crate::function::{single_or_tuple_any, Args, KwArgs, OptionalArg, PyFuncArgs};
use crate::obj::objbool::{self, IntoPyBool};
use crate::obj::objbyteinner::PyByteInner;
@@ -290,7 +291,7 @@ fn builtin_format(
})
}
fn catch_attr_exception<T>(ex: PyObjectRef, default: T, vm: &VirtualMachine) -> PyResult<T> {
fn catch_attr_exception<T>(ex: PyBaseExceptionRef, default: T, vm: &VirtualMachine) -> PyResult<T> {
if objtype::isinstance(&ex, &vm.ctx.exceptions.attribute_error) {
Ok(default)
} else {
@@ -628,7 +629,7 @@ impl Printer for std::io::StdoutLock<'_> {
pub fn builtin_exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
let code = exit_code_arg.unwrap_or_else(|| vm.new_int(0));
Err(vm.new_exception_obj(vm.ctx.exceptions.system_exit.clone(), vec![code])?)
Err(vm.new_exception(vm.ctx.exceptions.system_exit.clone(), vec![code]))
}
pub fn builtin_print(objects: Args, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> {

View File

@@ -1,11 +1,11 @@
use crate::function::PyFuncArgs;
use crate::obj::objiter;
use crate::obj::objnone::PyNone;
use crate::obj::objstr::{PyString, PyStringRef};
use crate::obj::objtraceback::PyTracebackRef;
use crate::obj::objtuple::{PyTuple, PyTupleRef};
use crate::obj::objtype;
use crate::obj::objtype::PyClassRef;
use crate::obj::objtype::{self, PyClass, PyClassRef};
use crate::pyobject::{
IdProtocol, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
};
use crate::types::create_type;
@@ -19,8 +19,8 @@ use std::io::{self, BufRead, BufReader, Write};
#[pyclass]
pub struct PyBaseException {
traceback: RefCell<Option<PyTracebackRef>>,
cause: RefCell<Option<PyObjectRef>>,
context: RefCell<Option<PyObjectRef>>,
cause: RefCell<Option<PyBaseExceptionRef>>,
context: RefCell<Option<PyBaseExceptionRef>>,
suppress_context: Cell<bool>,
args: RefCell<PyTupleRef>,
}
@@ -44,20 +44,19 @@ impl PyValue for PyBaseException {
#[pyimpl]
impl PyBaseException {
#[pyslot(new)]
fn tp_new(
cls: PyClassRef,
_args: PyFuncArgs,
vm: &VirtualMachine,
) -> PyResult<PyBaseExceptionRef> {
pub(crate) fn new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyBaseException {
PyBaseException {
traceback: RefCell::new(None),
cause: RefCell::new(None),
context: RefCell::new(None),
suppress_context: Cell::new(false),
args: RefCell::new(PyTuple::from(vec![]).into_ref(vm)),
args: RefCell::new(PyTuple::from(args).into_ref(vm)),
}
.into_ref_with_type(vm, cls)
}
#[pyslot(new)]
fn tp_new(cls: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
PyBaseException::new(args.args, vm).into_ref_with_type(vm, cls)
}
#[pymethod(name = "__init__")]
@@ -66,8 +65,8 @@ impl PyBaseException {
Ok(())
}
#[pyproperty]
fn args(&self, _vm: &VirtualMachine) -> PyTupleRef {
#[pyproperty(name = "args")]
fn get_args(&self, _vm: &VirtualMachine) -> PyTupleRef {
self.args.borrow().clone()
}
@@ -84,27 +83,27 @@ impl PyBaseException {
}
#[pyproperty(name = "__traceback__", setter)]
fn set_traceback(&self, traceback: Option<PyTracebackRef>, _vm: &VirtualMachine) {
fn setter_traceback(&self, traceback: Option<PyTracebackRef>, _vm: &VirtualMachine) {
self.traceback.replace(traceback);
}
#[pyproperty(name = "__cause__")]
fn get_cause(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
fn get_cause(&self, _vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
self.cause.borrow().clone()
}
#[pyproperty(name = "__cause__", setter)]
fn set_cause(&self, cause: Option<PyObjectRef>, _vm: &VirtualMachine) {
fn setter_cause(&self, cause: Option<PyBaseExceptionRef>, _vm: &VirtualMachine) {
self.cause.replace(cause);
}
#[pyproperty(name = "__context__")]
fn get_context(&self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
fn get_context(&self, _vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
self.context.borrow().clone()
}
#[pyproperty(name = "__context__", setter)]
fn set_context(&self, context: Option<PyObjectRef>, _vm: &VirtualMachine) {
fn setter_context(&self, context: Option<PyBaseExceptionRef>, _vm: &VirtualMachine) {
self.context.replace(context);
}
@@ -127,51 +126,88 @@ impl PyBaseException {
zelf.traceback.replace(tb);
Ok(zelf.as_object().clone())
}
#[pymethod(name = "__str__")]
fn str(&self, vm: &VirtualMachine) -> PyStringRef {
let str_args = exception_args_as_string(vm, self.args(), false);
match str_args.into_iter().exactly_one() {
Err(i) if i.len() == 0 => PyString::from("").into_ref(vm),
Ok(s) => s,
Err(i) => PyString::from(format!("({})", i.format(", "))).into_ref(vm),
}
}
#[pymethod(name = "__repr__")]
fn repr(zelf: PyRef<Self>, vm: &VirtualMachine) -> String {
let repr_args = exception_args_as_string(vm, zelf.args(), false);
let cls = zelf.class();
match repr_args.into_iter().exactly_one() {
Ok(one) => format!("{}({},)", cls.name, one),
Err(i) => format!("{}({})", cls.name, i.format(", ")),
}
}
pub fn args(&self) -> PyTupleRef {
self.args.borrow().clone()
}
pub fn traceback(&self) -> Option<PyTracebackRef> {
self.traceback.borrow().clone()
}
pub fn set_traceback(&self, tb: Option<PyTracebackRef>) {
self.traceback.replace(tb);
}
pub fn cause(&self) -> Option<PyBaseExceptionRef> {
self.cause.borrow().clone()
}
pub fn set_cause(&self, cause: Option<PyBaseExceptionRef>) {
self.cause.replace(cause);
}
pub fn context(&self) -> Option<PyBaseExceptionRef> {
self.context.borrow().clone()
}
pub fn set_context(&self, context: Option<PyBaseExceptionRef>) {
self.context.replace(context);
}
}
/// Print exception chain
pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
let _ = write_exception(io::stdout(), vm, exc);
pub fn print_exception(vm: &VirtualMachine, exc: &PyBaseExceptionRef) {
let stdout = io::stdout();
let mut stdout = stdout.lock();
let _ = write_exception(&mut stdout, vm, exc);
}
pub fn write_exception<W: Write>(
mut output: W,
output: &mut W,
vm: &VirtualMachine,
exc: &PyObjectRef,
exc: &PyBaseExceptionRef,
) -> io::Result<()> {
let mut had_cause = false;
if let Ok(cause) = vm.get_attribute(exc.clone(), "__cause__") {
if !vm.get_none().is(&cause) {
had_cause = true;
print_exception(vm, &cause);
writeln!(
output,
"\nThe above exception was the direct cause of the following exception:\n"
)?;
}
if let Some(cause) = exc.cause() {
write_exception(output, vm, &cause)?;
writeln!(
output,
"\nThe above exception was the direct cause of the following exception:\n"
)?;
} else if let Some(context) = exc.context() {
write_exception(output, vm, &context)?;
writeln!(
output,
"\nDuring handling of the above exception, another exception occurred:\n"
)?;
}
if !had_cause {
if let Ok(context) = vm.get_attribute(exc.clone(), "__context__") {
if !vm.get_none().is(&context) {
print_exception(vm, &context);
writeln!(
output,
"\nDuring handling of the above exception, another exception occurred:\n"
)?;
}
}
}
print_exception_inner(output, vm, exc)
write_exception_inner(output, vm, exc)
}
fn print_source_line<W: Write>(mut output: W, filename: &str, lineno: usize) -> io::Result<()> {
fn print_source_line<W: Write>(output: &mut W, filename: &str, lineno: usize) -> io::Result<()> {
// TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/master/Python/traceback.c#L393
// TODO: support different encodings
let file = match File::open(filename) {
Ok(file) => file,
Err(_) => {
return Ok(());
}
Err(_) => return Ok(()),
};
let file = BufReader::new(file);
@@ -189,7 +225,7 @@ fn print_source_line<W: Write>(mut output: W, filename: &str, lineno: usize) ->
}
/// Print exception occurrence location from traceback element
fn print_traceback_entry<W: Write>(mut output: W, tb_entry: &PyTracebackRef) -> io::Result<()> {
fn write_traceback_entry<W: Write>(output: &mut W, tb_entry: &PyTracebackRef) -> io::Result<()> {
let filename = tb_entry.frame.code.source_path.to_string();
writeln!(
output,
@@ -202,32 +238,23 @@ fn print_traceback_entry<W: Write>(mut output: W, tb_entry: &PyTracebackRef) ->
}
/// Print exception with traceback
pub fn print_exception_inner<W: Write>(
mut output: W,
pub fn write_exception_inner<W: Write>(
output: &mut W,
vm: &VirtualMachine,
exc: &PyObjectRef,
exc: &PyBaseExceptionRef,
) -> io::Result<()> {
if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") {
if objtype::isinstance(&tb, &vm.ctx.traceback_type()) {
writeln!(output, "Traceback (most recent call last):")?;
let mut tb: PyTracebackRef = tb.downcast().expect(" must be a traceback object");
loop {
print_traceback_entry(&mut output, &tb)?;
tb = match &tb.next {
Some(tb) => tb.clone(),
None => break,
};
}
if let Some(tb) = exc.traceback.borrow().clone() {
writeln!(output, "Traceback (most recent call last):")?;
let mut tb = Some(&tb);
while let Some(traceback) = tb {
write_traceback_entry(output, traceback)?;
tb = traceback.next.as_ref();
}
} else {
writeln!(output, "No traceback set on exception")?;
}
let varargs = vm
.get_attribute(exc.clone(), "args")
.unwrap()
.downcast::<PyTuple>()
.expect("'args' must be a tuple");
let varargs = exc.args();
let args_repr = exception_args_as_string(vm, varargs, true);
let exc_name = exc.class().name.clone();
@@ -247,71 +274,117 @@ fn exception_args_as_string(
vm: &VirtualMachine,
varargs: PyTupleRef,
str_single: bool,
) -> Vec<String> {
) -> Vec<PyStringRef> {
match varargs.elements.len() {
0 => vec![],
1 => {
let args0_repr = if str_single {
vm.to_pystr(&varargs.elements[0])
.unwrap_or_else(|_| "<element str() failed>".to_string())
vm.to_str(&varargs.elements[0])
.unwrap_or_else(|_| PyString::from("<element str() failed>").into_ref(vm))
} else {
vm.to_repr(&varargs.elements[0])
.map(|s| s.as_str().to_owned())
.unwrap_or_else(|_| "<element repr() failed>".to_string())
.unwrap_or_else(|_| PyString::from("<element repr() failed>").into_ref(vm))
};
vec![args0_repr]
}
_ => varargs
.elements
.iter()
.map(|vararg| match vm.to_repr(vararg) {
Ok(arg_repr) => arg_repr.as_str().to_string(),
Err(_) => "<element repr() failed>".to_string(),
.map(|vararg| {
vm.to_repr(vararg)
.unwrap_or_else(|_| PyString::from("<element repr() failed>").into_ref(vm))
})
.collect(),
}
}
fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))]
);
let args = vm
.get_attribute(exc.clone(), "args")
.unwrap()
.downcast::<PyTuple>()
.expect("'args' must be a tuple");
let args_str = exception_args_as_string(vm, args, false);
let joined_str = match args_str.len() {
0 => "".to_string(),
1 => args_str.into_iter().next().unwrap(),
_ => format!("({})", args_str.into_iter().format(", ")),
};
Ok(vm.new_str(joined_str))
#[derive(Clone)]
pub enum ExceptionCtor {
Class(PyClassRef),
Instance(PyBaseExceptionRef),
}
fn exception_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))]
);
let args = vm
.get_attribute(exc.clone(), "args")
.unwrap()
.downcast::<PyTuple>()
.expect("'args' must be a tuple");
let args_repr = exception_args_as_string(vm, args, false);
impl TryFromObject for ExceptionCtor {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
obj.downcast::<PyClass>()
.and_then(|cls| {
if objtype::issubclass(&cls, &vm.ctx.exceptions.base_exception_type) {
Ok(Self::Class(cls))
} else {
Err(cls.into_object())
}
})
.or_else(|obj| obj.downcast::<PyBaseException>().map(Self::Instance))
.map_err(|obj| {
vm.new_type_error(format!(
"exceptions must be classes or instances deriving from BaseException, not {}",
obj.class().name
))
})
}
}
let exc_name = exc.class().name.clone();
let joined_str = match args_repr.len() {
0 => format!("{}()", exc_name),
1 => format!("{}({},)", exc_name, args_repr[0]),
_ => format!("{}({})", exc_name, args_repr.join(", ")),
};
Ok(vm.new_str(joined_str))
pub fn invoke(
cls: PyClassRef,
args: Vec<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<PyBaseExceptionRef> {
// TODO: fast-path built-in exceptions by directly instantiating them? Is that really worth it?
let res = vm.invoke(cls.as_object(), args)?;
PyBaseExceptionRef::try_from_object(vm, res)
}
impl ExceptionCtor {
pub fn instantiate(self, vm: &VirtualMachine) -> PyResult<PyBaseExceptionRef> {
match self {
Self::Class(cls) => invoke(cls, vec![], vm),
Self::Instance(exc) => Ok(exc),
}
}
pub fn instantiate_value(
self,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyBaseExceptionRef> {
let exc_inst = value.clone().downcast::<PyBaseException>().ok();
match (self, exc_inst) {
// both are instances; which would we choose?
(Self::Instance(_exc_a), Some(_exc_b)) => {
Err(vm
.new_type_error("instance exception may not have a separate value".to_string()))
}
// if the "type" is an instance and the value isn't, use the "type"
(Self::Instance(exc), None) => Ok(exc),
// if the value is an instance of the type, use the instance value
(Self::Class(cls), Some(exc)) if objtype::isinstance(&exc, &cls) => Ok(exc),
// otherwise; construct an exception of the type using the value as args
(Self::Class(cls), _) => {
let args = match_class!(match value {
PyNone => vec![],
tup @ PyTuple => tup.elements.clone(),
exc @ PyBaseException => exc.args().elements.clone(),
obj => vec![obj],
});
invoke(cls, args, vm)
}
}
}
}
/// Similar to PyErr_NormalizeException in CPython
pub fn normalize(
exc_type: PyObjectRef,
exc_val: PyObjectRef,
exc_tb: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyBaseExceptionRef> {
let ctor = ExceptionCtor::try_from_object(vm, exc_type)?;
let exc = ctor.instantiate_value(exc_val, vm)?;
if let Some(tb) = Option::<PyTracebackRef>::try_from_object(vm, exc_tb)? {
exc.set_traceback(Some(tb));
}
Ok(exc)
}
#[derive(Debug)]
@@ -508,10 +581,7 @@ impl ExceptionZoo {
}
}
fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
let exc_self = args.args[0].clone();
vm.set_attr(&exc_self, "args", vm.ctx.new_tuple(args.args[1..].to_vec()))?;
fn import_error_init(exc_self: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> {
vm.set_attr(
&exc_self,
"name",
@@ -528,26 +598,21 @@ fn import_error_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
.cloned()
.unwrap_or_else(|| vm.get_none()),
)?;
vm.set_attr(
&exc_self,
"msg",
args.args.get(1).cloned().unwrap_or_else(|| vm.get_none()),
)?;
Ok(vm.get_none())
Ok(())
}
fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
vm.get_none()
}
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef, &VirtualMachine) -> PyResult {
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef, &VirtualMachine) -> PyObjectRef {
move |exc, vm| {
exc.args
.borrow()
.elements
.get(idx)
.cloned()
.ok_or_else(|| vm.new_value_error(format!("couldn't get arg {} of exception", idx)))
.unwrap_or_else(|| vm.get_none())
}
}
@@ -556,11 +621,6 @@ pub fn init(ctx: &PyContext) {
PyBaseException::extend_class(ctx, &excs.base_exception_type);
extend_class!(ctx, &excs.exception_type, {
"__str__" => ctx.new_rustfunc(exception_str),
"__repr__" => ctx.new_rustfunc(exception_repr),
});
extend_class!(ctx, &excs.syntax_error, {
"msg" => ctx.new_property(make_arg_getter(0)),
"filename" => ctx.new_property(make_arg_getter(1)),
@@ -570,13 +630,12 @@ pub fn init(ctx: &PyContext) {
});
extend_class!(ctx, &excs.import_error, {
"__init__" => ctx.new_rustfunc(import_error_init)
"__init__" => ctx.new_rustfunc(import_error_init),
"msg" => ctx.new_property(make_arg_getter(0)),
});
extend_class!(ctx, &excs.stop_iteration, {
"value" => ctx.new_rustfunc(|obj: PyObjectRef, vm: &VirtualMachine| {
objiter::stop_iter_value(vm, &obj)
}),
"value" => ctx.new_property(make_arg_getter(0)),
});
extend_class!(ctx, &excs.unicode_decode_error, {

View File

@@ -5,6 +5,7 @@ use indexmap::IndexMap;
use itertools::Itertools;
use crate::bytecode;
use crate::exceptions::{self, ExceptionCtor, PyBaseExceptionRef};
use crate::function::{single_or_tuple_any, PyFuncArgs};
use crate::obj::objbool;
use crate::obj::objcode::PyCodeRef;
@@ -15,7 +16,7 @@ use crate::obj::objiter;
use crate::obj::objlist;
use crate::obj::objslice::PySlice;
use crate::obj::objstr::{self, PyString};
use crate::obj::objtraceback::{PyTraceback, PyTracebackRef};
use crate::obj::objtraceback::PyTraceback;
use crate::obj::objtuple::PyTuple;
use crate::obj::objtype::{self, PyClassRef};
use crate::pyobject::{
@@ -63,7 +64,7 @@ enum UnwindReason {
Returning { value: PyObjectRef },
/// We hit an exception, so unwind any try-except and finally blocks.
Raising { exception: PyObjectRef },
Raising { exception: PyBaseExceptionRef },
// NoWorries,
/// We are unwinding blocks, since we hit break
@@ -126,7 +127,7 @@ impl ExecutionResult {
} else {
vec![value]
};
Err(vm.new_exception_obj(stop_iteration, args).unwrap())
Err(vm.new_exception(stop_iteration, args))
}
}
}
@@ -176,23 +177,8 @@ impl Frame {
// 1. Extract traceback from exception's '__traceback__' attr.
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
// 3. Unwind block stack till appropriate handler is found.
assert!(objtype::isinstance(
&exception,
&vm.ctx.exceptions.base_exception_type
));
let traceback = vm
.get_attribute(exception.clone(), "__traceback__")
.unwrap();
let next = if vm.is_none(&traceback) {
None
} else {
let traceback: PyTracebackRef = traceback
.downcast()
.expect("next must be a traceback object");
Some(traceback)
};
let next = exception.traceback();
let new_traceback = PyTraceback::new(
next,
@@ -200,8 +186,7 @@ impl Frame {
self.lasti.get(),
lineno.row(),
);
vm.set_attr(&exception, "__traceback__", new_traceback.into_ref(vm))
.unwrap();
exception.set_traceback(Some(new_traceback.into_ref(vm)));
vm_trace!("Adding to traceback: {:?} {:?}", new_traceback, lineno);
match self.unwind_blocks(vm, UnwindReason::Raising { exception }) {
@@ -223,26 +208,22 @@ impl Frame {
pub(crate) fn gen_throw(
&self,
vm: &VirtualMachine,
exc_type: PyClassRef,
exc_type: PyObjectRef,
exc_val: PyObjectRef,
exc_tb: PyObjectRef,
) -> PyResult<ExecutionResult> {
if let bytecode::Instruction::YieldFrom = self.code.instructions[self.lasti.get()] {
let coro = self.last_value();
vm.call_method(
&coro,
"throw",
vec![exc_type.into_object(), exc_val, exc_tb],
)
.or_else(|err| {
self.pop_value();
self.lasti.set(self.lasti.get() + 1);
let val = objiter::stop_iter_value(vm, &err)?;
self._send(coro, val, vm)
})
.map(ExecutionResult::Yield)
vm.call_method(&coro, "throw", vec![exc_type, exc_val, exc_tb])
.or_else(|err| {
self.pop_value();
self.lasti.set(self.lasti.get() + 1);
let val = objiter::stop_iter_value(vm, &err)?;
self._send(coro, val, vm)
})
.map(ExecutionResult::Yield)
} else {
let exception = vm.new_exception_obj(exc_type, vec![exc_val])?;
let exception = exceptions::normalize(exc_type, exc_val, exc_tb, vm)?;
match self.unwind_blocks(vm, UnwindReason::Raising { exception }) {
Ok(None) => self.run(vm),
Ok(Some(result)) => Ok(result),
@@ -464,8 +445,8 @@ impl Frame {
let args = if let Some(exc) = exc {
let exc_type = exc.class().into_object();
let exc_val = exc.clone();
let exc_tb = vm.ctx.none(); // TODO: retrieve traceback?
vec![exc_type, exc_val, exc_tb]
let exc_tb = exc.traceback().map_or(vm.get_none(), |tb| tb.into_object());
vec![exc_type, exc_val.into_object(), exc_tb]
} else {
vec![vm.ctx.none(), vm.ctx.none(), vm.ctx.none()]
};
@@ -745,7 +726,7 @@ impl Frame {
self.pop_block();
if let UnwindReason::Raising { exception } = &reason {
self.push_block(BlockType::ExceptHandler {});
self.push_value(exception.clone());
self.push_value(exception.clone().into_object());
vm.push_exception(exception.clone());
self.jump(handler);
return Ok(None);
@@ -978,36 +959,47 @@ impl Frame {
fn execute_raise(&self, vm: &VirtualMachine, argc: usize) -> FrameResult {
let cause = match argc {
2 => self.get_exception(vm, true)?,
_ => vm.get_none(),
2 => {
let val = self.pop_value();
if vm.is_none(&val) {
// if the cause arg is none, we clear the cause
Some(None)
} else {
// if the cause arg is an exception, we overwrite it
Some(Some(
ExceptionCtor::try_from_object(vm, val)?.instantiate(vm)?,
))
}
}
// if there's no cause arg, we keep the cause as is
_ => None,
};
let exception = match argc {
0 => match vm.current_exception() {
Some(exc) => exc,
None => {
return Err(vm.new_exception(
return Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
"No active exception to reraise".to_string(),
));
))
}
},
1 | 2 => self.get_exception(vm, false)?,
1 | 2 => ExceptionCtor::try_from_object(vm, self.pop_value())?.instantiate(vm)?,
3 => panic!("Not implemented!"),
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
};
let context = match argc {
0 => vm.get_none(), // We have already got the exception,
_ => match vm.current_exception() {
Some(exc) => exc,
None => vm.get_none(),
},
0 => None, // We have already got the exception,
_ => vm.current_exception(),
};
info!(
"Exception raised: {:?} with cause: {:?} and context: {:?}",
exception, cause, context
);
vm.set_attr(&exception, vm.new_str("__cause__".to_string()), cause)?;
vm.set_attr(&exception, vm.new_str("__context__".to_string()), context)?;
if let Some(cause) = cause {
exception.set_cause(cause);
}
exception.set_context(context);
Err(exception)
}
@@ -1383,29 +1375,6 @@ impl Frame {
let stack = self.stack.borrow();
stack[stack.len() - depth - 1].clone()
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn get_exception(&self, vm: &VirtualMachine, none_allowed: bool) -> PyResult {
let exception = self.pop_value();
if none_allowed && vm.get_none().is(&exception)
|| objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type)
{
Ok(exception)
} else if let Ok(exc_type) = PyClassRef::try_from_object(vm, exception) {
if objtype::issubclass(&exc_type, &vm.ctx.exceptions.base_exception_type) {
let exception = vm.new_empty_exception(exc_type)?;
Ok(exception)
} else {
let msg = format!(
"Can only raise BaseException derived types, not {}",
exc_type
);
Err(vm.new_type_error(msg))
}
} else {
Err(vm.new_type_error("exceptions must derive from BaseException".to_string()))
}
}
}
impl fmt::Debug for Frame {

View File

@@ -4,6 +4,7 @@ use std::ops::RangeInclusive;
use indexmap::IndexMap;
use crate::exceptions::PyBaseExceptionRef;
use crate::obj::objtuple::PyTuple;
use crate::obj::objtype::{isinstance, PyClassRef};
use crate::pyobject::{
@@ -207,11 +208,11 @@ pub enum ArgumentError {
RequiredKeywordArgument(String),
/// An exception was raised while binding arguments to the function
/// parameters.
Exception(PyObjectRef),
Exception(PyBaseExceptionRef),
}
impl From<PyObjectRef> for ArgumentError {
fn from(ex: PyObjectRef) -> Self {
impl From<PyBaseExceptionRef> for ArgumentError {
fn from(ex: PyBaseExceptionRef) -> Self {
ArgumentError::Exception(ex)
}
}

View File

@@ -4,9 +4,10 @@
use rand::Rng;
use crate::bytecode::CodeObject;
use crate::exceptions::PyBaseExceptionRef;
use crate::obj::objtraceback::{PyTraceback, PyTracebackRef};
use crate::obj::{objcode, objtype};
use crate::pyobject::{ItemProtocol, PyObjectRef, PyResult, PyValue};
use crate::pyobject::{ItemProtocol, PyResult, PyValue};
use crate::scope::Scope;
use crate::version::get_git_revision;
use crate::vm::{InitParameter, VirtualMachine};
@@ -150,15 +151,15 @@ fn remove_importlib_frames_inner(
// TODO: This function should do nothing on verbose mode.
// TODO: Fix this function after making PyTraceback.next mutable
pub fn remove_importlib_frames(vm: &VirtualMachine, exc: &PyObjectRef) -> PyObjectRef {
pub fn remove_importlib_frames(
vm: &VirtualMachine,
exc: &PyBaseExceptionRef,
) -> PyBaseExceptionRef {
let always_trim = objtype::isinstance(exc, &vm.ctx.exceptions.import_error);
if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") {
let base_tb: PyTracebackRef = tb.downcast().expect("must be a traceback object");
let trimed_tb = remove_importlib_frames_inner(vm, Some(base_tb), always_trim)
.0
.map_or(vm.get_none(), |x| x.into_object());
vm.set_attr(exc, "__traceback__", trimed_tb).unwrap();
if let Some(tb) = exc.traceback() {
let trimmed_tb = remove_importlib_frames_inner(vm, Some(tb), always_trim).0;
exc.set_traceback(trimmed_tb);
}
exc.clone()
}

View File

@@ -53,7 +53,7 @@ pub mod cformat;
mod dictdatatype;
#[cfg(feature = "rustpython-compiler")]
pub mod eval;
mod exceptions;
pub mod exceptions;
pub mod format;
mod frame;
mod frozen;
@@ -72,7 +72,6 @@ mod version;
mod vm;
// pub use self::pyobject::Executor;
pub use self::exceptions::{print_exception, write_exception};
pub use self::vm::{InitParameter, PySettings, VirtualMachine};
pub use rustpython_bytecode::*;

View File

@@ -1,5 +1,6 @@
use super::objiter::new_stop_iteration;
use super::objtype::{isinstance, issubclass, PyClassRef};
use super::objtype::{isinstance, PyClassRef};
use crate::exceptions;
use crate::frame::{ExecutionResult, FrameRef};
use crate::function::OptionalArg;
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
@@ -56,26 +57,18 @@ impl PyCoroutine {
#[pymethod]
fn throw(
&self,
exc_type: PyClassRef,
exc_type: PyObjectRef,
exc_val: OptionalArg,
exc_tb: OptionalArg,
vm: &VirtualMachine,
) -> PyResult {
let exc_val = exc_val.unwrap_or_else(|| vm.get_none());
let exc_tb = exc_tb.unwrap_or_else(|| vm.get_none());
if self.closed.get() {
return Err(vm.invoke(exc_type.as_object(), vec![])?);
}
// TODO what should we do with the other parameters? CPython normalises them with
// PyErr_NormalizeException, do we want to do the same.
if !issubclass(&exc_type, &vm.ctx.exceptions.base_exception_type) {
return Err(vm.new_type_error("Can't throw non exception".to_string()));
return Err(exceptions::normalize(exc_type, exc_val, exc_tb, vm)?);
}
vm.frames.borrow_mut().push(self.frame.clone());
let result = self.frame.gen_throw(
vm,
exc_type,
exc_val.unwrap_or(vm.get_none()),
exc_tb.unwrap_or(vm.get_none()),
);
let result = self.frame.gen_throw(vm, exc_type, exc_val, exc_tb);
self.maybe_close(&result);
vm.frames.borrow_mut().pop();
result?.into_result(vm)
@@ -89,14 +82,14 @@ impl PyCoroutine {
vm.frames.borrow_mut().push(self.frame.clone());
let result = self.frame.gen_throw(
vm,
vm.ctx.exceptions.generator_exit.clone(),
vm.ctx.exceptions.generator_exit.clone().into_object(),
vm.get_none(),
vm.get_none(),
);
vm.frames.borrow_mut().pop();
self.closed.set(true);
match result {
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception(
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
"generator ignored GeneratorExit".to_string(),
)),
@@ -149,7 +142,7 @@ impl PyCoroutineWrapper {
#[pymethod]
fn throw(
&self,
exc_type: PyClassRef,
exc_type: PyObjectRef,
exc_val: OptionalArg,
exc_tb: OptionalArg,
vm: &VirtualMachine,

View File

@@ -5,6 +5,7 @@ use super::objiter;
use super::objstr;
use super::objtype::{self, PyClassRef};
use crate::dictdatatype::{self, DictKey};
use crate::exceptions::PyBaseExceptionRef;
use crate::function::{KwArgs, OptionalArg};
use crate::pyobject::{
IdProtocol, IntoPyObject, ItemProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable,
@@ -60,7 +61,7 @@ impl PyDictRef {
vm: &VirtualMachine,
) -> PyResult<()> {
if let OptionalArg::Present(dict_obj) = dict_obj {
let dicted: PyResult<PyDictRef> = dict_obj.clone().downcast();
let dicted: Result<PyDictRef, _> = dict_obj.clone().downcast();
if let Ok(dict_obj) = dicted {
for (key, value) in dict_obj {
dict.borrow_mut().insert(vm, &key, value)?;
@@ -74,7 +75,7 @@ impl PyDictRef {
} else {
let iter = objiter::get_iter(vm, &dict_obj)?;
loop {
fn err(vm: &VirtualMachine) -> PyObjectRef {
fn err(vm: &VirtualMachine) -> PyBaseExceptionRef {
vm.new_type_error("Iterator must have exactly two elements".to_string())
}
let element = match objiter::get_next_object(vm, &iter)? {
@@ -526,7 +527,7 @@ macro_rules! dict_iterator {
let mut position = self.position.get();
let dict = self.dict.entries.borrow();
if dict.has_changed_size(&self.size) {
return Err(vm.new_exception(
return Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
"dictionary changed size during iteration".to_string(),
));

View File

@@ -7,6 +7,7 @@ use super::objbytes;
use super::objint::{self, PyInt, PyIntRef};
use super::objstr::{PyString, PyStringRef};
use super::objtype::{self, PyClassRef};
use crate::exceptions::PyBaseExceptionRef;
use crate::function::{OptionalArg, OptionalOption};
use crate::pyhash;
use crate::pyobject::{
@@ -619,7 +620,7 @@ fn str_to_float(vm: &VirtualMachine, literal: &str) -> PyResult<f64> {
}
}
fn invalid_convert(vm: &VirtualMachine, literal: &str) -> PyObjectRef {
fn invalid_convert(vm: &VirtualMachine, literal: &str) -> PyBaseExceptionRef {
vm.new_value_error(format!("could not convert string to float: '{}'", literal))
}

View File

@@ -3,7 +3,8 @@
*/
use super::objiter::new_stop_iteration;
use super::objtype::{isinstance, issubclass, PyClassRef};
use super::objtype::{isinstance, PyClassRef};
use crate::exceptions;
use crate::frame::{ExecutionResult, FrameRef};
use crate::function::OptionalArg;
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
@@ -69,26 +70,18 @@ impl PyGenerator {
#[pymethod]
fn throw(
&self,
exc_type: PyClassRef,
exc_type: PyObjectRef,
exc_val: OptionalArg,
exc_tb: OptionalArg,
vm: &VirtualMachine,
) -> PyResult {
let exc_val = exc_val.unwrap_or_else(|| vm.get_none());
let exc_tb = exc_tb.unwrap_or_else(|| vm.get_none());
if self.closed.get() {
return Err(vm.invoke(exc_type.as_object(), vec![])?);
}
// TODO what should we do with the other parameters? CPython normalises them with
// PyErr_NormalizeException, do we want to do the same.
if !issubclass(&exc_type, &vm.ctx.exceptions.base_exception_type) {
return Err(vm.new_type_error("Can't throw non exception".to_string()));
return Err(exceptions::normalize(exc_type, exc_val, exc_tb, vm)?);
}
vm.frames.borrow_mut().push(self.frame.clone());
let result = self.frame.gen_throw(
vm,
exc_type,
exc_val.unwrap_or(vm.get_none()),
exc_tb.unwrap_or(vm.get_none()),
);
let result = self.frame.gen_throw(vm, exc_type, exc_val, exc_tb);
self.maybe_close(&result);
vm.frames.borrow_mut().pop();
result?.into_result(vm)
@@ -102,14 +95,14 @@ impl PyGenerator {
vm.frames.borrow_mut().push(self.frame.clone());
let result = self.frame.gen_throw(
vm,
vm.ctx.exceptions.generator_exit.clone(),
vm.ctx.exceptions.generator_exit.clone().into_object(),
vm.get_none(),
vm.get_none(),
);
vm.frames.borrow_mut().pop();
self.closed.set(true);
match result {
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception(
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
"generator ignored GeneratorExit".to_string(),
)),

View File

@@ -12,6 +12,7 @@ use super::objbytes::PyBytes;
use super::objfloat;
use super::objstr::{PyString, PyStringRef};
use super::objtype::{self, PyClassRef};
use crate::exceptions::PyBaseExceptionRef;
use crate::format::FormatSpec;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::pyhash;
@@ -817,7 +818,7 @@ fn detect_base(literal: &str) -> Option<u32> {
}
}
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyObjectRef {
fn invalid_literal(vm: &VirtualMachine, literal: &str, base: &BigInt) -> PyBaseExceptionRef {
vm.new_value_error(format!(
"invalid literal for int() with base {}: '{}'",
base, literal

View File

@@ -4,8 +4,8 @@
use std::cell::Cell;
use super::objtuple::PyTuple;
use super::objtype::{self, PyClassRef};
use crate::exceptions::PyBaseExceptionRef;
use crate::pyobject::{
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
};
@@ -68,14 +68,13 @@ pub fn get_all<T: TryFromObject>(vm: &VirtualMachine, iter_obj: &PyObjectRef) ->
Ok(elements)
}
pub fn new_stop_iteration(vm: &VirtualMachine) -> PyObjectRef {
pub fn new_stop_iteration(vm: &VirtualMachine) -> PyBaseExceptionRef {
let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone();
vm.new_empty_exception(stop_iteration_type).unwrap()
vm.new_exception_empty(stop_iteration_type)
}
pub fn stop_iter_value(vm: &VirtualMachine, exc: &PyObjectRef) -> PyResult {
let args = vm.get_attribute(exc.clone(), "args")?;
let args: &PyTuple = args.payload().unwrap();
pub fn stop_iter_value(vm: &VirtualMachine, exc: &PyBaseExceptionRef) -> PyResult {
let args = exc.args();
let val = args
.elements
.first()

View File

@@ -287,8 +287,8 @@ fn _mro(cls: &PyClassRef) -> Vec<PyClassRef> {
/// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only
/// use this if `cls` is known to have not overridden the base __instancecheck__ magic method.
#[cfg_attr(feature = "flame-it", flame("objtype"))]
pub fn isinstance(obj: &PyObjectRef, cls: &PyClassRef) -> bool {
#[inline]
pub fn isinstance<T: TypeProtocol>(obj: &T, cls: &PyClassRef) -> bool {
issubclass(&obj.class(), &cls)
}

View File

@@ -41,7 +41,7 @@ impl PyWeakProxy {
fn getattr(&self, attr_name: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match self.weak.upgrade() {
Some(obj) => vm.get_attribute(obj, attr_name),
None => Err(vm.new_exception(
None => Err(vm.new_exception_msg(
vm.ctx.exceptions.reference_error.clone(),
"weakly-referenced object no longer exists".to_string(),
)),
@@ -52,7 +52,7 @@ impl PyWeakProxy {
fn setattr(&self, attr_name: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match self.weak.upgrade() {
Some(obj) => vm.set_attr(&obj, attr_name, value),
None => Err(vm.new_exception(
None => Err(vm.new_exception_msg(
vm.ctx.exceptions.reference_error.clone(),
"weakly-referenced object no longer exists".to_string(),
)),

View File

@@ -13,7 +13,7 @@ use num_traits::{One, ToPrimitive, Zero};
use crate::bytecode;
use crate::dictdatatype::DictKey;
use crate::exceptions;
use crate::exceptions::{self, PyBaseExceptionRef};
use crate::function::{IntoPyNativeFunc, PyFuncArgs};
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
use crate::obj::objbytearray;
@@ -64,7 +64,7 @@ pub type PyObjectRef = Rc<PyObject<dyn PyObjectPayload>>;
/// Use this type for functions which return a python object or an exception.
/// Both the python object and the python exception are `PyObjectRef` types
/// since exceptions are also python objects.
pub type PyResult<T = PyObjectRef> = Result<T, PyObjectRef>; // A valid value, or an exception
pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; // A valid value, or an exception
/// For attributes we do not use a dict, but a hashmap. This is probably
/// faster, unordered, and only supports strings as keys.
@@ -575,9 +575,6 @@ impl PyObject<dyn PyObjectPayload> {
///
/// If the downcast fails, the original ref is returned in as `Err` so
/// another downcast can be attempted without unnecessary cloning.
///
/// Note: The returned `Result` is _not_ a `PyResult`, even though the
/// types are compatible.
pub fn downcast<T: PyObjectPayload>(self: Rc<Self>) -> Result<PyRef<T>, PyObjectRef> {
if self.payload_is::<T>() {
Ok({
@@ -622,7 +619,7 @@ impl<T: PyValue> PyRef<T> {
if obj.payload_is::<T>() {
Ok(Self::new_ref_unchecked(obj))
} else {
Err(vm.new_exception(
Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
format!("Unexpected payload for type {:?}", obj.class().name),
))
@@ -802,6 +799,12 @@ impl<T> TypeProtocol for PyRef<T> {
}
}
impl<T: TypeProtocol> TypeProtocol for &'_ T {
fn class(&self) -> PyClassRef {
(&**self).class()
}
}
/// The python item protocol. Mostly applies to dictionaries.
/// Allows getting, setting and deletion of keys-value pairs.
pub trait ItemProtocol {
@@ -1082,7 +1085,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static {
fn class(vm: &VirtualMachine) -> PyClassRef;
fn into_ref(self, vm: &VirtualMachine) -> PyRef<Self> {
PyRef::new_ref_unchecked(PyObject::new(self, Self::class(vm), None))
self.into_ref_with_type_unchecked(Self::class(vm), None)
}
fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult<PyRef<Self>> {
@@ -1100,6 +1103,10 @@ pub trait PyValue: fmt::Debug + Sized + 'static {
Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype)))
}
}
fn into_ref_with_type_unchecked(self, cls: PyClassRef, dict: Option<PyDictRef>) -> PyRef<Self> {
PyRef::new_ref_unchecked(PyObject::new(self, cls, dict))
}
}
pub trait PyObjectPayload: Any + fmt::Debug + 'static {

View File

@@ -26,7 +26,7 @@ fn functools_reduce(
objiter::call_next(vm, &iterator).map_err(|err| {
if objtype::isinstance(&err, &vm.ctx.exceptions.stop_iteration) {
let exc_type = vm.ctx.exceptions.type_error.clone();
vm.new_exception(
vm.new_exception_msg(
exc_type,
"reduce() of empty sequence with no initial value".to_string(),
)

View File

@@ -386,7 +386,7 @@ fn io_base_checkclosed(
let msg = msg
.flat_option()
.unwrap_or_else(|| vm.new_str("I/O operation on closed file.".to_string()));
Err(vm.new_exception_obj(vm.ctx.exceptions.value_error.clone(), vec![msg])?)
Err(vm.new_exception(vm.ctx.exceptions.value_error.clone(), vec![msg]))
} else {
Ok(())
}
@@ -401,7 +401,7 @@ fn io_base_checkreadable(
let msg = msg
.flat_option()
.unwrap_or_else(|| vm.new_str("File or stream is not readable.".to_string()));
Err(vm.new_exception_obj(vm.ctx.exceptions.value_error.clone(), vec![msg])?)
Err(vm.new_exception(vm.ctx.exceptions.value_error.clone(), vec![msg]))
} else {
Ok(())
}
@@ -416,7 +416,7 @@ fn io_base_checkwritable(
let msg = msg
.flat_option()
.unwrap_or_else(|| vm.new_str("File or stream is not writable.".to_string()));
Err(vm.new_exception_obj(vm.ctx.exceptions.value_error.clone(), vec![msg])?)
Err(vm.new_exception(vm.ctx.exceptions.value_error.clone(), vec![msg]))
} else {
Ok(())
}
@@ -431,7 +431,7 @@ fn io_base_checkseekable(
let msg = msg
.flat_option()
.unwrap_or_else(|| vm.new_str("File or stream is not seekable.".to_string()));
Err(vm.new_exception_obj(vm.ctx.exceptions.value_error.clone(), vec![msg])?)
Err(vm.new_exception(vm.ctx.exceptions.value_error.clone(), vec![msg]))
} else {
Ok(())
}

View File

@@ -46,10 +46,10 @@ pub fn json_loads(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
.unwrap();
let json_decode_error = vm.get_attribute(module, "JSONDecodeError").unwrap();
let json_decode_error = json_decode_error.downcast().unwrap();
let exc = vm.new_exception(json_decode_error, format!("{}", err));
vm.set_attr(&exc, "lineno", vm.ctx.new_int(err.line()))
let exc = vm.new_exception_msg(json_decode_error, format!("{}", err));
vm.set_attr(exc.as_object(), "lineno", vm.ctx.new_int(err.line()))
.unwrap();
vm.set_attr(&exc, "colno", vm.ctx.new_int(err.column()))
vm.set_attr(exc.as_object(), "colno", vm.ctx.new_int(err.column()))
.unwrap();
exc
})

View File

@@ -24,6 +24,7 @@ use nix::unistd::{self, Gid, Pid, Uid, Whence};
use std::os::unix::io::RawFd;
use super::errno::errors;
use crate::exceptions::PyBaseExceptionRef;
use crate::function::{IntoPyNativeFunc, OptionalArg, PyFuncArgs};
use crate::obj::objbyteinner::PyBytesLike;
use crate::obj::objbytes::PyBytesRef;
@@ -155,7 +156,7 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
unimplemented!()
}
pub fn convert_io_error(vm: &VirtualMachine, err: io::Error) -> PyObjectRef {
pub fn convert_io_error(vm: &VirtualMachine, err: io::Error) -> PyBaseExceptionRef {
#[allow(unreachable_patterns)] // some errors are just aliases of each other
let exc_type = match err.kind() {
ErrorKind::NotFound => vm.ctx.exceptions.file_not_found_error.clone(),
@@ -170,38 +171,38 @@ pub fn convert_io_error(vm: &VirtualMachine, err: io::Error) -> PyObjectRef {
_ => vm.ctx.exceptions.os_error.clone(),
},
};
let os_error = vm.new_exception(exc_type, err.to_string());
let os_error = vm.new_exception_msg(exc_type, err.to_string());
let errno = match err.raw_os_error() {
Some(errno) => vm.new_int(errno),
None => vm.get_none(),
};
vm.set_attr(&os_error, "errno", errno).unwrap();
vm.set_attr(os_error.as_object(), "errno", errno).unwrap();
os_error
}
#[cfg(unix)]
pub fn convert_nix_error(vm: &VirtualMachine, err: nix::Error) -> PyObjectRef {
pub fn convert_nix_error(vm: &VirtualMachine, err: nix::Error) -> PyBaseExceptionRef {
let nix_error = match err {
nix::Error::InvalidPath => {
let exc_type = vm.ctx.exceptions.file_not_found_error.clone();
vm.new_exception(exc_type, err.to_string())
vm.new_exception_msg(exc_type, err.to_string())
}
nix::Error::InvalidUtf8 => {
let exc_type = vm.ctx.exceptions.unicode_error.clone();
vm.new_exception(exc_type, err.to_string())
vm.new_exception_msg(exc_type, err.to_string())
}
nix::Error::UnsupportedOperation => {
let exc_type = vm.ctx.exceptions.runtime_error.clone();
vm.new_exception(exc_type, err.to_string())
vm.new_exception_msg(exc_type, err.to_string())
}
nix::Error::Sys(errno) => {
let exc_type = convert_nix_errno(vm, errno);
vm.new_exception(exc_type, err.to_string())
vm.new_exception_msg(exc_type, err.to_string())
}
};
if let nix::Error::Sys(errno) = err {
vm.set_attr(&nix_error, "errno", vm.ctx.new_int(errno as i32))
vm.set_attr(nix_error.as_object(), "errno", vm.ctx.new_int(errno as i32))
.unwrap();
}
@@ -218,7 +219,7 @@ fn convert_nix_errno(vm: &VirtualMachine, errno: Errno) -> PyClassRef {
/// Convert the error stored in the `errno` variable into an Exception
#[inline]
pub fn errno_err(vm: &VirtualMachine) -> PyObjectRef {
pub fn errno_err(vm: &VirtualMachine) -> PyBaseExceptionRef {
convert_io_error(vm, io::Error::last_os_error())
}
@@ -1055,18 +1056,18 @@ fn os_getegid(vm: &VirtualMachine) -> PyObjectRef {
}
#[cfg(unix)]
fn os_getpgid(pid: u32, vm: &VirtualMachine) -> PyObjectRef {
fn os_getpgid(pid: u32, vm: &VirtualMachine) -> PyResult {
match unistd::getpgid(Some(Pid::from_raw(pid as i32))) {
Ok(pgid) => vm.new_int(pgid.as_raw()),
Err(err) => convert_nix_error(vm, err),
Ok(pgid) => Ok(vm.new_int(pgid.as_raw())),
Err(err) => Err(convert_nix_error(vm, err)),
}
}
#[cfg(all(unix, not(target_os = "redox")))]
fn os_getsid(pid: u32, vm: &VirtualMachine) -> PyObjectRef {
fn os_getsid(pid: u32, vm: &VirtualMachine) -> PyResult {
match unistd::getsid(Some(Pid::from_raw(pid as i32))) {
Ok(sid) => vm.new_int(sid.as_raw()),
Err(err) => convert_nix_error(vm, err),
Ok(sid) => Ok(vm.new_int(sid.as_raw())),
Err(err) => Err(convert_nix_error(vm, err)),
}
}

View File

@@ -19,7 +19,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
fn random_normalvariate(mu: f64, sigma: f64, vm: &VirtualMachine) -> PyResult<f64> {
let normal = Normal::new(mu, sigma).map_err(|rand_err| {
vm.new_exception(
vm.new_exception_msg(
vm.ctx.exceptions.arithmetic_error.clone(),
format!("invalid normal distribution: {:?}", rand_err),
)

View File

@@ -108,7 +108,7 @@ pub fn check_signals(vm: &VirtualMachine) -> PyResult<()> {
}
fn default_int_handler(_signum: PyObjectRef, _arg: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_empty_exception(vm.ctx.exceptions.keyboard_interrupt.clone())?)
Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.clone()))
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {

View File

@@ -12,6 +12,7 @@ use socket2::{Domain, Protocol, Socket, Type as SocketType};
use super::os::convert_io_error;
#[cfg(unix)]
use super::os::convert_nix_error;
use crate::exceptions::PyBaseExceptionRef;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objbytearray::PyByteArrayRef;
use crate::obj::objbyteinner::PyBytesLike;
@@ -484,7 +485,7 @@ fn socket_getaddrinfo(opts: GAIOptions, vm: &VirtualMachine) -> PyResult {
let addrs = dns_lookup::getaddrinfo(host, port, Some(hints)).map_err(|err| {
let error_type = vm.class("_socket", "gaierror");
vm.new_exception(error_type, io::Error::from(err).to_string())
vm.new_exception_msg(error_type, io::Error::from(err).to_string())
})?;
let list = addrs
@@ -536,7 +537,7 @@ where
Ok(mut sock_addrs) => {
if sock_addrs.len() == 0 {
let error_type = vm.class("_socket", "gaierror");
Err(vm.new_exception(
Err(vm.new_exception_msg(
error_type,
"nodename nor servname provided, or not known".to_string(),
))
@@ -546,7 +547,7 @@ where
}
Err(e) => {
let error_type = vm.class("_socket", "gaierror");
Err(vm.new_exception(error_type, e.to_string()))
Err(vm.new_exception_msg(error_type, e.to_string()))
}
}
}
@@ -589,10 +590,10 @@ fn invalid_sock() -> Socket {
}
}
fn convert_sock_error(vm: &VirtualMachine, err: io::Error) -> PyObjectRef {
fn convert_sock_error(vm: &VirtualMachine, err: io::Error) -> PyBaseExceptionRef {
if err.kind() == io::ErrorKind::TimedOut {
let socket_timeout = vm.class("_socket", "timeout");
vm.new_exception(socket_timeout, "Timed out".to_string())
vm.new_exception_msg(socket_timeout, "Timed out".to_string())
} else {
convert_io_error(vm, err)
}

View File

@@ -152,7 +152,7 @@ impl PopenRef {
.map_err(|s| vm.new_os_error(format!("Could not start program: {}", s)))?;
if timeout.is_none() {
let timeout_expired = vm.try_class("_subprocess", "TimeoutExpired")?;
Err(vm.new_exception(timeout_expired, "Timeout".to_string()))
Err(vm.new_exception_msg(timeout_expired, "Timeout".to_string()))
} else {
Ok(())
}

View File

@@ -110,7 +110,7 @@ impl PySymbolTable {
}
.into_ref(vm))
} else {
Err(vm.ctx.new_str(name.to_string()))
Err(vm.new_lookup_error(name.to_string()))
}
}

View File

@@ -1,3 +1,4 @@
use crate::exceptions::PyBaseExceptionRef;
use crate::function::OptionalArg;
use crate::obj::objbytes::PyBytesRef;
use crate::pyobject::{ItemProtocol, PyObjectRef, PyResult};
@@ -110,7 +111,7 @@ fn zlib_decompress(
}
}
fn zlib_error(message: &str, vm: &VirtualMachine) -> PyObjectRef {
fn zlib_error(message: &str, vm: &VirtualMachine) -> PyBaseExceptionRef {
let module = vm
.get_attribute(vm.sys_module.clone(), "modules")
.unwrap()
@@ -120,5 +121,5 @@ fn zlib_error(message: &str, vm: &VirtualMachine) -> PyObjectRef {
let zlib_error = vm.get_attribute(module, "error").unwrap();
let zlib_error = zlib_error.downcast().unwrap();
vm.new_exception(zlib_error, message.to_string())
vm.new_exception_msg(zlib_error, message.to_string())
}

View File

@@ -174,15 +174,18 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
value
}
fn sys_exc_info(vm: &VirtualMachine) -> PyResult {
Ok(vm.ctx.new_tuple(match vm.current_exception() {
fn sys_exc_info(vm: &VirtualMachine) -> PyObjectRef {
let exc_info = match vm.current_exception() {
Some(exception) => vec![
exception.class().into_object(),
exception.clone(),
vm.get_none(),
exception.clone().into_object(),
exception
.traceback()
.map_or(vm.get_none(), |tb| tb.into_object()),
],
None => vec![vm.get_none(), vm.get_none(), vm.get_none()],
}))
};
vm.ctx.new_tuple(exc_info)
}
fn sys_git_info(vm: &VirtualMachine) -> PyObjectRef {
@@ -195,7 +198,7 @@ fn sys_git_info(vm: &VirtualMachine) -> PyObjectRef {
fn sys_exit(code: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
let code = code.unwrap_or_else(|| vm.new_int(0));
Err(vm.new_exception_obj(vm.ctx.exceptions.system_exit.clone(), vec![code])?)
Err(vm.new_exception(vm.ctx.exceptions.system_exit.clone(), vec![code]))
}
pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) {

View File

@@ -19,6 +19,7 @@ use rustpython_compiler::{compile, error::CompileError};
use crate::builtins::{self, to_ascii};
use crate::bytecode;
use crate::exceptions::{PyBaseException, PyBaseExceptionRef};
use crate::frame::{ExecutionResult, Frame, FrameRef};
use crate::frozen;
use crate::function::PyFuncArgs;
@@ -59,7 +60,7 @@ pub struct VirtualMachine {
pub ctx: PyContext,
pub frames: RefCell<Vec<FrameRef>>,
pub wasm_id: Option<String>,
pub exceptions: RefCell<Vec<PyObjectRef>>,
pub exceptions: RefCell<Vec<PyBaseExceptionRef>>,
pub frozen: RefCell<HashMap<String, bytecode::FrozenModule>>,
pub import_func: RefCell<PyObjectRef>,
pub profile_func: RefCell<PyObjectRef>,
@@ -328,41 +329,64 @@ impl VirtualMachine {
PyObject::new(PyModule {}, self.ctx.types.module_type.clone(), Some(dict))
}
#[cfg_attr(feature = "flame-it", flame("VirtualMachine"))]
pub fn new_exception_obj(&self, exc_type: PyClassRef, args: Vec<PyObjectRef>) -> PyResult {
/// Instantiate an exception with arguments.
/// This function should only be used with builtin exception types; if a user-defined exception
/// type is passed in, it may not be fully initialized; try using [`exceptions::invoke`](invoke)
/// or [`exceptions::ExceptionCtor`](ctor) instead.
///
/// [invoke]: rustpython_vm::exceptions::invoke
/// [ctor]: rustpython_vm::exceptions::ExceptionCtor
pub fn new_exception(
&self,
exc_type: PyClassRef,
args: Vec<PyObjectRef>,
) -> PyBaseExceptionRef {
// TODO: add repr of args into logging?
vm_trace!("New exception created: {}", exc_type.name);
self.invoke(&exc_type.into_object(), args)
PyBaseException::new(args, self)
.into_ref_with_type_unchecked(exc_type, Some(self.ctx.new_dict()))
}
pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult {
self.new_exception_obj(exc_type, vec![])
/// Instantiate an exception with no arguments.
/// This function should only be used with builtin exception types; if a user-defined exception
/// type is passed in, it may not be fully initialized; try using [`exceptions::invoke`](invoke)
/// or [`exceptions::ExceptionCtor`](ctor) instead.
///
/// [invoke]: rustpython_vm::exceptions::invoke
/// [ctor]: rustpython_vm::exceptions::ExceptionCtor
pub fn new_exception_empty(&self, exc_type: PyClassRef) -> PyBaseExceptionRef {
self.new_exception(exc_type, vec![])
}
/// Create Python instance of `exc_type` with message as first element of `args` tuple
pub fn new_exception(&self, exc_type: PyClassRef, msg: String) -> PyObjectRef {
let pystr_msg = self.new_str(msg);
self.new_exception_obj(exc_type, vec![pystr_msg]).unwrap()
/// Instantiate an exception with `msg` as the only argument.
/// This function should only be used with builtin exception types; if a user-defined exception
/// type is passed in, it may not be fully initialized; try using [`exceptions::invoke`](invoke)
/// or [`exceptions::ExceptionCtor`](ctor) instead.
///
/// [invoke]: rustpython_vm::exceptions::invoke
/// [ctor]: rustpython_vm::exceptions::ExceptionCtor
pub fn new_exception_msg(&self, exc_type: PyClassRef, msg: String) -> PyBaseExceptionRef {
self.new_exception(exc_type, vec![self.new_str(msg)])
}
pub fn new_lookup_error(&self, msg: String) -> PyObjectRef {
pub fn new_lookup_error(&self, msg: String) -> PyBaseExceptionRef {
let lookup_error = self.ctx.exceptions.lookup_error.clone();
self.new_exception(lookup_error, msg)
self.new_exception_msg(lookup_error, msg)
}
pub fn new_attribute_error(&self, msg: String) -> PyObjectRef {
pub fn new_attribute_error(&self, msg: String) -> PyBaseExceptionRef {
let attribute_error = self.ctx.exceptions.attribute_error.clone();
self.new_exception(attribute_error, msg)
self.new_exception_msg(attribute_error, msg)
}
pub fn new_type_error(&self, msg: String) -> PyObjectRef {
pub fn new_type_error(&self, msg: String) -> PyBaseExceptionRef {
let type_error = self.ctx.exceptions.type_error.clone();
self.new_exception(type_error, msg)
self.new_exception_msg(type_error, msg)
}
pub fn new_name_error(&self, msg: String) -> PyObjectRef {
pub fn new_name_error(&self, msg: String) -> PyBaseExceptionRef {
let name_error = self.ctx.exceptions.name_error.clone();
self.new_exception(name_error, msg)
self.new_exception_msg(name_error, msg)
}
pub fn new_unsupported_operand_error(
@@ -370,7 +394,7 @@ impl VirtualMachine {
a: PyObjectRef,
b: PyObjectRef,
op: &str,
) -> PyObjectRef {
) -> PyBaseExceptionRef {
self.new_type_error(format!(
"Unsupported operand types for '{}': '{}' and '{}'",
op,
@@ -379,60 +403,60 @@ impl VirtualMachine {
))
}
pub fn new_os_error(&self, msg: String) -> PyObjectRef {
pub fn new_os_error(&self, msg: String) -> PyBaseExceptionRef {
let os_error = self.ctx.exceptions.os_error.clone();
self.new_exception(os_error, msg)
self.new_exception_msg(os_error, msg)
}
pub fn new_unicode_decode_error(&self, msg: String) -> PyObjectRef {
pub fn new_unicode_decode_error(&self, msg: String) -> PyBaseExceptionRef {
let unicode_decode_error = self.ctx.exceptions.unicode_decode_error.clone();
self.new_exception(unicode_decode_error, msg)
self.new_exception_msg(unicode_decode_error, msg)
}
pub fn new_unicode_encode_error(&self, msg: String) -> PyObjectRef {
pub fn new_unicode_encode_error(&self, msg: String) -> PyBaseExceptionRef {
let unicode_encode_error = self.ctx.exceptions.unicode_encode_error.clone();
self.new_exception(unicode_encode_error, msg)
self.new_exception_msg(unicode_encode_error, msg)
}
/// Create a new python ValueError object. Useful for raising errors from
/// python functions implemented in rust.
pub fn new_value_error(&self, msg: String) -> PyObjectRef {
pub fn new_value_error(&self, msg: String) -> PyBaseExceptionRef {
let value_error = self.ctx.exceptions.value_error.clone();
self.new_exception(value_error, msg)
self.new_exception_msg(value_error, msg)
}
pub fn new_key_error(&self, obj: PyObjectRef) -> PyObjectRef {
pub fn new_key_error(&self, obj: PyObjectRef) -> PyBaseExceptionRef {
let key_error = self.ctx.exceptions.key_error.clone();
self.new_exception_obj(key_error, vec![obj]).unwrap()
self.new_exception(key_error, vec![obj])
}
pub fn new_index_error(&self, msg: String) -> PyObjectRef {
pub fn new_index_error(&self, msg: String) -> PyBaseExceptionRef {
let index_error = self.ctx.exceptions.index_error.clone();
self.new_exception(index_error, msg)
self.new_exception_msg(index_error, msg)
}
pub fn new_not_implemented_error(&self, msg: String) -> PyObjectRef {
pub fn new_not_implemented_error(&self, msg: String) -> PyBaseExceptionRef {
let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone();
self.new_exception(not_implemented_error, msg)
self.new_exception_msg(not_implemented_error, msg)
}
pub fn new_recursion_error(&self, msg: String) -> PyObjectRef {
pub fn new_recursion_error(&self, msg: String) -> PyBaseExceptionRef {
let recursion_error = self.ctx.exceptions.recursion_error.clone();
self.new_exception(recursion_error, msg)
self.new_exception_msg(recursion_error, msg)
}
pub fn new_zero_division_error(&self, msg: String) -> PyObjectRef {
pub fn new_zero_division_error(&self, msg: String) -> PyBaseExceptionRef {
let zero_division_error = self.ctx.exceptions.zero_division_error.clone();
self.new_exception(zero_division_error, msg)
self.new_exception_msg(zero_division_error, msg)
}
pub fn new_overflow_error(&self, msg: String) -> PyObjectRef {
pub fn new_overflow_error(&self, msg: String) -> PyBaseExceptionRef {
let overflow_error = self.ctx.exceptions.overflow_error.clone();
self.new_exception(overflow_error, msg)
self.new_exception_msg(overflow_error, msg)
}
#[cfg(feature = "rustpython-compiler")]
pub fn new_syntax_error(&self, error: &CompileError) -> PyObjectRef {
pub fn new_syntax_error(&self, error: &CompileError) -> PyBaseExceptionRef {
let syntax_error_type = if error.is_indentation_error() {
self.ctx.exceptions.indentation_error.clone()
} else if error.is_tab_error() {
@@ -440,25 +464,31 @@ impl VirtualMachine {
} else {
self.ctx.exceptions.syntax_error.clone()
};
let syntax_error = self.new_exception(syntax_error_type, error.to_string());
let syntax_error = self.new_exception_msg(syntax_error_type, error.to_string());
let lineno = self.new_int(error.location.row());
let offset = self.new_int(error.location.column());
self.set_attr(&syntax_error, "lineno", lineno).unwrap();
self.set_attr(&syntax_error, "offset", offset).unwrap();
self.set_attr(syntax_error.as_object(), "lineno", lineno)
.unwrap();
self.set_attr(syntax_error.as_object(), "offset", offset)
.unwrap();
if let Some(v) = error.statement.as_ref() {
self.set_attr(&syntax_error, "text", self.new_str(v.to_owned()))
self.set_attr(syntax_error.as_object(), "text", self.new_str(v.to_owned()))
.unwrap();
}
if let Some(path) = error.source_path.as_ref() {
self.set_attr(&syntax_error, "filename", self.new_str(path.to_owned()))
.unwrap();
self.set_attr(
syntax_error.as_object(),
"filename",
self.new_str(path.to_owned()),
)
.unwrap();
}
syntax_error
}
pub fn new_import_error(&self, msg: String) -> PyObjectRef {
pub fn new_import_error(&self, msg: String) -> PyBaseExceptionRef {
let import_error = self.ctx.exceptions.import_error.clone();
self.new_exception(import_error, msg)
self.new_exception_msg(import_error, msg)
}
pub fn new_scope_with_builtins(&self) -> Scope {
@@ -1358,15 +1388,15 @@ impl VirtualMachine {
}
}
pub fn push_exception(&self, exc: PyObjectRef) {
pub fn push_exception(&self, exc: PyBaseExceptionRef) {
self.exceptions.borrow_mut().push(exc)
}
pub fn pop_exception(&self) -> Option<PyObjectRef> {
pub fn pop_exception(&self) -> Option<PyBaseExceptionRef> {
self.exceptions.borrow_mut().pop()
}
pub fn current_exception(&self) -> Option<PyObjectRef> {
pub fn current_exception(&self) -> Option<PyBaseExceptionRef> {
self.exceptions.borrow().last().cloned()
}

View File

@@ -25,8 +25,6 @@ serde-wasm-bindgen = "0.1"
serde = "1.0"
js-sys = "0.3"
futures = "0.1"
num-traits = "0.2"
num-bigint = { version = "0.2.3", features = ["serde"] }
[dependencies.web-sys]
version = "0.3"

View File

@@ -1,15 +1,12 @@
use futures::Future;
use js_sys::Promise;
use num_traits::cast::ToPrimitive;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::{future_to_promise, JsFuture};
use rustpython_vm::function::{OptionalArg, PyFuncArgs};
use rustpython_vm::import::import_file;
use rustpython_vm::obj::{
objdict::PyDictRef, objint::PyIntRef, objstr::PyStringRef, objtype::PyClassRef,
};
use rustpython_vm::obj::{objdict::PyDictRef, objstr::PyStringRef, objtype::PyClassRef};
use rustpython_vm::pyobject::{
PyCallable, PyClassImpl, PyObject, PyObjectRef, PyRef, PyResult, PyValue,
};
@@ -148,14 +145,7 @@ fn browser_request_animation_frame(func: PyCallable, vm: &VirtualMachine) -> PyR
Ok(vm.ctx.new_int(id))
}
fn browser_cancel_animation_frame(id: PyIntRef, vm: &VirtualMachine) -> PyResult {
let id = id.as_bigint().to_i32().ok_or_else(|| {
vm.new_exception(
vm.ctx.exceptions.value_error.clone(),
"Integer too large to convert to i32 for animationFrame id".into(),
)
})?;
fn browser_cancel_animation_frame(id: i32, vm: &VirtualMachine) -> PyResult {
window()
.cancel_animation_frame(id)
.map_err(|err| convert::js_py_typeerror(vm, err))?;
@@ -308,7 +298,7 @@ impl Element {
fn set_attr(&self, attr: PyStringRef, value: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
self.elem
.set_attribute(attr.as_str(), value.as_str())
.map_err(|err| convert::js_to_py(vm, err))
.map_err(|err| convert::js_py_typeerror(vm, err))
}
}

View File

@@ -1,20 +1,18 @@
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, Uint8Array};
use num_traits::cast::ToPrimitive;
use serde_wasm_bindgen;
use wasm_bindgen::{closure::Closure, prelude::*, JsCast};
use rustpython_vm::exceptions::PyBaseExceptionRef;
use rustpython_vm::function::PyFuncArgs;
use rustpython_vm::obj::{objbytes, objint, objsequence, objtype};
use rustpython_vm::obj::{objbytes, objtype};
use rustpython_vm::py_serde;
use rustpython_vm::pyobject::{ItemProtocol, PyObjectRef, PyResult, PyValue};
use rustpython_vm::VirtualMachine;
use num_bigint::BigInt;
use crate::browser_module;
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyBaseExceptionRef) -> JsValue {
macro_rules! map_exceptions {
($py_exc:ident, $msg:expr, { $($py_exc_ty:expr => $js_err_new:expr),*$(,)? }) => {
$(if objtype::isinstance($py_exc, $py_exc_ty) {
@@ -24,11 +22,11 @@ pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
}
};
}
let msg = match vm.to_pystr(py_err) {
let msg = match vm.to_str(py_err.as_object()) {
Ok(msg) => msg,
Err(_) => return js_sys::Error::new("error getting error").into(),
};
let js_err = map_exceptions!(py_err,& msg, {
let js_err = map_exceptions!(py_err, msg.as_str(), {
// TypeError is sort of a catch-all for "this value isn't what I thought it was like"
&vm.ctx.exceptions.type_error => js_sys::TypeError::new,
&vm.ctx.exceptions.value_error => js_sys::TypeError::new,
@@ -38,31 +36,36 @@ pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
&vm.ctx.exceptions.name_error => js_sys::ReferenceError::new,
&vm.ctx.exceptions.syntax_error => js_sys::SyntaxError::new,
});
if let Ok(tb) = vm.get_attribute(py_err.clone(), "__traceback__") {
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
let elements = objsequence::get_elements_list(&tb).to_vec();
if let Some(top) = elements.get(0) {
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
let element = objsequence::get_elements_tuple(&top);
if let Some(lineno) = objint::to_int(vm, &element[1], &BigInt::from(10))
.ok()
.and_then(|lineno| lineno.to_u32())
{
let _ = Reflect::set(&js_err, &"row".into(), &lineno.into());
}
}
}
}
if let Some(tb) = py_err.traceback() {
let _ = Reflect::set(&js_err, &"row".into(), &(tb.lineno as u32).into());
}
js_err
}
pub fn js_py_typeerror(vm: &VirtualMachine, js_err: JsValue) -> PyObjectRef {
pub fn js_py_typeerror(vm: &VirtualMachine, js_err: JsValue) -> PyBaseExceptionRef {
let msg = js_err.unchecked_into::<js_sys::Error>().to_string();
vm.new_type_error(msg.into())
}
pub fn js_err_to_py_err(vm: &VirtualMachine, js_err: &JsValue) -> PyBaseExceptionRef {
match js_err.dyn_ref::<js_sys::Error>() {
Some(err) => {
let exc_type = match String::from(err.name()).as_str() {
"TypeError" => &vm.ctx.exceptions.type_error,
"ReferenceError" => &vm.ctx.exceptions.name_error,
"SyntaxError" => &vm.ctx.exceptions.syntax_error,
_ => &vm.ctx.exceptions.exception_type,
}
.clone();
vm.new_exception_msg(exc_type, err.message().into())
}
None => vm.new_exception_msg(
vm.ctx.exceptions.exception_type.clone(),
format!("{:?}", js_err),
),
}
}
pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
if let Some(ref wasm_id) = vm.wasm_id {
if objtype::isinstance(&py_obj, &vm.ctx.function_type()) {
@@ -207,17 +210,10 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
}
func.apply(&this, &js_args)
.map(|val| js_to_py(vm, val))
.map_err(|err| js_to_py(vm, err))
.map_err(|err| js_err_to_py_err(vm, &err))
})
} else if let Some(err) = js_val.dyn_ref::<js_sys::Error>() {
let exc_type = match String::from(err.name()).as_str() {
"TypeError" => &vm.ctx.exceptions.type_error,
"ReferenceError" => &vm.ctx.exceptions.name_error,
"SyntaxError" => &vm.ctx.exceptions.syntax_error,
_ => &vm.ctx.exceptions.exception_type,
}
.clone();
vm.new_exception(exc_type, err.message().into())
js_err_to_py_err(vm, err).into_object()
} else if js_val.is_undefined() {
// Because `JSON.stringify(undefined)` returns undefined
vm.get_none()

View File

@@ -1,4 +1,5 @@
use js_sys::{Array, Object, Reflect};
use rustpython_vm::exceptions::PyBaseExceptionRef;
use rustpython_vm::function::Args;
use rustpython_vm::obj::{objfloat::PyFloatRef, objstr::PyStringRef, objtype::PyClassRef};
use rustpython_vm::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject};
@@ -233,10 +234,14 @@ struct NewObjectOptions {
prototype: Option<PyJsValueRef>,
}
fn new_js_error(vm: &VirtualMachine, err: JsValue) -> PyObjectRef {
let exc = vm.new_exception(vm.class("_js", "JsError"), format!("{:?}", err));
vm.set_attr(&exc, "js_value", PyJsValue::new(err).into_ref(vm))
.unwrap();
fn new_js_error(vm: &VirtualMachine, err: JsValue) -> PyBaseExceptionRef {
let exc = vm.new_exception_msg(vm.class("_js", "JsError"), format!("{:?}", err));
vm.set_attr(
exc.as_object(),
"js_value",
PyJsValue::new(err).into_ref(vm),
)
.unwrap();
exc
}

View File

@@ -232,7 +232,7 @@ impl WASMVirtualMachine {
&JsValue::UNDEFINED,
&wasm_builtins::format_print_args(vm, args)?.into(),
)
.map_err(|err| convert::js_to_py(vm, err))?;
.map_err(|err| convert::js_py_typeerror(vm, err))?;
Ok(vm.get_none())
})
} else if stdout.is_null() {