mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Add basic capi error support (#7787)
* Add basic capi error support * Add missing symbols to make tests compile again * Add `pyerrors` to dictionary * Return exception in `Py_GetConstantBorrowed` * Remove `allow(dead_code)` * Fix windows * Load stdlib when calling `Py_InitializeEx` * Debug tests * Revert "Load stdlib when calling `Py_InitializeEx`" This reverts commitbccd38e981. * Disable tests on windows for now * Truncate `PyType_GetFlags` to be always 32 bits * Add test for exception type checking * Remove subclass type flags * Use latest pyo3 to make test work on windows * Revert "Use latest pyo3 to make test work on windows" This reverts commitb2c2f6913f. * `set_main_interpreter` -> `init_main_interpreter`
This commit is contained in:
1 parent
0577e2ffb9
commit
4ec52c2e94
11 files changed
+515
-8
No files matched your search
@@ -169,6 +169,7 @@ pybuilddir
|
||||
pycore
|
||||
pyinner
|
||||
pydecimal
|
||||
pyerrors
|
||||
Pyfunc
|
||||
pylifecycle
|
||||
pymain
|
||||
|
||||
@@ -114,10 +114,15 @@ jobs:
|
||||
uses: ./.github/actions/install-macos-deps
|
||||
|
||||
- name: run rust tests
|
||||
run: cargo test --workspace ${{ env.WORKSPACE_EXCLUDES }} --features threading ${{ env.CARGO_ARGS }}
|
||||
run: cargo test --workspace --exclude rustpython-capi ${{ env.WORKSPACE_EXCLUDES }} --features threading ${{ env.CARGO_ARGS }}
|
||||
env:
|
||||
INSTA_WORKSPACE_ROOT: ${{ github.workspace }}
|
||||
|
||||
- name: run c-api tests
|
||||
working-directory: crates/capi
|
||||
run: cargo test
|
||||
if: runner.os != 'Windows' # Requires pyo3 0.29+ on Windows
|
||||
|
||||
- run: cargo doc --locked
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
|
||||
+15
-1
@@ -1,15 +1,19 @@
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use crate::pyerrors::init_exception_statics;
|
||||
use crate::pylifecycle::MAIN_INTERP;
|
||||
use rustpython_vm::Interpreter;
|
||||
pub use rustpython_vm::PyObject;
|
||||
use rustpython_vm::{Context, Interpreter};
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod object;
|
||||
pub mod pyerrors;
|
||||
pub mod pylifecycle;
|
||||
pub mod pystate;
|
||||
pub mod refcount;
|
||||
mod util;
|
||||
|
||||
/// Get main interpreter of this process. Will be None if it has not been initialized yet.
|
||||
pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> {
|
||||
@@ -17,3 +21,13 @@ pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> {
|
||||
.lock()
|
||||
.expect("Failed to lock interpreter mutex")
|
||||
}
|
||||
|
||||
/// Set the main interpreter of this process. This method will panic when there is already an
|
||||
/// interpreter set.
|
||||
pub fn init_main_interpreter(interpreter: Interpreter) {
|
||||
let mut interp = get_main_interpreter();
|
||||
assert!(interp.is_none(), "Main interpreter is already set");
|
||||
// Safety: Interpreter was not initialized before, so we can safely assume the statics are not used
|
||||
unsafe { init_exception_statics(&Context::genesis().exceptions) };
|
||||
*interp = Some(interpreter);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use crate::PyObject;
|
||||
use crate::pystate::with_vm;
|
||||
use core::ffi::{c_int, c_uint, c_ulong};
|
||||
use rustpython_vm::builtins::PyType;
|
||||
use rustpython_vm::{AsObject, Py};
|
||||
|
||||
pub type PyTypeObject = Py<PyType>;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn Py_TYPE(op: *mut PyObject) -> *const PyTypeObject {
|
||||
unsafe { (*op).class() }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn Py_IS_TYPE(op: *mut PyObject, ty: *mut PyTypeObject) -> c_int {
|
||||
with_vm(|_vm| {
|
||||
let obj = unsafe { &*op };
|
||||
let ty = unsafe { &*ty };
|
||||
obj.class().is(ty)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyType_GetFlags(ptr: *const PyTypeObject) -> c_ulong {
|
||||
let ty = unsafe { &*ptr };
|
||||
ty.slots.flags.bits() as u32 as c_ulong
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Py_GetConstantBorrowed(constant_id: c_uint) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let ctx = &vm.ctx;
|
||||
let constant = match constant_id {
|
||||
0 => ctx.none.as_object(),
|
||||
1 => ctx.false_value.as_object(),
|
||||
2 => ctx.true_value.as_object(),
|
||||
3 => ctx.ellipsis.as_object(),
|
||||
4 => ctx.not_implemented.as_object(),
|
||||
_ => {
|
||||
return Err(
|
||||
vm.new_system_error("Invalid constant ID passed to Py_GetConstantBorrowed")
|
||||
);
|
||||
}
|
||||
}
|
||||
.as_raw();
|
||||
Ok(constant)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
use crate::PyObject;
|
||||
use crate::pystate::with_vm;
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::{CStr, c_char, c_int};
|
||||
use core::ptr::NonNull;
|
||||
use rustpython_vm::builtins::{PyBaseException, PyTuple, PyType};
|
||||
use rustpython_vm::convert::IntoObject;
|
||||
use rustpython_vm::exceptions::ExceptionZoo;
|
||||
use rustpython_vm::{AsObject, PyObjectRef, PyResult};
|
||||
|
||||
macro_rules! define_exception_statics {
|
||||
($( $(#[$meta:meta])* $export:ident => $exc:ident ),* $(,)?) => {
|
||||
$(
|
||||
$(#[$meta])*
|
||||
#[unsafe(no_mangle)]
|
||||
pub static mut $export: *mut PyObject = core::ptr::null_mut();
|
||||
)*
|
||||
|
||||
#[allow(static_mut_refs)]
|
||||
pub(crate) unsafe fn init_exception_statics(zoo: &'static ExceptionZoo) {
|
||||
unsafe {
|
||||
$(
|
||||
$export = zoo.$exc.as_object().as_raw().cast_mut();
|
||||
)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_exception_statics! {
|
||||
PyExc_BaseException => base_exception_type,
|
||||
PyExc_BaseExceptionGroup => base_exception_group,
|
||||
PyExc_SystemExit => system_exit,
|
||||
PyExc_KeyboardInterrupt => keyboard_interrupt,
|
||||
PyExc_GeneratorExit => generator_exit,
|
||||
PyExc_Exception => exception_type,
|
||||
PyExc_StopIteration => stop_iteration,
|
||||
PyExc_StopAsyncIteration => stop_async_iteration,
|
||||
PyExc_ArithmeticError => arithmetic_error,
|
||||
PyExc_FloatingPointError => floating_point_error,
|
||||
PyExc_SystemError => system_error,
|
||||
PyExc_TypeError => type_error,
|
||||
PyExc_OverflowError => overflow_error,
|
||||
PyExc_ZeroDivisionError => zero_division_error,
|
||||
PyExc_AssertionError => assertion_error,
|
||||
PyExc_IndexError => index_error,
|
||||
PyExc_KeyError => key_error,
|
||||
PyExc_LookupError => lookup_error,
|
||||
PyExc_AttributeError => attribute_error,
|
||||
PyExc_BufferError => buffer_error,
|
||||
PyExc_EOFError => eof_error,
|
||||
PyExc_ImportError => import_error,
|
||||
PyExc_ModuleNotFoundError => module_not_found_error,
|
||||
PyExc_MemoryError => memory_error,
|
||||
PyExc_NameError => name_error,
|
||||
PyExc_UnboundLocalError => unbound_local_error,
|
||||
PyExc_OSError => os_error,
|
||||
PyExc_BlockingIOError => blocking_io_error,
|
||||
PyExc_ChildProcessError => child_process_error,
|
||||
PyExc_ConnectionError => connection_error,
|
||||
PyExc_BrokenPipeError => broken_pipe_error,
|
||||
PyExc_ConnectionAbortedError => connection_aborted_error,
|
||||
PyExc_ConnectionRefusedError => connection_refused_error,
|
||||
PyExc_ConnectionResetError => connection_reset_error,
|
||||
PyExc_FileExistsError => file_exists_error,
|
||||
PyExc_FileNotFoundError => file_not_found_error,
|
||||
PyExc_InterruptedError => interrupted_error,
|
||||
PyExc_IsADirectoryError => is_a_directory_error,
|
||||
PyExc_NotADirectoryError => not_a_directory_error,
|
||||
PyExc_PermissionError => permission_error,
|
||||
PyExc_ProcessLookupError => process_lookup_error,
|
||||
PyExc_TimeoutError => timeout_error,
|
||||
PyExc_ReferenceError => reference_error,
|
||||
PyExc_RuntimeError => runtime_error,
|
||||
PyExc_NotImplementedError => not_implemented_error,
|
||||
PyExc_RecursionError => recursion_error,
|
||||
PyExc_SyntaxError => syntax_error,
|
||||
PyExc_IndentationError => indentation_error,
|
||||
PyExc_TabError => tab_error,
|
||||
PyExc_ValueError => value_error,
|
||||
PyExc_UnicodeError => unicode_error,
|
||||
PyExc_UnicodeDecodeError => unicode_decode_error,
|
||||
PyExc_UnicodeEncodeError => unicode_encode_error,
|
||||
PyExc_UnicodeTranslateError => unicode_translate_error,
|
||||
PyExc_Warning => warning,
|
||||
PyExc_DeprecationWarning => deprecation_warning,
|
||||
PyExc_PendingDeprecationWarning => pending_deprecation_warning,
|
||||
PyExc_RuntimeWarning => runtime_warning,
|
||||
PyExc_SyntaxWarning => syntax_warning,
|
||||
PyExc_UserWarning => user_warning,
|
||||
PyExc_FutureWarning => future_warning,
|
||||
PyExc_ImportWarning => import_warning,
|
||||
PyExc_UnicodeWarning => unicode_warning,
|
||||
PyExc_BytesWarning => bytes_warning,
|
||||
PyExc_ResourceWarning => resource_warning,
|
||||
PyExc_EncodingWarning => encoding_warning,
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyErr_Occurred() -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
vm.current_exception()
|
||||
.map(|exc| exc.class().as_object().as_raw())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyErr_GetRaisedException() -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
vm.take_raised_exception()
|
||||
.map(|exc| exc.into_object().into_raw().as_ptr())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_SetRaisedException(exc: *mut PyObject) {
|
||||
with_vm(|vm| {
|
||||
if let Some(exc) = NonNull::new(exc) {
|
||||
let exception = unsafe { PyObjectRef::from_raw(exc).downcast_unchecked() };
|
||||
vm.set_exception(Some(exception));
|
||||
} else {
|
||||
vm.set_exception(None);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_SetObject(exception: *mut PyObject, value: *mut PyObject) {
|
||||
with_vm::<PyResult<Infallible>, _>(|vm| {
|
||||
let exc_type = unsafe { (&*exception).to_owned() };
|
||||
let exc_val = unsafe { (&*value).to_owned() };
|
||||
|
||||
let normalized = vm.normalize_exception(exc_type, exc_val, vm.ctx.none())?;
|
||||
Err(normalized)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_SetString(exception: *mut PyObject, message: *const c_char) {
|
||||
with_vm::<PyResult<Infallible>, _>(|vm| {
|
||||
let exc_type = unsafe { &*exception }.try_downcast_ref::<PyType>(vm)?;
|
||||
|
||||
let Ok(message) = unsafe { CStr::from_ptr(message) }.to_str() else {
|
||||
return Err(vm.new_type_error("Exception message is not valid UTF-8"));
|
||||
};
|
||||
|
||||
let exc = vm.invoke_exception(
|
||||
exc_type.to_owned(),
|
||||
vec![vm.ctx.new_str(message).into_object()],
|
||||
)?;
|
||||
|
||||
Err(exc)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyErr_PrintEx(_set_sys_last_vars: c_int) {
|
||||
with_vm(|vm| {
|
||||
let exception = vm
|
||||
.take_raised_exception()
|
||||
.expect("No exception set in PyErr_PrintEx");
|
||||
|
||||
vm.print_exception(exception);
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_DisplayException(exc: *mut PyObject) {
|
||||
with_vm(|vm| {
|
||||
let exception = unsafe { &*exc }
|
||||
.downcast_ref::<PyBaseException>()
|
||||
.expect("PyErr_DisplayException exc must be an exception instance")
|
||||
.to_owned();
|
||||
|
||||
vm.print_exception(exception);
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_WriteUnraisable(obj: *mut PyObject) {
|
||||
with_vm(|vm| {
|
||||
let exception = vm
|
||||
.take_raised_exception()
|
||||
.expect("No exception set in PyErr_WriteUnraisable");
|
||||
|
||||
let object = unsafe { vm.unwrap_or_none(obj.as_ref().map(|obj| obj.to_owned())) };
|
||||
|
||||
vm.run_unraisable(exception, None, object)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_NewException(
|
||||
name: *const c_char,
|
||||
base: *mut PyObject,
|
||||
dict: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let (module, name) = unsafe {
|
||||
CStr::from_ptr(name)
|
||||
.to_str()
|
||||
.expect("Exception name is not valid UTF-8")
|
||||
.rsplit_once('.')
|
||||
.expect("Exception name must be of the form 'module.ExceptionName'")
|
||||
};
|
||||
|
||||
let bases = unsafe { base.as_ref() }.map(|bases| {
|
||||
if let Some(ty) = bases.downcast_ref::<PyType>() {
|
||||
vec![ty.to_owned()]
|
||||
} else if let Some(tuple) = bases.downcast_ref::<PyTuple>() {
|
||||
tuple
|
||||
.iter()
|
||||
.map(|item| item.to_owned().downcast())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.expect("PyErr_NewException base tuple must contain only types")
|
||||
} else {
|
||||
panic!("PyErr_NewException base must be a type or a tuple of types");
|
||||
}
|
||||
});
|
||||
|
||||
assert!(
|
||||
dict.is_null(),
|
||||
"PyErr_NewException with non-null dict is not supported yet"
|
||||
);
|
||||
|
||||
vm.ctx.new_exception_type(module, name, bases)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_NewExceptionWithDoc(
|
||||
name: *const c_char,
|
||||
_doc: *const c_char,
|
||||
base: *mut PyObject,
|
||||
dict: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
unsafe { PyErr_NewException(name, base, dict) }
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyErr_GivenExceptionMatches(
|
||||
given: *mut PyObject,
|
||||
exc: *mut PyObject,
|
||||
) -> c_int {
|
||||
with_vm(|vm| {
|
||||
let given = unsafe { &*given };
|
||||
let exc = unsafe { &*exc };
|
||||
|
||||
given.is_subclass(exc, vm)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_raised_exception() {
|
||||
Python::attach(|py| {
|
||||
PyTypeError::new_err(py.None()).restore(py);
|
||||
assert!(PyErr::occurred(py));
|
||||
assert!(unsafe { !pyo3::ffi::PyErr_GetRaisedException().is_null() });
|
||||
assert!(!PyErr::occurred(py));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_is_instance() {
|
||||
Python::attach(|py| {
|
||||
let err = PyTypeError::new_err(py.None());
|
||||
assert!(err.is_instance_of::<PyTypeError>(py));
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::get_main_interpreter;
|
||||
use crate::pyerrors::init_exception_statics;
|
||||
use crate::pystate::ensure_thread_has_vm_attached;
|
||||
use core::ffi::c_int;
|
||||
use rustpython_vm::Interpreter;
|
||||
use rustpython_vm::vm::thread::ThreadedVirtualMachine;
|
||||
use rustpython_vm::{Context, Interpreter};
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub(crate) static MAIN_INTERP: Mutex<Option<Interpreter>> = Mutex::new(None);
|
||||
@@ -29,6 +30,8 @@ pub extern "C" fn Py_Initialize() {
|
||||
pub extern "C" fn Py_InitializeEx(_initsigs: c_int) {
|
||||
let mut interp = get_main_interpreter();
|
||||
if interp.is_none() {
|
||||
// Safety: Interpreter was not initialized before, so we can safely assume the statics are not used
|
||||
unsafe { init_exception_statics(&Context::genesis().exceptions) };
|
||||
*interp = Interpreter::with_init(Default::default(), |_vm| {}).into();
|
||||
drop(interp);
|
||||
ensure_thread_has_vm_attached();
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
use crate::pylifecycle::request_vm_from_interpreter;
|
||||
use crate::util::FfiResult;
|
||||
use core::ffi::c_int;
|
||||
use core::ptr;
|
||||
use rustpython_vm::VirtualMachine;
|
||||
use rustpython_vm::vm::thread::{
|
||||
CurrentVmAttachState, attach_current_thread, release_current_thread,
|
||||
CurrentVmAttachState, attach_current_thread, release_current_thread, with_current_vm,
|
||||
};
|
||||
|
||||
pub(crate) fn with_vm<R: FfiResult<O>, O>(f: impl FnOnce(&VirtualMachine) -> R) -> O {
|
||||
with_current_vm(|vm| f(vm).into_output(vm))
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type PyGILState_STATE = c_int;
|
||||
const PYGILSTATE_LOCKED: PyGILState_STATE = 0;
|
||||
@@ -41,6 +47,9 @@ pub extern "C" fn PyEval_SaveThread() -> *mut PyThreadState {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyEval_RestoreThread(_state: *mut PyThreadState) {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::get_main_interpreter;
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
use crate::PyObject;
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::{c_char, c_double, c_int, c_long, c_void};
|
||||
use rustpython_vm::{PyObjectRef, PyRef, PyResult, VirtualMachine};
|
||||
|
||||
pub(crate) trait FfiResult<Output = Self> {
|
||||
const ERR_VALUE: Output;
|
||||
|
||||
fn into_output(self, vm: &VirtualMachine) -> Output;
|
||||
}
|
||||
|
||||
impl FfiResult for () {
|
||||
const ERR_VALUE: () = ();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<c_int> for () {
|
||||
const ERR_VALUE: c_int = -1;
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> c_int {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FfiResult<*mut PyObject> for PyRef<T>
|
||||
where
|
||||
Self: Into<PyObjectRef>,
|
||||
{
|
||||
const ERR_VALUE: *mut PyObject = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut PyObject {
|
||||
self.into().into_raw().as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<*mut PyObject> for PyObjectRef {
|
||||
const ERR_VALUE: *mut PyObject = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut PyObject {
|
||||
self.into_raw().as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult for *mut PyObject {
|
||||
const ERR_VALUE: *mut PyObject = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut PyObject {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<*mut PyObject> for *const PyObject {
|
||||
const ERR_VALUE: *mut PyObject = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut PyObject {
|
||||
self.cast_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult for *mut c_void {
|
||||
const ERR_VALUE: *mut c_void = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut c_void {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<*mut c_char> for *const u8 {
|
||||
const ERR_VALUE: *mut c_char = core::ptr::null_mut();
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> *mut c_char {
|
||||
self.cast_mut().cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<isize> for usize {
|
||||
const ERR_VALUE: isize = -1;
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> isize {
|
||||
self.try_into()
|
||||
.expect("Output value is too large to fit into target type")
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult for c_long {
|
||||
const ERR_VALUE: c_long = -1;
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> c_long {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult for c_double {
|
||||
const ERR_VALUE: c_double = -1.0;
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> c_double {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<c_int> for bool {
|
||||
const ERR_VALUE: c_int = -1;
|
||||
|
||||
fn into_output(self, _vm: &VirtualMachine) -> c_int {
|
||||
self as c_int
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiResult<()> for PyResult<Infallible> {
|
||||
const ERR_VALUE: () = ();
|
||||
|
||||
fn into_output(self, vm: &VirtualMachine) {
|
||||
match self {
|
||||
Err(err) => vm.set_exception(Some(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Output, T> FfiResult<Output> for PyResult<T>
|
||||
where
|
||||
T: FfiResult<Output>,
|
||||
{
|
||||
const ERR_VALUE: Output = T::ERR_VALUE;
|
||||
|
||||
fn into_output(self, vm: &VirtualMachine) -> Output {
|
||||
self.map_or_else(
|
||||
|err| {
|
||||
vm.set_exception(Some(err));
|
||||
T::ERR_VALUE
|
||||
},
|
||||
|obj| obj.into_output(vm),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1259,7 +1259,7 @@ impl PyType {
|
||||
}
|
||||
|
||||
impl Py<PyType> {
|
||||
pub(crate) fn is_subtype(&self, other: &Self) -> bool {
|
||||
pub fn is_subtype(&self, other: &Self) -> bool {
|
||||
is_subtype_with_mro(&self.mro.read(), self, other)
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -2060,12 +2060,12 @@ impl VirtualMachine {
|
||||
exc
|
||||
}
|
||||
|
||||
pub(crate) fn current_exception(&self) -> Option<PyBaseExceptionRef> {
|
||||
pub fn current_exception(&self) -> Option<PyBaseExceptionRef> {
|
||||
self.exceptions.borrow().stack.last().cloned().flatten()
|
||||
}
|
||||
|
||||
/// Set the current exc_info slot value (PUSH_EXC_INFO / POP_EXCEPT).
|
||||
pub(crate) fn set_exception(&self, exc: Option<PyBaseExceptionRef>) {
|
||||
pub fn set_exception(&self, exc: Option<PyBaseExceptionRef>) {
|
||||
// don't be holding the RefCell guard while __del__ is called
|
||||
let mut excs = self.exceptions.borrow_mut();
|
||||
debug_assert!(
|
||||
@@ -2084,6 +2084,19 @@ impl VirtualMachine {
|
||||
thread::update_thread_exception(self.topmost_exception());
|
||||
}
|
||||
|
||||
pub fn take_raised_exception(&self) -> Option<PyBaseExceptionRef> {
|
||||
let mut excs = self.exceptions.borrow_mut();
|
||||
if let Some(top) = excs.stack.last_mut() {
|
||||
let exc = top.take();
|
||||
drop(excs);
|
||||
#[cfg(feature = "threading")]
|
||||
thread::update_thread_exception(self.topmost_exception());
|
||||
exc
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contextualize_exception(&self, exception: &Py<PyBaseException>) {
|
||||
if let Some(context_exc) = self.topmost_exception()
|
||||
&& !context_exc.is(exception)
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ pub fn run(mut builder: InterpreterBuilder) -> ExitCode {
|
||||
let exitcode = cfg_select! {
|
||||
feature = "capi" => {{
|
||||
let local_vm = interp.enter(|vm| vm.new_thread());
|
||||
*(rustpython_capi::get_main_interpreter()) = Some(interp);
|
||||
rustpython_capi::init_main_interpreter(interp);
|
||||
let result = local_vm.run(|vm| run_rustpython(vm, run_mode));
|
||||
rustpython_capi::get_main_interpreter().take().unwrap().finalize(result.err())
|
||||
}},
|
||||
|
||||
Reference in new issue
Block a user