|
1 | 1 | use super::{setting::Settings, thread, Context, VirtualMachine}; |
2 | 2 | use crate::{ |
3 | 3 | stdlib::{atexit, sys}, |
| 4 | + vm::PyBaseExceptionRef, |
4 | 5 | PyResult, |
5 | 6 | }; |
6 | 7 | use std::sync::atomic::Ordering; |
@@ -59,26 +60,74 @@ impl Interpreter { |
59 | 60 | Self { vm } |
60 | 61 | } |
61 | 62 |
|
| 63 | + /// Run a function with the main virtual machine and return a PyResult of the result. |
| 64 | + /// |
| 65 | + /// To enter vm context multiple times or to avoid buffer/exception management, this function is preferred. |
| 66 | + /// `enter` is lightweight and it returns a python object in PyResult. |
| 67 | + /// You can stop or continue the execution multiple times by calling `enter`. |
| 68 | + /// |
| 69 | + /// To finalize the vm once all desired `enter`s are called, calling `finalize` will be helpful. |
| 70 | + /// |
| 71 | + /// See also [`run`] for managed way to run the interpreter. |
62 | 72 | pub fn enter<F, R>(&self, f: F) -> R |
63 | 73 | where |
64 | 74 | F: FnOnce(&VirtualMachine) -> R, |
65 | 75 | { |
66 | 76 | thread::enter_vm(&self.vm, || f(&self.vm)) |
67 | 77 | } |
68 | 78 |
|
| 79 | + /// Run [`enter`] and call `expect_pyresult` for the result. |
| 80 | + /// |
| 81 | + /// This function is useful when you want to expect a result from the function, |
| 82 | + /// but also print useful panic information when exception raised. |
| 83 | + /// |
| 84 | + /// See [`enter`] for more information. |
| 85 | + /// See [`expect_pyresult`] for more information. |
| 86 | + pub fn enter_and_expect<F, R>(&self, f: F, msg: &str) -> R |
| 87 | + where |
| 88 | + F: FnOnce(&VirtualMachine) -> PyResult<R>, |
| 89 | + { |
| 90 | + self.enter(|vm| { |
| 91 | + let result = f(vm); |
| 92 | + vm.expect_pyresult(result, msg) |
| 93 | + }) |
| 94 | + } |
| 95 | + |
| 96 | + /// Run a function with the main virtual machine and return exit code. |
| 97 | + /// |
| 98 | + /// To enter vm context only once and safely terminate the vm, this function is preferred. |
| 99 | + /// Unlike [`enter`], `run` calls finalize and returns exit code. |
| 100 | + /// You will not be able to obtain Python exception in this way. |
| 101 | + /// |
| 102 | + /// See [`finalize`] for the finalization steps. |
| 103 | + /// See also [`enter`] for pure function call to obtain Python exception. |
69 | 104 | pub fn run<F, R>(self, f: F) -> u8 |
70 | 105 | where |
71 | 106 | F: FnOnce(&VirtualMachine) -> PyResult<R>, |
72 | 107 | { |
| 108 | + let res = self.enter(|vm| f(vm)); |
| 109 | + self.finalize(res.err()) |
| 110 | + } |
| 111 | + |
| 112 | + /// Finalize vm and turns an exception to exit code. |
| 113 | + /// |
| 114 | + /// Finalization steps including 4 steps: |
| 115 | + /// 1. Flush stdout and stderr. |
| 116 | + /// 1. Handle exit exception and turn it to exit code. |
| 117 | + /// 1. Run atexit exit functions. |
| 118 | + /// 1. Mark vm as finalized. |
| 119 | + /// |
| 120 | + /// Note that calling `finalize` is not necessary by purpose though. |
| 121 | + pub fn finalize(self, exc: Option<PyBaseExceptionRef>) -> u8 { |
73 | 122 | self.enter(|vm| { |
74 | | - let res = f(vm); |
75 | 123 | flush_std(vm); |
76 | 124 |
|
77 | 125 | // See if any exception leaked out: |
78 | | - let exit_code = res |
79 | | - .map(|_| 0) |
80 | | - .map_err(|exc| vm.handle_exit_exception(exc)) |
81 | | - .unwrap_or_else(|code| code); |
| 126 | + let exit_code = if let Some(exc) = exc { |
| 127 | + vm.handle_exit_exception(exc) |
| 128 | + } else { |
| 129 | + 0 |
| 130 | + }; |
82 | 131 |
|
83 | 132 | atexit::_run_exitfuncs(vm); |
84 | 133 |
|
|
0 commit comments