Skip to content

Commit 549cce2

Browse files
committed
Add #[pystruct(skip)]
1 parent 7ac90f5 commit 549cce2

File tree

3 files changed

+84
-24
lines changed

3 files changed

+84
-24
lines changed

.cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
"origname",
149149
"posixsubprocess",
150150
"pyexpat",
151+
"pytraverse",
151152
"PYTHONDEBUG",
152153
"PYTHONHOME",
153154
"PYTHONINSPECT",

derive-impl/src/pystructseq.rs

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,93 @@
11
use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{DeriveInput, Ident, Result};
4+
use syn_ext::ext::{AttributeExt, GetIdent};
5+
use syn_ext::types::Meta;
46

5-
fn field_names(input: &DeriveInput) -> Result<Vec<&Ident>> {
6-
let fields = if let syn::Data::Struct(ref struc) = input.data {
7-
&struc.fields
8-
} else {
7+
// returning a pair of not-skipped and skipped field names
8+
fn field_names(input: &mut DeriveInput) -> Result<(Vec<Ident>, Vec<Ident>)> {
9+
let syn::Data::Struct(struc) = &mut input.data else {
910
bail_span!(
1011
input,
1112
"#[pystruct_sequence] can only be on a struct declaration"
1213
)
1314
};
1415

15-
let field_names: Vec<_> = match fields {
16-
syn::Fields::Named(fields) => fields
17-
.named
18-
.iter()
19-
.map(|field| field.ident.as_ref().unwrap())
20-
.collect(),
21-
_ => bail_span!(
16+
let syn::Fields::Named(fields) = &mut struc.fields else {
17+
bail_span!(
2218
input,
2319
"#[pystruct_sequence] can only be on a struct with named fields"
24-
),
20+
);
2521
};
2622

27-
Ok(field_names)
23+
let mut not_skipped = Vec::with_capacity(fields.named.len());
24+
let mut skipped = Vec::with_capacity(fields.named.len());
25+
for field in &mut fields.named {
26+
let mut skip = false;
27+
// Collect all attributes with pystruct and their indices
28+
let mut attrs_to_remove = Vec::new();
29+
30+
for (i, attr) in field.attrs.iter().enumerate() {
31+
if !attr.path().is_ident("pystruct") {
32+
continue;
33+
}
34+
35+
let Ok(meta) = attr.parse_meta() else {
36+
continue;
37+
};
38+
39+
let Meta::List(l) = meta else {
40+
bail_span!(input, "Only #[pystruct(...)] form is allowed");
41+
};
42+
43+
let idents: Vec<_> = l
44+
.nested
45+
.iter()
46+
.filter_map(|n| n.get_ident())
47+
.cloned()
48+
.collect();
49+
50+
// Follow #[serde(skip)] convention.
51+
// Consider to add skip_serializing and skip_deserializing if required.
52+
for ident in idents {
53+
match ident.to_string().as_str() {
54+
"skip" => {
55+
skip = true;
56+
}
57+
_ => {
58+
bail_span!(ident, "Unknown item for #[pystruct(...)]")
59+
}
60+
}
61+
}
62+
63+
attrs_to_remove.push(i);
64+
}
65+
66+
// Remove attributes in reverse order to maintain valid indices
67+
attrs_to_remove.sort_unstable_by(|a, b| b.cmp(a)); // Sort in descending order
68+
for index in attrs_to_remove {
69+
field.attrs.remove(index);
70+
}
71+
let ident = field.ident.clone().unwrap();
72+
if skip {
73+
skipped.push(ident.clone());
74+
} else {
75+
not_skipped.push(ident.clone());
76+
}
77+
}
78+
79+
Ok((not_skipped, skipped))
2880
}
2981

30-
pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream> {
31-
let field_names = field_names(&input)?;
82+
pub(crate) fn impl_pystruct_sequence(mut input: DeriveInput) -> Result<TokenStream> {
83+
let (not_skipped_fields, _skipped_fields) = field_names(&mut input)?;
3284
let ty = &input.ident;
3385
let ret = quote! {
3486
impl ::rustpython_vm::types::PyStructSequence for #ty {
35-
const FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#field_names)),*];
87+
const FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#not_skipped_fields)),*];
3688
fn into_tuple(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::builtins::PyTuple {
3789
let items = vec![#(::rustpython_vm::convert::ToPyObject::to_pyobject(
38-
self.#field_names,
90+
self.#not_skipped_fields,
3991
vm,
4092
)),*];
4193
::rustpython_vm::builtins::PyTuple::new_unchecked(items.into_boxed_slice())
@@ -50,8 +102,10 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
50102
Ok(ret)
51103
}
52104

53-
pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Result<TokenStream> {
54-
let field_names = field_names(&input)?;
105+
pub(crate) fn impl_pystruct_sequence_try_from_object(
106+
mut input: DeriveInput,
107+
) -> Result<TokenStream> {
108+
let (not_skipped_fields, skipped_fields) = field_names(&mut input)?;
55109
let ty = &input.ident;
56110
let ret = quote! {
57111
impl ::rustpython_vm::TryFromObject for #ty {
@@ -60,9 +114,14 @@ pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Resu
60114
let seq = Self::try_elements_from::<LEN>(seq, vm)?;
61115
// TODO: this is possible to be written without iterator
62116
let mut iter = seq.into_iter();
63-
Ok(Self {#(
64-
#field_names: iter.next().unwrap().clone().try_into_value(vm)?
65-
),*})
117+
Ok(Self {
118+
#(
119+
#not_skipped_fields: iter.next().unwrap().clone().try_into_value(vm)?,
120+
)*
121+
#(
122+
#skipped_fields: vm.ctx.none(),
123+
)*
124+
})
66125
}
67126
}
68127
};

derive/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ pub fn pymodule(attr: TokenStream, item: TokenStream) -> TokenStream {
225225
derive_impl::pymodule(attr, item).into()
226226
}
227227

228-
#[proc_macro_derive(PyStructSequence)]
228+
#[proc_macro_derive(PyStructSequence, attributes(pystruct))]
229229
pub fn pystruct_sequence(input: TokenStream) -> TokenStream {
230230
let input = parse_macro_input!(input);
231231
derive_impl::pystruct_sequence(input).into()
232232
}
233233

234-
#[proc_macro_derive(TryIntoPyStructSequence)]
234+
#[proc_macro_derive(TryIntoPyStructSequence, attributes(pystruct))]
235235
pub fn pystruct_sequence_try_from_object(input: TokenStream) -> TokenStream {
236236
let input = parse_macro_input!(input);
237237
derive_impl::pystruct_sequence_try_from_object(input).into()

0 commit comments

Comments
 (0)