diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dcc38fc..704d7fc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - `prepare_freethreaded_python` will no longer register an `atexit` handler to call `Py_Finalize`. [#1355](https://github.com/PyO3/pyo3/pull/1355) +### Fixed +- Fix support for using `r#raw_idents` as argument names in pyfunctions. [#1383](https://github.com/PyO3/pyo3/pull/1383) + ## [0.13.1] - 2021-01-10 ### Added - Add support for `#[pyclass(dict)]` and `#[pyclass(weakref)]` with the `abi3` feature on Python 3.9 and up. [#1342](https://github.com/PyO3/pyo3/pull/1342) diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index dd1fa011..2575385b 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -215,7 +215,7 @@ impl<'a> FnSpec<'a> { } let ty = get_return_info(&sig.output); - let python_name = python_name.unwrap_or_else(|| name.unraw()); + let python_name = python_name.as_ref().unwrap_or(name).unraw(); let mut parse_erroneous_text_signature = |error_msg: &str| { // try to parse anyway to give better error messages diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index a505de01..d6234cda 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -392,13 +392,13 @@ pub fn impl_arg_params( if arg.py || spec.is_args(&arg.name) || spec.is_kwargs(&arg.name) { continue; } - let name = arg.name; + let name = arg.name.unraw().to_string(); let kwonly = spec.is_kw_only(&arg.name); let opt = arg.optional.is_some() || spec.default_value(&arg.name).is_some(); params.push(quote! { pyo3::derive_utils::ParamDescription { - name: stringify!(#name), + name: #name, is_optional: #opt, kw_only: #kwonly } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 9c62b3f0..e2d49f1d 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -609,3 +609,106 @@ fn test_from_sequence() { let typeobj = py.get_type::(); py_assert!(py, typeobj, "typeobj(range(0, 4)).numbers == [0, 1, 2, 3]") } + +#[pyclass] +struct r#RawIdents { + #[pyo3(get, set)] + r#type: PyObject, + r#subtype: PyObject, + r#subsubtype: PyObject, +} + +#[pymethods] +impl r#RawIdents { + #[new] + pub fn r#new( + r#_py: Python, + r#type: PyObject, + r#subtype: PyObject, + r#subsubtype: PyObject, + ) -> PyResult { + Ok(Self { + r#type, + r#subtype, + r#subsubtype, + }) + } + + #[getter(r#subtype)] + pub fn r#get_subtype(&self) -> PyObject { + self.r#subtype.clone() + } + + #[setter(r#subtype)] + pub fn r#set_subtype(&mut self, r#subtype: PyObject) { + self.r#subtype = r#subtype; + } + + #[getter] + pub fn r#get_subsubtype(&self) -> PyObject { + self.r#subsubtype.clone() + } + + #[setter] + pub fn r#set_subsubtype(&mut self, r#subsubtype: PyObject) { + self.r#subsubtype = r#subsubtype; + } + + #[call] + pub fn r#call(&mut self, r#type: PyObject) { + self.r#type = r#type; + } + + #[staticmethod] + pub fn r#static_method(r#type: PyObject) -> PyObject { + r#type + } + + #[classmethod] + pub fn r#class_method(_: &PyType, r#type: PyObject) -> PyObject { + r#type + } + + #[classattr] + pub fn r#class_attr_fn() -> i32 { + 5 + } + + #[classattr] + const r#CLASS_ATTR_CONST: i32 = 6; +} + +#[test] +fn test_raw_idents() { + Python::with_gil(|py| { + let raw_idents_type = py.get_type::(); + py_run!( + py, + raw_idents_type, + r#" + instance = raw_idents_type(type=None, subtype=5, subsubtype="foo") + + assert instance.type is None + assert instance.subtype == 5 + assert instance.subsubtype == "foo" + + instance.type = 1 + instance.subtype = 2 + instance.subsubtype = 3 + + assert instance.type == 1 + assert instance.subtype == 2 + assert instance.subsubtype == 3 + + assert raw_idents_type.static_method(type=30) == 30 + assert instance.class_method(type=40) == 40 + + instance(type=50) + assert instance.type == 50 + + assert raw_idents_type.class_attr_fn == 5 + assert raw_idents_type.CLASS_ATTR_CONST == 6 + "# + ); + }) +}