Merge pull request #1163 from sebpuetz/pyfunction

Add native Function types.
This commit is contained in:
David Hewitt 2020-09-10 19:05:57 +01:00 committed by GitHub
commit 73507dbf5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 275 additions and 104 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

@ -67,7 +67,7 @@ fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
/// A Python module implemented in Rust.
#[pymodule]
fn string_sum(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string))?;
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}

View file

@ -215,29 +215,29 @@ impl TzClass {
#[pymodule]
fn datetime(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(make_date))?;
m.add_function(wrap_pyfunction!(get_date_tuple))?;
m.add_function(wrap_pyfunction!(date_from_timestamp))?;
m.add_function(wrap_pyfunction!(make_time))?;
m.add_function(wrap_pyfunction!(get_time_tuple))?;
m.add_function(wrap_pyfunction!(make_delta))?;
m.add_function(wrap_pyfunction!(get_delta_tuple))?;
m.add_function(wrap_pyfunction!(make_datetime))?;
m.add_function(wrap_pyfunction!(get_datetime_tuple))?;
m.add_function(wrap_pyfunction!(datetime_from_timestamp))?;
m.add_function(wrap_pyfunction!(make_date, m)?)?;
m.add_function(wrap_pyfunction!(get_date_tuple, m)?)?;
m.add_function(wrap_pyfunction!(date_from_timestamp, m)?)?;
m.add_function(wrap_pyfunction!(make_time, m)?)?;
m.add_function(wrap_pyfunction!(get_time_tuple, m)?)?;
m.add_function(wrap_pyfunction!(make_delta, m)?)?;
m.add_function(wrap_pyfunction!(get_delta_tuple, m)?)?;
m.add_function(wrap_pyfunction!(make_datetime, m)?)?;
m.add_function(wrap_pyfunction!(get_datetime_tuple, m)?)?;
m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?;
// Python 3.6+ functions
#[cfg(Py_3_6)]
{
m.add_function(wrap_pyfunction!(time_with_fold))?;
m.add_function(wrap_pyfunction!(time_with_fold, m)?)?;
#[cfg(not(PyPy))]
{
m.add_function(wrap_pyfunction!(get_time_tuple_fold))?;
m.add_function(wrap_pyfunction!(get_datetime_tuple_fold))?;
m.add_function(wrap_pyfunction!(get_time_tuple_fold, m)?)?;
m.add_function(wrap_pyfunction!(get_datetime_tuple_fold, m)?)?;
}
}
m.add_function(wrap_pyfunction!(issue_219))?;
m.add_function(wrap_pyfunction!(issue_219, m)?)?;
m.add_class::<TzClass>()?;
Ok(())

View file

@ -31,7 +31,7 @@ fn double(x: i32) -> i32 {
#[pymodule]
fn othermod(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double))?;
m.add_function(wrap_pyfunction!(double, m)?)?;
m.add_class::<ModClass>()?;

View file

@ -56,8 +56,8 @@ fn count_line(line: &str, needle: &str) -> usize {
#[pymodule]
fn word_count(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(search))?;
m.add_function(wrap_pyfunction!(search_sequential))?;
m.add_function(wrap_pyfunction!(search_sequential_allow_threads))?;
m.add_function(wrap_pyfunction!(search_sequential, m)?)?;
m.add_function(wrap_pyfunction!(search_sequential_allow_threads, m)?)?;
Ok(())
}

View file

@ -36,7 +36,7 @@ fn double(x: usize) -> usize {
#[pymodule]
fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double)).unwrap();
m.add_function(wrap_pyfunction!(double, m)?).unwrap();
Ok(())
}
@ -65,7 +65,7 @@ fn num_kwds(kwds: Option<&PyDict>) -> usize {
#[pymodule]
fn module_with_functions(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(num_kwds)).unwrap();
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
Ok(())
}
@ -206,7 +206,7 @@ fn pyfunction_with_module(module: &PyModule) -> PyResult<&str> {
#[pymodule]
fn module_with_fn(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(pyfunction_with_module))
m.add_function(wrap_pyfunction!(pyfunction_with_module, m)?)
}
# fn main() {}

View file

@ -73,7 +73,7 @@ fn subfunction() -> String {
}
fn init_submodule(module: &PyModule) -> PyResult<()> {
module.add_function(wrap_pyfunction!(subfunction))?;
module.add_function(wrap_pyfunction!(subfunction, module)?)?;
Ok(())
}

View file

@ -488,7 +488,7 @@ pub struct UserModel {
#[pymodule]
fn trait_exposure(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<UserModel>()?;
m.add_function(wrap_pyfunction!(solve_wrapper))?;
m.add_function(wrap_pyfunction!(solve_wrapper, m)?)?;
Ok(())
}

View file

@ -44,7 +44,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) -> syn::Result<()> {
let item: syn::ItemFn = syn::parse_quote! {
fn block_wrapper() {
#function_to_python
#module_name.add_function(&#function_wrapper_ident)?;
#module_name.add_function(#function_wrapper_ident(#module_name)?)?;
}
};
stmts.extend(item.block.stmts.into_iter());
@ -204,50 +204,26 @@ pub fn add_fn_to_module(
let python_name = &spec.python_name;
let wrapper = function_c_wrapper(&func.sig.ident, &spec, pyfn_attrs.pass_module);
let name = &func.sig.ident;
let wrapper_ident = format_ident!("__pyo3_raw_{}", name);
let wrapper = function_c_wrapper(name, &wrapper_ident, &spec, pyfn_attrs.pass_module);
Ok(quote! {
#wrapper
fn #function_wrapper_ident<'a>(
args: impl Into<pyo3::derive_utils::WrapPyFunctionArguments<'a>>
) -> pyo3::PyResult<pyo3::PyObject> {
let arg = args.into();
let (py, maybe_module) = arg.into_py_and_maybe_module();
#wrapper
let _def = pyo3::class::PyMethodDef {
ml_name: stringify!(#python_name),
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
ml_doc: #doc,
};
let (mod_ptr, name) = if let Some(m) = maybe_module {
let mod_ptr = <pyo3::types::PyModule as ::pyo3::conversion::AsPyPointer>::as_ptr(m);
let name = m.name()?;
let name = <&str as pyo3::conversion::IntoPy<PyObject>>::into_py(name, py);
(mod_ptr, <PyObject as pyo3::AsPyPointer>::as_ptr(&name))
} else {
(std::ptr::null_mut(), std::ptr::null_mut())
};
let function = unsafe {
pyo3::PyObject::from_owned_ptr(
py,
pyo3::ffi::PyCFunction_NewEx(
Box::into_raw(Box::new(_def.as_method_def())),
mod_ptr,
name
)
)
};
Ok(function)
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
pyo3::types::PyCFunction::new_with_keywords(#wrapper_ident, stringify!(#python_name), #doc, args.into())
}
})
}
/// Generate static function wrapper (PyCFunction, PyCFunctionWithKeywords)
fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>, pass_module: bool) -> TokenStream {
fn function_c_wrapper(
name: &Ident,
wrapper_ident: &Ident,
spec: &method::FnSpec<'_>,
pass_module: bool,
) -> TokenStream {
let names: Vec<Ident> = get_arg_names(&spec);
let cb;
let slf_module;
@ -265,9 +241,8 @@ fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>, pass_module: bool
slf_module = None;
};
let body = pymethod::impl_arg_params(spec, None, cb);
quote! {
unsafe extern "C" fn __wrap(
unsafe extern "C" fn #wrapper_ident(
_slf: *mut pyo3::ffi::PyObject,
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject

View file

@ -209,17 +209,16 @@ where
}
/// Enum to abstract over the arguments of Python function wrappers.
#[doc(hidden)]
pub enum WrapPyFunctionArguments<'a> {
pub enum PyFunctionArguments<'a> {
Python(Python<'a>),
PyModule(&'a PyModule),
}
impl<'a> WrapPyFunctionArguments<'a> {
impl<'a> PyFunctionArguments<'a> {
pub fn into_py_and_maybe_module(self) -> (Python<'a>, Option<&'a PyModule>) {
match self {
WrapPyFunctionArguments::Python(py) => (py, None),
WrapPyFunctionArguments::PyModule(module) => {
PyFunctionArguments::Python(py) => (py, None),
PyFunctionArguments::PyModule(module) => {
let py = module.py();
(py, Some(module))
}
@ -227,14 +226,14 @@ impl<'a> WrapPyFunctionArguments<'a> {
}
}
impl<'a> From<Python<'a>> for WrapPyFunctionArguments<'a> {
fn from(py: Python<'a>) -> WrapPyFunctionArguments<'a> {
WrapPyFunctionArguments::Python(py)
impl<'a> From<Python<'a>> for PyFunctionArguments<'a> {
fn from(py: Python<'a>) -> PyFunctionArguments<'a> {
PyFunctionArguments::Python(py)
}
}
impl<'a> From<&'a PyModule> for WrapPyFunctionArguments<'a> {
fn from(module: &'a PyModule) -> WrapPyFunctionArguments<'a> {
WrapPyFunctionArguments::PyModule(module)
impl<'a> From<&'a PyModule> for PyFunctionArguments<'a> {
fn from(module: &'a PyModule) -> PyFunctionArguments<'a> {
PyFunctionArguments::PyModule(module)
}
}

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;

View file

@ -71,7 +71,7 @@
//! #[pymodule]
//! /// A Python module implemented in Rust.
//! fn string_sum(py: Python, m: &PyModule) -> PyResult<()> {
//! m.add_function(wrap_pyfunction!(sum_as_string))?;
//! m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
//!
//! Ok(())
//! }
@ -218,6 +218,20 @@ macro_rules! wrap_pyfunction {
($function_name: ident) => {{
&pyo3::paste::expr! { [<__pyo3_get_function_ $function_name>] }
}};
($function_name: ident, $arg: expr) => {
pyo3::wrap_pyfunction!($function_name)(pyo3::derive_utils::PyFunctionArguments::from($arg))
};
}
/// Returns the function that is called in the C-FFI.
///
/// Use this together with `#[pyfunction]` and [types::PyCFunction].
#[macro_export]
macro_rules! raw_pycfunction {
($function_name: ident) => {{
pyo3::paste::expr! { [<__pyo3_raw_ $function_name>] }
}};
}
/// Returns a function that takes a [Python] instance and returns a Python module.

View file

@ -134,7 +134,7 @@ impl<'p> Python<'p> {
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let m = PyModule::new(py, "pcount").unwrap();
/// m.add_function(wrap_pyfunction!(parallel_count)).unwrap();
/// m.add_function(wrap_pyfunction!(parallel_count, m).unwrap()).unwrap();
/// let locals = [("pcount", m)].into_py_dict(py);
/// py.run(r#"
/// s = ["Flow", "my", "tears", "the", "Policeman", "Said"]

89
src/types/function.rs Normal file
View file

@ -0,0 +1,89 @@
use std::ffi::{CStr, CString};
use crate::derive_utils::PyFunctionArguments;
use crate::exceptions::PyValueError;
use crate::prelude::*;
use crate::{class, ffi, AsPyPointer, PyMethodType};
/// Represents a builtin Python function object.
#[repr(transparent)]
pub struct PyCFunction(PyAny);
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
impl PyCFunction {
/// Create a new built-in function with keywords.
pub fn new_with_keywords<'a>(
fun: ffi::PyCFunctionWithKeywords,
name: &str,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let fun = PyMethodType::PyCFunctionWithKeywords(fun);
Self::new_(fun, name, doc, py_or_module)
}
/// Create a new built-in function without keywords.
pub fn new<'a>(
fun: ffi::PyCFunction,
name: &str,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let fun = PyMethodType::PyCFunction(fun);
Self::new_(fun, name, doc, py_or_module)
}
fn new_<'a>(
fun: class::PyMethodType,
name: &str,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let (py, module) = py_or_module.into_py_and_maybe_module();
let doc: &'static CStr = CStr::from_bytes_with_nul(doc.as_bytes())
.map_err(|_| PyValueError::py_err("docstring must end with NULL byte."))?;
let name = CString::new(name.as_bytes())
.map_err(|_| PyValueError::py_err("Function name cannot contain contain NULL byte."))?;
let def = match fun {
PyMethodType::PyCFunction(fun) => ffi::PyMethodDef {
ml_name: name.into_raw() as _,
ml_meth: Some(fun),
ml_flags: ffi::METH_VARARGS,
ml_doc: doc.as_ptr() as _,
},
PyMethodType::PyCFunctionWithKeywords(fun) => ffi::PyMethodDef {
ml_name: name.into_raw() as _,
ml_meth: Some(unsafe { std::mem::transmute(fun) }),
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
ml_doc: doc.as_ptr() as _,
},
_ => {
return Err(PyValueError::py_err(
"Only PyCFunction and PyCFunctionWithKeywords are valid.",
))
}
};
let (mod_ptr, module_name) = if let Some(m) = module {
let mod_ptr = m.as_ptr();
let name = m.name()?.into_py(py);
(mod_ptr, name.as_ptr())
} else {
(std::ptr::null_mut(), std::ptr::null_mut())
};
unsafe {
py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
Box::into_raw(Box::new(def)),
mod_ptr,
module_name,
))
}
}
}
/// 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;
@ -226,6 +227,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;
@ -258,7 +258,7 @@ impl PyModule {
/// }
/// #[pymodule]
/// fn double_mod(_py: Python, module: &PyModule) -> PyResult<()> {
/// module.add_function(pyo3::wrap_pyfunction!(double))
/// module.add_function(pyo3::wrap_pyfunction!(double, module)?)
/// }
/// ```
///
@ -272,17 +272,11 @@ impl PyModule {
/// }
/// #[pymodule]
/// fn double_mod(_py: Python, module: &PyModule) -> PyResult<()> {
/// module.add("also_double", pyo3::wrap_pyfunction!(double)(module)?)
/// module.add("also_double", pyo3::wrap_pyfunction!(double, module)?)
/// }
/// ```
pub fn add_function<'a>(
&'a self,
wrapper: &impl Fn(&'a Self) -> PyResult<PyObject>,
) -> PyResult<()> {
let py = self.py();
let function = wrapper(self)?;
let name = function.getattr(py, "__name__")?;
let name = name.extract(py)?;
self.add(name, function)
pub fn add_function<'a>(&'a self, fun: &'a PyCFunction) -> PyResult<()> {
let name = fun.getattr("__name__")?.extract()?;
self.add(name, fun)
}
}

View file

@ -65,8 +65,8 @@ fn module_with_functions(_py: Python, m: &PyModule) -> PyResult<()> {
m.add("foo", "bar").unwrap();
m.add_function(wrap_pyfunction!(double)).unwrap();
m.add("also_double", wrap_pyfunction!(double)(m)?).unwrap();
m.add_function(wrap_pyfunction!(double, m)?).unwrap();
m.add("also_double", wrap_pyfunction!(double, m)?).unwrap();
Ok(())
}
@ -163,7 +163,7 @@ fn r#move() -> usize {
fn raw_ident_module(_py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(r#move))
module.add_function(wrap_pyfunction!(r#move, module)?)
}
#[test]
@ -188,7 +188,7 @@ fn custom_named_fn() -> usize {
fn foobar_module(_py: Python, m: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
m.add_function(wrap_pyfunction!(custom_named_fn))?;
m.add_function(wrap_pyfunction!(custom_named_fn, m)?)?;
m.dict().set_item("yay", "me")?;
Ok(())
}
@ -221,7 +221,7 @@ fn subfunction() -> String {
fn submodule(module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(subfunction))?;
module.add_function(wrap_pyfunction!(subfunction, module)?)?;
Ok(())
}
@ -229,7 +229,7 @@ fn submodule(module: &PyModule) -> PyResult<()> {
fn submodule_with_init_fn(_py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(subfunction))?;
module.add_function(wrap_pyfunction!(subfunction, module)?)?;
Ok(())
}
@ -242,7 +242,7 @@ fn superfunction() -> String {
fn supermodule(py: Python, module: &PyModule) -> PyResult<()> {
use pyo3::wrap_pyfunction;
module.add_function(wrap_pyfunction!(superfunction))?;
module.add_function(wrap_pyfunction!(superfunction, module)?)?;
let module_to_add = PyModule::new(py, "submodule")?;
submodule(module_to_add)?;
module.add_submodule(module_to_add)?;
@ -291,7 +291,7 @@ fn vararg_module(_py: Python, m: &PyModule) -> PyResult<()> {
ext_vararg_fn(py, a, vararg)
}
m.add_function(pyo3::wrap_pyfunction!(ext_vararg_fn))
m.add_function(pyo3::wrap_pyfunction!(ext_vararg_fn, m)?)
.unwrap();
Ok(())
}
@ -368,15 +368,17 @@ fn pyfunction_with_module_and_args_kwargs<'a>(
#[pymodule]
fn module_with_functions_with_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module))?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_py))?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_arg))?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_py, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(pyfunction_with_module_and_arg, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(
pyfunction_with_module_and_default_arg
))?;
pyfunction_with_module_and_default_arg,
m
)?)?;
m.add_function(pyo3::wrap_pyfunction!(
pyfunction_with_module_and_args_kwargs
))
pyfunction_with_module_and_args_kwargs,
m
)?)
}
#[test]

View file

@ -1,6 +1,7 @@
use pyo3::buffer::PyBuffer;
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use pyo3::types::{PyCFunction, PyFunction};
use pyo3::{raw_pycfunction, wrap_pyfunction};
mod common;
@ -62,3 +63,59 @@ 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)"
"#
)
}
#[test]
fn test_raw_function() {
let gil = Python::acquire_gil();
let py = gil.python();
let raw_func = raw_pycfunction!(optional_bool);
let fun = PyCFunction::new_with_keywords(raw_func, "fun", "\0", py.into()).unwrap();
let res = fun.call((), None).unwrap().extract::<&str>().unwrap();
assert_eq!(res, "Some(true)");
let res = fun.call((false,), None).unwrap().extract::<&str>().unwrap();
assert_eq!(res, "Some(false)");
let no_module = fun.getattr("__module__").unwrap().is_none();
assert!(no_module);
let module = PyModule::new(py, "cool_module").unwrap();
module.add_function(fun).unwrap();
let res = module
.getattr("fun")
.unwrap()
.call((), None)
.unwrap()
.extract::<&str>()
.unwrap();
assert_eq!(res, "Some(true)");
}