Merge pull request #1287 from davidhewitt/vectorcall
vectorcall: fix bindings & use for call0 and call_method0
This commit is contained in:
commit
47a731bd5d
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259)
|
||||
- Add `PyAny::is_instance()` method. [#1276](https://github.com/PyO3/pyo3/pull/1276)
|
||||
- Add support for conversion between `char` and `PyString`. [#1282](https://github.com/PyO3/pyo3/pull/1282)
|
||||
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
|
||||
|
||||
### Changed
|
||||
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
|
||||
|
@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Change `Debug` and `Display` impls for `PyException` to be consistent with `PyAny`. [#1275](https://github.com/PyO3/pyo3/pull/1275)
|
||||
- Change `Debug` impl of `PyErr` to output more helpful information (acquiring the GIL if necessary). [#1275](https://github.com/PyO3/pyo3/pull/1275)
|
||||
- Rename `PyTypeInfo::is_instance` and `PyTypeInfo::is_exact_instance` to `PyTypeInfo::is_type_of` and `PyTypeInfo::is_exact_type_of`. [#1278](https://github.com/PyO3/pyo3/pull/1278)
|
||||
- Optimize `PyAny::call0`, `Py::call0` and `PyAny::call_method0` and `Py::call_method0` on Python 3.9 and up. [#1287](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Deprecate `Python::is_instance`, `Python::is_subclass`, `Python::release`, and `Python::xdecref`. [#1292](https://github.com/PyO3/pyo3/pull/1292)
|
||||
|
||||
### Removed
|
||||
|
@ -39,6 +41,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Fix missing field in `PyCodeObject` struct (`co_posonlyargcount`) - caused invalid access to other fields in Python >3.7. [#1260](https://github.com/PyO3/pyo3/pull/1260)
|
||||
- Fix building for `x86_64-unknown-linux-musl` target from `x86_65-unknown-linux-gnu` host. [#1267](https://github.com/PyO3/pyo3/pull/1267)
|
||||
- Fix `#[text_signature]` interacting badly with rust `r#raw_identifiers`. [#1286](https://github.com/PyO3/pyo3/pull/1286)
|
||||
- Fix FFI definitions for `PyObject_Vectorcall` and `PyVectorcall_Call`. [#1287](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Fix building with Anaconda python inside a virtualenv. [#1290](https://github.com/PyO3/pyo3/pull/1290)
|
||||
|
||||
## [0.12.3] - 2020-10-12
|
||||
|
|
|
@ -15,6 +15,7 @@ build = "build.rs"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cfg-if = { version = "1.0" }
|
||||
ctor = { version = "0.1", optional = true }
|
||||
indoc = { version = "1.0.3", optional = true }
|
||||
inventory = { version = "0.1.4", optional = true }
|
||||
|
|
53
benches/bench_call.rs
Normal file
53
benches/bench_call.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
use pyo3::prelude::*;
|
||||
use test::Bencher;
|
||||
|
||||
macro_rules! test_module {
|
||||
($py:ident, $code:literal) => {
|
||||
PyModule::from_code($py, indoc::indoc!($code), file!(), "test_module")
|
||||
.expect("module creation failed")
|
||||
};
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_call_0(b: &mut Bencher) {
|
||||
Python::with_gil(|py| {
|
||||
let module = test_module!(
|
||||
py,
|
||||
r#"
|
||||
def foo(): pass
|
||||
"#
|
||||
);
|
||||
|
||||
let foo = module.getattr("foo").unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..1000 {
|
||||
foo.call0().unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_call_method_0(b: &mut Bencher) {
|
||||
Python::with_gil(|py| {
|
||||
let module = test_module!(
|
||||
py,
|
||||
r#"
|
||||
class Foo:
|
||||
def foo(self): pass
|
||||
"#
|
||||
);
|
||||
|
||||
let foo = module.getattr("Foo").unwrap().call0().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..1000 {
|
||||
foo.call_method0("foo").unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
306
src/ffi/cpython/abstract_.rs
Normal file
306
src/ffi/cpython/abstract_.rs
Normal file
|
@ -0,0 +1,306 @@
|
|||
use crate::ffi::{PyObject, Py_TYPE, Py_buffer, Py_ssize_t};
|
||||
use libc::{c_char, c_int, c_void};
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
use crate::ffi::{
|
||||
vectorcallfunc, PyCallable_Check, PyThreadState, PyThreadState_GET, PyTuple_Check,
|
||||
PyType_HasFeature, Py_TPFLAGS_HAVE_VECTORCALL,
|
||||
};
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
use libc::size_t;
|
||||
|
||||
extern "C" {
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
pub fn _PyStack_AsDict(values: *const *mut PyObject, kwnames: *mut PyObject) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
const _PY_FASTCALL_SMALL_STACK: size_t = 5;
|
||||
|
||||
extern "C" {
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
pub fn _Py_CheckFunctionResult(
|
||||
tstate: *mut PyThreadState,
|
||||
callable: *mut PyObject,
|
||||
result: *mut PyObject,
|
||||
where_: *const c_char,
|
||||
) -> *mut PyObject;
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
pub fn _PyObject_MakeTpCall(
|
||||
tstate: *mut PyThreadState,
|
||||
callable: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargs: Py_ssize_t,
|
||||
keywords: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
const PY_VECTORCALL_ARGUMENTS_OFFSET: Py_ssize_t =
|
||||
1 << (8 * std::mem::size_of::<Py_ssize_t>() as Py_ssize_t - 1);
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyVectorcall_NARGS(n: size_t) -> Py_ssize_t {
|
||||
assert!(n <= (Py_ssize_t::MAX as size_t));
|
||||
(n as Py_ssize_t) & !PY_VECTORCALL_ARGUMENTS_OFFSET
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyVectorcall_Function(callable: *mut PyObject) -> Option<vectorcallfunc> {
|
||||
assert!(!callable.is_null());
|
||||
let tp = Py_TYPE(callable);
|
||||
if PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL) == 0 {
|
||||
return None;
|
||||
}
|
||||
assert!(PyCallable_Check(callable) > 0);
|
||||
let offset = (*tp).tp_vectorcall_offset;
|
||||
assert!(offset > 0);
|
||||
let ptr = (callable as *const c_char).offset(offset) as *const Option<vectorcallfunc>;
|
||||
*ptr
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn _PyObject_VectorcallTstate(
|
||||
tstate: *mut PyThreadState,
|
||||
callable: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargsf: size_t,
|
||||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
assert!(kwnames.is_null() || PyTuple_Check(kwnames) > 0);
|
||||
assert!(!args.is_null() || PyVectorcall_NARGS(nargsf) == 0);
|
||||
|
||||
match PyVectorcall_Function(callable) {
|
||||
None => {
|
||||
let nargs = PyVectorcall_NARGS(nargsf);
|
||||
_PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames)
|
||||
}
|
||||
Some(func) => {
|
||||
let res = func(callable, args, nargsf, kwnames);
|
||||
_Py_CheckFunctionResult(tstate, callable, res, std::ptr::null_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyObject_Vectorcall(
|
||||
callable: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargsf: size_t,
|
||||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
_PyObject_VectorcallTstate(PyThreadState_GET(), callable, args, nargsf, kwnames)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[cfg_attr(not(Py_3_9), link_name = "_PyObject_VectorcallDict")]
|
||||
pub fn PyObject_VectorcallDict(
|
||||
callable: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargsf: size_t,
|
||||
kwargs: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[cfg_attr(not(Py_3_9), link_name = "_PyVectorcall_Call")]
|
||||
pub fn PyVectorcall_Call(
|
||||
callable: *mut PyObject,
|
||||
tuple: *mut PyObject,
|
||||
dict: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn _PyObject_FastCallTstate(
|
||||
tstate: *mut PyThreadState,
|
||||
func: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargs: Py_ssize_t,
|
||||
) -> *mut PyObject {
|
||||
_PyObject_VectorcallTstate(tstate, func, args, nargs as size_t, std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn _PyObject_FastCall(
|
||||
func: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargs: Py_ssize_t,
|
||||
) -> *mut PyObject {
|
||||
_PyObject_FastCallTstate(PyThreadState_GET(), func, args, nargs)
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn _PyObject_CallNoArg(func: *mut PyObject) -> *mut PyObject {
|
||||
_PyObject_VectorcallTstate(
|
||||
PyThreadState_GET(),
|
||||
func,
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *mut PyObject {
|
||||
assert!(!arg.is_null());
|
||||
let _args = [std::ptr::null_mut(), arg];
|
||||
let args = _args.as_ptr().offset(1); // For PY_VECTORCALL_ARGUMENTS_OFFSET
|
||||
let tstate = PyThreadState_GET();
|
||||
let nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
|
||||
_PyObject_VectorcallTstate(tstate, func, args, nargsf as size_t, std::ptr::null_mut())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||
pub fn PyObject_VectorcallMethod(
|
||||
name: *mut PyObject,
|
||||
args: *const *mut PyObject,
|
||||
nargsf: size_t,
|
||||
kwnames: *mut PyObject,
|
||||
) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyObject_CallMethodNoArgs(
|
||||
self_: *mut PyObject,
|
||||
name: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
PyObject_VectorcallMethod(
|
||||
name,
|
||||
&self_,
|
||||
1 | PY_VECTORCALL_ARGUMENTS_OFFSET as size_t,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyObject_CallMethodOneArg(
|
||||
self_: *mut PyObject,
|
||||
name: *mut PyObject,
|
||||
arg: *mut PyObject,
|
||||
) -> *mut PyObject {
|
||||
let args = [self_, arg];
|
||||
assert!(!arg.is_null());
|
||||
PyObject_VectorcallMethod(
|
||||
name,
|
||||
args.as_ptr(),
|
||||
2 | PY_VECTORCALL_ARGUMENTS_OFFSET as size_t,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
|
||||
// skipped _PyObject_VectorcallMethodId
|
||||
// skipped _PyObject_CallMethodIdNoArgs
|
||||
// skipped _PyObject_CallMethodIdOneArg
|
||||
|
||||
// skipped _PyObject_HasLen
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_LengthHint")]
|
||||
pub fn PyObject_LengthHint(o: *mut PyObject, arg1: Py_ssize_t) -> Py_ssize_t;
|
||||
|
||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||
pub fn PyObject_CheckBuffer(obj: *mut PyObject) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(not(any(Py_3_9, PyPy)))]
|
||||
#[inline]
|
||||
pub unsafe fn PyObject_CheckBuffer(o: *mut PyObject) -> c_int {
|
||||
let tp_as_buffer = (*Py_TYPE(o)).tp_as_buffer;
|
||||
(!tp_as_buffer.is_null() && (*tp_as_buffer).bf_getbuffer.is_some()) as c_int
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_GetBuffer")]
|
||||
pub fn PyObject_GetBuffer(obj: *mut PyObject, view: *mut Py_buffer, flags: c_int) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_GetPointer")]
|
||||
pub fn PyBuffer_GetPointer(view: *mut Py_buffer, indices: *mut Py_ssize_t) -> *mut c_void;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_SizeFromFormat")]
|
||||
pub fn PyBuffer_SizeFromFormat(format: *const c_char) -> Py_ssize_t;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_ToContiguous")]
|
||||
pub fn PyBuffer_ToContiguous(
|
||||
buf: *mut c_void,
|
||||
view: *mut Py_buffer,
|
||||
len: Py_ssize_t,
|
||||
order: c_char,
|
||||
) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FromContiguous")]
|
||||
pub fn PyBuffer_FromContiguous(
|
||||
view: *mut Py_buffer,
|
||||
buf: *mut c_void,
|
||||
len: Py_ssize_t,
|
||||
order: c_char,
|
||||
) -> c_int;
|
||||
pub fn PyObject_CopyData(dest: *mut PyObject, src: *mut PyObject) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_IsContiguous")]
|
||||
pub fn PyBuffer_IsContiguous(view: *const Py_buffer, fort: c_char) -> c_int;
|
||||
pub fn PyBuffer_FillContiguousStrides(
|
||||
ndims: c_int,
|
||||
shape: *mut Py_ssize_t,
|
||||
strides: *mut Py_ssize_t,
|
||||
itemsize: c_int,
|
||||
fort: c_char,
|
||||
);
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FillInfo")]
|
||||
pub fn PyBuffer_FillInfo(
|
||||
view: *mut Py_buffer,
|
||||
o: *mut PyObject,
|
||||
buf: *mut c_void,
|
||||
len: Py_ssize_t,
|
||||
readonly: c_int,
|
||||
flags: c_int,
|
||||
) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_Release")]
|
||||
pub fn PyBuffer_Release(view: *mut Py_buffer);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
|
||||
(match (*Py_TYPE(o)).tp_iternext {
|
||||
Some(tp_iternext) => {
|
||||
tp_iternext as *const c_void != crate::ffi::object::_PyObject_NextNotImplemented as _
|
||||
}
|
||||
None => false,
|
||||
}) as c_int
|
||||
}
|
||||
|
||||
// skipped PySequence_ITEM
|
||||
|
||||
pub const PY_ITERSEARCH_COUNT: c_int = 1;
|
||||
pub const PY_ITERSEARCH_INDEX: c_int = 2;
|
||||
pub const PY_ITERSEARCH_CONTAINS: c_int = 3;
|
||||
|
||||
extern "C" {
|
||||
#[cfg(not(PyPy))]
|
||||
pub fn _PySequence_IterSearch(
|
||||
seq: *mut PyObject,
|
||||
obj: *mut PyObject,
|
||||
operation: c_int,
|
||||
) -> Py_ssize_t;
|
||||
}
|
||||
|
||||
// skipped _PyObject_RealIsInstance
|
||||
// skipped _PyObject_RealIsSubclass
|
||||
|
||||
// skipped _PySequence_BytesToCharpArray
|
||||
|
||||
// skipped _Py_FreeCharPArray
|
||||
|
||||
// skipped _Py_add_one_to_index_F
|
||||
// skipped _Py_add_one_to_index_C
|
||||
|
||||
// skipped _Py_convert_optional_to_ssize_t
|
||||
|
||||
// skipped _PyNumber_Index(*mut PyObject o)
|
3
src/ffi/cpython/mod.rs
Normal file
3
src/ffi/cpython/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod abstract_;
|
||||
|
||||
pub use self::abstract_::*;
|
|
@ -16,40 +16,6 @@ pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int {
|
|||
pub type PyCFunction =
|
||||
unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject;
|
||||
|
||||
// TODO(davidhewitt)[1283] - Fix this definition
|
||||
// #[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
||||
// #[cfg_attr(Py_3_8, link_name = "_PyObject_Vectorcall")]
|
||||
// pub type PyObject_Vectorcall = unsafe extern "C" fn(
|
||||
// slf: *mut PyObject,
|
||||
// // positional and keyword arguments
|
||||
// args: *const *mut PyObject,
|
||||
// // number of position arguments in args, after which values are kwargs
|
||||
// nargs: crate::ffi::pyport::Py_ssize_t,
|
||||
// // tuple of kwargs, if given, or null
|
||||
// kwnames: *mut PyObject,
|
||||
// ) -> *mut PyObject;
|
||||
|
||||
// TODO(davidhewitt)[1283] - Fix this definition
|
||||
// #[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
||||
// #[cfg_attr(Py_3_8, link_name = "PyVectorcall_Call")]
|
||||
// pub type PyVectorcall_Call = unsafe extern "C" fn(
|
||||
// obj: *mut PyObject,
|
||||
// tuple: *mut PyObject,
|
||||
// dict: *mut PyObject,
|
||||
// ) -> *mut PyObject;
|
||||
|
||||
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
|
||||
const PY_VECTORCALL_ARGUMENTS_OFFSET: crate::ffi::pyport::Py_ssize_t =
|
||||
1 << (8 * std::mem::size_of::<usize>() - 1);
|
||||
|
||||
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn PyVectorcall_NARGS(
|
||||
n: crate::ffi::pyport::Py_ssize_t,
|
||||
) -> crate::ffi::pyport::Py_ssize_t {
|
||||
n & !PY_VECTORCALL_ARGUMENTS_OFFSET
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_7, not(Py_LIMITED_API)))]
|
||||
pub type _PyCFunctionFast = unsafe extern "C" fn(
|
||||
slf: *mut PyObject,
|
||||
|
|
|
@ -61,6 +61,9 @@ pub use self::unicodeobject::*;
|
|||
pub use self::warnings::*;
|
||||
pub use self::weakrefobject::*;
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub use self::cpython::*;
|
||||
|
||||
mod pyport;
|
||||
// mod pymacro; contains nothing of interest for Rust
|
||||
// mod pyatomic; contains nothing of interest for Rust
|
||||
|
@ -169,3 +172,6 @@ pub(crate) mod datetime;
|
|||
pub(crate) mod marshal;
|
||||
|
||||
pub(crate) mod funcobject;
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
mod cpython;
|
||||
|
|
|
@ -758,7 +758,7 @@ pub const Py_TPFLAGS_BASETYPE: c_ulong = 1 << 10;
|
|||
|
||||
/// Set if the type implements the vectorcall protocol (PEP 590)
|
||||
#[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
|
||||
pub const _Py_TPFLAGS_HAVE_VECTORCALL: c_ulong = 1 << 11;
|
||||
pub const Py_TPFLAGS_HAVE_VECTORCALL: c_ulong = 1 << 11;
|
||||
|
||||
/// Set if the type is 'ready' -- fully initialized
|
||||
pub const Py_TPFLAGS_READY: c_ulong = 1 << 12;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ffi::object::*;
|
||||
use crate::ffi::pyport::Py_ssize_t;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::ptr;
|
||||
|
||||
#[inline]
|
||||
|
@ -16,6 +16,11 @@ pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_
|
|||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg(all(
|
||||
not(PyPy),
|
||||
any(not(Py_LIMITED_API), Py_3_9) // Added to limited API in 3.9
|
||||
))]
|
||||
pub fn PyObject_CallNoArgs(func: *mut PyObject) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_Call")]
|
||||
pub fn PyObject_Call(
|
||||
callable_object: *mut PyObject,
|
||||
|
@ -61,10 +66,6 @@ pub unsafe fn PyObject_Length(o: *mut PyObject) -> Py_ssize_t {
|
|||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_LengthHint")]
|
||||
pub fn PyObject_LengthHint(o: *mut PyObject, arg1: Py_ssize_t) -> Py_ssize_t;
|
||||
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_GetItem")]
|
||||
pub fn PyObject_GetItem(o: *mut PyObject, key: *mut PyObject) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_SetItem")]
|
||||
|
@ -73,78 +74,15 @@ extern "C" {
|
|||
pub fn PyObject_DelItem(o: *mut PyObject, key: *mut PyObject) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[inline]
|
||||
pub unsafe fn PyObject_CheckBuffer(o: *mut PyObject) -> c_int {
|
||||
let tp_as_buffer = (*Py_TYPE(o)).tp_as_buffer;
|
||||
(!tp_as_buffer.is_null() && (*tp_as_buffer).bf_getbuffer.is_some()) as c_int
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_GetBuffer")]
|
||||
pub fn PyObject_GetBuffer(obj: *mut PyObject, view: *mut Py_buffer, flags: c_int) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_GetPointer")]
|
||||
pub fn PyBuffer_GetPointer(view: *mut Py_buffer, indices: *mut Py_ssize_t) -> *mut c_void;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_ToContiguous")]
|
||||
pub fn PyBuffer_ToContiguous(
|
||||
buf: *mut c_void,
|
||||
view: *mut Py_buffer,
|
||||
len: Py_ssize_t,
|
||||
order: c_char,
|
||||
) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FromContiguous")]
|
||||
pub fn PyBuffer_FromContiguous(
|
||||
view: *mut Py_buffer,
|
||||
buf: *mut c_void,
|
||||
len: Py_ssize_t,
|
||||
order: c_char,
|
||||
) -> c_int;
|
||||
pub fn PyObject_CopyData(dest: *mut PyObject, src: *mut PyObject) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_IsContiguous")]
|
||||
pub fn PyBuffer_IsContiguous(view: *const Py_buffer, fort: c_char) -> c_int;
|
||||
pub fn PyBuffer_FillContiguousStrides(
|
||||
ndims: c_int,
|
||||
shape: *mut Py_ssize_t,
|
||||
strides: *mut Py_ssize_t,
|
||||
itemsize: c_int,
|
||||
fort: c_char,
|
||||
);
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_FillInfo")]
|
||||
pub fn PyBuffer_FillInfo(
|
||||
view: *mut Py_buffer,
|
||||
o: *mut PyObject,
|
||||
buf: *mut c_void,
|
||||
len: Py_ssize_t,
|
||||
readonly: c_int,
|
||||
flags: c_int,
|
||||
) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyBuffer_Release")]
|
||||
pub fn PyBuffer_Release(view: *mut Py_buffer);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_Format")]
|
||||
pub fn PyObject_Format(obj: *mut PyObject, format_spec: *mut PyObject) -> *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyObject_GetIter")]
|
||||
pub fn PyObject_GetIter(arg1: *mut PyObject) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[inline]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyIter_Check")]
|
||||
pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
|
||||
(match (*Py_TYPE(o)).tp_iternext {
|
||||
Some(tp_iternext) => {
|
||||
tp_iternext as *const c_void
|
||||
!= crate::ffi::object::_PyObject_NextNotImplemented as *const c_void
|
||||
}
|
||||
None => false,
|
||||
}) as c_int
|
||||
}
|
||||
|
||||
#[cfg(all(Py_LIMITED_API, Py_3_8))]
|
||||
extern "C" {
|
||||
// PyIter_Check for unlimited API is in cpython/abstract_.rs
|
||||
#[cfg(all(Py_LIMITED_API, Py_3_8))]
|
||||
#[cfg_attr(PyPy, link_name = "PyPyIter_Check")]
|
||||
pub fn PyIter_Check(obj: *mut PyObject) -> c_int;
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,18 @@ impl<T> Py<T> {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self, py: Python) -> PyResult<PyObject> {
|
||||
self.call(py, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
// TODO: Use PyObject_CallNoArgs instead after https://bugs.python.org/issue42415.
|
||||
// Once the issue is resolved, we can enable this optimization for limited API.
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::_PyObject_CallNoArg(self.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call(py, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
|
@ -316,7 +327,17 @@ impl<T> Py<T> {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(py);
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call_method(py, name, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
|
|
|
@ -226,7 +226,18 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self) -> PyResult<&PyAny> {
|
||||
self.call((), None)
|
||||
cfg_if::cfg_if! {
|
||||
// TODO: Use PyObject_CallNoArgs instead after https://bugs.python.org/issue42415.
|
||||
// Once the issue is resolved, we can enable this optimization for limited API.
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
self.py().from_owned_ptr_or_err(ffi::_PyObject_CallNoArg(self.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call((), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
|
@ -283,7 +294,17 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
|
||||
self.call_method(name, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(self.py());
|
||||
self.py().from_owned_ptr_or_err(ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call_method(name, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a method on the object with only positional arguments.
|
||||
|
@ -464,8 +485,17 @@ impl PyAny {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::types::{IntoPyDict, PyList, PyLong};
|
||||
use crate::{Python, ToPyObject};
|
||||
use crate::{
|
||||
types::{IntoPyDict, PyList, PyLong, PyModule},
|
||||
Python, ToPyObject,
|
||||
};
|
||||
|
||||
macro_rules! test_module {
|
||||
($py:ident, $code:literal) => {
|
||||
PyModule::from_code($py, indoc::indoc!($code), file!(), "test_module")
|
||||
.expect("module creation failed")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_for_non_existing_method() {
|
||||
|
@ -488,6 +518,30 @@ mod test {
|
|||
assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_method0() {
|
||||
Python::with_gil(|py| {
|
||||
let module = test_module!(
|
||||
py,
|
||||
r#"
|
||||
class SimpleClass:
|
||||
def foo(self):
|
||||
return 42
|
||||
"#
|
||||
);
|
||||
|
||||
let simple_class = module.getattr("SimpleClass").unwrap().call0().unwrap();
|
||||
assert_eq!(
|
||||
simple_class
|
||||
.call_method0("foo")
|
||||
.unwrap()
|
||||
.extract::<u32>()
|
||||
.unwrap(),
|
||||
42
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
Loading…
Reference in a new issue