Merge pull request #1837 from davidhewitt/more-proc-macro-hygiene

pyo3-macros-backend: more tests for macro hygiene
This commit is contained in:
David Hewitt 2021-08-28 14:14:47 +01:00 committed by GitHub
commit a6d6367241
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 56 deletions

View file

@ -103,12 +103,12 @@ impl FnType {
}
FnType::FnClass => {
quote! {
let _slf = pyo3::types::PyType::from_type_ptr(_py, _slf as *mut pyo3::ffi::PyTypeObject);
let _slf = ::pyo3::types::PyType::from_type_ptr(_py, _slf as *mut ::pyo3::ffi::PyTypeObject);
}
}
FnType::FnModule => {
quote! {
let _slf = _py.from_borrowed_ptr::<pyo3::types::PyModule>(_slf);
let _slf = _py.from_borrowed_ptr::<::pyo3::types::PyModule>(_slf);
}
}
}
@ -455,17 +455,17 @@ impl<'a> FnSpec<'a> {
quote!(#func_name)
};
let rust_call =
quote! { pyo3::callback::convert(#py, #rust_name(#self_arg #(#arg_names),*)) };
quote! { ::pyo3::callback::convert(#py, #rust_name(#self_arg #(#arg_names),*)) };
Ok(match self.convention {
CallingConvention::Noargs => {
quote! {
unsafe extern "C" fn #ident (
_slf: *mut pyo3::ffi::PyObject,
_args: *mut pyo3::ffi::PyObject,
) -> *mut pyo3::ffi::PyObject
_slf: *mut ::pyo3::ffi::PyObject,
_args: *mut ::pyo3::ffi::PyObject,
) -> *mut ::pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
::pyo3::callback::handle_panic(|#py| {
#self_conversion
#rust_call
})
@ -476,23 +476,24 @@ impl<'a> FnSpec<'a> {
let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, true)?;
quote! {
unsafe extern "C" fn #ident (
_slf: *mut pyo3::ffi::PyObject,
_args: *const *mut pyo3::ffi::PyObject,
_nargs: pyo3::ffi::Py_ssize_t,
_kwnames: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
_slf: *mut ::pyo3::ffi::PyObject,
_args: *const *mut ::pyo3::ffi::PyObject,
_nargs: ::pyo3::ffi::Py_ssize_t,
_kwnames: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
::pyo3::callback::handle_panic(|#py| {
#self_conversion
let _kwnames: Option<&pyo3::types::PyTuple> = #py.from_borrowed_ptr_or_opt(_kwnames);
use ::std::option::Option;
let _kwnames: Option<&::pyo3::types::PyTuple> = #py.from_borrowed_ptr_or_opt(_kwnames);
// Safety: &PyAny has the same memory layout as `*mut ffi::PyObject`
let _args = _args as *const &pyo3::PyAny;
let _kwargs = if let Some(kwnames) = _kwnames {
std::slice::from_raw_parts(_args.offset(_nargs), kwnames.len())
let _args = _args as *const &::pyo3::PyAny;
let _kwargs = if let Option::Some(kwnames) = _kwnames {
::std::slice::from_raw_parts(_args.offset(_nargs), kwnames.len())
} else {
&[]
};
let _args = std::slice::from_raw_parts(_args, _nargs as usize);
let _args = ::std::slice::from_raw_parts(_args, _nargs as usize);
#arg_convert_and_rust_call
})
@ -503,15 +504,15 @@ impl<'a> FnSpec<'a> {
let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, false)?;
quote! {
unsafe extern "C" fn #ident (
_slf: *mut pyo3::ffi::PyObject,
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
_slf: *mut ::pyo3::ffi::PyObject,
_args: *mut ::pyo3::ffi::PyObject,
_kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject
{
#deprecations
pyo3::callback::handle_panic(|#py| {
::pyo3::callback::handle_panic(|#py| {
#self_conversion
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
let _args = #py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: ::std::option::Option<&::pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
#arg_convert_and_rust_call
})
@ -523,20 +524,20 @@ impl<'a> FnSpec<'a> {
let arg_convert_and_rust_call = impl_arg_params(self, cls, rust_call, &py, false)?;
quote! {
unsafe extern "C" fn #ident (
subtype: *mut pyo3::ffi::PyTypeObject,
_args: *mut pyo3::ffi::PyObject,
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
subtype: *mut ::pyo3::ffi::PyTypeObject,
_args: *mut ::pyo3::ffi::PyObject,
_kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject
{
#deprecations
use pyo3::callback::IntoPyCallbackOutput;
pyo3::callback::handle_panic(|#py| {
let _args = #py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
use ::pyo3::callback::IntoPyCallbackOutput;
::pyo3::callback::handle_panic(|#py| {
let _args = #py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: ::std::option::Option<&::pyo3::types::PyDict> = #py.from_borrowed_ptr_or_opt(_kwargs);
let result = #arg_convert_and_rust_call;
let initializer: pyo3::PyClassInitializer::<#cls> = result.convert(#py)?;
let initializer: ::pyo3::PyClassInitializer::<#cls> = result.convert(#py)?;
let cell = initializer.create_cell_from_subtype(#py, subtype)?;
Ok(cell as *mut pyo3::ffi::PyObject)
Ok(cell as *mut ::pyo3::ffi::PyObject)
})
}
}
@ -551,23 +552,23 @@ impl<'a> FnSpec<'a> {
let doc = &self.doc;
match self.convention {
CallingConvention::Noargs => quote! {
pyo3::class::methods::PyMethodDef::noargs(
::pyo3::class::methods::PyMethodDef::noargs(
#python_name,
pyo3::class::methods::PyCFunction(#wrapper),
::pyo3::class::methods::PyCFunction(#wrapper),
#doc,
)
},
CallingConvention::Fastcall => quote! {
pyo3::class::methods::PyMethodDef::fastcall_cfunction_with_keywords(
::pyo3::class::methods::PyMethodDef::fastcall_cfunction_with_keywords(
#python_name,
pyo3::class::methods::PyCFunctionFastWithKeywords(#wrapper),
::pyo3::class::methods::PyCFunctionFastWithKeywords(#wrapper),
#doc,
)
},
CallingConvention::Varargs => quote! {
pyo3::class::methods::PyMethodDef::cfunction_with_keywords(
::pyo3::class::methods::PyMethodDef::cfunction_with_keywords(
#python_name,
pyo3::class::methods::PyCFunctionWithKeywords(#wrapper),
::pyo3::class::methods::PyCFunctionWithKeywords(#wrapper),
#doc,
)
},

View file

@ -73,15 +73,15 @@ pub fn py_init(fnname: &Ident, options: PyModuleOptions, doc: syn::LitStr) -> To
#[allow(non_snake_case)]
/// This autogenerated function is called by the python interpreter when importing
/// the module.
pub unsafe extern "C" fn #cb_name() -> *mut pyo3::ffi::PyObject {
use pyo3::derive_utils::ModuleDef;
pub unsafe extern "C" fn #cb_name() -> *mut ::pyo3::ffi::PyObject {
use ::pyo3::derive_utils::ModuleDef;
static NAME: &str = concat!(stringify!(#name), "\0");
static DOC: &str = #doc;
static MODULE_DEF: ModuleDef = unsafe { ModuleDef::new(NAME, DOC) };
#deprecations
pyo3::callback::handle_panic(|_py| { MODULE_DEF.make_module(_py, #fnname) })
::pyo3::callback::handle_panic(|_py| { MODULE_DEF.make_module(_py, #fnname) })
}
}
}

View file

@ -123,9 +123,9 @@ pub fn impl_arg_params(
let (accept_args, accept_kwargs) = accept_args_kwargs(&spec.attrs);
let cls_name = if let Some(cls) = self_ {
quote! { Some(<#cls as pyo3::type_object::PyTypeInfo>::NAME) }
quote! { ::std::option::Option::Some(<#cls as pyo3::type_object::PyTypeInfo>::NAME) }
} else {
quote! { None }
quote! { ::std::option::Option::None }
};
let python_name = &spec.python_name;
@ -134,8 +134,9 @@ pub fn impl_arg_params(
// keyword names of the keyword args in _kwargs
(
// need copied() for &&PyAny -> &PyAny
quote! { _args.iter().copied() },
quote! { ::std::iter::Iterator::copied(_args.iter()) },
quote! { _kwnames.map(|kwnames| {
use ::std::iter::Iterator;
kwnames.as_slice().iter().copied().zip(_kwargs.iter().copied())
}) },
)
@ -149,7 +150,7 @@ pub fn impl_arg_params(
// create array of arguments, and then parse
Ok(quote! {{
const DESCRIPTION: pyo3::derive_utils::FunctionDescription = pyo3::derive_utils::FunctionDescription {
const DESCRIPTION: ::pyo3::derive_utils::FunctionDescription = ::pyo3::derive_utils::FunctionDescription {
cls_name: #cls_name,
func_name: stringify!(#python_name),
positional_parameter_names: &[#(#positional_parameter_names),*],
@ -161,7 +162,7 @@ pub fn impl_arg_params(
accept_varkeywords: #accept_kwargs,
};
let mut #args_array = [None; #num_params];
let mut #args_array = [::std::option::Option::None; #num_params];
let (_args, _kwargs) = DESCRIPTION.extract_arguments(
#py,
#args_to_extract,
@ -201,7 +202,7 @@ fn impl_arg_param(
let ty = arg.ty;
let name = arg.name;
let transform_error = quote! {
|e| pyo3::derive_utils::argument_extraction_error(#py, stringify!(#name), e)
|e| ::pyo3::derive_utils::argument_extraction_error(#py, stringify!(#name), e)
};
if is_args(&spec.attrs, name) {

View file

@ -433,9 +433,9 @@ pub fn impl_wrap_pyfunction(
let wrapped_pyfunction = quote! {
#wrapper
pub(crate) fn #function_wrapper_ident<'a>(
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
pyo3::types::PyCFunction::internal_new(#methoddef, args.into())
args: impl ::std::convert::Into<::pyo3::derive_utils::PyFunctionArguments<'a>>
) -> ::pyo3::PyResult<&'a ::pyo3::types::PyCFunction> {
::pyo3::types::PyCFunction::internal_new(#methoddef, args.into())
}
};
Ok((function_wrapper_ident, wrapped_pyfunction))

View file

@ -358,11 +358,13 @@ pub mod proc_macro {
#[macro_export]
macro_rules! wrap_pyfunction {
($function_name: ident) => {{
&|py| pyo3::paste::expr! { [<__pyo3_get_function_ $function_name>] }(py)
&|py| $crate::paste::expr! { [<__pyo3_get_function_ $function_name>] }(py)
}};
($function_name: ident, $arg: expr) => {
pyo3::wrap_pyfunction!($function_name)(pyo3::derive_utils::PyFunctionArguments::from($arg))
$crate::wrap_pyfunction!($function_name)(
<$crate::derive_utils::PyFunctionArguments as ::std::convert::From<_>>::from($arg),
)
};
}
@ -372,8 +374,8 @@ macro_rules! wrap_pyfunction {
#[macro_export]
macro_rules! wrap_pymodule {
($module_name:ident) => {{
pyo3::paste::expr! {
&|py| unsafe { pyo3::PyObject::from_owned_ptr(py, [<PyInit_ $module_name>]()) }
$crate::paste::expr! {
&|py| unsafe { $crate::PyObject::from_owned_ptr(py, [<PyInit_ $module_name>]()) }
}
}};
}
@ -479,14 +481,15 @@ macro_rules! py_run_impl {
$crate::py_run_impl!($py, *d, $code)
}};
($py:expr, *$dict:expr, $code:expr) => {{
if let Err(e) = $py.run($code, None, Some($dict)) {
use ::std::option::Option::*;
if let ::std::result::Result::Err(e) = $py.run($code, None, Some($dict)) {
e.print($py);
// So when this c api function the last line called printed the error to stderr,
// the output is only written into a buffer which is never flushed because we
// panic before flushing. This is where this hack comes into place
$py.run("import sys; sys.stderr.flush()", None, None)
.unwrap();
panic!("{}", $code)
::std::panic!("{}", $code)
}
}};
}

View file

@ -42,3 +42,22 @@ impl ::pyo3::class::gc::PyGCProtocol for Bar {
self.c = ::std::option::Option::None;
}
}
#[::pyo3::proc_macro::pyfunction]
fn do_something(x: i32) -> ::pyo3::PyResult<i32> {
::std::result::Result::Ok(x)
}
#[::pyo3::proc_macro::pymodule]
fn my_module(_py: ::pyo3::Python, m: &::pyo3::types::PyModule) -> ::pyo3::PyResult<()> {
m.add_function(::pyo3::wrap_pyfunction!(do_something, m)?)?;
::std::result::Result::Ok(())
}
#[test]
fn invoke_wrap_pyfunction() {
::pyo3::Python::with_gil(|py| {
let func = ::pyo3::wrap_pyfunction!(do_something)(py).unwrap();
::pyo3::py_run!(py, func, r#"func(5)"#);
});
}