mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Merge pull request #1644 from RustPython/coolreader18/exc-cleanup
Use PyBaseExceptionRef in places where we expect exceptions
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
11
src/shell.rs
11
src/shell.rs
@@ -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 => {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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, {
|
||||
|
||||
115
vm/src/frame.rs
115
vm/src/frame.rs
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
132
vm/src/vm.rs
132
vm/src/vm.rs
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user