Skip to content

Commit 5cc3ce9

Browse files
committed
pyclass: unify pyclass with its pyo3 arguments
1 parent 3eb654c commit 5cc3ce9

17 files changed

+280
-311
lines changed

pyo3-macros-backend/src/attributes.rs

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,108 @@
1+
use proc_macro2::TokenStream;
2+
use quote::ToTokens;
13
use syn::{
24
parse::{Parse, ParseStream},
35
punctuated::Punctuated,
6+
spanned::Spanned,
47
token::Comma,
5-
Attribute, ExprPath, Ident, LitStr, Path, Result, Token,
8+
Attribute, Expr, ExprPath, Ident, LitStr, Path, Result, Token,
69
};
710

811
pub mod kw {
912
syn::custom_keyword!(annotation);
1013
syn::custom_keyword!(attribute);
14+
syn::custom_keyword!(dict);
15+
syn::custom_keyword!(extends);
16+
syn::custom_keyword!(freelist);
1117
syn::custom_keyword!(from_py_with);
18+
syn::custom_keyword!(gc);
1219
syn::custom_keyword!(get);
1320
syn::custom_keyword!(item);
14-
syn::custom_keyword!(pass_module);
21+
syn::custom_keyword!(module);
1522
syn::custom_keyword!(name);
23+
syn::custom_keyword!(pass_module);
1624
syn::custom_keyword!(set);
1725
syn::custom_keyword!(signature);
26+
syn::custom_keyword!(subclass);
1827
syn::custom_keyword!(text_signature);
1928
syn::custom_keyword!(transparent);
29+
syn::custom_keyword!(unsendable);
30+
syn::custom_keyword!(weakref);
31+
}
32+
33+
#[derive(Clone, Debug)]
34+
pub struct KeywordAttribute<K, V> {
35+
pub kw: K,
36+
pub value: V,
2037
}
2138

39+
/// A helper type which parses the inner type via a literal string
40+
/// e.g. LitStrValue<Path> -> parses "some::path" in quotes.
2241
#[derive(Clone, Debug, PartialEq)]
23-
pub struct FromPyWithAttribute(pub ExprPath);
42+
pub struct LitStrValue<T>(pub T);
2443

25-
impl Parse for FromPyWithAttribute {
44+
impl<T: Parse> Parse for LitStrValue<T> {
2645
fn parse(input: ParseStream) -> Result<Self> {
27-
let _: kw::from_py_with = input.parse()?;
28-
let _: Token![=] = input.parse()?;
29-
let string_literal: LitStr = input.parse()?;
30-
string_literal.parse().map(FromPyWithAttribute)
46+
let lit_str: LitStr = input.parse()?;
47+
lit_str.parse().map(LitStrValue)
3148
}
3249
}
3350

34-
#[derive(Clone, Debug, PartialEq)]
35-
pub struct NameAttribute(pub Ident);
36-
37-
impl Parse for NameAttribute {
38-
fn parse(input: ParseStream) -> Result<Self> {
39-
let _: kw::name = input.parse()?;
40-
let _: Token![=] = input.parse()?;
41-
let string_literal: LitStr = input.parse()?;
42-
string_literal.parse().map(NameAttribute)
51+
impl<T: ToTokens> ToTokens for LitStrValue<T> {
52+
fn to_tokens(&self, tokens: &mut TokenStream) {
53+
self.0.to_tokens(tokens)
4354
}
4455
}
4556

46-
/// For specifying the path to the pyo3 crate.
57+
/// A helper type which parses a name via a literal string
4758
#[derive(Clone, Debug, PartialEq)]
48-
pub struct CrateAttribute(pub Path);
59+
pub struct NameLitStr(pub Ident);
4960

50-
impl Parse for CrateAttribute {
61+
impl Parse for NameLitStr {
5162
fn parse(input: ParseStream) -> Result<Self> {
52-
let _: Token![crate] = input.parse()?;
53-
let _: Token![=] = input.parse()?;
5463
let string_literal: LitStr = input.parse()?;
55-
string_literal.parse().map(CrateAttribute)
64+
if let Ok(ident) = string_literal.parse() {
65+
Ok(NameLitStr(ident))
66+
} else {
67+
bail_spanned!(string_literal.span() => "expected a single identifier in double quotes")
68+
}
5669
}
5770
}
5871

59-
#[derive(Clone, Debug, PartialEq)]
60-
pub struct TextSignatureAttribute {
61-
pub kw: kw::text_signature,
62-
pub eq_token: Token![=],
63-
pub lit: LitStr,
72+
impl ToTokens for NameLitStr {
73+
fn to_tokens(&self, tokens: &mut TokenStream) {
74+
self.0.to_tokens(tokens)
75+
}
6476
}
6577

66-
impl Parse for TextSignatureAttribute {
78+
pub type ExtendsAttribute = KeywordAttribute<kw::extends, Path>;
79+
pub type FreelistAttribute = KeywordAttribute<kw::freelist, Box<Expr>>;
80+
pub type ModuleAttribute = KeywordAttribute<kw::module, LitStr>;
81+
pub type NameAttribute = KeywordAttribute<kw::name, NameLitStr>;
82+
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, LitStr>;
83+
84+
impl<K: Parse + std::fmt::Debug, V: Parse> Parse for KeywordAttribute<K, V> {
6785
fn parse(input: ParseStream) -> Result<Self> {
68-
Ok(TextSignatureAttribute {
69-
kw: input.parse()?,
70-
eq_token: input.parse()?,
71-
lit: input.parse()?,
72-
})
86+
let kw: K = input.parse()?;
87+
let _: Token![=] = input.parse()?;
88+
let value = input.parse()?;
89+
Ok(KeywordAttribute { kw, value })
7390
}
7491
}
7592

93+
impl<K: ToTokens, V: ToTokens> ToTokens for KeywordAttribute<K, V> {
94+
fn to_tokens(&self, tokens: &mut TokenStream) {
95+
self.kw.to_tokens(tokens);
96+
Token![=](self.kw.span()).to_tokens(tokens);
97+
self.value.to_tokens(tokens);
98+
}
99+
}
100+
101+
pub type FromPyWithAttribute = KeywordAttribute<kw::from_py_with, LitStrValue<ExprPath>>;
102+
103+
/// For specifying the path to the pyo3 crate.
104+
pub type CrateAttribute = KeywordAttribute<Token![crate], LitStrValue<Path>>;
105+
76106
pub fn get_pyo3_options<T: Parse>(attr: &syn::Attribute) -> Result<Option<Punctuated<T, Comma>>> {
77107
if is_attribute_ident(attr, "pyo3") {
78108
attr.parse_args_with(Punctuated::parse_terminated).map(Some)

pyo3-macros-backend/src/frompyobject.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,9 @@ impl<'a> Container<'a> {
252252
None => quote!(
253253
obj.get_item(#index)?.extract()
254254
),
255-
Some(FromPyWithAttribute(expr_path)) => quote! (
255+
Some(FromPyWithAttribute {
256+
value: expr_path, ..
257+
}) => quote! (
256258
#expr_path(obj.get_item(#index)?)
257259
),
258260
};
@@ -308,7 +310,9 @@ impl<'a> Container<'a> {
308310
new_err.set_cause(py, ::std::option::Option::Some(inner));
309311
new_err
310312
})?),
311-
Some(FromPyWithAttribute(expr_path)) => quote! (
313+
Some(FromPyWithAttribute {
314+
value: expr_path, ..
315+
}) => quote! (
312316
#expr_path(#get_field).map_err(|inner| {
313317
let py = _pyo3::PyNativeType::py(obj);
314318
let new_err = _pyo3::exceptions::PyTypeError::new_err(#conversion_error_msg);
@@ -388,7 +392,7 @@ impl ContainerOptions {
388392
ContainerPyO3Attribute::Crate(path) => {
389393
ensure_spanned!(
390394
options.krate.is_none(),
391-
path.0.span() => "`crate` may only be provided once"
395+
path.span() => "`crate` may only be provided once"
392396
);
393397
options.krate = Some(path);
394398
}

pyo3-macros-backend/src/konst.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub struct ConstSpec {
2121
impl ConstSpec {
2222
pub fn python_name(&self) -> Cow<Ident> {
2323
if let Some(name) = &self.attributes.name {
24-
Cow::Borrowed(&name.0)
24+
Cow::Borrowed(&name.value.0)
2525
} else {
2626
Cow::Owned(self.rust_ident.unraw())
2727
}
@@ -89,7 +89,7 @@ impl ConstAttributes {
8989
fn set_name(&mut self, name: NameAttribute) -> Result<()> {
9090
ensure_spanned!(
9191
self.name.is_none(),
92-
name.0.span() => "`name` may only be specified once"
92+
name.span() => "`name` may only be specified once"
9393
);
9494
self.name = Some(name);
9595
Ok(())

pyo3-macros-backend/src/method.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use syn::ext::IdentExt;
1414
use syn::spanned::Spanned;
1515
use syn::Result;
1616

17-
#[derive(Clone, PartialEq, Debug)]
17+
#[derive(Clone, Debug)]
1818
pub struct FnArg<'a> {
1919
pub name: &'a syn::Ident,
2020
pub by_ref: &'a Option<syn::token::Ref>,
@@ -273,7 +273,7 @@ impl<'a> FnSpec<'a> {
273273
ty: fn_type_attr,
274274
args: fn_attrs,
275275
mut python_name,
276-
} = parse_method_attributes(meth_attrs, name.map(|name| name.0), &mut deprecations)?;
276+
} = parse_method_attributes(meth_attrs, name.map(|name| name.value.0), &mut deprecations)?;
277277

278278
let (fn_type, skip_first_arg, fixed_convention) =
279279
Self::parse_fn_type(sig, fn_type_attr, &mut python_name)?;

pyo3-macros-backend/src/module.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl PyModuleOptions {
3131

3232
for option in take_pyo3_options(attrs)? {
3333
match option {
34-
PyModulePyO3Option::Name(name) => options.set_name(name.0)?,
34+
PyModulePyO3Option::Name(name) => options.set_name(name.value.0)?,
3535
PyModulePyO3Option::Crate(path) => options.set_crate(path)?,
3636
}
3737
}
@@ -52,7 +52,7 @@ impl PyModuleOptions {
5252
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
5353
ensure_spanned!(
5454
self.krate.is_none(),
55-
path.0.span() => "`crate` may only be specified once"
55+
path.span() => "`crate` may only be specified once"
5656
);
5757

5858
self.krate = Some(path);

pyo3-macros-backend/src/params.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ fn impl_arg_param(
231231
let arg_value = quote_arg_span!(#args_array[#option_pos]);
232232
*option_pos += 1;
233233

234-
let arg_value_or_default = if let Some(FromPyWithAttribute(expr_path)) = &arg.attrs.from_py_with
234+
let arg_value_or_default = if let Some(FromPyWithAttribute {
235+
value: expr_path, ..
236+
}) = &arg.attrs.from_py_with
235237
{
236238
match (spec.default_value(name), arg.optional.is_some()) {
237239
(Some(default), true) if default.to_string() != "None" => {

0 commit comments

Comments
 (0)