Implement atexit functionality

This commit is contained in:
Noah committed 2020-09-18 12:00:24 -05:00
1 parent a5c368ac43
commit ee2dff68b3
9 files changed
+127 -28

No files matched your search

-9
View File
@@ -1,9 +0,0 @@
# Dummy implementation of atexit
def register(func, *args, **kwargs):
return func
def unregister(func):
pass
+2 -2
View File
@@ -2051,8 +2051,8 @@ def shutdown(handlerList=_handlerList):
#else, swallow
#Let's try and shutdown automatically on application exit...
# import atexit
# atexit.register(shutdown)
import atexit
atexit.register(shutdown)
# Null handler
+25 -14
View File
@@ -53,7 +53,7 @@ fn main() {
let interp = Interpreter::new(settings, init);
interp.enter(move |vm| {
let exitcode = interp.enter(move |vm| {
let res = run_rustpython(vm, &matches);
#[cfg(feature = "flame-it")]
@@ -65,22 +65,25 @@ fn main() {
}
// See if any exception leaked out:
if let Err(err) = res {
if objtype::isinstance(&err, &vm.ctx.exceptions.system_exit) {
let exitcode = match res {
Ok(()) => 0,
Err(err) if objtype::isinstance(&err, &vm.ctx.exceptions.system_exit) => {
let args = err.args();
match args.borrow_value().len() {
0 => return,
0 => 0,
1 => match_class!(match args.borrow_value()[0].clone() {
i @ PyInt => {
use num_traits::cast::ToPrimitive;
process::exit(i.borrow_value().to_i32().unwrap_or(0));
i.borrow_value().to_i32().unwrap_or(0)
}
arg => {
if vm.is_none(&arg) {
return;
}
if let Ok(s) = vm.to_str(&arg) {
eprintln!("{}", s);
0
} else {
if let Ok(s) = vm.to_str(&arg) {
eprintln!("{}", s);
}
1
}
}
}),
@@ -88,14 +91,22 @@ fn main() {
if let Ok(r) = vm.to_repr(args.as_object()) {
eprintln!("{}", r);
}
1
}
}
} else {
print_exception(&vm, err);
}
process::exit(1);
}
})
Err(err) => {
print_exception(&vm, err);
1
}
};
let _ = vm.run_atexit_funcs();
exitcode
});
process::exit(exitcode);
}
fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> {
+2 -2
View File
@@ -162,10 +162,10 @@ pub fn print_exception(vm: &VirtualMachine, exc: PyBaseExceptionRef) {
}
};
if let Ok(excepthook) = vm.get_attribute(vm.sys_module.clone(), "excepthook") {
let (exc_type, exc_val, exc_tb) = split(exc, vm);
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(&eh_exc, "Original exception was:");
write_fallback(&exc, "Original exception was:");
}
} else {
write_fallback(&exc, "missing sys.excepthook");
+3
View File
@@ -7,6 +7,9 @@
// for methods like vm.to_str(), not the typical use of 'to' as a method prefix
#![allow(clippy::wrong_self_convention, clippy::implicit_hasher)]
// to allow `mod foo {}` in foo.rs; clippy thinks this is a mistake/misunderstanding of
// how `mod` works, but we want this sometimes for pymodule declarations
#![allow(clippy::module_inception)]
#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/master/logo.png")]
#![doc(html_root_url = "https://docs.rs/rustpython-vm/")]
#![cfg_attr(
+45
View File
@@ -0,0 +1,45 @@
pub(crate) use atexit::make_module;
#[pymodule]
mod atexit {
use crate::function::PyFuncArgs;
use crate::pyobject::{PyObjectRef, PyResult};
use crate::VirtualMachine;
#[pyfunction]
fn register(func: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyObjectRef {
vm.state.atexit_funcs.lock().push((func.clone(), args));
func
}
#[pyfunction]
fn _clear(vm: &VirtualMachine) {
vm.state.atexit_funcs.lock().clear();
}
#[pyfunction]
fn unregister(func: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let mut funcs = vm.state.atexit_funcs.lock();
let mut i = 0;
while i < funcs.len() {
if vm.bool_eq(funcs[i].0.clone(), func.clone())? {
funcs.remove(i);
} else {
i += 1;
}
}
Ok(())
}
#[pyfunction]
fn _run_exitfuncs(vm: &VirtualMachine) -> PyResult<()> {
vm.run_atexit_funcs()
}
#[pyfunction]
fn _ncallbacks(vm: &VirtualMachine) -> usize {
vm.state.atexit_funcs.lock().len()
}
}
+2
View File
@@ -5,6 +5,7 @@ use std::collections::HashMap;
pub mod array;
#[cfg(feature = "rustpython-parser")]
pub(crate) mod ast;
mod atexit;
mod binascii;
mod collections;
mod csv;
@@ -73,6 +74,7 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
#[allow(unused_mut)]
let mut modules = hashmap! {
"array".to_owned() => Box::new(array::make_module) as StdlibInitFunc,
"atexit".to_owned() => Box::new(atexit::make_module),
"binascii".to_owned() => Box::new(binascii::make_module),
"_collections".to_owned() => Box::new(collections::make_module),
"_csv".to_owned() => Box::new(csv::make_module),
+28
View File
@@ -332,6 +332,34 @@ pub fn get_stderr(vm: &VirtualMachine) -> PyResult {
.map_err(|_| vm.new_runtime_error("lost sys.stderr".to_owned()))
}
/// Similar to PySys_WriteStderr in CPython.
///
/// # Usage
///
/// ```rust,ignore
/// writeln!(sysmodule::PyStderr(vm), "foo bar baz :)");
/// ```
///
/// Unlike writing to a `std::io::Write` with the `write[ln]!()` macro, there's no error condition here;
/// this is intended to be a replacement for the `eprint[ln]!()` macro, so `write!()`-ing to PyStderr just
/// returns `()`.
pub struct PyStderr<'vm>(pub &'vm VirtualMachine);
impl PyStderr<'_> {
pub fn write_fmt(&self, args: std::fmt::Arguments<'_>) {
use py_io::Write;
let vm = self.0;
if let Ok(stderr) = get_stderr(vm) {
let mut stderr = py_io::PyWriter(stderr, vm);
if let Ok(()) = stderr.write_fmt(args) {
return;
}
}
eprint!("{}", args)
}
}
fn sys_excepthook(
exc_type: PyObjectRef,
exc_val: PyObjectRef,
+20 -1
View File
@@ -19,7 +19,7 @@ use rustpython_compiler::{
use crate::builtins::{self, to_ascii};
use crate::bytecode;
use crate::common::{hash::HashSecret, rc::PyRc};
use crate::common::{cell::PyMutex, hash::HashSecret, rc::PyRc};
use crate::exceptions::{self, PyBaseException, PyBaseExceptionRef};
use crate::frame::{ExecutionResult, Frame, FrameRef};
use crate::frozen;
@@ -122,6 +122,7 @@ pub struct PyGlobalState {
pub stacksize: AtomicCell<usize>,
pub thread_count: AtomicCell<usize>,
pub hash_secret: HashSecret,
pub atexit_funcs: PyMutex<Vec<(PyObjectRef, PyFuncArgs)>>,
}
pub const NSIG: usize = 64;
@@ -258,6 +259,7 @@ impl VirtualMachine {
stacksize: AtomicCell::new(0),
thread_count: AtomicCell::new(0),
hash_secret,
atexit_funcs: PyMutex::default(),
}),
initialized: false,
};
@@ -344,6 +346,23 @@ impl VirtualMachine {
}
}
pub fn run_atexit_funcs(&self) -> PyResult<()> {
let mut last_exc = None;
for (func, args) in self.state.atexit_funcs.lock().drain(..).rev() {
if let Err(e) = self.invoke(&func, args) {
last_exc = Some(e.clone());
if !objtype::isinstance(&e, &self.ctx.exceptions.system_exit) {
writeln!(sysmodule::PyStderr(self), "Error in atexit._run_exitfuncs:");
exceptions::print_exception(self, e);
}
}
}
match last_exc {
None => Ok(()),
Some(e) => Err(e),
}
}
pub fn run_code_obj(&self, code: PyCodeRef, scope: Scope) -> PyResult {
let frame = Frame::new(code, scope, self).into_ref(self);
self.run_frame_full(frame)