use crate::builtins::pystr::{PyStr, PyStrRef};
use crate::builtins::pytype::{PyType, PyTypeRef};
use crate::builtins::singletons::{PyNone, PyNoneRef};
use crate::builtins::traceback::PyTracebackRef;
use crate::builtins::tuple::{PyTuple, PyTupleRef};
use crate::common::lock::PyRwLock;
use crate::function::FuncArgs;
use crate::py_io::{self, Write};
use crate::pyobject::StaticType;
use crate::pyobject::{
BorrowValue, IntoPyObject, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult,
PyValue, TryFromObject, TypeProtocol,
};
use crate::types::create_type_with_slots;
use crate::VirtualMachine;
use crate::{py_serde, sysmodule};
use crossbeam_utils::atomic::AtomicCell;
use itertools::Itertools;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
#[pyclass(module = false, name = "BaseException")]
pub struct PyBaseException {
traceback: PyRwLock>,
cause: PyRwLock >,
context: PyRwLock >,
suppress_context: AtomicCell,
args: PyRwLock,
}
impl fmt::Debug for PyBaseException {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO: implement more detailed, non-recursive Debug formatter
f.write_str("PyBaseException")
}
}
pub type PyBaseExceptionRef = PyRef;
pub trait IntoPyException {
fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef;
}
impl PyValue for PyBaseException {
fn class(vm: &VirtualMachine) -> &PyTypeRef {
&vm.ctx.exceptions.base_exception_type
}
}
#[pyimpl(flags(BASETYPE, HAS_DICT))]
impl PyBaseException {
pub(crate) fn new(args: Vec, vm: &VirtualMachine) -> PyBaseException {
PyBaseException {
traceback: PyRwLock::new(None),
cause: PyRwLock::new(None),
context: PyRwLock::new(None),
suppress_context: AtomicCell::new(false),
args: PyRwLock::new(PyTupleRef::with_elements(args, &vm.ctx)),
}
}
#[pyslot]
fn tp_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult> {
PyBaseException::new(args.args, vm).into_ref_with_type(vm, cls)
}
#[pymethod(name = "__init__")]
fn init(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
*self.args.write() = PyTupleRef::with_elements(args.args, &vm.ctx);
Ok(())
}
pub fn get_arg(&self, idx: usize) -> Option {
self.args.read().borrow_value().get(idx).cloned()
}
#[pyproperty]
pub fn args(&self) -> PyTupleRef {
self.args.read().clone()
}
#[pyproperty(setter)]
fn set_args(&self, args: PyIterable, vm: &VirtualMachine) -> PyResult<()> {
let args = args.iter(vm)?.collect::>>()?;
*self.args.write() = PyTupleRef::with_elements(args, &vm.ctx);
Ok(())
}
#[pyproperty(name = "__traceback__")]
pub fn traceback(&self) -> Option {
self.traceback.read().clone()
}
#[pyproperty(name = "__traceback__", setter)]
pub fn set_traceback(&self, traceback: Option) {
*self.traceback.write() = traceback;
}
#[pyproperty(name = "__cause__")]
pub fn cause(&self) -> Option {
self.cause.read().clone()
}
#[pyproperty(name = "__cause__", setter)]
pub fn set_cause(&self, cause: Option) {
*self.cause.write() = cause;
}
#[pyproperty(name = "__context__")]
pub fn context(&self) -> Option {
self.context.read().clone()
}
#[pyproperty(name = "__context__", setter)]
pub fn set_context(&self, context: Option) {
*self.context.write() = context;
}
#[pyproperty(name = "__suppress_context__")]
fn get_suppress_context(&self) -> bool {
self.suppress_context.load()
}
#[pyproperty(name = "__suppress_context__", setter)]
fn set_suppress_context(&self, suppress_context: bool) {
self.suppress_context.store(suppress_context);
}
#[pymethod]
fn with_traceback(zelf: PyRef, tb: Option) -> PyResult {
*zelf.traceback.write() = tb;
Ok(zelf.as_object().clone())
}
#[pymethod(name = "__str__")]
fn str(&self, vm: &VirtualMachine) -> PyStrRef {
let str_args = exception_args_as_string(vm, self.args(), true);
match str_args.into_iter().exactly_one() {
Err(i) if i.len() == 0 => PyStr::from("").into_ref(vm),
Ok(s) => s,
Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(vm),
}
}
#[pymethod(name = "__repr__")]
fn repr(zelf: PyRef, vm: &VirtualMachine) -> String {
let repr_args = exception_args_as_string(vm, zelf.args(), false);
let cls = zelf.class();
format!("{}({})", cls.name, repr_args.iter().format(", "))
}
}
pub fn chain(e1: PyResult<()>, e2: PyResult) -> PyResult {
match (e1, e2) {
(Err(e1), Err(e)) => {
e.set_context(Some(e1));
Err(e)
}
(Err(e), Ok(_)) | (Ok(()), Err(e)) => Err(e),
(Ok(()), Ok(close_res)) => Ok(close_res),
}
}
/// Print exception chain by calling sys.excepthook
pub fn print_exception(vm: &VirtualMachine, exc: PyBaseExceptionRef) {
let write_fallback = |exc, errstr| {
if let Ok(stderr) = sysmodule::get_stderr(vm) {
let mut stderr = py_io::PyWriter(stderr, vm);
// if this fails stderr might be closed -- ignore it
let _ = writeln!(stderr, "{}", errstr);
let _ = write_exception(&mut stderr, vm, exc);
} else {
eprintln!("{}\nlost sys.stderr", errstr);
let _ = write_exception(&mut io::stderr(), vm, exc);
}
};
if let Ok(excepthook) = vm.get_attribute(vm.sys_module.clone(), "excepthook") {
let (exc_type, exc_val, exc_tb) = split(exc.clone(), vm);
if let Err(eh_exc) = vm.invoke(&excepthook, vec![exc_type, exc_val, exc_tb]) {
write_fallback(&eh_exc, "Error in sys.excepthook:");
write_fallback(&exc, "Original exception was:");
}
} else {
write_fallback(&exc, "missing sys.excepthook");
}
}
pub fn write_exception(
output: &mut W,
vm: &VirtualMachine,
exc: &PyBaseExceptionRef,
) -> Result<(), W::Error> {
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"
)?;
}
write_exception_inner(output, vm, exc)
}
fn print_source_line(
output: &mut W,
filename: &str,
lineno: usize,
) -> Result<(), W::Error> {
// 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(()),
};
let file = BufReader::new(file);
for (i, line) in file.lines().enumerate() {
if i + 1 == lineno {
if let Ok(line) = line {
// Indented with 4 spaces
writeln!(output, " {}", line.trim_start())?;
}
return Ok(());
}
}
Ok(())
}
/// Print exception occurrence location from traceback element
fn write_traceback_entry(
output: &mut W,
tb_entry: &PyTracebackRef,
) -> Result<(), W::Error> {
let filename = tb_entry.frame.code.source_path.to_owned();
writeln!(
output,
r##" File "{}", line {}, in {}"##,
filename, tb_entry.lineno, tb_entry.frame.code.obj_name
)?;
print_source_line(output, &filename, tb_entry.lineno)?;
Ok(())
}
/// Print exception with traceback
pub fn write_exception_inner(
output: &mut W,
vm: &VirtualMachine,
exc: &PyBaseExceptionRef,
) -> Result<(), W::Error> {
if let Some(tb) = exc.traceback.read().clone() {
writeln!(output, "Traceback (most recent call last):")?;
for tb in tb.iter() {
write_traceback_entry(output, &tb)?;
}
}
let varargs = exc.args();
let args_repr = exception_args_as_string(vm, varargs, true);
let exc_name = exc.class().name.clone();
match args_repr.len() {
0 => writeln!(output, "{}", exc_name),
1 => writeln!(output, "{}: {}", exc_name, args_repr[0]),
_ => writeln!(
output,
"{}: ({})",
exc_name,
args_repr.into_iter().format(", ")
),
}
}
fn exception_args_as_string(
vm: &VirtualMachine,
varargs: PyTupleRef,
str_single: bool,
) -> Vec {
let varargs = varargs.borrow_value();
match varargs.len() {
0 => vec![],
1 => {
let args0_repr = if str_single {
vm.to_str(&varargs[0])
.unwrap_or_else(|_| PyStr::from("").into_ref(vm))
} else {
vm.to_repr(&varargs[0])
.unwrap_or_else(|_| PyStr::from("").into_ref(vm))
};
vec![args0_repr]
}
_ => varargs
.iter()
.map(|vararg| {
vm.to_repr(vararg)
.unwrap_or_else(|_| PyStr::from("").into_ref(vm))
})
.collect(),
}
}
#[derive(Clone)]
pub enum ExceptionCtor {
Class(PyTypeRef),
Instance(PyBaseExceptionRef),
}
impl TryFromObject for ExceptionCtor {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult {
obj.downcast::()
.and_then(|cls| {
if cls.issubclass(&vm.ctx.exceptions.base_exception_type) {
Ok(Self::Class(cls))
} else {
Err(cls.into_object())
}
})
.or_else(|obj| obj.downcast::().map(Self::Instance))
.map_err(|obj| {
vm.new_type_error(format!(
"exceptions must be classes or instances deriving from BaseException, not {}",
obj.class().name
))
})
}
}
pub fn invoke(
cls: PyTypeRef,
args: Vec,
vm: &VirtualMachine,
) -> PyResult {
// 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 {
match self {
Self::Class(cls) => invoke(cls, vec![], vm),
Self::Instance(exc) => Ok(exc),
}
}
pub fn instantiate_value(
self,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let exc_inst = value.clone().downcast::().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_owned()))
}
// 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 exc.isinstance(&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.borrow_value().to_vec(),
exc @ PyBaseException => exc.args().borrow_value().to_vec(),
obj => vec![obj],
});
invoke(cls, args, vm)
}
}
}
}
pub fn split(
exc: PyBaseExceptionRef,
vm: &VirtualMachine,
) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
let tb = exc.traceback().into_pyobject(vm);
(exc.clone_class().into_object(), exc.into_object(), tb)
}
/// Similar to PyErr_NormalizeException in CPython
pub fn normalize(
exc_type: PyObjectRef,
exc_val: PyObjectRef,
exc_tb: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let ctor = ExceptionCtor::try_from_object(vm, exc_type)?;
let exc = ctor.instantiate_value(exc_val, vm)?;
if let Some(tb) = Option::::try_from_object(vm, exc_tb)? {
exc.set_traceback(Some(tb));
}
Ok(exc)
}
#[derive(Debug, Clone)]
pub struct ExceptionZoo {
pub base_exception_type: PyTypeRef,
pub system_exit: PyTypeRef,
pub keyboard_interrupt: PyTypeRef,
pub generator_exit: PyTypeRef,
pub exception_type: PyTypeRef,
pub stop_iteration: PyTypeRef,
pub stop_async_iteration: PyTypeRef,
pub arithmetic_error: PyTypeRef,
pub floating_point_error: PyTypeRef,
pub overflow_error: PyTypeRef,
pub zero_division_error: PyTypeRef,
pub assertion_error: PyTypeRef,
pub attribute_error: PyTypeRef,
pub buffer_error: PyTypeRef,
pub eof_error: PyTypeRef,
pub import_error: PyTypeRef,
pub module_not_found_error: PyTypeRef,
pub lookup_error: PyTypeRef,
pub index_error: PyTypeRef,
pub key_error: PyTypeRef,
pub memory_error: PyTypeRef,
pub name_error: PyTypeRef,
pub unbound_local_error: PyTypeRef,
pub os_error: PyTypeRef,
pub blocking_io_error: PyTypeRef,
pub child_process_error: PyTypeRef,
pub connection_error: PyTypeRef,
pub broken_pipe_error: PyTypeRef,
pub connection_aborted_error: PyTypeRef,
pub connection_refused_error: PyTypeRef,
pub connection_reset_error: PyTypeRef,
pub file_exists_error: PyTypeRef,
pub file_not_found_error: PyTypeRef,
pub interrupted_error: PyTypeRef,
pub is_a_directory_error: PyTypeRef,
pub not_a_directory_error: PyTypeRef,
pub permission_error: PyTypeRef,
pub process_lookup_error: PyTypeRef,
pub timeout_error: PyTypeRef,
pub reference_error: PyTypeRef,
pub runtime_error: PyTypeRef,
pub not_implemented_error: PyTypeRef,
pub recursion_error: PyTypeRef,
pub syntax_error: PyTypeRef,
pub target_scope_error: PyTypeRef,
pub indentation_error: PyTypeRef,
pub tab_error: PyTypeRef,
pub system_error: PyTypeRef,
pub type_error: PyTypeRef,
pub value_error: PyTypeRef,
pub unicode_error: PyTypeRef,
pub unicode_decode_error: PyTypeRef,
pub unicode_encode_error: PyTypeRef,
pub unicode_translate_error: PyTypeRef,
#[cfg(feature = "jit")]
pub jit_error: PyTypeRef,
pub warning: PyTypeRef,
pub deprecation_warning: PyTypeRef,
pub pending_deprecation_warning: PyTypeRef,
pub runtime_warning: PyTypeRef,
pub syntax_warning: PyTypeRef,
pub user_warning: PyTypeRef,
pub future_warning: PyTypeRef,
pub import_warning: PyTypeRef,
pub unicode_warning: PyTypeRef,
pub bytes_warning: PyTypeRef,
pub resource_warning: PyTypeRef,
}
impl ExceptionZoo {
pub(crate) fn init() -> Self {
let base_exception_type = PyBaseException::init_bare_type().clone();
let create_exception_type = |name: &str, base: &PyTypeRef| {
create_type_with_slots(
name,
PyType::static_type(),
base,
PyBaseException::make_slots(),
)
};
// Sorted By Hierarchy then alphabetized.
let system_exit = create_exception_type("SystemExit", &base_exception_type);
let keyboard_interrupt = create_exception_type("KeyboardInterrupt", &base_exception_type);
let generator_exit = create_exception_type("GeneratorExit", &base_exception_type);
let exception_type = create_exception_type("Exception", &base_exception_type);
let stop_iteration = create_exception_type("StopIteration", &exception_type);
let stop_async_iteration = create_exception_type("StopAsyncIteration", &exception_type);
let arithmetic_error = create_exception_type("ArithmeticError", &exception_type);
let floating_point_error = create_exception_type("FloatingPointError", &arithmetic_error);
let overflow_error = create_exception_type("OverflowError", &arithmetic_error);
let zero_division_error = create_exception_type("ZeroDivisionError", &arithmetic_error);
let assertion_error = create_exception_type("AssertionError", &exception_type);
let attribute_error = create_exception_type("AttributeError", &exception_type);
let buffer_error = create_exception_type("BufferError", &exception_type);
let eof_error = create_exception_type("EOFError", &exception_type);
let import_error = create_exception_type("ImportError", &exception_type);
let module_not_found_error = create_exception_type("ModuleNotFoundError", &import_error);
let lookup_error = create_exception_type("LookupError", &exception_type);
let index_error = create_exception_type("IndexError", &lookup_error);
let key_error = create_exception_type("KeyError", &lookup_error);
let memory_error = create_exception_type("MemoryError", &exception_type);
let name_error = create_exception_type("NameError", &exception_type);
let unbound_local_error = create_exception_type("UnboundLocalError", &name_error);
// os errors
let os_error = create_exception_type("OSError", &exception_type);
let blocking_io_error = create_exception_type("BlockingIOError", &os_error);
let child_process_error = create_exception_type("ChildProcessError", &os_error);
let connection_error = create_exception_type("ConnectionError", &os_error);
let connection_aborted_error =
create_exception_type("ConnectionAbortedError", &connection_error);
let connection_refused_error =
create_exception_type("ConnectionRefusedError", &connection_error);
let connection_reset_error =
create_exception_type("ConnectionResetError", &connection_error);
let file_exists_error = create_exception_type("FileExistsError", &os_error);
let file_not_found_error = create_exception_type("FileNotFoundError", &os_error);
let interrupted_error = create_exception_type("InterruptedError", &os_error);
let is_a_directory_error = create_exception_type("IsADirectoryError", &os_error);
let not_a_directory_error = create_exception_type("NotADirectoryError", &os_error);
let broken_pipe_error = create_exception_type("BrokenPipeError", &connection_error);
let permission_error = create_exception_type("PermissionError", &os_error);
let process_lookup_error = create_exception_type("ProcessLookupError", &os_error);
let timeout_error = create_exception_type("TimeoutError", &os_error);
let reference_error = create_exception_type("ReferenceError", &exception_type);
let runtime_error = create_exception_type("RuntimeError", &exception_type);
let not_implemented_error = create_exception_type("NotImplementedError", &runtime_error);
let recursion_error = create_exception_type("RecursionError", &runtime_error);
let syntax_error = create_exception_type("SyntaxError", &exception_type);
let indentation_error = create_exception_type("IndentationError", &syntax_error);
let tab_error = create_exception_type("TabError", &indentation_error);
let target_scope_error = create_exception_type("TargetScopeError", &syntax_error);
let system_error = create_exception_type("SystemError", &exception_type);
let type_error = create_exception_type("TypeError", &exception_type);
let value_error = create_exception_type("ValueError", &exception_type);
let unicode_error = create_exception_type("UnicodeError", &value_error);
let unicode_decode_error = create_exception_type("UnicodeDecodeError", &unicode_error);
let unicode_encode_error = create_exception_type("UnicodeEncodeError", &unicode_error);
let unicode_translate_error =
create_exception_type("UnicodeTranslateError", &unicode_error);
#[cfg(feature = "jit")]
let jit_error = create_exception_type("JitError", &exception_type);
let warning = create_exception_type("Warning", &exception_type);
let deprecation_warning = create_exception_type("DeprecationWarning", &warning);
let pending_deprecation_warning =
create_exception_type("PendingDeprecationWarning", &warning);
let runtime_warning = create_exception_type("RuntimeWarning", &warning);
let syntax_warning = create_exception_type("SyntaxWarning", &warning);
let user_warning = create_exception_type("UserWarning", &warning);
let future_warning = create_exception_type("FutureWarning", &warning);
let import_warning = create_exception_type("ImportWarning", &warning);
let unicode_warning = create_exception_type("UnicodeWarning", &warning);
let bytes_warning = create_exception_type("BytesWarning", &warning);
let resource_warning = create_exception_type("ResourceWarning", &warning);
Self {
base_exception_type,
system_exit,
keyboard_interrupt,
generator_exit,
exception_type,
stop_iteration,
stop_async_iteration,
arithmetic_error,
floating_point_error,
overflow_error,
zero_division_error,
assertion_error,
attribute_error,
buffer_error,
eof_error,
import_error,
module_not_found_error,
lookup_error,
index_error,
key_error,
memory_error,
name_error,
unbound_local_error,
os_error,
blocking_io_error,
child_process_error,
connection_error,
broken_pipe_error,
connection_aborted_error,
connection_refused_error,
connection_reset_error,
file_exists_error,
file_not_found_error,
interrupted_error,
is_a_directory_error,
not_a_directory_error,
permission_error,
process_lookup_error,
timeout_error,
reference_error,
runtime_error,
not_implemented_error,
recursion_error,
syntax_error,
target_scope_error,
indentation_error,
tab_error,
system_error,
type_error,
value_error,
unicode_error,
unicode_decode_error,
unicode_encode_error,
unicode_translate_error,
#[cfg(feature = "jit")]
jit_error,
warning,
deprecation_warning,
pending_deprecation_warning,
runtime_warning,
syntax_warning,
user_warning,
future_warning,
import_warning,
unicode_warning,
bytes_warning,
resource_warning,
}
}
pub fn extend(ctx: &PyContext) {
let excs = &ctx.exceptions;
PyBaseException::extend_class(ctx, &excs.base_exception_type);
extend_class!(ctx, &excs.syntax_error, {
"msg" => ctx.new_readonly_getset("msg", make_arg_getter(0)),
// TODO: members
"filename" => ctx.none(),
"lineno" => ctx.none(),
"offset" => ctx.none(),
"text" => ctx.none(),
});
extend_class!(ctx, &excs.system_exit, {
"code" => ctx.new_readonly_getset("code", system_exit_code),
});
extend_class!(ctx, &excs.import_error, {
"__init__" => ctx.new_method(import_error_init),
"msg" => ctx.new_readonly_getset("msg", make_arg_getter(0)),
});
extend_class!(ctx, &excs.stop_iteration, {
"value" => ctx.new_readonly_getset("value", make_arg_getter(0)),
});
extend_class!(ctx, &excs.key_error, {
"__str__" => ctx.new_method(key_error_str),
});
extend_class!(ctx, &excs.unicode_decode_error, {
"encoding" => ctx.new_readonly_getset("encoding", make_arg_getter(0)),
"object" => ctx.new_readonly_getset("object", make_arg_getter(1)),
"start" => ctx.new_readonly_getset("start", make_arg_getter(2)),
"end" => ctx.new_readonly_getset("end", make_arg_getter(3)),
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(4)),
});
extend_class!(ctx, &excs.unicode_encode_error, {
"encoding" => ctx.new_readonly_getset("encoding", make_arg_getter(0)),
"object" => ctx.new_readonly_getset("object", make_arg_getter(1)),
"start" => ctx.new_readonly_getset("start", make_arg_getter(2)),
"end" => ctx.new_readonly_getset("end", make_arg_getter(3)),
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(4)),
});
extend_class!(ctx, &excs.unicode_translate_error, {
"encoding" => ctx.new_readonly_getset("encoding", none_getter),
"object" => ctx.new_readonly_getset("object", make_arg_getter(0)),
"start" => ctx.new_readonly_getset("start", make_arg_getter(1)),
"end" => ctx.new_readonly_getset("end", make_arg_getter(2)),
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(3)),
});
}
}
fn import_error_init(exc_self: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
vm.set_attr(
&exc_self,
"name",
vm.unwrap_or_none(args.kwargs.get("name").cloned()),
)?;
vm.set_attr(
&exc_self,
"path",
vm.unwrap_or_none(args.kwargs.get("path").cloned()),
)?;
Ok(())
}
fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyNoneRef {
vm.ctx.none.clone()
}
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option {
move |exc| exc.get_arg(idx)
}
fn key_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
let args = exc.args();
if args.borrow_value().len() == 1 {
exception_args_as_string(vm, args, false)
.into_iter()
.exactly_one()
.unwrap()
} else {
exc.str(vm)
}
}
fn system_exit_code(exc: PyBaseExceptionRef) -> Option {
exc.args.read().borrow_value().first().map(|code| {
match_class!(match code {
ref tup @ PyTuple => match tup.borrow_value() {
[x] => x.clone(),
_ => code.clone(),
},
other => other.clone(),
})
})
}
pub struct SerializeException<'s> {
vm: &'s VirtualMachine,
exc: &'s PyBaseExceptionRef,
}
impl<'s> SerializeException<'s> {
pub fn new(vm: &'s VirtualMachine, exc: &'s PyBaseExceptionRef) -> Self {
SerializeException { vm, exc }
}
}
impl serde::Serialize for SerializeException<'_> {
fn serialize(&self, s: S) -> Result {
use serde::ser::*;
let mut struc = s.serialize_struct("PyBaseException", 7)?;
struc.serialize_field("exc_type", &self.exc.class().name)?;
let tbs = {
struct Tracebacks(PyTracebackRef);
impl serde::Serialize for Tracebacks {
fn serialize(&self, s: S) -> Result {
let mut s = s.serialize_seq(None)?;
for tb in self.0.iter() {
s.serialize_element(&*tb)?;
}
s.end()
}
}
self.exc.traceback().map(Tracebacks)
};
struc.serialize_field("traceback", &tbs)?;
struc.serialize_field(
"cause",
&self.exc.cause().as_ref().map(|e| Self::new(self.vm, e)),
)?;
struc.serialize_field(
"context",
&self.exc.context().as_ref().map(|e| Self::new(self.vm, e)),
)?;
struc.serialize_field("suppress_context", &self.exc.suppress_context.load())?;
let args = {
struct Args<'vm>(&'vm VirtualMachine, PyTupleRef);
impl serde::Serialize for Args<'_> {
fn serialize(&self, s: S) -> Result {
s.collect_seq(
self.1
.borrow_value()
.iter()
.map(|arg| py_serde::PyObjectSerializer::new(self.0, arg)),
)
}
}
Args(self.vm, self.exc.args())
};
struc.serialize_field("args", &args)?;
let rendered = {
let mut rendered = Vec::::new();
write_exception(&mut rendered, self.vm, &self.exc).map_err(S::Error::custom)?;
String::from_utf8(rendered).map_err(S::Error::custom)?
};
struc.serialize_field("rendered", &rendered)?;
struc.end()
}
}