Skip to content

Commit 1347f6e

Browse files
committed
PyStructSequence
1 parent 5ad536e commit 1347f6e

File tree

20 files changed

+1137
-271
lines changed

20 files changed

+1137
-271
lines changed

Lib/test/datetimetester.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,8 +2943,6 @@ def newmeth(self, start):
29432943
self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
29442944
dt1.second - 7)
29452945

2946-
# TODO: RUSTPYTHON
2947-
@unittest.expectedFailure
29482946
def test_subclass_alternate_constructors_datetime(self):
29492947
# Test that alternate constructors call the constructor
29502948
class DateTimeSubclass(self.theclass):

Lib/test/test_logging.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4748,7 +4748,6 @@ def test_defaults_parameter(self):
47484748
def test_invalid_style(self):
47494749
self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
47504750

4751-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'struct_time' object has no attribute 'tm_gmtoff'
47524751
def test_time(self):
47534752
r = self.get_record()
47544753
dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
@@ -4763,7 +4762,6 @@ def test_time(self):
47634762
f.format(r)
47644763
self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
47654764

4766-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'struct_time' object has no attribute 'tm_gmtoff'
47674765
def test_default_msec_format_none(self):
47684766
class NoMsecFormatter(logging.Formatter):
47694767
default_msec_format = None

Lib/test/test_structseq.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ def test_cmp(self):
7575
self.assertTrue(t1 >= t2)
7676
self.assertTrue(not (t1 != t2))
7777

78-
# TODO: RUSTPYTHON
79-
@unittest.expectedFailure
8078
def test_fields(self):
8179
t = time.gmtime()
8280
self.assertEqual(len(t), t.n_sequence_fields)
@@ -129,8 +127,6 @@ def test_match_args(self):
129127
'tm_sec', 'tm_wday', 'tm_yday', 'tm_isdst')
130128
self.assertEqual(time.struct_time.__match_args__, expected_args)
131129

132-
# TODO: RUSTPYTHON
133-
@unittest.expectedFailure
134130
def test_match_args_with_unnamed_fields(self):
135131
expected_args = ('st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid',
136132
'st_gid', 'st_size')

crates/derive-impl/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ pub fn pymodule(attr: PunctuatedNestedMeta, item: Item) -> TokenStream {
5858
result_to_tokens(pymodule::impl_pymodule(attr, item))
5959
}
6060

61-
pub fn pystruct_sequence(input: DeriveInput) -> TokenStream {
62-
result_to_tokens(pystructseq::impl_pystruct_sequence(input))
61+
pub fn pystruct_sequence(attr: PunctuatedNestedMeta, item: Item) -> TokenStream {
62+
result_to_tokens(pystructseq::impl_pystruct_sequence(attr, item))
6363
}
6464

65-
pub fn pystruct_sequence_try_from_object(input: DeriveInput) -> TokenStream {
66-
result_to_tokens(pystructseq::impl_pystruct_sequence_try_from_object(input))
65+
pub fn pystruct_sequence_data(attr: PunctuatedNestedMeta, item: Item) -> TokenStream {
66+
result_to_tokens(pystructseq::impl_pystruct_sequence_data(attr, item))
6767
}
6868

6969
pub fn py_compile(input: TokenStream, compiler: &dyn Compiler) -> TokenStream {

crates/derive-impl/src/pymodule.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum AttrName {
1818
Attr,
1919
Class,
2020
Exception,
21+
StructSequence,
2122
}
2223

2324
impl std::fmt::Display for AttrName {
@@ -27,6 +28,7 @@ impl std::fmt::Display for AttrName {
2728
Self::Attr => "pyattr",
2829
Self::Class => "pyclass",
2930
Self::Exception => "pyexception",
31+
Self::StructSequence => "pystruct_sequence",
3032
};
3133
s.fmt(f)
3234
}
@@ -41,6 +43,7 @@ impl FromStr for AttrName {
4143
"pyattr" => Self::Attr,
4244
"pyclass" => Self::Class,
4345
"pyexception" => Self::Exception,
46+
"pystruct_sequence" => Self::StructSequence,
4447
s => {
4548
return Err(s.to_owned());
4649
}
@@ -235,6 +238,10 @@ fn module_item_new(
235238
inner: ContentItemInner { index, attr_name },
236239
py_attrs,
237240
}),
241+
AttrName::StructSequence => Box::new(StructSequenceItem {
242+
inner: ContentItemInner { index, attr_name },
243+
py_attrs,
244+
}),
238245
}
239246
}
240247

@@ -402,6 +409,12 @@ struct AttributeItem {
402409
py_attrs: Vec<usize>,
403410
}
404411

412+
/// #[pystruct_sequence]
413+
struct StructSequenceItem {
414+
inner: ContentItemInner<AttrName>,
415+
py_attrs: Vec<usize>,
416+
}
417+
405418
impl ContentItem for FunctionItem {
406419
type AttrName = AttrName;
407420

@@ -426,6 +439,14 @@ impl ContentItem for AttributeItem {
426439
}
427440
}
428441

442+
impl ContentItem for StructSequenceItem {
443+
type AttrName = AttrName;
444+
445+
fn inner(&self) -> &ContentItemInner<AttrName> {
446+
&self.inner
447+
}
448+
}
449+
429450
struct ModuleItemArgs<'a> {
430451
item: &'a mut Item,
431452
attrs: &'a mut Vec<Attribute>,
@@ -602,6 +623,112 @@ impl ModuleItem for ClassItem {
602623
}
603624
}
604625

626+
impl ModuleItem for StructSequenceItem {
627+
fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
628+
// Get the struct identifier (this IS the Python type, e.g., PyStructTime)
629+
let pytype_ident = match args.item {
630+
Item::Struct(s) => s.ident.clone(),
631+
other => bail_span!(other, "#[pystruct_sequence] can only be on a struct"),
632+
};
633+
634+
// Parse the #[pystruct_sequence(name = "...", module = "...", no_attr)] attribute
635+
let structseq_attr = &args.attrs[self.inner.index];
636+
let (class_name, module_name, no_attr) = {
637+
use syn_ext::ext::AttributeExt as _;
638+
let meta = structseq_attr.parse_meta().ok();
639+
let mut name = None;
640+
let mut module = None;
641+
let mut no_attr = false;
642+
if let Some(syn_ext::types::Meta::List(list)) = meta {
643+
for nested in list.nested.iter() {
644+
match nested {
645+
syn_ext::types::NestedMeta::Meta(syn_ext::types::Meta::NameValue(nv)) => {
646+
if let syn::Expr::Lit(syn::ExprLit {
647+
lit: syn::Lit::Str(lit),
648+
..
649+
}) = &nv.value
650+
{
651+
if nv.path.is_ident("name") {
652+
name = Some(lit.value());
653+
} else if nv.path.is_ident("module") {
654+
module = Some(lit.value());
655+
}
656+
}
657+
}
658+
syn_ext::types::NestedMeta::Meta(syn_ext::types::Meta::Path(path)) => {
659+
if path.is_ident("no_attr") {
660+
no_attr = true;
661+
}
662+
}
663+
_ => {}
664+
}
665+
}
666+
}
667+
// name parameter is required
668+
let class_name = name.ok_or_else(|| {
669+
syn::Error::new_spanned(
670+
structseq_attr,
671+
"#[pystruct_sequence] requires name parameter",
672+
)
673+
})?;
674+
// Use explicit module if provided, otherwise use context module name
675+
let module_name = module.unwrap_or_else(|| args.context.name.clone());
676+
(class_name, module_name, no_attr)
677+
};
678+
679+
// Generate the class creation code
680+
let class_new = quote_spanned!(pytype_ident.span() =>
681+
let new_class = <#pytype_ident as ::rustpython_vm::class::PyClassImpl>::make_class(ctx);
682+
new_class.set_attr(rustpython_vm::identifier!(ctx, __module__), vm.new_pyobj(#module_name));
683+
);
684+
685+
// Handle py_attrs for custom names, or use class_name as default
686+
let mut py_names = Vec::new();
687+
for attr_index in self.py_attrs.iter().rev() {
688+
let attr_attr = args.attrs.remove(*attr_index);
689+
let item_meta = SimpleItemMeta::from_attr(pytype_ident.clone(), &attr_attr)?;
690+
let py_name = item_meta
691+
.optional_name()
692+
.unwrap_or_else(|| class_name.clone());
693+
py_names.push(py_name);
694+
}
695+
696+
// If no #[pyattr] was used and no_attr is not set, auto-register with the class name
697+
if py_names.is_empty() && !no_attr {
698+
py_names.push(class_name.clone());
699+
}
700+
701+
let set_attr = match py_names.len() {
702+
0 => quote! {
703+
let _ = new_class; // suppress warning
704+
},
705+
1 => {
706+
let py_name = &py_names[0];
707+
quote! {
708+
vm.__module_set_attr(&module, vm.ctx.intern_str(#py_name), new_class).unwrap();
709+
}
710+
}
711+
_ => quote! {
712+
for name in [#(#py_names,)*] {
713+
vm.__module_set_attr(&module, vm.ctx.intern_str(name), new_class.clone()).unwrap();
714+
}
715+
},
716+
};
717+
718+
args.context.attribute_items.add_item(
719+
pytype_ident.clone(),
720+
py_names,
721+
args.cfgs.to_vec(),
722+
quote_spanned! { pytype_ident.span() =>
723+
#class_new
724+
#set_attr
725+
},
726+
0,
727+
)?;
728+
Ok(())
729+
}
730+
}
731+
605732
impl ModuleItem for AttributeItem {
606733
fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
607734
let cfgs = args.cfgs.to_vec();

0 commit comments

Comments
 (0)