Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add #[pystruct(skip)] #5397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"origname",
"posixsubprocess",
"pyexpat",
"pytraverse",
"PYTHONDEBUG",
"PYTHONHOME",
"PYTHONINSPECT",
Expand Down
103 changes: 81 additions & 22 deletions 103 derive-impl/src/pystructseq.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,93 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Ident, Result};
use syn_ext::ext::{AttributeExt, GetIdent};
use syn_ext::types::Meta;

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

let field_names: Vec<_> = match fields {
syn::Fields::Named(fields) => fields
.named
.iter()
.map(|field| field.ident.as_ref().unwrap())
.collect(),
_ => bail_span!(
let syn::Fields::Named(fields) = &mut struc.fields else {
bail_span!(
input,
"#[pystruct_sequence] can only be on a struct with named fields"
),
);
};

Ok(field_names)
let mut not_skipped = Vec::with_capacity(fields.named.len());
let mut skipped = Vec::with_capacity(fields.named.len());
for field in &mut fields.named {
let mut skip = false;
// Collect all attributes with pystruct and their indices
let mut attrs_to_remove = Vec::new();

for (i, attr) in field.attrs.iter().enumerate() {
if !attr.path().is_ident("pystruct") {
continue;
}

let Ok(meta) = attr.parse_meta() else {
continue;
};

let Meta::List(l) = meta else {
bail_span!(input, "Only #[pystruct(...)] form is allowed");
};

let idents: Vec<_> = l
.nested
.iter()
.filter_map(|n| n.get_ident())
.cloned()
.collect();

// Follow #[serde(skip)] convention.
// Consider to add skip_serializing and skip_deserializing if required.
for ident in idents {
match ident.to_string().as_str() {
"skip" => {
skip = true;
}
_ => {
bail_span!(ident, "Unknown item for #[pystruct(...)]")
}
}
}

attrs_to_remove.push(i);
}

// Remove attributes in reverse order to maintain valid indices
attrs_to_remove.sort_unstable_by(|a, b| b.cmp(a)); // Sort in descending order
for index in attrs_to_remove {
field.attrs.remove(index);
}
let ident = field.ident.clone().unwrap();
if skip {
skipped.push(ident.clone());
} else {
not_skipped.push(ident.clone());
}
}

Ok((not_skipped, skipped))
}

pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream> {
let field_names = field_names(&input)?;
pub(crate) fn impl_pystruct_sequence(mut input: DeriveInput) -> Result<TokenStream> {
let (not_skipped_fields, _skipped_fields) = field_names(&mut input)?;
let ty = &input.ident;
let ret = quote! {
impl ::rustpython_vm::types::PyStructSequence for #ty {
const FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#field_names)),*];
const FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#not_skipped_fields)),*];
fn into_tuple(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::builtins::PyTuple {
let items = vec![#(::rustpython_vm::convert::ToPyObject::to_pyobject(
self.#field_names,
self.#not_skipped_fields,
vm,
)),*];
::rustpython_vm::builtins::PyTuple::new_unchecked(items.into_boxed_slice())
Expand All @@ -50,8 +102,10 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
Ok(ret)
}

pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Result<TokenStream> {
let field_names = field_names(&input)?;
pub(crate) fn impl_pystruct_sequence_try_from_object(
mut input: DeriveInput,
) -> Result<TokenStream> {
let (not_skipped_fields, skipped_fields) = field_names(&mut input)?;
let ty = &input.ident;
let ret = quote! {
impl ::rustpython_vm::TryFromObject for #ty {
Expand All @@ -60,9 +114,14 @@ pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Resu
let seq = Self::try_elements_from::<LEN>(seq, vm)?;
// TODO: this is possible to be written without iterator
let mut iter = seq.into_iter();
Ok(Self {#(
#field_names: iter.next().unwrap().clone().try_into_value(vm)?
),*})
Ok(Self {
#(
#not_skipped_fields: iter.next().unwrap().clone().try_into_value(vm)?,
)*
#(
#skipped_fields: vm.ctx.none(),
)*
})
}
}
};
Expand Down
4 changes: 2 additions & 2 deletions 4 derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@ pub fn pymodule(attr: TokenStream, item: TokenStream) -> TokenStream {
derive_impl::pymodule(attr, item).into()
}

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

#[proc_macro_derive(TryIntoPyStructSequence)]
#[proc_macro_derive(TryIntoPyStructSequence, attributes(pystruct))]
pub fn pystruct_sequence_try_from_object(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
derive_impl::pystruct_sequence_try_from_object(input).into()
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.