Change add_function, ensure static docstrings.

Change add_function to take `&PyCFunction` instead of a wrapper
fn and ensure that dostrings of functions are `&'static str`.
This commit is contained in:
Sebastian Pütz 2020-09-09 11:53:24 +02:00
parent be877d133f
commit 22881a3c2f
15 changed files with 92 additions and 99 deletions

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());
@ -210,11 +210,9 @@ pub fn add_fn_to_module(
Ok(quote! {
#wrapper
fn #function_wrapper_ident<'a>(
args: impl Into<pyo3::derive_utils::WrapPyFunctionArguments<'a>>
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
let arg = args.into();
let (py, maybe_module) = arg.into_py_and_maybe_module();
pyo3::types::PyCFunction::new_with_keywords(#wrapper_ident, stringify!(#python_name), #doc, maybe_module, py)
pyo3::types::PyCFunction::new_with_keywords(#wrapper_ident, stringify!(#python_name), #doc, args.into())
}
})
}

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)
}
}

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,10 @@ 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.

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"]

View File

@ -1,6 +1,9 @@
use std::ffi::{CStr, CString};
use crate::derive_utils::PyFunctionArguments;
use crate::exceptions::PyValueError;
use crate::prelude::*;
use crate::{class, ffi, AsPyPointer, PyMethodDef, PyMethodType};
use crate::{class, ffi, AsPyPointer, PyMethodType};
/// Represents a builtin Python function object.
#[repr(transparent)]
@ -13,54 +16,47 @@ impl PyCFunction {
pub fn new_with_keywords<'a>(
fun: ffi::PyCFunctionWithKeywords,
name: &str,
doc: &str,
module: Option<&'a PyModule>,
py: Python<'a>,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let fun = PyMethodType::PyCFunctionWithKeywords(fun);
Self::new_(fun, name, doc, module, py)
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: &str,
module: Option<&'a PyModule>,
py: Python<'a>,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let fun = PyMethodType::PyCFunction(fun);
Self::new_(fun, name, doc, module, py)
Self::new_(fun, name, doc, py_or_module)
}
fn new_<'a>(
fun: class::PyMethodType,
name: &str,
doc: &str,
module: Option<&'a PyModule>,
py: Python<'a>,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a PyCFunction> {
let name = name.to_string();
let name: &'static str = Box::leak(name.into_boxed_str());
// this is ugly but necessary since `PyMethodDef::ml_doc` is &str and not `CStr`
let doc = if doc.ends_with('\0') {
doc.to_string()
} else {
format!("{}\0", doc)
};
let doc: &'static str = Box::leak(doc.into_boxed_str());
let def = match &fun {
PyMethodType::PyCFunction(_) => PyMethodDef {
ml_name: name,
ml_meth: fun,
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,
ml_doc: doc.as_ptr() as _,
},
PyMethodType::PyCFunctionWithKeywords(_) => PyMethodDef {
ml_name: name,
ml_meth: fun,
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,
ml_doc: doc.as_ptr() as _,
},
_ => {
return Err(PyValueError::py_err(
@ -68,7 +64,6 @@ impl PyCFunction {
))
}
};
let def = def.as_method_def();
let (mod_ptr, module_name) = if let Some(m) = module {
let mod_ptr = m.as_ptr();
let name = m.name()?.into_py(py);

View File

@ -256,7 +256,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)?)
/// }
/// ```
///
@ -270,16 +270,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<&'a PyCFunction>,
) -> PyResult<()> {
let function = wrapper(self)?;
let name = function.getattr("__name__")?;
let name = name.extract()?;
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

@ -100,7 +100,7 @@ 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", "", None, py).unwrap();
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();
@ -109,7 +109,7 @@ fn test_raw_function() {
assert!(no_module);
let module = PyModule::new(py, "cool_module").unwrap();
module.add_function(&|_| Ok(fun)).unwrap();
module.add_function(fun).unwrap();
let res = module
.getattr("fun")
.unwrap()