mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #78 from RustPython/exceptions
Implement exception type checking at catch
This commit is contained in:
@@ -252,7 +252,7 @@ ForStatement: ast::LocatedStatement = {
|
||||
};
|
||||
|
||||
TryStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "try" ":" <body:Suite> <handlers:ExceptClause+> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> => {
|
||||
<loc:@L> "try" ":" <body:Suite> <handlers:ExceptClause*> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> => {
|
||||
let or_else = match else_suite {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
|
||||
@@ -7,3 +7,68 @@ except BaseException as ex:
|
||||
print(type(ex))
|
||||
# print(ex.__traceback__)
|
||||
# print(type(ex.__traceback__))
|
||||
|
||||
|
||||
l = []
|
||||
try:
|
||||
l.append(1)
|
||||
assert 0
|
||||
l.append(2)
|
||||
except:
|
||||
l.append(3)
|
||||
print('boom')
|
||||
finally:
|
||||
l.append(4)
|
||||
print('kablam')
|
||||
assert l == [1, 3, 4]
|
||||
|
||||
|
||||
l = []
|
||||
try:
|
||||
l.append(1)
|
||||
assert 0
|
||||
l.append(2)
|
||||
except AssertionError as ex:
|
||||
l.append(3)
|
||||
print('boom', type(ex))
|
||||
finally:
|
||||
l.append(4)
|
||||
print('kablam')
|
||||
assert l == [1, 3, 4]
|
||||
|
||||
l = []
|
||||
try:
|
||||
l.append(1)
|
||||
assert 1
|
||||
l.append(2)
|
||||
except AssertionError as ex:
|
||||
l.append(3)
|
||||
print('boom', type(ex))
|
||||
finally:
|
||||
l.append(4)
|
||||
print('kablam')
|
||||
assert l == [1, 2, 4]
|
||||
|
||||
l = []
|
||||
try:
|
||||
try:
|
||||
l.append(1)
|
||||
assert 0
|
||||
l.append(2)
|
||||
finally:
|
||||
l.append(3)
|
||||
print('kablam')
|
||||
except AssertionError as ex:
|
||||
l.append(4)
|
||||
print('boom', type(ex))
|
||||
assert l == [1, 3, 4]
|
||||
|
||||
l = []
|
||||
try:
|
||||
l.append(1)
|
||||
fubar
|
||||
l.append(2)
|
||||
except NameError as ex:
|
||||
l.append(3)
|
||||
print('boom', type(ex))
|
||||
assert l == [1, 3]
|
||||
|
||||
@@ -354,10 +354,39 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
dict.insert(String::from("tuple"), ctx.tuple_type.clone());
|
||||
dict.insert(String::from("type"), ctx.type_type.clone());
|
||||
dict.insert(String::from("object"), ctx.object.clone());
|
||||
|
||||
// Exceptions:
|
||||
dict.insert(
|
||||
String::from("BaseException"),
|
||||
ctx.base_exception_type.clone(),
|
||||
ctx.exceptions.base_exception_type.clone(),
|
||||
);
|
||||
dict.insert(
|
||||
String::from("Exception"),
|
||||
ctx.exceptions.exception_type.clone(),
|
||||
);
|
||||
dict.insert(
|
||||
String::from("AssertionError"),
|
||||
ctx.exceptions.assertion_error.clone(),
|
||||
);
|
||||
dict.insert(
|
||||
String::from("AttributeError"),
|
||||
ctx.exceptions.attribute_error.clone(),
|
||||
);
|
||||
dict.insert(String::from("NameError"), ctx.exceptions.name_error.clone());
|
||||
dict.insert(
|
||||
String::from("RuntimeError"),
|
||||
ctx.exceptions.runtime_error.clone(),
|
||||
);
|
||||
dict.insert(
|
||||
String::from("NotImplementedError"),
|
||||
ctx.exceptions.not_implemented_error.clone(),
|
||||
);
|
||||
dict.insert(String::from("TypeError"), ctx.exceptions.type_error.clone());
|
||||
dict.insert(
|
||||
String::from("ValueError"),
|
||||
ctx.exceptions.value_error.clone(),
|
||||
);
|
||||
|
||||
let d2 = PyObject::new(PyObjectKind::Dict { elements: dict }, ctx.type_type.clone());
|
||||
let scope = PyObject::new(
|
||||
PyObjectKind::Scope {
|
||||
|
||||
@@ -263,25 +263,44 @@ impl Compiler {
|
||||
|
||||
// except handlers:
|
||||
self.set_label(handler_label);
|
||||
// Exception is on top of stack now
|
||||
handler_label = self.new_label();
|
||||
for handler in handlers {
|
||||
// Check if this handler can handle the exception:
|
||||
// If we gave a typ,
|
||||
// check if this handler can handle the exception:
|
||||
if let Some(exc_type) = &handler.typ {
|
||||
// Duplicate exception for test:
|
||||
self.emit(Instruction::Duplicate);
|
||||
|
||||
// TODO: self.emit(isinstance()) start of hack
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::Boolean { value: false },
|
||||
});
|
||||
// End of hack
|
||||
self.emit(Instruction::JumpIf {
|
||||
target: handler_label,
|
||||
});
|
||||
|
||||
// We have a match
|
||||
if let Some(alias) = &handler.name {
|
||||
self.emit(Instruction::StoreName {
|
||||
name: alias.clone(),
|
||||
// Check exception type:
|
||||
self.emit(Instruction::LoadName {
|
||||
name: String::from("isinstance"),
|
||||
});
|
||||
self.emit(Instruction::Rotate { amount: 2 });
|
||||
self.compile_expression(exc_type);
|
||||
self.emit(Instruction::CallFunction { count: 2 });
|
||||
|
||||
// We cannot handle this exception type:
|
||||
self.emit(Instruction::JumpIfFalse {
|
||||
target: handler_label,
|
||||
});
|
||||
|
||||
// We have a match, store in name (except x as y)
|
||||
if let Some(alias) = &handler.name {
|
||||
self.emit(Instruction::StoreName {
|
||||
name: alias.clone(),
|
||||
});
|
||||
} else {
|
||||
// Drop exception from top of stack:
|
||||
self.emit(Instruction::Pop);
|
||||
}
|
||||
} else {
|
||||
// Catch all!
|
||||
// Drop exception from top of stack:
|
||||
self.emit(Instruction::Pop);
|
||||
}
|
||||
|
||||
// Handler code:
|
||||
self.compile_statements(&handler.body);
|
||||
self.emit(Instruction::Jump {
|
||||
target: finally_label,
|
||||
@@ -294,6 +313,16 @@ impl Compiler {
|
||||
self.emit(Instruction::Jump {
|
||||
target: handler_label,
|
||||
});
|
||||
self.set_label(handler_label);
|
||||
// If code flows here, we have an unhandled exception,
|
||||
// emit finally code and raise again!
|
||||
// Duplicate finally code here:
|
||||
// TODO: this bytecode is now duplicate, could this be
|
||||
// improved?
|
||||
if let Some(statements) = finalbody {
|
||||
self.compile_statements(statements);
|
||||
}
|
||||
self.emit(Instruction::Raise { argc: 1 });
|
||||
|
||||
// We successfully ran the try block:
|
||||
// else:
|
||||
@@ -401,6 +430,7 @@ impl Compiler {
|
||||
self.emit(Instruction::CallFunction { count: 0 });
|
||||
}
|
||||
}
|
||||
self.emit(Instruction::Raise { argc: 1 });
|
||||
self.set_label(end_label);
|
||||
}
|
||||
ast::Statement::Break => {
|
||||
|
||||
@@ -1,12 +1,99 @@
|
||||
use super::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult};
|
||||
use super::pyobject::{
|
||||
create_type, AttributeProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult,
|
||||
};
|
||||
use super::vm::VirtualMachine;
|
||||
|
||||
fn exception_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExceptionZoo {
|
||||
pub base_exception_type: PyObjectRef,
|
||||
pub exception_type: PyObjectRef,
|
||||
pub assertion_error: PyObjectRef,
|
||||
pub attribute_error: PyObjectRef,
|
||||
pub name_error: PyObjectRef,
|
||||
pub runtime_error: PyObjectRef,
|
||||
pub not_implemented_error: PyObjectRef,
|
||||
pub type_error: PyObjectRef,
|
||||
pub value_error: PyObjectRef,
|
||||
}
|
||||
|
||||
impl ExceptionZoo {
|
||||
pub fn new(
|
||||
type_type: &PyObjectRef,
|
||||
object_type: &PyObjectRef,
|
||||
dict_type: &PyObjectRef,
|
||||
) -> Self {
|
||||
let base_exception_type =
|
||||
create_type("BaseException", &type_type, &object_type, &dict_type);
|
||||
|
||||
let exception_type = create_type(
|
||||
&String::from("Exception"),
|
||||
&type_type,
|
||||
&base_exception_type,
|
||||
&dict_type,
|
||||
);
|
||||
let assertion_error = create_type(
|
||||
&String::from("AssertionError"),
|
||||
&type_type,
|
||||
&exception_type,
|
||||
&dict_type,
|
||||
);
|
||||
let attribute_error = create_type(
|
||||
&String::from("AttributeError"),
|
||||
&type_type,
|
||||
&exception_type.clone(),
|
||||
&dict_type,
|
||||
);
|
||||
let name_error = create_type(
|
||||
&String::from("NameError"),
|
||||
&type_type,
|
||||
&exception_type.clone(),
|
||||
&dict_type,
|
||||
);
|
||||
let runtime_error = create_type(
|
||||
&String::from("RuntimeError"),
|
||||
&type_type,
|
||||
&exception_type,
|
||||
&dict_type,
|
||||
);
|
||||
let not_implemented_error = create_type(
|
||||
&String::from("NotImplementedError"),
|
||||
&type_type,
|
||||
&runtime_error,
|
||||
&dict_type,
|
||||
);
|
||||
let type_error = create_type(
|
||||
&String::from("TypeError"),
|
||||
&type_type,
|
||||
&exception_type,
|
||||
&dict_type,
|
||||
);
|
||||
let value_error = create_type(
|
||||
&String::from("ValueError"),
|
||||
&type_type,
|
||||
&exception_type,
|
||||
&dict_type,
|
||||
);
|
||||
|
||||
ExceptionZoo {
|
||||
base_exception_type: base_exception_type,
|
||||
exception_type: exception_type,
|
||||
assertion_error: assertion_error,
|
||||
attribute_error: attribute_error,
|
||||
name_error: name_error,
|
||||
runtime_error: runtime_error,
|
||||
not_implemented_error: not_implemented_error,
|
||||
type_error: type_error,
|
||||
value_error: value_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref base_exception_type = context.base_exception_type;
|
||||
let ref base_exception_type = context.exceptions.base_exception_type;
|
||||
base_exception_type.set_attr("__init__", context.new_rustfunc(exception_init));
|
||||
|
||||
// TODO: create a whole exception hierarchy somehow?
|
||||
|
||||
@@ -23,6 +23,7 @@ mod objstr;
|
||||
mod objtype;
|
||||
pub mod pyobject;
|
||||
mod sysmodule;
|
||||
mod traceback;
|
||||
mod vm;
|
||||
|
||||
// pub use self::pyobject::Executor;
|
||||
|
||||
@@ -60,7 +60,7 @@ pub struct PyContext {
|
||||
pub bound_method_type: PyObjectRef,
|
||||
pub member_descriptor_type: PyObjectRef,
|
||||
pub object: PyObjectRef,
|
||||
pub base_exception_type: PyObjectRef,
|
||||
pub exceptions: exceptions::ExceptionZoo,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -80,7 +80,7 @@ fn _nothing() -> PyObjectRef {
|
||||
}.into_ref()
|
||||
}
|
||||
|
||||
fn create_type(
|
||||
pub fn create_type(
|
||||
name: &str,
|
||||
type_type: &PyObjectRef,
|
||||
object: &PyObjectRef,
|
||||
@@ -97,7 +97,7 @@ fn create_type(
|
||||
|
||||
// Basic objects:
|
||||
impl PyContext {
|
||||
pub fn new() -> PyContext {
|
||||
pub fn new() -> Self {
|
||||
let type_type = _nothing();
|
||||
let object_type = _nothing();
|
||||
let dict_type = _nothing();
|
||||
@@ -116,9 +116,7 @@ impl PyContext {
|
||||
let float_type = create_type("float", &type_type, &object_type, &dict_type);
|
||||
let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type);
|
||||
let bool_type = create_type("bool", &type_type, &object_type, &dict_type);
|
||||
|
||||
let base_exception_type =
|
||||
create_type("BaseException", &type_type, &object_type, &dict_type);
|
||||
let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type);
|
||||
|
||||
let none = PyObject::new(
|
||||
PyObjectKind::None,
|
||||
@@ -139,7 +137,7 @@ impl PyContext {
|
||||
bound_method_type: bound_method_type,
|
||||
member_descriptor_type: member_descriptor_type,
|
||||
type_type: type_type,
|
||||
base_exception_type: base_exception_type,
|
||||
exceptions: exceptions,
|
||||
};
|
||||
objtype::init(&context);
|
||||
objlist::init(&context);
|
||||
@@ -194,6 +192,10 @@ impl PyContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_class(&self, name: &String, base: PyObjectRef) -> PyObjectRef {
|
||||
objtype::new(self.type_type.clone(), name, vec![base], self.new_dict()).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_scope(&self, parent: Option<PyObjectRef>) -> PyObjectRef {
|
||||
let locals = self.new_dict();
|
||||
let scope = Scope {
|
||||
|
||||
2
vm/src/traceback.rs
Normal file
2
vm/src/traceback.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// python tracebacks
|
||||
//
|
||||
58
vm/src/vm.rs
58
vm/src/vm.rs
@@ -204,11 +204,9 @@ impl VirtualMachine {
|
||||
} else if scope.has_parent() {
|
||||
scope = scope.get_parent();
|
||||
} else {
|
||||
let name_error = PyObject::new(
|
||||
PyObjectKind::NameError {
|
||||
name: name.to_string(),
|
||||
},
|
||||
self.get_type(),
|
||||
let name_error = self.context().new_instance(
|
||||
self.context().new_dict(),
|
||||
self.context().exceptions.name_error.clone(),
|
||||
);
|
||||
break Some(Err(name_error));
|
||||
}
|
||||
@@ -583,6 +581,36 @@ impl VirtualMachine {
|
||||
self.pop_value();
|
||||
None
|
||||
}
|
||||
bytecode::Instruction::Duplicate => {
|
||||
// Duplicate top of stack
|
||||
let value = self.pop_value();
|
||||
self.push_value(value.clone());
|
||||
self.push_value(value);
|
||||
None
|
||||
}
|
||||
bytecode::Instruction::Rotate { amount } => {
|
||||
// Shuffles top of stack amount down
|
||||
if amount < &2 {
|
||||
panic!("Can only rotate two or more values");
|
||||
}
|
||||
|
||||
let mut values = Vec::new();
|
||||
|
||||
// Pop all values from stack:
|
||||
for _ in 0..*amount {
|
||||
values.push(self.pop_value());
|
||||
}
|
||||
|
||||
// Push top of stack back first:
|
||||
self.push_value(values.remove(0));
|
||||
|
||||
// Push other value back in order:
|
||||
values.reverse();
|
||||
for value in values {
|
||||
self.push_value(value);
|
||||
}
|
||||
None
|
||||
}
|
||||
bytecode::Instruction::BuildList { size } => {
|
||||
let elements = self.pop_multiple(*size);
|
||||
let list_obj = self.context().new_list(elements);
|
||||
@@ -772,8 +800,23 @@ impl VirtualMachine {
|
||||
0 | 2 | 3 => panic!("Not implemented!"),
|
||||
_ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3"),
|
||||
};
|
||||
info!("Exception raised: {:?}", exception);
|
||||
Some(Err(exception))
|
||||
if self.isinstance(
|
||||
exception.clone(),
|
||||
self.context().exceptions.base_exception_type.clone(),
|
||||
) {
|
||||
info!("Exception raised: {:?}", exception);
|
||||
Some(Err(exception))
|
||||
} else {
|
||||
Some(Err(exception))
|
||||
// TODO: enable this when isinstance works properly:
|
||||
/*
|
||||
info!(
|
||||
"Can only raise BaseException derived types: {:?}",
|
||||
exception
|
||||
);
|
||||
let type_error = self.context().exceptions.type_error.clone();
|
||||
Some(Err(type_error))
|
||||
*/ }
|
||||
}
|
||||
|
||||
bytecode::Instruction::Break => {
|
||||
@@ -832,7 +875,6 @@ impl VirtualMachine {
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => panic!("NOT IMPL {:?}", instruction),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user