Skip to content

Commit 2f8f123

Browse files
committed
Add BoundPyFunction helper struct
This standardises the return value of `_wrap_pyfunction` and `_wrap_pyfunction_bound`.
1 parent 500b114 commit 2f8f123

File tree

5 files changed

+91
-28
lines changed

5 files changed

+91
-28
lines changed

pyo3-macros-backend/src/module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn
272272
let name = &func.sig.ident;
273273
let statements: Vec<syn::Stmt> = syn::parse_quote! {
274274
#wrapped_function
275-
#module_name.add_function(#krate::impl_::pyfunction::_wrap_pyfunction(&#name::DEF, #module_name)?)?;
275+
#module_name.add_function(#krate::impl_::pyfunction::_wrap_pyfunction(&#name::DEF, #module_name)?.into())?;
276276
};
277277
stmts.extend(statements);
278278
}

src/impl_/pyfunction.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,44 @@
11
use crate::{
22
derive_utils::{PyFunctionArguments, PyFunctionArgumentsBound},
33
types::PyCFunction,
4-
Bound, PyResult,
4+
Bound, Py, PyResult,
55
};
66

77
pub use crate::impl_::pymethods::PyMethodDef;
88

99
pub fn _wrap_pyfunction<'a>(
1010
method_def: &PyMethodDef,
1111
py_or_module: impl Into<PyFunctionArguments<'a>>,
12-
) -> PyResult<&'a PyCFunction> {
13-
PyCFunction::internal_new(method_def, py_or_module.into()).map(|x| x.into_gil_ref())
12+
) -> PyResult<BoundPyFunction<'a>> {
13+
PyCFunction::internal_new(method_def, py_or_module.into()).map(BoundPyFunction)
1414
}
1515

1616
pub fn _wrap_pyfunction_bound<'a, 'py: 'a>(
1717
method_def: &PyMethodDef,
1818
py_or_module: impl Into<PyFunctionArgumentsBound<'a, 'py>>,
19-
) -> PyResult<Bound<'py, PyCFunction>> {
20-
PyCFunction::internal_new_bound(method_def, py_or_module.into())
19+
) -> PyResult<BoundPyFunction<'py>> {
20+
PyCFunction::internal_new_bound(method_def, py_or_module.into()).map(BoundPyFunction)
21+
}
22+
23+
pub struct BoundPyFunction<'py>(pub Bound<'py, PyCFunction>);
24+
25+
impl<'a> From<BoundPyFunction<'a>> for &'a PyCFunction {
26+
#[inline]
27+
fn from(bound: BoundPyFunction<'a>) -> Self {
28+
bound.0.into_gil_ref()
29+
}
30+
}
31+
32+
impl<'py> From<BoundPyFunction<'py>> for Bound<'py, PyCFunction> {
33+
#[inline]
34+
fn from(bound: BoundPyFunction<'py>) -> Self {
35+
bound.0
36+
}
37+
}
38+
39+
impl<'py> From<BoundPyFunction<'py>> for Py<PyCFunction> {
40+
#[inline]
41+
fn from(bound: BoundPyFunction<'py>) -> Self {
42+
bound.0.unbind()
43+
}
2144
}

src/macros.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,25 @@ macro_rules! py_run_impl {
126126
macro_rules! wrap_pyfunction {
127127
($function:path) => {
128128
&|py_or_module| {
129+
use ::std::convert::Into;
130+
use $crate::types::PyCFunction;
131+
use $crate::PyResult;
129132
use $function as wrapped_pyfunction;
130-
$crate::impl_::pyfunction::_wrap_pyfunction(&wrapped_pyfunction::DEF, py_or_module)
133+
let wrapped: PyResult<&PyCFunction> =
134+
$crate::impl_::pyfunction::_wrap_pyfunction(&wrapped_pyfunction::DEF, py_or_module)
135+
.map(|f| f.into());
136+
wrapped
131137
}
132138
};
133139
($function:path, $py_or_module:expr) => {{
140+
use ::std::convert::Into;
141+
use $crate::types::PyCFunction;
142+
use $crate::PyResult;
134143
use $function as wrapped_pyfunction;
135-
$crate::impl_::pyfunction::_wrap_pyfunction(&wrapped_pyfunction::DEF, $py_or_module)
144+
let wrapped: PyResult<&PyCFunction> =
145+
$crate::impl_::pyfunction::_wrap_pyfunction(&wrapped_pyfunction::DEF, $py_or_module)
146+
.map(|f| f.into());
147+
wrapped
136148
}};
137149
}
138150

@@ -144,16 +156,31 @@ macro_rules! wrap_pyfunction {
144156
macro_rules! wrap_pyfunction_bound {
145157
($function:path) => {
146158
&|py_or_module| {
159+
use ::std::convert::Into;
160+
use $crate::types::PyCFunction;
161+
use $crate::PyResult;
147162
use $function as wrapped_pyfunction;
148-
$crate::impl_::pyfunction::_wrap_pyfunction_bound(
149-
&wrapped_pyfunction::DEF,
150-
py_or_module,
151-
)
163+
let wrapped: PyResult<Bound<'_, PyCFunction>> =
164+
$crate::impl_::pyfunction::_wrap_pyfunction_bound(
165+
&wrapped_pyfunction::DEF,
166+
py_or_module,
167+
)
168+
.map(|f| f.into());
169+
wrapped
152170
}
153171
};
154172
($function:path, $py_or_module:expr) => {{
173+
use ::std::convert::Into;
174+
use $crate::types::PyCFunction;
175+
use $crate::PyResult;
155176
use $function as wrapped_pyfunction;
156-
$crate::impl_::pyfunction::_wrap_pyfunction_bound(&wrapped_pyfunction::DEF, $py_or_module)
177+
let wrapped: PyResult<Bound<'_, PyCFunction>> =
178+
$crate::impl_::pyfunction::_wrap_pyfunction_bound(
179+
&wrapped_pyfunction::DEF,
180+
$py_or_module,
181+
)
182+
.map(|f| f.into());
183+
wrapped
157184
}};
158185
}
159186

src/types/function.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,25 @@ impl PyCFunction {
3434
doc: &'static str,
3535
py_or_module: PyFunctionArguments<'a>,
3636
) -> PyResult<&'a Self> {
37-
Self::new_with_keywords_bound(fun, name, doc, py_or_module).map(Bound::into_gil_ref)
37+
Self::internal_new(
38+
&PyMethodDef::cfunction_with_keywords(
39+
name,
40+
pymethods::PyCFunctionWithKeywords(fun),
41+
doc,
42+
),
43+
py_or_module,
44+
)
45+
.map(Bound::into_gil_ref)
3846
}
3947

4048
/// Create a new built-in function with keywords (*args and/or **kwargs).
41-
pub fn new_with_keywords_bound<'a>(
49+
pub fn new_with_keywords_bound<'py>(
4250
fun: ffi::PyCFunctionWithKeywords,
4351
name: &'static str,
4452
doc: &'static str,
45-
py_or_module: PyFunctionArguments<'a>,
46-
) -> PyResult<Bound<'a, Self>> {
47-
Self::internal_new(
53+
py_or_module: PyFunctionArgumentsBound<'_, 'py>,
54+
) -> PyResult<Bound<'py, Self>> {
55+
Self::internal_new_bound(
4856
&PyMethodDef::cfunction_with_keywords(
4957
name,
5058
pymethods::PyCFunctionWithKeywords(fun),
@@ -68,17 +76,21 @@ impl PyCFunction {
6876
doc: &'static str,
6977
py_or_module: PyFunctionArguments<'a>,
7078
) -> PyResult<&'a Self> {
71-
Self::new_bound(fun, name, doc, py_or_module).map(Bound::into_gil_ref)
79+
Self::internal_new(
80+
&PyMethodDef::noargs(name, pymethods::PyCFunction(fun), doc),
81+
py_or_module,
82+
)
83+
.map(Bound::into_gil_ref)
7284
}
7385

7486
/// Create a new built-in function which takes no arguments.
75-
pub fn new_bound<'a>(
87+
pub fn new_bound<'py>(
7688
fun: ffi::PyCFunction,
7789
name: &'static str,
7890
doc: &'static str,
79-
py_or_module: PyFunctionArguments<'a>,
80-
) -> PyResult<Bound<'a, Self>> {
81-
Self::internal_new(
91+
py_or_module: PyFunctionArgumentsBound<'_, 'py>,
92+
) -> PyResult<Bound<'py, Self>> {
93+
Self::internal_new_bound(
8294
&PyMethodDef::noargs(name, pymethods::PyCFunction(fun), doc),
8395
py_or_module,
8496
)

tests/test_module.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -295,18 +295,19 @@ fn test_module_nesting() {
295295
// Test that argument parsing specification works for pyfunctions
296296

297297
#[pyfunction(signature = (a=5, *args))]
298-
fn ext_vararg_fn(py: Python<'_>, a: i32, args: &PyTuple) -> PyObject {
299-
[a.to_object(py), args.into()].to_object(py)
298+
fn ext_vararg_fn(py: Python<'_>, a: i32, args: &Bound<'_, PyTuple>) -> PyObject {
299+
[a.to_object(py), args.into_py(py)].to_object(py)
300300
}
301301

302302
#[pymodule]
303-
fn vararg_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
303+
fn vararg_module(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
304304
#[pyfn(m, signature = (a=5, *args))]
305-
fn int_vararg_fn(py: Python<'_>, a: i32, args: &PyTuple) -> PyObject {
305+
fn int_vararg_fn(py: Python<'_>, a: i32, args: &Bound<'_, PyTuple>) -> PyObject {
306306
ext_vararg_fn(py, a, args)
307307
}
308308

309-
m.add_function(wrap_pyfunction!(ext_vararg_fn, m)?).unwrap();
309+
m.add_function(&wrap_pyfunction_bound!(ext_vararg_fn, m)?)
310+
.unwrap();
310311
Ok(())
311312
}
312313

0 commit comments

Comments
 (0)