mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Use PyO3 main & enable c-api tests (#8000)
Enable `abi3t` support in PyO3 Add left over c-api functions Fix PyLong_AsUnsignedLongLongMask Update pyo3 to `0.29` Add `PyInt::as_u64_mask` Fix `PyInt::as_u32_mask`
This commit is contained in:
1 parent
b39bec4063
commit
4caf959e58
38 files changed
+449
-59
No files matched your search
@@ -170,6 +170,7 @@ nvars
|
||||
opname
|
||||
opnames
|
||||
orelse
|
||||
osmodule
|
||||
outparam
|
||||
outparm
|
||||
paramfunc
|
||||
|
||||
@@ -343,6 +343,7 @@ jobs:
|
||||
with:
|
||||
openssl: true
|
||||
|
||||
# Keep features in sync with update-caches.yml CARGO_ARGS.
|
||||
- name: build rustpython
|
||||
run: cargo build --release --verbose --features=threading,jit ${{ env.CARGO_ARGS }}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ env:
|
||||
CARGO_PROFILE_TEST_DEBUG: 0
|
||||
CARGO_PROFILE_DEV_DEBUG: 0
|
||||
CARGO_PROFILE_RELEASE_DEBUG: 0
|
||||
# Keep feature list in sync with CI's release build in .github/workflows/ci.yaml.
|
||||
CARGO_ARGS: --workspace --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl-rustls-aws-lc,host_env,threading,jit --exclude rustpython_wasm --exclude rustpython-compiler-source --exclude rustpython-venvlauncher
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -22,7 +22,7 @@ rustpython-stdlib = {workspace = true, features = ["threading"] }
|
||||
rustpython-pylib = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pyo3 = { workspace = true, features = ["auto-initialize", "abi3"] }
|
||||
pyo3 = { workspace = true, features = ["auto-initialize", "abi3t"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,5 +1,5 @@
|
||||
implementation=CPython
|
||||
version=3.14
|
||||
implementation=RustPython
|
||||
version=3.15
|
||||
shared=true
|
||||
abi3=true
|
||||
target_abi=RustPython-abi3t-3.15
|
||||
suppress_build_script_link_lines=true
|
||||
@@ -178,3 +178,40 @@ pub unsafe extern "C" fn PyObject_Size(obj: *mut PyObject) -> isize {
|
||||
obj.length(vm)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyString};
|
||||
|
||||
#[test]
|
||||
fn call_method1() {
|
||||
Python::attach(|py| {
|
||||
let string = PyString::new(py, "Hello, World!");
|
||||
assert!(
|
||||
string
|
||||
.call_method1("endswith", ("!",))
|
||||
.unwrap()
|
||||
.is_truthy()
|
||||
.unwrap()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_set_get_del_item() {
|
||||
Python::attach(|py| {
|
||||
let obj = PyDict::new(py).into_any();
|
||||
obj.set_item("key", "value").unwrap();
|
||||
assert_eq!(
|
||||
obj.get_item("key")
|
||||
.unwrap()
|
||||
.cast_into::<PyString>()
|
||||
.unwrap(),
|
||||
"value"
|
||||
);
|
||||
obj.del_item("key").unwrap();
|
||||
assert!(obj.get_item("key").is_err());
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ pub unsafe extern "C" fn PyIter_Send(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyAnyMethods, PyIterator, PyList, PySendResult};
|
||||
|
||||
@@ -194,7 +194,7 @@ pub unsafe extern "C" fn PyMapping_SetItemString(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyMapping, PyMappingMethods, PyTuple};
|
||||
|
||||
@@ -232,7 +232,7 @@ pub unsafe extern "C" fn PyNumber_Subtract(o1: *mut PyObject, o2: *mut PyObject)
|
||||
with_vm(|vm| vm._sub(unsafe { &*o1 }, unsafe { &*o2 }))
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ pub unsafe extern "C" fn PySequence_In(obj: *mut PyObject, value: *mut PyObject)
|
||||
unsafe { PySequence_Contains(obj, value) }
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyAnyMethods, PyDict, PyList, PySequence, PySequenceMethods, PyTuple};
|
||||
|
||||
@@ -71,7 +71,7 @@ pub unsafe extern "C" fn PyByteArray_Resize(bytearray: *mut PyObject, len: isize
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyByteArray, PyBytes};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::PyObject;
|
||||
use crate::object::define_py_check;
|
||||
use crate::pystate::with_vm;
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use core::ffi::c_char;
|
||||
use rustpython_vm::builtins::PyBytes;
|
||||
|
||||
@@ -46,13 +45,13 @@ pub unsafe extern "C" fn PyBytes_AsString(bytes: *mut PyObject) -> *mut c_char {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyBytes;
|
||||
|
||||
#[test]
|
||||
fn test_bytes() {
|
||||
fn bytes() {
|
||||
Python::attach(|py| {
|
||||
let bytes = PyBytes::new(py, b"Hello, World!");
|
||||
assert_eq!(bytes.as_bytes(), b"Hello, World!");
|
||||
@@ -60,7 +59,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bytes_uninit() {
|
||||
fn bytes_uninit() {
|
||||
Python::attach(|py| {
|
||||
let bytes = PyBytes::new_with(py, 13, |data| {
|
||||
data.copy_from_slice(b"Hello, World!");
|
||||
|
||||
@@ -52,13 +52,13 @@ pub extern "C" fn PyEval_GetBuiltins() -> *mut PyObject {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::PyException;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_code_eval() {
|
||||
fn code_eval() {
|
||||
Python::attach(|py| {
|
||||
let result = py.eval(c"1 + 1", None, None).unwrap();
|
||||
assert_eq!(result.extract::<u32>().unwrap(), 2);
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_run_exception() {
|
||||
fn code_run_exception() {
|
||||
Python::attach(|py| {
|
||||
let err = py.run(c"raise Exception()", None, None).unwrap_err();
|
||||
assert!(err.is_instance_of::<PyException>(py));
|
||||
|
||||
@@ -36,13 +36,13 @@ pub unsafe extern "C" fn PyComplex_ImagAsDouble(obj: *mut PyObject) -> c_double
|
||||
with_vm(|vm| try_to_complex(vm, unsafe { &*obj }).map(|complex| complex.im))
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyComplex;
|
||||
|
||||
#[test]
|
||||
fn test_py_int() {
|
||||
fn py_int() {
|
||||
Python::attach(|py| {
|
||||
let number = PyComplex::from_doubles(py, 1.0, 2.0);
|
||||
assert_eq!(number.real(), 1.0);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::PyObject;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PyCriticalSection;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PyCriticalSection2;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyCriticalSection_Begin(c: *mut PyCriticalSection, op: *mut PyObject) {
|
||||
let _ = (c, op);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyCriticalSection_End(c: *mut PyCriticalSection) {
|
||||
let _ = c;
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyCriticalSection2_Begin(
|
||||
c: *mut PyCriticalSection2,
|
||||
a: *mut PyObject,
|
||||
b: *mut PyObject,
|
||||
) {
|
||||
let _ = (c, a, b);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyCriticalSection2_End(c: *mut PyCriticalSection2) {
|
||||
let _ = c;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ pub unsafe extern "C" fn PyDictProxy_New(mapping: *mut PyObject) -> *mut PyObjec
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyInt, PyMappingProxy};
|
||||
@@ -23,7 +23,7 @@ mod tests {
|
||||
dict.set_item("x", 7).unwrap();
|
||||
|
||||
let mapping = dict.as_mapping();
|
||||
let proxy = PyMappingProxy::new(py, &mapping);
|
||||
let proxy = PyMappingProxy::new(py, mapping);
|
||||
let value = proxy.get_item("x").unwrap().cast_into::<PyInt>().unwrap();
|
||||
assert_eq!(value, 7);
|
||||
})
|
||||
|
||||
@@ -57,6 +57,43 @@ pub unsafe extern "C" fn PyDict_GetItemRef(
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyDict_SetDefaultRef(
|
||||
dict: *mut PyObject,
|
||||
key: *mut PyObject,
|
||||
default_value: *mut PyObject,
|
||||
result: *mut *mut PyObject,
|
||||
) -> c_int {
|
||||
with_vm(|vm| {
|
||||
let result = NonNull::new(result);
|
||||
if let Some(result) = result {
|
||||
unsafe {
|
||||
result.write(core::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
let dict = unsafe { &*dict }.try_downcast_ref::<PyDict>(vm)?;
|
||||
let key = unsafe { &*key };
|
||||
|
||||
if let Some(value) = dict.inner_getitem_opt(key, vm)? {
|
||||
if let Some(result) = result {
|
||||
unsafe {
|
||||
result.write(value.into_raw().as_ptr());
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
let value = unsafe { &*default_value }.to_owned();
|
||||
dict.inner_setitem(key, value.clone(), vm)?;
|
||||
if let Some(result) = result {
|
||||
unsafe {
|
||||
result.write(value.into_raw().as_ptr());
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyDict_Size(dict: *mut PyObject) -> isize {
|
||||
with_vm(|vm| {
|
||||
@@ -187,13 +224,13 @@ pub unsafe extern "C" fn PyDict_Next(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{IntoPyDict, PyDict, PyDictMethods, PyInt, PyList};
|
||||
|
||||
#[test]
|
||||
fn test_create_empty_dict() {
|
||||
fn create_empty_dict() {
|
||||
Python::attach(|py| {
|
||||
let dict = PyDict::new(py);
|
||||
assert!(dict.is_instance_of::<PyDict>());
|
||||
@@ -201,7 +238,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_dict_with_items() {
|
||||
fn create_dict_with_items() {
|
||||
Python::attach(|py| {
|
||||
let dict = [(1, 2), (3, 4)].into_py_dict(py)?;
|
||||
let value = dict.get_item(1)?.unwrap().cast_into::<PyInt>()?;
|
||||
@@ -214,7 +251,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dict_iter() {
|
||||
fn dict_iter() {
|
||||
Python::attach(|py| {
|
||||
let dict = [(1, 2), (3, 4)].into_py_dict(py).unwrap();
|
||||
let values = dict
|
||||
|
||||
@@ -57,14 +57,14 @@ pub unsafe extern "C" fn PyFloat_FromString(obj: *mut PyObject) -> *mut PyObject
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::f64::consts::PI;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyFloat;
|
||||
|
||||
#[test]
|
||||
fn test_py_float() {
|
||||
fn py_float() {
|
||||
Python::attach(|py| {
|
||||
let pi = PyFloat::new(py, PI);
|
||||
assert!(pi.is_instance_of::<PyFloat>());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use rustpython_vm::PyPayload;
|
||||
use rustpython_vm::builtins::PyGenericAlias;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn Py_GenericAlias(
|
||||
origin: *mut PyObject,
|
||||
args: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let origin = unsafe { &*origin }.to_owned();
|
||||
let args = unsafe { &*args }.to_owned();
|
||||
PyGenericAlias::from_args(origin, args, vm).into_pyobject(vm)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use rustpython_vm::builtins::PyStr;
|
||||
use core::ffi::{CStr, c_char};
|
||||
use rustpython_vm::builtins::{PyCode, PyDict, PyModule, PyStr};
|
||||
use rustpython_vm::import::import_code_obj;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyImport_Import(name: *mut PyObject) -> *mut PyObject {
|
||||
@@ -9,12 +11,64 @@ pub unsafe extern "C" fn PyImport_Import(name: *mut PyObject) -> *mut PyObject {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyImport_AddModuleRef(name: *const c_char) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let name = unsafe { CStr::from_ptr(name) }
|
||||
.to_str()
|
||||
.map_err(|_| vm.new_system_error("PyImport_AddModuleRef called with non utf8 name"))?;
|
||||
|
||||
let sys_modules = vm
|
||||
.sys_module
|
||||
.get_attr(rustpython_vm::identifier!(vm, modules), vm)?;
|
||||
|
||||
sys_modules
|
||||
.try_downcast_ref::<PyDict>(vm)?
|
||||
.get_item_opt(name, vm)?
|
||||
.map_or_else(
|
||||
|| {
|
||||
let module = vm.new_module(name, vm.ctx.new_dict(), None);
|
||||
sys_modules.set_item(name, module.clone().into(), vm)?;
|
||||
Ok(module)
|
||||
},
|
||||
|module| {
|
||||
let module = module.try_downcast_ref::<PyModule>(vm)?;
|
||||
Ok(module.to_owned())
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyImport_ExecCodeModuleEx(
|
||||
name: *const c_char,
|
||||
co: *mut PyObject,
|
||||
pathname: *const c_char,
|
||||
) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let name = unsafe { CStr::from_ptr(name) }.to_str().map_err(|_| {
|
||||
vm.new_system_error("PyImport_ExecCodeModuleEx called with non utf8 name")
|
||||
})?;
|
||||
let code = unsafe { &*co }.try_downcast_ref::<PyCode>(vm)?;
|
||||
let module = import_code_obj(vm, name, code.to_owned(), false)?;
|
||||
|
||||
if !pathname.is_null() {
|
||||
let pathname = unsafe { CStr::from_ptr(pathname) }.to_str().map_err(|_| {
|
||||
vm.new_system_error("PyImport_ExecCodeModuleEx called with non utf8 pathname")
|
||||
})?;
|
||||
module.set_attr("__file__", vm.ctx.new_str(pathname), vm)?;
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_import() {
|
||||
fn import() {
|
||||
Python::attach(|py| {
|
||||
let _module = py.import("sys").unwrap();
|
||||
})
|
||||
|
||||
@@ -14,15 +14,18 @@ pub mod bytearrayobject;
|
||||
pub mod bytesobject;
|
||||
pub mod ceval;
|
||||
pub mod complexobject;
|
||||
pub mod critical_section;
|
||||
pub mod descrobject;
|
||||
pub mod dictobject;
|
||||
pub mod floatobject;
|
||||
pub mod genericaliasobject;
|
||||
pub mod import;
|
||||
pub mod listobject;
|
||||
pub mod longobject;
|
||||
pub mod methodobject;
|
||||
pub mod moduleobject;
|
||||
pub mod object;
|
||||
pub mod osmodule;
|
||||
pub mod pycapsule;
|
||||
pub mod pyerrors;
|
||||
pub mod pylifecycle;
|
||||
|
||||
@@ -165,7 +165,7 @@ pub unsafe extern "C" fn PyList_Sort(list: *mut PyObject) -> c_int {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::PyIndexError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::PyObject;
|
||||
use crate::object::define_py_check;
|
||||
use crate::pystate::with_vm;
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use bitflags::bitflags;
|
||||
use core::ffi::{CStr, c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
|
||||
use malachite_bigint::{BigInt, Sign};
|
||||
@@ -373,13 +372,13 @@ pub unsafe extern "C" fn PyLong_AsUnsignedLongLong(obj: *mut PyObject) -> c_ulon
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyInt;
|
||||
|
||||
#[test]
|
||||
fn test_py_int_u32() {
|
||||
fn py_int_u32() {
|
||||
Python::attach(|py| {
|
||||
let number = PyInt::new(py, 123);
|
||||
assert!(number.is_instance_of::<PyInt>());
|
||||
@@ -388,7 +387,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_py_int_u64() {
|
||||
fn py_int_u64() {
|
||||
Python::attach(|py| {
|
||||
let number = PyInt::new(py, 123u64);
|
||||
assert!(number.is_instance_of::<PyInt>());
|
||||
|
||||
@@ -306,7 +306,7 @@ pub unsafe extern "C" fn PyCFunction_NewEx(
|
||||
unsafe { PyCMethod_New(ml, slf, module, core::ptr::null_mut()) }
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::PyException;
|
||||
use pyo3::ffi::{PyLong_FromLong, PyObject};
|
||||
@@ -314,7 +314,7 @@ mod tests {
|
||||
use pyo3::types::{PyCFunction, PyInt, PyString};
|
||||
|
||||
#[test]
|
||||
fn test_closure_function() {
|
||||
fn closure_function() {
|
||||
Python::attach(|py| {
|
||||
let f = PyCFunction::new_closure(py, None, None, |_args, _kwargs| "Hello from Rust!")
|
||||
.unwrap();
|
||||
@@ -327,7 +327,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_no_args() {
|
||||
fn function_no_args() {
|
||||
Python::attach(|py| {
|
||||
unsafe extern "C" fn c_fn(_self: *mut PyObject, _args: *mut PyObject) -> *mut PyObject {
|
||||
assert!(_self.is_null());
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_closure_function_error() {
|
||||
fn closure_function_error() {
|
||||
Python::attach(|py| {
|
||||
let f = PyCFunction::new_closure(py, None, None, |_args, _kwargs| {
|
||||
Err::<(), _>(PyException::new_err("Something went wrong"))
|
||||
|
||||
@@ -541,7 +541,7 @@ pub unsafe extern "C" fn PyObject_GenericSetDict(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::class::basic::CompareOp;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use rustpython_vm::convert::ToPyObject;
|
||||
use rustpython_vm::function::FsPath;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyOS_FSPath(path: *mut PyObject) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let path = unsafe { &*path }.to_owned();
|
||||
let fspath = FsPath::try_from_path_like(path, false, vm)?;
|
||||
Ok(fspath.to_pyobject(vm))
|
||||
})
|
||||
}
|
||||
@@ -142,13 +142,13 @@ fn checked_capsule<'a>(
|
||||
Ok(capsule)
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyCapsule;
|
||||
|
||||
#[test]
|
||||
fn test_capsule_new() {
|
||||
fn capsule_new() {
|
||||
Python::attach(|py| {
|
||||
let value = String::from("Some data");
|
||||
let capsule = PyCapsule::new_with_value(py, value, c"my_capsule").unwrap();
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::{PyObject, pystate::with_vm};
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::{CStr, c_char, c_int};
|
||||
use core::ptr::NonNull;
|
||||
use core::slice;
|
||||
use rustpython_vm::builtins::{PyBaseException, PyTuple, PyType};
|
||||
use rustpython_vm::convert::IntoObject;
|
||||
use rustpython_vm::exceptions::ExceptionZoo;
|
||||
@@ -299,9 +300,90 @@ pub unsafe extern "C" fn PyException_GetContext(exc: *mut PyObject) -> *mut PyOb
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyException_SetCause(exc: *mut PyObject, cause: *mut PyObject) {
|
||||
with_vm(|vm| {
|
||||
let exc = unsafe { &*exc }.try_downcast_ref::<PyBaseException>(vm)?;
|
||||
let cause = NonNull::new(cause)
|
||||
.map(|obj| unsafe { PyObjectRef::from_raw(obj).downcast_unchecked() });
|
||||
exc.set___cause__(cause);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyException_SetContext(exc: *mut PyObject, context: *mut PyObject) {
|
||||
with_vm(|vm| {
|
||||
let exc = unsafe { &*exc }.try_downcast_ref::<PyBaseException>(vm)?;
|
||||
let context = NonNull::new(context)
|
||||
.map(|obj| unsafe { PyObjectRef::from_raw(obj).downcast_unchecked() });
|
||||
exc.set___context__(context);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyUnicodeDecodeError_Create(
|
||||
encoding: *const c_char,
|
||||
object: *const c_char,
|
||||
length: isize,
|
||||
start: isize,
|
||||
end: isize,
|
||||
reason: *const c_char,
|
||||
) -> *mut PyObject {
|
||||
with_vm(|vm| {
|
||||
let encoding = unsafe { CStr::from_ptr(encoding) }
|
||||
.to_str()
|
||||
.map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?;
|
||||
let reason = unsafe { CStr::from_ptr(reason) }
|
||||
.to_str()
|
||||
.map_err(|_| vm.new_system_error("reason must be valid UTF-8"))?;
|
||||
let length: usize = length
|
||||
.try_into()
|
||||
.map_err(|_| vm.new_system_error("length must be non-negative"))?;
|
||||
let start: usize = start
|
||||
.try_into()
|
||||
.map_err(|_| vm.new_system_error("start must be non-negative"))?;
|
||||
let end: usize = end
|
||||
.try_into()
|
||||
.map_err(|_| vm.new_system_error("end must be non-negative"))?;
|
||||
|
||||
let bytes = if object.is_null() {
|
||||
if length != 0 {
|
||||
return Err(vm.new_system_error(
|
||||
"PyUnicodeDecodeError_Create called with null object and non-zero length",
|
||||
));
|
||||
}
|
||||
Vec::new()
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(object.cast::<u8>(), length) }.to_vec()
|
||||
};
|
||||
|
||||
let exc = vm.new_unicode_decode_error_real(
|
||||
vm.ctx.new_str(encoding),
|
||||
vm.ctx.new_bytes(bytes),
|
||||
start,
|
||||
end,
|
||||
vm.ctx.new_str(reason),
|
||||
);
|
||||
Ok(exc)
|
||||
})
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn PyException_SetTraceback(exc: *mut PyObject, tb: *mut PyObject) -> c_int {
|
||||
with_vm(|vm| {
|
||||
let exc = unsafe { &*exc }.try_downcast_ref::<PyBaseException>(vm)?;
|
||||
let traceback = unsafe { tb.as_ref() }.map(|obj| obj.to_owned());
|
||||
exc.set___traceback__(vm.unwrap_or_none(traceback), vm)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::PyTypeInfo;
|
||||
use pyo3::create_exception;
|
||||
use pyo3::exceptions::{PyException, PyTypeError};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
@@ -309,7 +391,7 @@ mod tests {
|
||||
Python::attach(|py| {
|
||||
PyTypeError::new_err(py.None()).restore(py);
|
||||
assert!(PyErr::occurred(py));
|
||||
assert!(unsafe { !pyo3::ffi::PyErr_GetRaisedException().is_null() });
|
||||
assert!(PyErr::take(py).is_some());
|
||||
assert!(!PyErr::occurred(py));
|
||||
})
|
||||
}
|
||||
@@ -321,4 +403,19 @@ mod tests {
|
||||
assert!(err.is_instance_of::<PyTypeError>(py));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_exception_type() {
|
||||
create_exception!(my_module, MyError, PyException, "Some description.");
|
||||
|
||||
Python::attach(|py| {
|
||||
let exc = MyError::new_err("This is a new exception");
|
||||
assert!(exc.is_instance_of::<MyError>(py));
|
||||
let exc_type = MyError::type_object(py);
|
||||
assert_eq!(
|
||||
exc_type.fully_qualified_name().unwrap(),
|
||||
"my_module.MyError"
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
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 alloc::ffi::CString;
|
||||
use core::ffi::{c_char, c_int, c_ulong};
|
||||
use rustpython_vm::common::rc::PyRc;
|
||||
use rustpython_vm::version::{MAJOR, MICRO, MINOR, VERSION_HEX};
|
||||
use rustpython_vm::vm::thread::ThreadedVirtualMachine;
|
||||
use rustpython_vm::{Context, Interpreter};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
pub(crate) static MAIN_INTERP: Mutex<Option<Interpreter>> = Mutex::new(None);
|
||||
|
||||
@@ -17,6 +19,9 @@ pub(crate) fn request_vm_from_interpreter() -> ThreadedVirtualMachine {
|
||||
.enter(|vm| vm.new_thread())
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static Py_Version: c_ulong = VERSION_HEX as c_ulong;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Py_IsInitialized() -> c_int {
|
||||
get_main_interpreter().is_some() as c_int
|
||||
@@ -65,3 +70,27 @@ pub extern "C" fn Py_FinalizeEx() -> c_int {
|
||||
pub extern "C" fn Py_IsFinalizing() -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn Py_GetVersion() -> *const c_char {
|
||||
static VERSION: LazyLock<CString> = LazyLock::new(|| {
|
||||
CString::new(format!("{MAJOR}.{MINOR}.{MICRO}"))
|
||||
.expect("version string must not contain interior NULs")
|
||||
});
|
||||
VERSION.as_ptr()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn get_version() {
|
||||
Python::attach(|py| {
|
||||
let version = py.version_info();
|
||||
assert!(version >= (3, 14));
|
||||
});
|
||||
|
||||
assert!(unsafe { pyo3::ffi::Py_Version } >= 0x030d0000);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::get_main_interpreter;
|
||||
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, with_current_vm,
|
||||
};
|
||||
use rustpython_vm::{Interpreter, VirtualMachine};
|
||||
|
||||
pub(crate) fn with_vm<R: FfiResult<O>, O>(f: impl FnOnce(&VirtualMachine) -> R) -> O {
|
||||
with_current_vm(|vm| f(vm).into_output(vm))
|
||||
@@ -16,9 +17,11 @@ type PyGILState_STATE = c_int;
|
||||
const PYGILSTATE_LOCKED: PyGILState_STATE = 0;
|
||||
const PYGILSTATE_UNLOCKED: PyGILState_STATE = 1;
|
||||
|
||||
pub type PyInterpreterState = Interpreter;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PyThreadState {
|
||||
_interp: *mut core::ffi::c_void,
|
||||
pub interp: *mut PyInterpreterState,
|
||||
}
|
||||
|
||||
/// Make sure this thread has a running vm attached. This only creates a new vm if we don't already
|
||||
@@ -50,6 +53,25 @@ pub extern "C" fn PyEval_SaveThread() -> *mut PyThreadState {
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyEval_RestoreThread(_state: *mut PyThreadState) {}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyInterpreterState_Get() -> *mut PyInterpreterState {
|
||||
get_main_interpreter()
|
||||
.as_ref()
|
||||
.map(|interp| interp as *const PyInterpreterState)
|
||||
.expect("PyInterpreterState_Get called but no main interpreter was found")
|
||||
.cast_mut()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn PyInterpreterState_GetID(interp: *mut PyInterpreterState) -> i64 {
|
||||
with_vm(|vm| {
|
||||
if interp.is_null() {
|
||||
return Err(vm.new_system_error("PyInterpreterState_GetID called with null interp"));
|
||||
}
|
||||
Ok(interp as usize as i64)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::get_main_interpreter;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::PyObject;
|
||||
use crate::{PyObject, pystate::with_vm};
|
||||
use core::ptr::NonNull;
|
||||
use rustpython_vm::PyObjectRef;
|
||||
|
||||
@@ -13,3 +13,32 @@ pub unsafe extern "C" fn _Py_IncRef(op: *mut PyObject) {
|
||||
// Don't drop the owned value, as we just want to increment the refcount.
|
||||
core::mem::forget(unsafe { (*op).to_owned() });
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn Py_NewRef(op: *mut PyObject) -> *mut PyObject {
|
||||
with_vm(|_vm| unsafe { (*op).to_owned() })
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn Py_REFCNT(op: *mut PyObject) -> isize {
|
||||
with_vm(|_vm| unsafe { &*op }.strong_count())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyInt;
|
||||
use pyo3::{PyTypeInfo, ffi};
|
||||
|
||||
#[test]
|
||||
fn refcount() {
|
||||
Python::attach(|py| unsafe {
|
||||
let obj = PyInt::type_object(py);
|
||||
let ref_count = ffi::Py_REFCNT(obj.as_ptr());
|
||||
let obj_clone = obj.clone();
|
||||
assert_eq!(ffi::Py_REFCNT(obj.as_ptr()), ref_count + 1);
|
||||
drop(obj_clone);
|
||||
assert_eq!(ffi::Py_REFCNT(obj.as_ptr()), ref_count);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ pub unsafe extern "C" fn PySet_Size(anyset: *mut PyObject) -> isize {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyFrozenSet, PyInt, PySet};
|
||||
|
||||
@@ -72,7 +72,7 @@ pub unsafe extern "C" fn PySlice_AdjustIndices(
|
||||
slice_len as isize
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PySlice, PySliceMethods};
|
||||
|
||||
@@ -90,13 +90,13 @@ pub unsafe extern "C" fn PyTuple_GetSlice(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyTuple;
|
||||
|
||||
#[test]
|
||||
fn test_empty_tuple() {
|
||||
fn empty_tuple() {
|
||||
Python::attach(|py| {
|
||||
let tuple = PyTuple::empty(py);
|
||||
assert_eq!(tuple.len(), 0);
|
||||
@@ -104,7 +104,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_into_python() {
|
||||
fn tuple_into_python() {
|
||||
Python::attach(|py| {
|
||||
let tuple = (1, 2, 3).into_pyobject(py).unwrap();
|
||||
assert_eq!(tuple.len(), 3);
|
||||
@@ -112,7 +112,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_get_slice() {
|
||||
fn tuple_get_slice() {
|
||||
Python::attach(|py| {
|
||||
let tuple = (1, 2, 3).into_pyobject(py).unwrap();
|
||||
let slice = tuple.get_slice(1, 2);
|
||||
|
||||
@@ -241,7 +241,7 @@ pub unsafe extern "C" fn PyUnicode_EqualToUTF8AndSize(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ pub unsafe extern "C" fn PyErr_WarnExplicit(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::exceptions::{PyRuntimeWarning, PyUserWarning};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
@@ -65,7 +65,7 @@ pub unsafe extern "C" fn PyWeakref_NewRef(
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(false)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyAnyMethods;
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::{
|
||||
use alloc::borrow::Cow;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt::Debug;
|
||||
use core::ptr::NonNull;
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
use rustpython_common::lock::PyRwLock;
|
||||
#[cfg(windows)]
|
||||
@@ -1432,6 +1433,28 @@ fn convert_raw_result(
|
||||
|
||||
let info = stg_info.unwrap();
|
||||
|
||||
// py_object: interpret return value as PyObject* and materialize it.
|
||||
if let Ok(type_attr) = restype_type
|
||||
.as_object()
|
||||
.get_attr(vm.ctx.intern_str("_type_"), vm)
|
||||
&& let Some(type_str) = type_attr.downcast_ref::<PyStr>()
|
||||
&& type_str.to_str() == Some("O")
|
||||
{
|
||||
let ptr = match raw_result {
|
||||
RawResult::Pointer(p) => *p,
|
||||
RawResult::Value(v) => *v as usize,
|
||||
RawResult::Void => 0,
|
||||
};
|
||||
let ptr = NonNull::new(ptr as *mut PyObject).or_else(|| {
|
||||
vm.set_exception(Some(vm.new_value_error("PyObject is NULL")));
|
||||
None
|
||||
})?;
|
||||
unsafe {
|
||||
let obj = PyObjectRef::from_raw(ptr);
|
||||
return Some(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Simple type with getfunc → use bytes_to_pyobject (info->getfunc)
|
||||
// is_simple_instance returns TRUE for c_int, c_void_p, etc.
|
||||
if super::base::is_simple_instance(&restype_type) {
|
||||
|
||||
Reference in new issue
Block a user