Use `Ident::parse_any` for `name` attributes (#4226)
This makes it possible to use rust keywords as the name of python class methods and standalone functions. For example: ``` struct MyClass { } impl MyClass { #[new] fn new() -> Self { MyClass {} } #[pyo3(name = "struct")] fn struct_method(&self) -> usize { 42 } } fn struct_function() -> usize { 42 } ``` From the [`syn::Ident` documentation](https://docs.rs/syn/2.0.66/syn/struct.Ident.html): > An identifier constructed with `Ident::new` is permitted to be a Rust keyword, though parsing one through its [`Parse`](https://docs.rs/syn/2.0.66/syn/parse/trait.Parse.html) implementation rejects Rust keywords. Use `input.call(Ident::parse_any)` when parsing to match the behaviour of `Ident::new`. Fixes issue #4225
This commit is contained in:
parent
7e5884c40b
commit
93ef056711
|
@ -0,0 +1 @@
|
||||||
|
Fixes a compile error when declaring a standalone function or class method with a Python name that is a Rust keyword.
|
|
@ -1,6 +1,7 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{
|
use syn::{
|
||||||
|
ext::IdentExt,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
|
@ -72,7 +73,7 @@ pub struct NameLitStr(pub Ident);
|
||||||
impl Parse for NameLitStr {
|
impl Parse for NameLitStr {
|
||||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
let string_literal: LitStr = input.parse()?;
|
let string_literal: LitStr = input.parse()?;
|
||||||
if let Ok(ident) = string_literal.parse() {
|
if let Ok(ident) = string_literal.parse_with(Ident::parse_any) {
|
||||||
Ok(NameLitStr(ident))
|
Ok(NameLitStr(ident))
|
||||||
} else {
|
} else {
|
||||||
bail_spanned!(string_literal.span() => "expected a single identifier in double quotes")
|
bail_spanned!(string_literal.span() => "expected a single identifier in double quotes")
|
||||||
|
|
|
@ -123,6 +123,36 @@ fn custom_names() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyclass(name = "loop")]
|
||||||
|
struct ClassRustKeywords {
|
||||||
|
#[pyo3(name = "unsafe", get, set)]
|
||||||
|
unsafe_variable: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl ClassRustKeywords {
|
||||||
|
#[pyo3(name = "struct")]
|
||||||
|
fn struct_method(&self) {}
|
||||||
|
|
||||||
|
#[staticmethod]
|
||||||
|
#[pyo3(name = "type")]
|
||||||
|
fn type_method() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keyword_names() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let typeobj = py.get_type_bound::<ClassRustKeywords>();
|
||||||
|
py_assert!(py, typeobj, "typeobj.__name__ == 'loop'");
|
||||||
|
py_assert!(py, typeobj, "typeobj.struct.__name__ == 'struct'");
|
||||||
|
py_assert!(py, typeobj, "typeobj.type.__name__ == 'type'");
|
||||||
|
py_assert!(py, typeobj, "typeobj.unsafe.__name__ == 'unsafe'");
|
||||||
|
py_assert!(py, typeobj, "not hasattr(typeobj, 'unsafe_variable')");
|
||||||
|
py_assert!(py, typeobj, "not hasattr(typeobj, 'struct_method')");
|
||||||
|
py_assert!(py, typeobj, "not hasattr(typeobj, 'type_method')");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct RawIdents {
|
struct RawIdents {
|
||||||
#[pyo3(get, set)]
|
#[pyo3(get, set)]
|
||||||
|
|
|
@ -14,6 +14,18 @@ use pyo3::types::{self, PyCFunction};
|
||||||
#[path = "../src/tests/common.rs"]
|
#[path = "../src/tests/common.rs"]
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
#[pyfunction(name = "struct")]
|
||||||
|
fn struct_function() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rust_keyword_name() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let f = wrap_pyfunction_bound!(struct_function)(py).unwrap();
|
||||||
|
|
||||||
|
py_assert!(py, f, "f.__name__ == 'struct'");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[pyfunction(signature = (arg = true))]
|
#[pyfunction(signature = (arg = true))]
|
||||||
fn optional_bool(arg: Option<bool>) -> String {
|
fn optional_bool(arg: Option<bool>) -> String {
|
||||||
format!("{:?}", arg)
|
format!("{:?}", arg)
|
||||||
|
|
Loading…
Reference in New Issue