Add `#[name]` attribute support for `#[pyfunction]`
This commit is contained in:
parent
881cb0a5a4
commit
4b18830f1e
|
@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Support for `#[name = "foo"]` attribute in `#[pymethods]`. [#692](https://github.com/PyO3/pyo3/pull/692)
|
||||
* Support for `#[name = "foo"]` attribute for `#[pyfunction]` and in `#[pymethods]`. [#692](https://github.com/PyO3/pyo3/pull/692)
|
||||
|
||||
## [0.8.4]
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ mod utils;
|
|||
|
||||
pub use module::{add_fn_to_module, process_functions_in_module, py_init};
|
||||
pub use pyclass::{build_py_class, PyClassArgs};
|
||||
pub use pyfunction::PyFunctionAttr;
|
||||
pub use pyfunction::{build_py_function, PyFunctionAttr};
|
||||
pub use pyimpl::{build_py_methods, impl_methods};
|
||||
pub use pyproto::build_py_proto;
|
||||
pub use utils::get_doc;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::pyfunction::Argument;
|
||||
use crate::pyfunction::PyFunctionAttr;
|
||||
use crate::pyfunction::{PyFunctionAttr, parse_name_attribute};
|
||||
use crate::utils;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::ToTokens;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
@ -36,8 +37,9 @@ pub struct FnSpec<'a> {
|
|||
pub tp: FnType,
|
||||
// Rust function name
|
||||
pub name: &'a syn::Ident,
|
||||
// Wrapped python name
|
||||
pub python_name: Option<syn::Ident>,
|
||||
// Wrapped python name. This should have been sent through syn::IdentExt::unraw()
|
||||
// to ensure that any leading r# is removed.
|
||||
pub python_name: syn::Ident,
|
||||
pub attrs: Vec<Argument>,
|
||||
pub args: Vec<FnArg<'a>>,
|
||||
pub output: syn::Type,
|
||||
|
@ -59,24 +61,8 @@ impl<'a> FnSpec<'a> {
|
|||
allow_custom_name: bool,
|
||||
) -> syn::Result<FnSpec<'a>> {
|
||||
let name = &sig.ident;
|
||||
let (mut fn_type, fn_attrs, mut python_name) =
|
||||
parse_attributes(meth_attrs, allow_custom_name)?;
|
||||
|
||||
// "Tweak" getter / setter names: strip off set_ and get_ if needed
|
||||
if let FnType::Getter | FnType::Setter = &fn_type {
|
||||
if python_name.is_none() {
|
||||
let prefix = match &fn_type {
|
||||
FnType::Getter => "get_",
|
||||
FnType::Setter => "set_",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ident = sig.ident.to_string();
|
||||
if ident.starts_with(prefix) {
|
||||
python_name = Some(syn::Ident::new(&ident[prefix.len()..], ident.span()))
|
||||
}
|
||||
}
|
||||
}
|
||||
let MethodAttributes { ty: mut fn_type, args: fn_attrs, mut python_name } =
|
||||
parse_method_attributes(meth_attrs, allow_custom_name)?;
|
||||
|
||||
let mut has_self = false;
|
||||
let mut arguments = Vec::new();
|
||||
|
@ -136,12 +122,28 @@ impl<'a> FnSpec<'a> {
|
|||
fn_type = FnType::PySelf(tp);
|
||||
}
|
||||
|
||||
let mut parse_erroneous_text_signature = |error_msg: &str| {
|
||||
let py_name = python_name.as_ref().unwrap_or(name);
|
||||
// "Tweak" getter / setter names: strip off set_ and get_ if needed
|
||||
if let FnType::Getter | FnType::Setter = &fn_type {
|
||||
if python_name.is_none() {
|
||||
let prefix = match &fn_type {
|
||||
FnType::Getter => "get_",
|
||||
FnType::Setter => "set_",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ident = sig.ident.unraw().to_string();
|
||||
if ident.starts_with(prefix) {
|
||||
python_name = Some(syn::Ident::new(&ident[prefix.len()..], ident.span()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let python_name = python_name.unwrap_or_else(|| name.unraw());
|
||||
|
||||
let mut parse_erroneous_text_signature = |error_msg: &str| {
|
||||
// try to parse anyway to give better error messages
|
||||
if let Some(text_signature) =
|
||||
utils::parse_text_signature_attrs(&mut *meth_attrs, py_name)?
|
||||
utils::parse_text_signature_attrs(meth_attrs, &python_name)?
|
||||
{
|
||||
Err(syn::Error::new_spanned(text_signature, error_msg))
|
||||
} else {
|
||||
|
@ -181,10 +183,6 @@ impl<'a> FnSpec<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn py_name(&self) -> &syn::Ident {
|
||||
self.python_name.as_ref().unwrap_or(self.name)
|
||||
}
|
||||
|
||||
pub fn is_args(&self, name: &syn::Ident) -> bool {
|
||||
for s in self.attrs.iter() {
|
||||
if let Argument::VarArgs(ref path) = s {
|
||||
|
@ -344,14 +342,20 @@ pub fn check_arg_ty_and_optional<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_attributes(
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct MethodAttributes {
|
||||
ty: FnType,
|
||||
args: Vec<Argument>,
|
||||
python_name: Option<syn::Ident>
|
||||
}
|
||||
|
||||
fn parse_method_attributes(
|
||||
attrs: &mut Vec<syn::Attribute>,
|
||||
allow_custom_name: bool,
|
||||
) -> syn::Result<(FnType, Vec<Argument>, Option<syn::Ident>)> {
|
||||
) -> syn::Result<MethodAttributes> {
|
||||
let mut new_attrs = Vec::new();
|
||||
let mut spec = Vec::new();
|
||||
let mut args = Vec::new();
|
||||
let mut res: Option<FnType> = None;
|
||||
let mut name_with_span = None;
|
||||
let mut property_name = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
|
@ -454,77 +458,66 @@ fn parse_attributes(
|
|||
};
|
||||
} else if path.is_ident("args") {
|
||||
let attrs = PyFunctionAttr::from_meta(nested)?;
|
||||
spec.extend(attrs.arguments)
|
||||
args.extend(attrs.arguments)
|
||||
} else {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
}
|
||||
syn::Meta::NameValue(ref nv) if allow_custom_name && nv.path.is_ident("name") => {
|
||||
if name_with_span.is_some() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&nv.path,
|
||||
"name can not be specified multiple times",
|
||||
));
|
||||
}
|
||||
|
||||
match &nv.lit {
|
||||
syn::Lit::Str(s) => name_with_span = Some((s.parse()?, nv.path.span())),
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&nv.lit,
|
||||
"Expected string literal for method name",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Meta::NameValue(_) => new_attrs.push(attr.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
attrs.clear();
|
||||
attrs.extend(new_attrs);
|
||||
|
||||
if let Some((_, span)) = &name_with_span {
|
||||
match &res {
|
||||
Some(FnType::FnNew) => {
|
||||
return Err(syn::Error::new(
|
||||
*span,
|
||||
"name can not be specified with #[new]",
|
||||
))
|
||||
}
|
||||
Some(FnType::FnCall) => {
|
||||
return Err(syn::Error::new(
|
||||
*span,
|
||||
"name can not be specified with #[call]",
|
||||
))
|
||||
}
|
||||
Some(FnType::Getter) => {
|
||||
return Err(syn::Error::new(
|
||||
*span,
|
||||
"name can not be specified for getter",
|
||||
))
|
||||
}
|
||||
Some(FnType::Setter) => {
|
||||
return Err(syn::Error::new(
|
||||
*span,
|
||||
"name can not be specified for setter",
|
||||
))
|
||||
}
|
||||
let ty = res.unwrap_or(FnType::Fn);
|
||||
let python_name = if allow_custom_name {
|
||||
parse_method_name_attribute(&ty, attrs, property_name)?
|
||||
} else {
|
||||
property_name
|
||||
};
|
||||
|
||||
Ok(MethodAttributes { ty, args, python_name })
|
||||
}
|
||||
|
||||
fn parse_method_name_attribute(
|
||||
ty: &FnType,
|
||||
attrs: &mut Vec<syn::Attribute>,
|
||||
property_name: Option<syn::Ident>
|
||||
) -> syn::Result<Option<syn::Ident>> {
|
||||
|
||||
let name = parse_name_attribute(attrs)?;
|
||||
|
||||
// Reject some invalid combinations
|
||||
if let Some(name) = &name {
|
||||
match ty {
|
||||
FnType::FnNew => return Err(syn::Error::new_spanned(
|
||||
name,
|
||||
"name can not be specified with #[new]",
|
||||
)),
|
||||
FnType::FnCall => return Err(syn::Error::new_spanned(
|
||||
name,
|
||||
"name can not be specified with #[call]",
|
||||
)),
|
||||
FnType::Getter => return Err(syn::Error::new_spanned(
|
||||
name,
|
||||
"name can not be specified for getter",
|
||||
)),
|
||||
FnType::Setter => return Err(syn::Error::new_spanned(
|
||||
name,
|
||||
"name can not be specified for setter",
|
||||
)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to check above we can be sure that this generates the right python name
|
||||
let python_name = match res {
|
||||
Some(FnType::FnNew) => Some(syn::Ident::new("__new__", proc_macro2::Span::call_site())),
|
||||
Some(FnType::FnCall) => Some(syn::Ident::new("__call__", proc_macro2::Span::call_site())),
|
||||
Some(FnType::Getter) | Some(FnType::Setter) => property_name,
|
||||
_ => name_with_span.map(|ns| ns.0),
|
||||
};
|
||||
|
||||
match res {
|
||||
Some(tp) => Ok((tp, spec, python_name)),
|
||||
None => Ok((FnType::Fn, spec, python_name)),
|
||||
}
|
||||
Ok(match ty {
|
||||
FnType::FnNew => Some(syn::Ident::new("__new__", proc_macro2::Span::call_site())),
|
||||
FnType::FnCall => Some(syn::Ident::new("__call__", proc_macro2::Span::call_site())),
|
||||
FnType::Getter | FnType::Setter => property_name,
|
||||
_ => name,
|
||||
})
|
||||
}
|
||||
|
||||
// Replace A<Self> with A<_>
|
||||
|
|
|
@ -161,7 +161,7 @@ pub fn add_fn_to_module(
|
|||
let spec = method::FnSpec {
|
||||
tp: method::FnType::Fn,
|
||||
name: &function_wrapper_ident,
|
||||
python_name: Some(python_name),
|
||||
python_name,
|
||||
attrs: pyfn_attrs,
|
||||
args: arguments,
|
||||
output: ty,
|
||||
|
@ -170,7 +170,7 @@ pub fn add_fn_to_module(
|
|||
|
||||
let doc = &spec.doc;
|
||||
|
||||
let python_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
|
||||
let wrapper = function_c_wrapper(&func.sig.ident, &spec);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, impl_wrap_getter,
|
|||
use crate::utils;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{parse_quote, Expr, Token};
|
||||
|
@ -455,7 +456,7 @@ fn impl_descriptors(
|
|||
let spec = FnSpec {
|
||||
tp: FnType::Getter,
|
||||
name: &name,
|
||||
python_name: None,
|
||||
python_name: name.unraw(),
|
||||
attrs: Vec::new(),
|
||||
args: Vec::new(),
|
||||
output: parse_quote!(PyResult<#field_ty>),
|
||||
|
@ -469,7 +470,7 @@ fn impl_descriptors(
|
|||
let spec = FnSpec {
|
||||
tp: FnType::Setter,
|
||||
name: &setter_name,
|
||||
python_name: Some(name.clone()),
|
||||
python_name: name.unraw(),
|
||||
attrs: Vec::new(),
|
||||
args: vec![FnArg {
|
||||
name: &name,
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::ParseBuffer;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{NestedMeta, Path};
|
||||
use proc_macro2::TokenStream;
|
||||
use crate::module::add_fn_to_module;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Argument {
|
||||
|
@ -193,6 +197,47 @@ impl PyFunctionAttr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_name_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<syn::Ident>> {
|
||||
let mut name_attrs = Vec::new();
|
||||
|
||||
// Using retain will extract all name attributes from the attribute list
|
||||
attrs.retain(|attr| {
|
||||
match attr.parse_meta() {
|
||||
Ok(syn::Meta::NameValue(ref nv)) if nv.path.is_ident("name") => {
|
||||
name_attrs.push((nv.lit.clone(), attr.span()));
|
||||
false
|
||||
}
|
||||
_ => true
|
||||
}
|
||||
});
|
||||
|
||||
let mut name = None;
|
||||
|
||||
for (lit, span) in name_attrs {
|
||||
if name.is_some() {
|
||||
return Err(syn::Error::new(span, "#[name] can not be specified multiple times"))
|
||||
}
|
||||
|
||||
name = match lit {
|
||||
syn::Lit::Str(s) => {
|
||||
let mut ident: syn::Ident = s.parse()?;
|
||||
// This span is the whole attribute span, which is nicer for reporting errors.
|
||||
ident.set_span(span);
|
||||
Some(ident)
|
||||
},
|
||||
_ => return Err(syn::Error::new(span, "Expected string literal for #[name] argument"))
|
||||
};
|
||||
}
|
||||
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
pub fn build_py_function(ast: &mut syn::ItemFn, args: PyFunctionAttr) -> syn::Result<TokenStream> {
|
||||
let python_name =
|
||||
parse_name_attribute(&mut ast.attrs)?.unwrap_or_else(|| ast.sig.ident.unraw());
|
||||
Ok(add_fn_to_module(ast, python_name, args.arguments))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{Argument, PyFunctionAttr};
|
||||
|
|
|
@ -72,7 +72,7 @@ fn impl_wrap_common(
|
|||
slf: TokenStream,
|
||||
body: TokenStream,
|
||||
) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
if spec.args.is_empty() && noargs {
|
||||
quote! {
|
||||
unsafe extern "C" fn __wrap(
|
||||
|
@ -80,7 +80,7 @@ fn impl_wrap_common(
|
|||
) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".", stringify!(#py_name), "()");
|
||||
stringify!(#cls), ".", stringify!(#python_name), "()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
#slf
|
||||
|
@ -102,7 +102,7 @@ fn impl_wrap_common(
|
|||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".", stringify!(#py_name), "()");
|
||||
stringify!(#cls), ".", stringify!(#python_name), "()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
#slf
|
||||
|
@ -120,7 +120,7 @@ fn impl_wrap_common(
|
|||
|
||||
/// Generate function wrapper for protocol method (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let cb = impl_call(cls, &spec);
|
||||
let body = impl_arg_params(&spec, cb);
|
||||
|
||||
|
@ -131,7 +131,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
_args: *mut pyo3::ffi::PyObject,
|
||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
|
||||
|
@ -149,7 +149,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||
let name = &spec.name;
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let names: Vec<syn::Ident> = get_arg_names(&spec);
|
||||
let cb = quote! { #cls::#name(&_obj, #(#names),*) };
|
||||
|
||||
|
@ -164,7 +164,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
{
|
||||
use pyo3::type_object::PyTypeInfo;
|
||||
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
match pyo3::type_object::PyRawObject::new(_py, #cls::type_object(), _cls) {
|
||||
|
@ -194,7 +194,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||
let name = &spec.name;
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let names: Vec<syn::Ident> = get_arg_names(&spec);
|
||||
let cb = quote! { #cls::#name(&_cls, #(#names),*) };
|
||||
|
||||
|
@ -207,7 +207,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
_args: *mut pyo3::ffi::PyObject,
|
||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
let _cls = pyo3::types::PyType::from_type_ptr(_py, _cls as *mut pyo3::ffi::PyTypeObject);
|
||||
|
@ -225,7 +225,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
/// Generate static method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||
let name = &spec.name;
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let names: Vec<syn::Ident> = get_arg_names(&spec);
|
||||
let cb = quote! { #cls::#name(#(#names),*) };
|
||||
|
||||
|
@ -238,7 +238,7 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
|||
_args: *mut pyo3::ffi::PyObject,
|
||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
||||
|
@ -266,7 +266,7 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result<To
|
|||
};
|
||||
|
||||
let name = &spec.name;
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
|
||||
let fncall = if takes_py {
|
||||
quote! { _slf.#name(_py) }
|
||||
|
@ -278,7 +278,7 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result<To
|
|||
unsafe extern "C" fn __wrap(
|
||||
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
|
@ -302,7 +302,7 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result<To
|
|||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStream> {
|
||||
let name = &spec.name;
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
|
||||
let val_ty = match &*spec.args {
|
||||
[] => {
|
||||
|
@ -326,7 +326,7 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul
|
|||
_slf: *mut pyo3::ffi::PyObject,
|
||||
_value: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> pyo3::libc::c_int
|
||||
{
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#py_name),"()");
|
||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||
let _py = pyo3::Python::assume_gil_acquired();
|
||||
let _pool = pyo3::GILPool::new(_py);
|
||||
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
|
||||
|
@ -517,7 +517,7 @@ fn impl_arg_param(
|
|||
}
|
||||
|
||||
pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
if spec.args.is_empty() {
|
||||
quote! {
|
||||
|
@ -525,7 +525,7 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
|||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyNoArgsFunction(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_NOARGS,
|
||||
ml_doc: #doc,
|
||||
|
@ -538,7 +538,7 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
|||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
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,
|
||||
|
@ -549,14 +549,14 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
pub fn impl_py_method_def_new(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::New({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyNewFunc(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: #doc,
|
||||
|
@ -566,14 +566,14 @@ pub fn impl_py_method_def_new(spec: &FnSpec, wrapper: &TokenStream) -> TokenStre
|
|||
}
|
||||
|
||||
pub fn impl_py_method_def_class(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Class({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS |
|
||||
pyo3::ffi::METH_CLASS,
|
||||
|
@ -584,14 +584,14 @@ pub fn impl_py_method_def_class(spec: &FnSpec, wrapper: &TokenStream) -> TokenSt
|
|||
}
|
||||
|
||||
pub fn impl_py_method_def_static(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Static({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | pyo3::ffi::METH_STATIC,
|
||||
ml_doc: #doc,
|
||||
|
@ -601,14 +601,14 @@ pub fn impl_py_method_def_static(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
|||
}
|
||||
|
||||
pub fn impl_py_method_def_call(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = spec.py_name();
|
||||
let python_name = &spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Call({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#py_name),
|
||||
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,
|
||||
|
@ -618,7 +618,7 @@ pub fn impl_py_method_def_call(spec: &FnSpec, wrapper: &TokenStream) -> TokenStr
|
|||
}
|
||||
|
||||
pub(crate) fn impl_py_setter_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = &spec.py_name();
|
||||
let python_name = &&spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
|
||||
quote! {
|
||||
|
@ -626,7 +626,7 @@ pub(crate) fn impl_py_setter_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
|||
#wrapper
|
||||
|
||||
pyo3::class::PySetterDef {
|
||||
name: stringify!(#py_name),
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
doc: #doc,
|
||||
}
|
||||
|
@ -635,7 +635,7 @@ pub(crate) fn impl_py_setter_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
|||
}
|
||||
|
||||
pub(crate) fn impl_py_getter_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
||||
let py_name = &spec.py_name();
|
||||
let python_name = &&spec.python_name;
|
||||
let doc = &spec.doc;
|
||||
|
||||
quote! {
|
||||
|
@ -643,7 +643,7 @@ pub(crate) fn impl_py_getter_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
|||
#wrapper
|
||||
|
||||
pyo3::class::PyGetterDef {
|
||||
name: stringify!(#py_name),
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
doc: #doc,
|
||||
}
|
||||
|
|
|
@ -4,13 +4,11 @@
|
|||
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use pyo3_derive_backend::{
|
||||
add_fn_to_module, build_py_class, build_py_methods, build_py_proto, get_doc,
|
||||
build_py_class, build_py_function, build_py_methods, build_py_proto, get_doc,
|
||||
process_functions_in_module, py_init, PyClassArgs, PyFunctionAttr,
|
||||
};
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// Internally, this proc macro create a new c function called `PyInit_{my_module}`
|
||||
|
@ -83,9 +81,7 @@ pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let mut ast = parse_macro_input!(input as syn::ItemFn);
|
||||
let args = parse_macro_input!(attr as PyFunctionAttr);
|
||||
|
||||
// TODO: Support #[name = "..."] attribute?
|
||||
let python_name = syn::Ident::new(&ast.sig.ident.unraw().to_string(), Span::call_site());
|
||||
let expanded = add_fn_to_module(&mut ast, python_name, args.arguments);
|
||||
let expanded = build_py_function(&mut ast, args).unwrap_or_else(|e| e.to_compile_error());
|
||||
|
||||
quote!(
|
||||
#ast
|
||||
|
|
|
@ -68,6 +68,23 @@ fn custom_names() {
|
|||
py_assert!(py, typeobj, "not hasattr(typeobj, 'bar_static')");
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct RawIdents {}
|
||||
|
||||
#[pymethods]
|
||||
impl RawIdents {
|
||||
fn r#fn(&self) { }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_idents() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let typeobj = py.get_type::<RawIdents>();
|
||||
py_assert!(py, typeobj, "not hasattr(typeobj, 'r#fn')");
|
||||
py_assert!(py, typeobj, "hasattr(typeobj, 'fn')");
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct EmptyClassInModule {}
|
||||
|
||||
|
|
|
@ -148,6 +148,32 @@ fn test_raw_idents() {
|
|||
py_assert!(py, module, "module.move() == 42");
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
#[name = "foobar"]
|
||||
fn custom_named_fn() -> usize {
|
||||
42
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn foobar_module(_py: Python, module: &PyModule) -> PyResult<()> {
|
||||
use pyo3::wrap_pyfunction;
|
||||
|
||||
module.add_wrapped(wrap_pyfunction!(custom_named_fn))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_names() {
|
||||
use pyo3::wrap_pymodule;
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let module = wrap_pymodule!(foobar_module)(py);
|
||||
|
||||
py_assert!(py, module, "not hasattr(module, 'custom_named_fn')");
|
||||
py_assert!(py, module, "module.foobar() == 42");
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn subfunction() -> String {
|
||||
"Subfunction".to_string()
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
error: name can not be specified for getter
|
||||
--> $DIR/invalid_pymethod_names.rs:10:7
|
||||
--> $DIR/invalid_pymethod_names.rs:10:5
|
||||
|
|
||||
10 | #[name = "num"]
|
||||
| ^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: name can not be specified multiple times
|
||||
--> $DIR/invalid_pymethod_names.rs:18:7
|
||||
error: #[name] can not be specified multiple times
|
||||
--> $DIR/invalid_pymethod_names.rs:18:5
|
||||
|
|
||||
18 | #[name = "bar"]
|
||||
| ^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: name can not be specified with #[new]
|
||||
--> $DIR/invalid_pymethod_names.rs:24:7
|
||||
--> $DIR/invalid_pymethod_names.rs:24:5
|
||||
|
|
||||
24 | #[name = "makenew"]
|
||||
| ^^^^
|
||||
|
||||
error: cannot find attribute `name` in this scope
|
||||
--> $DIR/invalid_pymethod_names.rs:17:7
|
||||
|
|
||||
17 | #[name = "foo"]
|
||||
| ^^^^
|
||||
|
||||
error: cannot find attribute `name` in this scope
|
||||
--> $DIR/invalid_pymethod_names.rs:18:7
|
||||
|
|
||||
18 | #[name = "bar"]
|
||||
| ^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
Loading…
Reference in New Issue