Merge pull request #3384 from DataTriny/pyclass_rename_variants
Add `rename_all` attribute to `#[pyclass]`
This commit is contained in:
commit
9363491d54
|
@ -11,6 +11,7 @@
|
|||
| `mapping` | Inform PyO3 that this class is a [`Mapping`][params-mapping], and so leave its implementation of sequence C-API slots empty. |
|
||||
| <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
|
||||
| <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
|
||||
| `rename_all = "renaming_rule"` | Applies renaming rules to every getters and setters of a struct, or every variants of an enum. Possible values are: "camelCase", "kebab-case", "lowercase", "PascalCase", "SCREAMING-KEBAB-CASE", "SCREAMING_SNAKE_CASE", "snake_case", "UPPERCASE". |
|
||||
| `sequence` | Inform PyO3 that this class is a [`Sequence`][params-sequence], and so leave its C-API mapping length slot empty. |
|
||||
| `set_all` | Generates setters for all fields of the pyclass. |
|
||||
| `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. |
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
`#[pyclass]` now accepts `rename_all = "renaming_rule"`: this allows renaming all getters and setters of a struct, or all variants of an enum. Available renaming rules are: `"camelCase"`, `"kebab-case"`, `"lowercase"`, `"PascalCase"`, `"SCREAMING-KEBAB-CASE"`, `"SCREAMING_SNAKE_CASE"`, `"snake_case"`, `"UPPERCASE"`.
|
|
@ -16,6 +16,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
quote = { version = "1", default-features = false }
|
||||
proc-macro2 = { version = "1", default-features = false }
|
||||
heck = "0.4"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2"
|
||||
|
|
|
@ -26,6 +26,7 @@ pub mod kw {
|
|||
syn::custom_keyword!(module);
|
||||
syn::custom_keyword!(name);
|
||||
syn::custom_keyword!(pass_module);
|
||||
syn::custom_keyword!(rename_all);
|
||||
syn::custom_keyword!(sequence);
|
||||
syn::custom_keyword!(set);
|
||||
syn::custom_keyword!(set_all);
|
||||
|
@ -82,6 +83,55 @@ impl ToTokens for NameLitStr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Available renaming rules
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RenamingRule {
|
||||
CamelCase,
|
||||
KebabCase,
|
||||
Lowercase,
|
||||
PascalCase,
|
||||
ScreamingKebabCase,
|
||||
ScreamingSnakeCase,
|
||||
SnakeCase,
|
||||
Uppercase,
|
||||
}
|
||||
|
||||
/// A helper type which parses a renaming rule via a literal string
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RenamingRuleLitStr {
|
||||
pub lit: LitStr,
|
||||
pub rule: RenamingRule,
|
||||
}
|
||||
|
||||
impl Parse for RenamingRuleLitStr {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
let string_literal: LitStr = input.parse()?;
|
||||
let rule = match string_literal.value().as_ref() {
|
||||
"camelCase" => RenamingRule::CamelCase,
|
||||
"kebab-case" => RenamingRule::KebabCase,
|
||||
"lowercase" => RenamingRule::Lowercase,
|
||||
"PascalCase" => RenamingRule::PascalCase,
|
||||
"SCREAMING-KEBAB-CASE" => RenamingRule::ScreamingKebabCase,
|
||||
"SCREAMING_SNAKE_CASE" => RenamingRule::ScreamingSnakeCase,
|
||||
"snake_case" => RenamingRule::SnakeCase,
|
||||
"UPPERCASE" => RenamingRule::Uppercase,
|
||||
_ => {
|
||||
bail_spanned!(string_literal.span() => "expected a valid renaming rule, possible values are: \"camelCase\", \"kebab-case\", \"lowercase\", \"PascalCase\", \"SCREAMING-KEBAB-CASE\", \"SCREAMING_SNAKE_CASE\", \"snake_case\", \"UPPERCASE\"")
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
lit: string_literal,
|
||||
rule,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for RenamingRuleLitStr {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.lit.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
/// Text signatue can be either a literal string or opt-in/out
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum TextSignatureAttributeValue {
|
||||
|
@ -121,6 +171,7 @@ pub type ExtendsAttribute = KeywordAttribute<kw::extends, Path>;
|
|||
pub type FreelistAttribute = KeywordAttribute<kw::freelist, Box<Expr>>;
|
||||
pub type ModuleAttribute = KeywordAttribute<kw::module, LitStr>;
|
||||
pub type NameAttribute = KeywordAttribute<kw::name, NameLitStr>;
|
||||
pub type RenameAllAttribute = KeywordAttribute<kw::rename_all, RenamingRuleLitStr>;
|
||||
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, TextSignatureAttributeValue>;
|
||||
|
||||
impl<K: Parse + std::fmt::Debug, V: Parse> Parse for KeywordAttribute<K, V> {
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
|||
use crate::attributes::kw::frozen;
|
||||
use crate::attributes::{
|
||||
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
||||
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
||||
ModuleAttribute, NameAttribute, NameLitStr, RenameAllAttribute, TextSignatureAttribute,
|
||||
TextSignatureAttributeValue,
|
||||
};
|
||||
use crate::deprecations::{Deprecation, Deprecations};
|
||||
|
@ -14,9 +14,9 @@ use crate::pymethod::{
|
|||
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
|
||||
SlotDef, __INT__, __REPR__, __RICHCMP__,
|
||||
};
|
||||
use crate::utils::{self, get_pyo3_crate, PythonDoc};
|
||||
use crate::utils::{self, apply_renaming_rule, get_pyo3_crate, PythonDoc};
|
||||
use crate::PyFunctionOptions;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
|
@ -66,6 +66,7 @@ pub struct PyClassPyO3Options {
|
|||
pub mapping: Option<kw::mapping>,
|
||||
pub module: Option<ModuleAttribute>,
|
||||
pub name: Option<NameAttribute>,
|
||||
pub rename_all: Option<RenameAllAttribute>,
|
||||
pub sequence: Option<kw::sequence>,
|
||||
pub set_all: Option<kw::set_all>,
|
||||
pub subclass: Option<kw::subclass>,
|
||||
|
@ -86,6 +87,7 @@ enum PyClassPyO3Option {
|
|||
Mapping(kw::mapping),
|
||||
Module(ModuleAttribute),
|
||||
Name(NameAttribute),
|
||||
RenameAll(RenameAllAttribute),
|
||||
Sequence(kw::sequence),
|
||||
SetAll(kw::set_all),
|
||||
Subclass(kw::subclass),
|
||||
|
@ -115,6 +117,8 @@ impl Parse for PyClassPyO3Option {
|
|||
input.parse().map(PyClassPyO3Option::Module)
|
||||
} else if lookahead.peek(kw::name) {
|
||||
input.parse().map(PyClassPyO3Option::Name)
|
||||
} else if lookahead.peek(kw::rename_all) {
|
||||
input.parse().map(PyClassPyO3Option::RenameAll)
|
||||
} else if lookahead.peek(attributes::kw::sequence) {
|
||||
input.parse().map(PyClassPyO3Option::Sequence)
|
||||
} else if lookahead.peek(attributes::kw::set_all) {
|
||||
|
@ -173,6 +177,7 @@ impl PyClassPyO3Options {
|
|||
PyClassPyO3Option::Mapping(mapping) => set_option!(mapping),
|
||||
PyClassPyO3Option::Module(module) => set_option!(module),
|
||||
PyClassPyO3Option::Name(name) => set_option!(name),
|
||||
PyClassPyO3Option::RenameAll(rename_all) => set_option!(rename_all),
|
||||
PyClassPyO3Option::Sequence(sequence) => set_option!(sequence),
|
||||
PyClassPyO3Option::SetAll(set_all) => set_option!(set_all),
|
||||
PyClassPyO3Option::Subclass(subclass) => set_option!(subclass),
|
||||
|
@ -356,7 +361,12 @@ fn impl_class(
|
|||
cls,
|
||||
args,
|
||||
methods_type,
|
||||
descriptors_to_items(cls, args.options.frozen, field_options)?,
|
||||
descriptors_to_items(
|
||||
cls,
|
||||
args.options.rename_all.as_ref(),
|
||||
args.options.frozen,
|
||||
field_options,
|
||||
)?,
|
||||
vec![],
|
||||
)
|
||||
.doc(doc)
|
||||
|
@ -379,12 +389,20 @@ struct PyClassEnumVariant<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PyClassEnumVariant<'a> {
|
||||
fn python_name(&self) -> Cow<'_, syn::Ident> {
|
||||
fn python_name(&self, args: &PyClassArgs) -> Cow<'_, syn::Ident> {
|
||||
self.options
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|name_attr| Cow::Borrowed(&name_attr.value.0))
|
||||
.unwrap_or_else(|| Cow::Owned(self.ident.unraw()))
|
||||
.unwrap_or_else(|| {
|
||||
let name = self.ident.unraw();
|
||||
if let Some(attr) = &args.options.rename_all {
|
||||
let new_name = apply_renaming_rule(attr.value.rule, &name.to_string());
|
||||
Cow::Owned(Ident::new(&new_name, Span::call_site()))
|
||||
} else {
|
||||
Cow::Owned(name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,7 +533,7 @@ fn impl_enum(
|
|||
let repr = format!(
|
||||
"{}.{}",
|
||||
get_class_python_name(cls, args),
|
||||
variant.python_name(),
|
||||
variant.python_name(args),
|
||||
);
|
||||
quote! { #cls::#variant_name => #repr, }
|
||||
});
|
||||
|
@ -597,7 +615,7 @@ fn impl_enum(
|
|||
cls,
|
||||
args,
|
||||
methods_type,
|
||||
enum_default_methods(cls, variants.iter().map(|v| (v.ident, v.python_name()))),
|
||||
enum_default_methods(cls, variants.iter().map(|v| (v.ident, v.python_name(args)))),
|
||||
default_slots,
|
||||
)
|
||||
.doc(doc)
|
||||
|
@ -675,6 +693,7 @@ fn extract_variant_data(variant: &mut syn::Variant) -> syn::Result<PyClassEnumVa
|
|||
|
||||
fn descriptors_to_items(
|
||||
cls: &syn::Ident,
|
||||
rename_all: Option<&RenameAllAttribute>,
|
||||
frozen: Option<frozen>,
|
||||
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
|
||||
) -> syn::Result<Vec<MethodAndMethodDef>> {
|
||||
|
@ -697,6 +716,7 @@ fn descriptors_to_items(
|
|||
field_index,
|
||||
field,
|
||||
python_name: options.name.as_ref(),
|
||||
renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
|
||||
},
|
||||
)?;
|
||||
items.push(getter);
|
||||
|
@ -710,6 +730,7 @@ fn descriptors_to_items(
|
|||
field_index,
|
||||
field,
|
||||
python_name: options.name.as_ref(),
|
||||
renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
|
||||
},
|
||||
)?;
|
||||
items.push(setter);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::attributes::NameAttribute;
|
||||
use crate::attributes::{NameAttribute, RenamingRule};
|
||||
use crate::method::{CallingConvention, ExtractErrorMode};
|
||||
use crate::utils::{ensure_not_async_fn, PythonDoc};
|
||||
use crate::{
|
||||
|
@ -724,6 +724,7 @@ pub enum PropertyType<'a> {
|
|||
field_index: usize,
|
||||
field: &'a syn::Field,
|
||||
python_name: Option<&'a NameAttribute>,
|
||||
renaming_rule: Option<RenamingRule>,
|
||||
},
|
||||
Function {
|
||||
self_type: &'a SelfType,
|
||||
|
@ -736,11 +737,21 @@ impl PropertyType<'_> {
|
|||
fn null_terminated_python_name(&self) -> Result<syn::LitStr> {
|
||||
match self {
|
||||
PropertyType::Descriptor {
|
||||
field, python_name, ..
|
||||
field,
|
||||
python_name,
|
||||
renaming_rule,
|
||||
..
|
||||
} => {
|
||||
let name = match (python_name, &field.ident) {
|
||||
(Some(name), _) => name.value.0.to_string(),
|
||||
(None, Some(field_name)) => format!("{}\0", field_name.unraw()),
|
||||
(None, Some(field_name)) => {
|
||||
let mut name = field_name.unraw().to_string();
|
||||
if let Some(rule) = renaming_rule {
|
||||
name = utils::apply_renaming_rule(*rule, &name);
|
||||
}
|
||||
name.push('\0');
|
||||
name
|
||||
}
|
||||
(None, None) => {
|
||||
bail_spanned!(field.span() => "`get` and `set` with tuple struct fields require `name`");
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream};
|
|||
use quote::ToTokens;
|
||||
use syn::{punctuated::Punctuated, spanned::Spanned, Token};
|
||||
|
||||
use crate::attributes::CrateAttribute;
|
||||
use crate::attributes::{CrateAttribute, RenamingRule};
|
||||
|
||||
/// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span.
|
||||
macro_rules! err_spanned {
|
||||
|
@ -161,3 +161,18 @@ pub(crate) fn get_pyo3_crate(attr: &Option<CrateAttribute>) -> syn::Path {
|
|||
.map(|p| p.value.0.clone())
|
||||
.unwrap_or_else(|| syn::parse_str("::pyo3").unwrap())
|
||||
}
|
||||
|
||||
pub fn apply_renaming_rule(rule: RenamingRule, name: &str) -> String {
|
||||
use heck::*;
|
||||
|
||||
match rule {
|
||||
RenamingRule::CamelCase => name.to_lower_camel_case(),
|
||||
RenamingRule::KebabCase => name.to_kebab_case(),
|
||||
RenamingRule::Lowercase => name.to_lowercase(),
|
||||
RenamingRule::PascalCase => name.to_upper_camel_case(),
|
||||
RenamingRule::ScreamingKebabCase => name.to_shouty_kebab_case(),
|
||||
RenamingRule::ScreamingSnakeCase => name.to_shouty_snake_case(),
|
||||
RenamingRule::SnakeCase => name.to_snake_case(),
|
||||
RenamingRule::Uppercase => name.to_uppercase(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,3 +160,128 @@ RuntimeError: An error occurred while initializing class BrokenClass"
|
|||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[pyclass(get_all, set_all, rename_all = "camelCase")]
|
||||
struct StructWithRenamedFields {
|
||||
first_field: bool,
|
||||
second_field: u8,
|
||||
#[pyo3(name = "third_field")]
|
||||
fourth_field: bool,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl StructWithRenamedFields {
|
||||
#[new]
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
first_field: true,
|
||||
second_field: 5,
|
||||
fourth_field: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_renaming_all_struct_fields() {
|
||||
use pyo3::types::PyBool;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let struct_class = py.get_type::<StructWithRenamedFields>();
|
||||
let struct_obj = struct_class.call0().unwrap();
|
||||
assert!(struct_obj
|
||||
.setattr("firstField", PyBool::new(py, false))
|
||||
.is_ok());
|
||||
py_assert!(py, struct_obj, "struct_obj.firstField == False");
|
||||
py_assert!(py, struct_obj, "struct_obj.secondField == 5");
|
||||
assert!(struct_obj
|
||||
.setattr("third_field", PyBool::new(py, true))
|
||||
.is_ok());
|
||||
py_assert!(py, struct_obj, "struct_obj.third_field == True");
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! test_case {
|
||||
($struct_name: ident, $rule: literal, $field_name: ident, $renamed_field_name: literal, $test_name: ident) => {
|
||||
#[pyclass(get_all, set_all, rename_all = $rule)]
|
||||
#[allow(non_snake_case)]
|
||||
struct $struct_name {
|
||||
$field_name: u8,
|
||||
}
|
||||
#[pymethods]
|
||||
impl $struct_name {
|
||||
#[new]
|
||||
fn new() -> Self {
|
||||
Self { $field_name: 0 }
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
//use pyo3::types::PyInt;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let struct_class = py.get_type::<$struct_name>();
|
||||
let struct_obj = struct_class.call0().unwrap();
|
||||
assert!(struct_obj.setattr($renamed_field_name, 2).is_ok());
|
||||
let attr = struct_obj.getattr($renamed_field_name).unwrap();
|
||||
assert_eq!(2, PyAny::extract::<u8>(attr).unwrap());
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_case!(
|
||||
LowercaseTest,
|
||||
"lowercase",
|
||||
fieldOne,
|
||||
"fieldone",
|
||||
test_rename_all_lowercase
|
||||
);
|
||||
test_case!(
|
||||
CamelCaseTest,
|
||||
"camelCase",
|
||||
field_one,
|
||||
"fieldOne",
|
||||
test_rename_all_camel_case
|
||||
);
|
||||
test_case!(
|
||||
KebabCaseTest,
|
||||
"kebab-case",
|
||||
field_one,
|
||||
"field-one",
|
||||
test_rename_all_kebab_case
|
||||
);
|
||||
test_case!(
|
||||
PascalCaseTest,
|
||||
"PascalCase",
|
||||
field_one,
|
||||
"FieldOne",
|
||||
test_rename_all_pascal_case
|
||||
);
|
||||
test_case!(
|
||||
ScreamingSnakeCaseTest,
|
||||
"SCREAMING_SNAKE_CASE",
|
||||
field_one,
|
||||
"FIELD_ONE",
|
||||
test_rename_all_screaming_snake_case
|
||||
);
|
||||
test_case!(
|
||||
ScreamingKebabCaseTest,
|
||||
"SCREAMING-KEBAB-CASE",
|
||||
field_one,
|
||||
"FIELD-ONE",
|
||||
test_rename_all_screaming_kebab_case
|
||||
);
|
||||
test_case!(
|
||||
SnakeCaseTest,
|
||||
"snake_case",
|
||||
fieldOne,
|
||||
"field_one",
|
||||
test_rename_all_snake_case
|
||||
);
|
||||
test_case!(
|
||||
UppercaseTest,
|
||||
"UPPERCASE",
|
||||
fieldOne,
|
||||
"FIELDONE",
|
||||
test_rename_all_uppercase
|
||||
);
|
||||
|
|
|
@ -190,3 +190,26 @@ fn test_rename_variant_repr_correct() {
|
|||
py_assert!(py, var1, "repr(var1) == 'RenameVariantEnum.VARIANT'");
|
||||
})
|
||||
}
|
||||
|
||||
#[pyclass(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum RenameAllVariantsEnum {
|
||||
VariantOne,
|
||||
VariantTwo,
|
||||
#[pyo3(name = "VariantThree")]
|
||||
VariantFour,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_renaming_all_enum_variants() {
|
||||
Python::with_gil(|py| {
|
||||
let enum_obj = py.get_type::<RenameAllVariantsEnum>();
|
||||
py_assert!(py, enum_obj, "enum_obj.VARIANT_ONE == enum_obj.VARIANT_ONE");
|
||||
py_assert!(py, enum_obj, "enum_obj.VARIANT_TWO == enum_obj.VARIANT_TWO");
|
||||
py_assert!(
|
||||
py,
|
||||
enum_obj,
|
||||
"enum_obj.VariantThree == enum_obj.VariantThree"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,12 @@ struct InvalidName2 {}
|
|||
#[pyclass(name = CustomName)]
|
||||
struct DeprecatedName {}
|
||||
|
||||
#[pyclass(rename_all = camelCase)]
|
||||
struct InvalidRenamingRule {}
|
||||
|
||||
#[pyclass(rename_all = "Camel-Case")]
|
||||
struct InvalidRenamingRule2 {}
|
||||
|
||||
#[pyclass(module = my_module)]
|
||||
struct InvalidModule {}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `get_all`, `mapping`, `module`, `name`, `sequence`, `set_all`, `subclass`, `text_signature`, `unsendable`, `weakref`
|
||||
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `get_all`, `mapping`, `module`, `name`, `rename_all`, `sequence`, `set_all`, `subclass`, `text_signature`, `unsendable`, `weakref`
|
||||
--> tests/ui/invalid_pyclass_args.rs:3:11
|
||||
|
|
||||
3 | #[pyclass(extend=pyo3::types::PyDict)]
|
||||
|
@ -29,19 +29,31 @@ error: expected string literal
|
|||
| ^^^^^^^^^^
|
||||
|
||||
error: expected string literal
|
||||
--> tests/ui/invalid_pyclass_args.rs:18:20
|
||||
--> tests/ui/invalid_pyclass_args.rs:18:24
|
||||
|
|
||||
18 | #[pyclass(module = my_module)]
|
||||
18 | #[pyclass(rename_all = camelCase)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected a valid renaming rule, possible values are: "camelCase", "kebab-case", "lowercase", "PascalCase", "SCREAMING-KEBAB-CASE", "SCREAMING_SNAKE_CASE", "snake_case", "UPPERCASE"
|
||||
--> tests/ui/invalid_pyclass_args.rs:21:24
|
||||
|
|
||||
21 | #[pyclass(rename_all = "Camel-Case")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: expected string literal
|
||||
--> tests/ui/invalid_pyclass_args.rs:24:20
|
||||
|
|
||||
24 | #[pyclass(module = my_module)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `get_all`, `mapping`, `module`, `name`, `sequence`, `set_all`, `subclass`, `text_signature`, `unsendable`, `weakref`
|
||||
--> tests/ui/invalid_pyclass_args.rs:21:11
|
||||
error: expected one of: `crate`, `dict`, `extends`, `freelist`, `frozen`, `get_all`, `mapping`, `module`, `name`, `rename_all`, `sequence`, `set_all`, `subclass`, `text_signature`, `unsendable`, `weakref`
|
||||
--> tests/ui/invalid_pyclass_args.rs:27:11
|
||||
|
|
||||
21 | #[pyclass(weakrev)]
|
||||
27 | #[pyclass(weakrev)]
|
||||
| ^^^^^^^
|
||||
|
||||
error: a `#[pyclass]` cannot be both a `mapping` and a `sequence`
|
||||
--> tests/ui/invalid_pyclass_args.rs:25:8
|
||||
--> tests/ui/invalid_pyclass_args.rs:31:8
|
||||
|
|
||||
25 | struct CannotBeMappingAndSequence {}
|
||||
31 | struct CannotBeMappingAndSequence {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
Loading…
Reference in New Issue