Add native Function native types.

Add bindings for PyCFunction, PyFunction, PyClassMethod and
PyStaticMethod.
This commit is contained in:
Sebastian Pütz 2020-09-05 15:54:03 +02:00
parent 64b06ea9ec
commit 2e8010b5df
8 changed files with 93 additions and 8 deletions

View File

@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add macro attribute to `#[pyfn]` and `#[pyfunction]` to pass the module of a Python function to the function
body. [#1143](https://github.com/PyO3/pyo3/pull/1143)
- Add `add_function()` and `add_submodule()` functions to `PyModule` [#1143](https://github.com/PyO3/pyo3/pull/1143)
- Add native `PyCFunction` and `PyFunction` types, change `add_function` to take a wrapper returning
a `&PyCFunction`instead of `PyObject`. [#1163](https://github.com/PyO3/pyo3/pull/1163)
### Changed
- Exception types have been renamed from e.g. `RuntimeError` to `PyRuntimeError`, and are now only accessible by `&T` or `Py<T>` similar to other Python-native types. The old names continue to exist but are deprecated. [#1024](https://github.com/PyO3/pyo3/pull/1024)

View File

@ -209,7 +209,7 @@ pub fn add_fn_to_module(
Ok(quote! {
fn #function_wrapper_ident<'a>(
args: impl Into<pyo3::derive_utils::WrapPyFunctionArguments<'a>>
) -> pyo3::PyResult<pyo3::PyObject> {
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
let arg = args.into();
let (py, maybe_module) = arg.into_py_and_maybe_module();
#wrapper
@ -231,8 +231,7 @@ pub fn add_fn_to_module(
};
let function = unsafe {
pyo3::PyObject::from_owned_ptr(
py,
py.from_owned_ptr::<pyo3::types::PyCFunction>(
pyo3::ffi::PyCFunction_NewEx(
Box::into_raw(Box::new(_def.as_method_def())),
mod_ptr,

34
src/ffi/funcobject.rs Normal file
View File

@ -0,0 +1,34 @@
use std::os::raw::c_int;
use crate::ffi::object::{PyObject, PyTypeObject, Py_TYPE};
#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyFunction_Type")]
pub static mut PyFunction_Type: PyTypeObject;
}
#[inline]
pub unsafe fn PyFunction_Check(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == &mut PyFunction_Type) as c_int
}
extern "C" {
pub fn PyFunction_NewWithQualName(
code: *mut PyObject,
globals: *mut PyObject,
qualname: *mut PyObject,
) -> *mut PyObject;
pub fn PyFunction_New(code: *mut PyObject, globals: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_Code(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_GetGlobals(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_GetModule(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_GetDefaults(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_SetDefaults(op: *mut PyObject, defaults: *mut PyObject) -> c_int;
pub fn PyFunction_GetKwDefaults(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_SetKwDefaults(op: *mut PyObject, defaults: *mut PyObject) -> c_int;
pub fn PyFunction_GetClosure(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_SetClosure(op: *mut PyObject, closure: *mut PyObject) -> c_int;
pub fn PyFunction_GetAnnotations(op: *mut PyObject) -> *mut PyObject;
pub fn PyFunction_SetAnnotations(op: *mut PyObject, annotations: *mut PyObject) -> c_int;
}

View File

@ -19,6 +19,7 @@ pub use self::eval::*;
pub use self::fileobject::*;
pub use self::floatobject::*;
pub use self::frameobject::PyFrameObject;
pub use self::funcobject::*;
pub use self::genobject::*;
pub use self::import::*;
pub use self::intrcheck::*;
@ -157,3 +158,5 @@ pub mod frameobject {
pub(crate) mod datetime;
pub(crate) mod marshal;
pub(crate) mod funcobject;

14
src/types/function.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::ffi;
use crate::prelude::*;
/// Represents a builtin Python function object.
#[repr(transparent)]
pub struct PyCFunction(PyAny);
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
/// Represents a Python function object.
#[repr(transparent)]
pub struct PyFunction(PyAny);
pyobject_native_var_type!(PyFunction, ffi::PyFunction_Type, ffi::PyFunction_Check);

View File

@ -13,6 +13,7 @@ pub use self::datetime::{
};
pub use self::dict::{IntoPyDict, PyDict};
pub use self::floatob::PyFloat;
pub use self::function::{PyCFunction, PyFunction};
pub use self::iterator::PyIterator;
pub use self::list::PyList;
pub use self::module::PyModule;
@ -225,6 +226,7 @@ mod complex;
mod datetime;
mod dict;
mod floatob;
mod function;
mod iterator;
mod list;
mod module;

View File

@ -9,8 +9,8 @@ use crate::ffi;
use crate::instance::PyNativeType;
use crate::pyclass::PyClass;
use crate::type_object::PyTypeObject;
use crate::types::PyTuple;
use crate::types::{PyAny, PyDict, PyList};
use crate::types::{PyCFunction, PyTuple};
use crate::{AsPyPointer, IntoPy, Py, PyObject, Python};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
@ -275,12 +275,11 @@ impl PyModule {
/// ```
pub fn add_function<'a>(
&'a self,
wrapper: &impl Fn(&'a Self) -> PyResult<PyObject>,
wrapper: &impl Fn(&'a Self) -> PyResult<&'a PyCFunction>,
) -> PyResult<()> {
let py = self.py();
let function = wrapper(self)?;
let name = function.getattr(py, "__name__")?;
let name = name.extract(py)?;
let name = function.getattr("__name__")?;
let name = name.extract()?;
self.add(name, function)
}
}

View File

@ -1,5 +1,6 @@
use pyo3::buffer::PyBuffer;
use pyo3::prelude::*;
use pyo3::types::{PyCFunction, PyFunction};
use pyo3::wrap_pyfunction;
mod common;
@ -62,3 +63,34 @@ assert a, array.array("i", [2, 4, 6, 8])
"#
);
}
#[pyfunction]
fn function_with_pyfunction_arg(fun: &PyFunction) -> PyResult<&PyAny> {
fun.call((), None)
}
#[pyfunction]
fn function_with_pycfunction_arg(fun: &PyCFunction) -> PyResult<&PyAny> {
fun.call((), None)
}
#[test]
fn test_functions_with_function_args() {
let gil = Python::acquire_gil();
let py = gil.python();
let py_func_arg = wrap_pyfunction!(function_with_pyfunction_arg)(py).unwrap();
let py_cfunc_arg = wrap_pyfunction!(function_with_pycfunction_arg)(py).unwrap();
let bool_to_string = wrap_pyfunction!(optional_bool)(py).unwrap();
pyo3::py_run!(
py,
py_func_arg
py_cfunc_arg
bool_to_string,
r#"
def foo(): return "bar"
assert py_func_arg(foo) == "bar"
assert py_cfunc_arg(bool_to_string) == "Some(true)"
"#
)
}