Skip to content

Commit 74bb4b6

Browse files
authored
Merge pull request RustPython#3672 from youknowone/exit-handling
Fix stderr usage in exit handling
2 parents abdb04f + e8c5936 commit 74bb4b6

File tree

6 files changed

+97
-91
lines changed

6 files changed

+97
-91
lines changed

src/lib.rs

Lines changed: 10 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ extern crate log;
4646
mod shell;
4747

4848
use clap::{App, AppSettings, Arg, ArgMatches};
49-
use rustpython_vm::{
50-
builtins::PyInt,
51-
match_class,
52-
scope::Scope,
53-
stdlib::{atexit, sys},
54-
AsObject, Interpreter, PyResult, Settings, VirtualMachine,
55-
};
49+
use rustpython_vm::{scope::Scope, Interpreter, PyResult, Settings, VirtualMachine};
5650
use std::{env, process, str::FromStr};
5751

5852
pub use rustpython_vm as vm;
@@ -68,7 +62,8 @@ where
6862
env_logger::init();
6963
let app = App::new("RustPython");
7064
let matches = parse_arguments(app);
71-
let settings = create_settings(&matches);
65+
let matches = &matches;
66+
let settings = create_settings(matches);
7267

7368
// don't translate newlines (\r\n <=> \n)
7469
#[cfg(windows)]
@@ -88,75 +83,19 @@ where
8883
init(vm);
8984
});
9085

91-
let exitcode = interp.enter(move |vm| {
92-
let res = run_rustpython(vm, &matches);
86+
let exitcode = interp.run(move |vm| run_rustpython(vm, matches));
9387

94-
flush_std(vm);
95-
96-
#[cfg(feature = "flame-it")]
97-
{
98-
main_guard.end();
99-
if let Err(e) = write_profile(&matches) {
100-
error!("Error writing profile information: {}", e);
101-
}
88+
#[cfg(feature = "flame-it")]
89+
{
90+
main_guard.end();
91+
if let Err(e) = write_profile(&matches) {
92+
error!("Error writing profile information: {}", e);
10293
}
103-
104-
// See if any exception leaked out:
105-
let exitcode = match res {
106-
Ok(()) => 0,
107-
Err(err) if err.fast_isinstance(&vm.ctx.exceptions.system_exit) => {
108-
let args = err.args();
109-
match args.as_slice() {
110-
[] => 0,
111-
[arg] => match_class!(match arg {
112-
ref i @ PyInt => {
113-
use num_traits::cast::ToPrimitive;
114-
i.as_bigint().to_i32().unwrap_or(0)
115-
}
116-
arg => {
117-
if vm.is_none(arg) {
118-
0
119-
} else {
120-
if let Ok(s) = arg.str(vm) {
121-
eprintln!("{}", s);
122-
}
123-
1
124-
}
125-
}
126-
}),
127-
_ => {
128-
if let Ok(r) = args.as_object().repr(vm) {
129-
eprintln!("{}", r);
130-
}
131-
1
132-
}
133-
}
134-
}
135-
Err(exc) => {
136-
vm.print_exception(exc);
137-
1
138-
}
139-
};
140-
141-
let _ = atexit::_run_exitfuncs(vm);
142-
143-
flush_std(vm);
144-
145-
exitcode
146-
});
94+
}
14795

14896
process::exit(exitcode)
14997
}
15098

151-
fn flush_std(vm: &VirtualMachine) {
152-
if let Ok(stdout) = sys::get_stdout(vm) {
153-
let _ = vm.call_method(&stdout, "flush", ());
154-
}
155-
if let Ok(stderr) = sys::get_stderr(vm) {
156-
let _ = vm.call_method(&stderr, "flush", ());
157-
}
158-
}
159-
16099
fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> {
161100
let app = app
162101
.setting(AppSettings::TrailingVarArg)

stdlib/src/faulthandler.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ pub(crate) use decl::make_module;
22

33
#[pymodule(name = "faulthandler")]
44
mod decl {
5-
use crate::vm::{frame::FrameRef, function::OptionalArg, VirtualMachine};
5+
use crate::vm::{
6+
frame::FrameRef, function::OptionalArg, stdlib::sys::PyStderr, VirtualMachine,
7+
};
68

7-
fn dump_frame(frame: &FrameRef) {
8-
eprintln!(
9+
fn dump_frame(frame: &FrameRef, vm: &VirtualMachine) {
10+
let stderr = PyStderr(vm);
11+
writeln!(
12+
stderr,
913
" File \"{}\", line {} in {}",
1014
frame.code.source_path,
1115
frame.current_location().row(),
@@ -19,10 +23,11 @@ mod decl {
1923
_all_threads: OptionalArg<bool>,
2024
vm: &VirtualMachine,
2125
) {
22-
eprintln!("Stack (most recent call first):");
26+
let stderr = PyStderr(vm);
27+
writeln!(stderr, "Stack (most recent call first):");
2328

2429
for frame in vm.frames.borrow().iter() {
25-
dump_frame(frame);
30+
dump_frame(frame, vm);
2631
}
2732
}
2833

vm/src/stdlib/sys.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ mod sys {
501501
#[pyfunction(name = "__unraisablehook__")]
502502
fn unraisablehook(unraisable: UnraisableHookArgs, vm: &VirtualMachine) {
503503
if let Err(e) = _unraisablehook(unraisable, vm) {
504-
println!("{}", e.as_object().repr(vm).unwrap().as_str());
504+
let stderr = super::PyStderr(vm);
505+
writeln!(stderr, "{}", e.as_object().repr(vm).unwrap().as_str());
505506
}
506507
}
507508

vm/src/stdlib/warnings.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod _warnings {
55
use crate::{
66
builtins::{PyStrRef, PyTypeRef},
77
function::OptionalArg,
8+
stdlib::sys::PyStderr,
89
AsObject, PyResult, VirtualMachine,
910
};
1011

@@ -33,7 +34,14 @@ mod _warnings {
3334
} else {
3435
vm.ctx.exceptions.user_warning.clone()
3536
};
36-
eprintln!("level:{}: {}: {}", level, category.name(), args.message);
37+
let stderr = PyStderr(vm);
38+
writeln!(
39+
stderr,
40+
"level:{}: {}: {}",
41+
level,
42+
category.name(),
43+
args.message
44+
);
3745
Ok(())
3846
}
3947
}

vm/src/vm/interpreter.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
use super::{setting::Settings, thread, VirtualMachine};
2+
use crate::{
3+
stdlib::{atexit, sys},
4+
PyResult,
5+
};
26

37
/// The general interface for the VM
48
///
@@ -53,16 +57,36 @@ impl Interpreter {
5357
thread::enter_vm(&self.vm, || f(&self.vm))
5458
}
5559

56-
// TODO: interpreter shutdown
57-
// pub fn run<F>(self, f: F)
58-
// where
59-
// F: FnOnce(&VirtualMachine),
60-
// {
61-
// self.enter(f);
62-
// self.shutdown();
63-
// }
60+
pub fn run<F, R>(self, f: F) -> i32
61+
where
62+
F: FnOnce(&VirtualMachine) -> PyResult<R>,
63+
{
64+
self.enter(|vm| {
65+
let res = f(vm);
66+
flush_std(vm);
67+
68+
// See if any exception leaked out:
69+
let exit_code = res
70+
.map(|_| 0)
71+
.map_err(|exc| vm.handle_exit_exception(exc))
72+
.unwrap_or_else(|code| code);
6473

65-
// pub fn shutdown(self) {}
74+
let _ = atexit::_run_exitfuncs(vm);
75+
76+
flush_std(vm);
77+
78+
exit_code
79+
})
80+
}
81+
}
82+
83+
fn flush_std(vm: &VirtualMachine) {
84+
if let Ok(stdout) = sys::get_stdout(vm) {
85+
let _ = vm.call_method(&stdout, "flush", ());
86+
}
87+
if let Ok(stderr) = sys::get_stderr(vm) {
88+
let _ = vm.call_method(&stderr, "flush", ());
89+
}
6690
}
6791

6892
#[cfg(test)]

vm/src/vm/mod.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
code::{self, PyCode},
2020
pystr::IntoPyStrRef,
2121
tuple::{PyTuple, PyTupleTyped},
22-
PyBaseExceptionRef, PyDictRef, PyList, PyModule, PyStrRef, PyTypeRef,
22+
PyBaseExceptionRef, PyDictRef, PyInt, PyList, PyModule, PyStrRef, PyTypeRef,
2323
},
2424
bytecode,
2525
codecs::CodecsRegistry,
@@ -278,15 +278,13 @@ impl VirtualMachine {
278278

279279
#[cold]
280280
pub fn run_unraisable(&self, e: PyBaseExceptionRef, msg: Option<String>, object: PyObjectRef) {
281-
use crate::stdlib::sys::UnraisableHookArgs;
282-
283281
let sys_module = self.import("sys", None, 0).unwrap();
284282
let unraisablehook = sys_module.get_attr("unraisablehook", self).unwrap();
285283

286284
let exc_type = e.class().clone();
287285
let exc_traceback = e.traceback().to_pyobject(self); // TODO: actual traceback
288286
let exc_value = e.into();
289-
let args = UnraisableHookArgs {
287+
let args = stdlib::sys::UnraisableHookArgs {
290288
exc_type,
291289
exc_value,
292290
exc_traceback,
@@ -660,6 +658,37 @@ impl VirtualMachine {
660658
}
661659
}
662660

661+
pub fn handle_exit_exception(&self, exc: PyBaseExceptionRef) -> i32 {
662+
if exc.fast_isinstance(&self.ctx.exceptions.system_exit) {
663+
let args = exc.args();
664+
let msg = match args.as_slice() {
665+
[] => return 0,
666+
[arg] => match_class!(match arg {
667+
ref i @ PyInt => {
668+
use num_traits::cast::ToPrimitive;
669+
return i.as_bigint().to_i32().unwrap_or(0);
670+
}
671+
arg => {
672+
if self.is_none(arg) {
673+
return 0;
674+
} else {
675+
arg.str(self).ok()
676+
}
677+
}
678+
}),
679+
_ => args.as_object().repr(self).ok(),
680+
};
681+
if let Some(msg) = msg {
682+
let stderr = stdlib::sys::PyStderr(self);
683+
writeln!(stderr, "{}", msg);
684+
}
685+
1
686+
} else {
687+
self.print_exception(exc);
688+
1
689+
}
690+
}
691+
663692
pub fn map_codeobj(&self, code: bytecode::CodeObject) -> code::CodeObject {
664693
code.map_bag(&code::PyObjBag(self))
665694
}

0 commit comments

Comments
 (0)