deprecate pyclass name without quotes

This commit is contained in:
David Hewitt 2020-12-12 09:50:25 +00:00
parent 7dadf59fde
commit 5133f81e6d
6 changed files with 48 additions and 27 deletions

View File

@ -21,7 +21,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add `PyAny::is_instance()` method. [#1276](https://github.com/PyO3/pyo3/pull/1276)
- Add support for conversion between `char` and `PyString`. [#1282](https://github.com/PyO3/pyo3/pull/1282)
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
- Allow the use of a string literal in `#[pyclass(name = "string literal")]`. [#1295](https://github.com/PyO3/pyo3/pull/1295)
- Add section about Python::check_signals to the FAQ page of the user guide. [#1301](https://github.com/PyO3/pyo3/pull/1301)
### Changed
@ -34,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Rename `PyTypeInfo::is_instance` and `PyTypeInfo::is_exact_instance` to `PyTypeInfo::is_type_of` and `PyTypeInfo::is_exact_type_of`. [#1278](https://github.com/PyO3/pyo3/pull/1278)
- Optimize `PyAny::call0`, `Py::call0` and `PyAny::call_method0` and `Py::call_method0` on Python 3.9 and up. [#1287](https://github.com/PyO3/pyo3/pull/1285)
- Deprecate `Python::is_instance`, `Python::is_subclass`, `Python::release`, and `Python::xdecref`. [#1292](https://github.com/PyO3/pyo3/pull/1292)
- Require double-quotes for pyclass name argument e.g `#[pyclass(name = "MyClass")]`. [#1303](https://github.com/PyO3/pyo3/pull/1303)
### Removed
- Remove deprecated ffi definitions `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer`, which will be removed in Python 3.10. [#1217](https://github.com/PyO3/pyo3/pull/1217)

View File

@ -15,7 +15,7 @@ use syn::{parse_quote, Expr, Token};
/// The parsed arguments of the pyclass macro
pub struct PyClassArgs {
pub freelist: Option<syn::Expr>,
pub name: Option<syn::ExprPath>,
pub name: Option<syn::Ident>,
pub flags: Vec<syn::Expr>,
pub base: syn::TypePath,
pub has_extends: bool,
@ -96,15 +96,26 @@ impl PyClassArgs {
lit: syn::Lit::Str(lit),
..
}) => {
self.name = match lit.parse() {
Ok(name) => Some(name),
Err(..) => expected!("type name (e.g., Name or \"Name\")"),
}
self.name = Some(lit.parse().map_err(|_| {
syn::Error::new_spanned(
lit,
"expected a single identifier in double-quotes",
)
})?);
}
syn::Expr::Path(exp) if exp.path.segments.len() == 1 => {
self.name = Some(exp.clone());
return Err(syn::Error::new_spanned(
exp,
format!(
concat!(
"since PyO3 0.13 a pyclass name should be in double-quotes, ",
"e.g. \"{}\""
),
exp.path.get_ident().expect("path has 1 segment")
),
));
}
_ => expected!("type name (e.g., Name or \"Name\")"),
_ => expected!("type name (e.g. \"Name\")"),
},
"extends" => match &**right {
syn::Expr::Path(exp) => {
@ -271,11 +282,8 @@ fn impl_proto_inventory(cls: &syn::Ident) -> TokenStream {
}
}
fn get_class_python_name(cls: &syn::Ident, attr: &PyClassArgs) -> TokenStream {
match &attr.name {
Some(name) => quote! { #name },
None => quote! { #cls },
}
fn get_class_python_name<'a>(cls: &'a syn::Ident, attr: &'a PyClassArgs) -> &'a syn::Ident {
attr.name.as_ref().unwrap_or(cls)
}
fn impl_class(

View File

@ -76,9 +76,9 @@ fn parse_text_signature_attr<T: Display + quote::ToTokens + ?Sized>(
}
}
pub fn parse_text_signature_attrs<T: Display + quote::ToTokens + ?Sized>(
pub fn parse_text_signature_attrs(
attrs: &mut Vec<syn::Attribute>,
python_name: &T,
python_name: &syn::Ident,
) -> syn::Result<Option<syn::LitStr>> {
let mut text_signature = None;
let mut attrs_out = Vec::with_capacity(attrs.len());

View File

@ -66,7 +66,7 @@ fn class_with_docstr() {
}
}
#[pyclass(name=CustomName)]
#[pyclass(name = "CustomName")]
struct EmptyClass2 {}
#[pymethods]
@ -79,17 +79,12 @@ impl EmptyClass2 {
fn bar_static() {}
}
#[pyclass(name = "CustomName")]
struct EmptyClass3 {}
#[test]
fn custom_names() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<EmptyClass2>();
let typeobj2 = py.get_type::<EmptyClass3>();
py_assert!(py, typeobj, "typeobj.__name__ == 'CustomName'");
py_assert!(py, typeobj2, "typeobj2.__name__ == 'CustomName'");
py_assert!(py, typeobj, "typeobj.custom_fn.__name__ == 'custom_fn'");
py_assert!(
py,

View File

@ -9,6 +9,12 @@ struct InvalidExtends {}
#[pyclass(name = m::MyClass)]
struct InvalidName {}
#[pyclass(name = "Custom Name")]
struct InvalidName2 {}
#[pyclass(name = CustomName)]
struct DeprecatedName {}
#[pyclass(module = my_module)]
struct InvalidModule {}

View File

@ -10,20 +10,32 @@ error: Expected type path (e.g., my_mod::BaseClass)
6 | #[pyclass(extends = "PyDict")]
| ^^^^^^^^
error: Expected type name (e.g., Name or "Name")
error: Expected type name (e.g. "Name")
--> $DIR/invalid_pyclass_args.rs:9:18
|
9 | #[pyclass(name = m::MyClass)]
| ^^^^^^^^^^
error: Expected string literal (e.g., "my_mod")
--> $DIR/invalid_pyclass_args.rs:12:20
error: expected a single identifier in double-quotes
--> $DIR/invalid_pyclass_args.rs:12:18
|
12 | #[pyclass(module = my_module)]
12 | #[pyclass(name = "Custom Name")]
| ^^^^^^^^^^^^^
error: since PyO3 0.13 a pyclass name should be in double-quotes, e.g. "CustomName"
--> $DIR/invalid_pyclass_args.rs:15:18
|
15 | #[pyclass(name = CustomName)]
| ^^^^^^^^^^
error: Expected string literal (e.g., "my_mod")
--> $DIR/invalid_pyclass_args.rs:18:20
|
18 | #[pyclass(module = my_module)]
| ^^^^^^^^^
error: Expected one of gc/weakref/subclass/dict/unsendable
--> $DIR/invalid_pyclass_args.rs:15:11
--> $DIR/invalid_pyclass_args.rs:21:11
|
15 | #[pyclass(weakrev)]
21 | #[pyclass(weakrev)]
| ^^^^^^^