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 `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 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) - 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) - Add section about Python::check_signals to the FAQ page of the user guide. [#1301](https://github.com/PyO3/pyo3/pull/1301)
### Changed ### 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) - 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) - 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) - 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 ### 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) - 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 /// The parsed arguments of the pyclass macro
pub struct PyClassArgs { pub struct PyClassArgs {
pub freelist: Option<syn::Expr>, pub freelist: Option<syn::Expr>,
pub name: Option<syn::ExprPath>, pub name: Option<syn::Ident>,
pub flags: Vec<syn::Expr>, pub flags: Vec<syn::Expr>,
pub base: syn::TypePath, pub base: syn::TypePath,
pub has_extends: bool, pub has_extends: bool,
@ -96,15 +96,26 @@ impl PyClassArgs {
lit: syn::Lit::Str(lit), lit: syn::Lit::Str(lit),
.. ..
}) => { }) => {
self.name = match lit.parse() { self.name = Some(lit.parse().map_err(|_| {
Ok(name) => Some(name), syn::Error::new_spanned(
Err(..) => expected!("type name (e.g., Name or \"Name\")"), lit,
} "expected a single identifier in double-quotes",
)
})?);
} }
syn::Expr::Path(exp) if exp.path.segments.len() == 1 => { 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 { "extends" => match &**right {
syn::Expr::Path(exp) => { 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 { fn get_class_python_name<'a>(cls: &'a syn::Ident, attr: &'a PyClassArgs) -> &'a syn::Ident {
match &attr.name { attr.name.as_ref().unwrap_or(cls)
Some(name) => quote! { #name },
None => quote! { #cls },
}
} }
fn impl_class( 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>, attrs: &mut Vec<syn::Attribute>,
python_name: &T, python_name: &syn::Ident,
) -> syn::Result<Option<syn::LitStr>> { ) -> syn::Result<Option<syn::LitStr>> {
let mut text_signature = None; let mut text_signature = None;
let mut attrs_out = Vec::with_capacity(attrs.len()); 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 {} struct EmptyClass2 {}
#[pymethods] #[pymethods]
@ -79,17 +79,12 @@ impl EmptyClass2 {
fn bar_static() {} fn bar_static() {}
} }
#[pyclass(name = "CustomName")]
struct EmptyClass3 {}
#[test] #[test]
fn custom_names() { fn custom_names() {
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let typeobj = py.get_type::<EmptyClass2>(); let typeobj = py.get_type::<EmptyClass2>();
let typeobj2 = py.get_type::<EmptyClass3>();
py_assert!(py, typeobj, "typeobj.__name__ == 'CustomName'"); 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, typeobj, "typeobj.custom_fn.__name__ == 'custom_fn'");
py_assert!( py_assert!(
py, py,

View File

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

View File

@ -10,20 +10,32 @@ error: Expected type path (e.g., my_mod::BaseClass)
6 | #[pyclass(extends = "PyDict")] 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 --> $DIR/invalid_pyclass_args.rs:9:18
| |
9 | #[pyclass(name = m::MyClass)] 9 | #[pyclass(name = m::MyClass)]
| ^^^^^^^^^^ | ^^^^^^^^^^
error: Expected string literal (e.g., "my_mod") error: expected a single identifier in double-quotes
--> $DIR/invalid_pyclass_args.rs:12:20 --> $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 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)]
| ^^^^^^^ | ^^^^^^^