Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
fix windows fileio
  • Loading branch information
youknowone committed Dec 28, 2025
commit 6ac1266dcd3ba1a68217cd7554f735482f5b52e4
16 changes: 15 additions & 1 deletion crates/common/src/crt_fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,21 @@ pub fn close(fd: Owned) -> io::Result<()> {
}

pub fn ftruncate(fd: Borrowed<'_>, len: Offset) -> io::Result<()> {
cvt(unsafe { suppress_iph!(c::ftruncate(fd.as_raw(), len)) })?;
let ret = unsafe { suppress_iph!(c::ftruncate(fd.as_raw(), len)) };
// On Windows, _chsize_s returns 0 on success, or a positive error code (errno value) on failure.
// On other platforms, ftruncate returns 0 on success, or -1 on failure with errno set.
#[cfg(windows)]
{
if ret != 0 {
// _chsize_s returns errno directly, convert to Windows error code
let winerror = crate::os::errno_to_winerror(ret);
return Err(io::Error::from_raw_os_error(winerror));
}
}
#[cfg(not(windows))]
{
cvt(ret)?;
}
Ok(())
}

Expand Down
121 changes: 100 additions & 21 deletions crates/derive-impl/src/pystructseq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ enum FieldKind {
struct ParsedField {
ident: Ident,
kind: FieldKind,
/// Optional cfg attributes for conditional compilation
cfg_attrs: Vec<syn::Attribute>,
}

/// Parsed field info from struct
Expand All @@ -31,27 +33,24 @@ struct FieldInfo {
}

impl FieldInfo {
fn named_fields(&self) -> Vec<Ident> {
fn named_fields(&self) -> Vec<&ParsedField> {
self.fields
.iter()
.filter(|f| f.kind == FieldKind::Named)
.map(|f| f.ident.clone())
.collect()
}

fn visible_fields(&self) -> Vec<Ident> {
fn visible_fields(&self) -> Vec<&ParsedField> {
self.fields
.iter()
.filter(|f| f.kind != FieldKind::Skipped)
.map(|f| f.ident.clone())
.collect()
}

fn skipped_fields(&self) -> Vec<Ident> {
fn skipped_fields(&self) -> Vec<&ParsedField> {
self.fields
.iter()
.filter(|f| f.kind == FieldKind::Skipped)
.map(|f| f.ident.clone())
.collect()
}

Expand Down Expand Up @@ -82,8 +81,15 @@ fn parse_fields(input: &mut DeriveInput) -> Result<FieldInfo> {
let mut skip = false;
let mut unnamed = false;
let mut attrs_to_remove = Vec::new();
let mut cfg_attrs = Vec::new();

for (i, attr) in field.attrs.iter().enumerate() {
// Collect cfg attributes for conditional compilation
if attr.path().is_ident("cfg") {
cfg_attrs.push(attr.clone());
continue;
}

if !attr.path().is_ident("pystruct_sequence") {
continue;
}
Expand Down Expand Up @@ -135,7 +141,11 @@ fn parse_fields(input: &mut DeriveInput) -> Result<FieldInfo> {
FieldKind::Named
};

parsed_fields.push(ParsedField { ident, kind });
parsed_fields.push(ParsedField {
ident,
kind,
cfg_attrs,
});
}

Ok(FieldInfo {
Expand Down Expand Up @@ -194,18 +204,91 @@ pub(crate) fn impl_pystruct_sequence_data(
let skipped_fields = field_info.skipped_fields();
let n_unnamed_fields = field_info.n_unnamed_fields();

// Generate field index constants for visible fields
// Generate field index constants for visible fields (with cfg guards)
let field_indices: Vec<_> = visible_fields
.iter()
.enumerate()
.map(|(i, field)| {
let const_name = format_ident!("{}_INDEX", field.to_string().to_uppercase());
let const_name = format_ident!("{}_INDEX", field.ident.to_string().to_uppercase());
let cfg_attrs = &field.cfg_attrs;
quote! {
#(#cfg_attrs)*
pub const #const_name: usize = #i;
}
})
.collect();

// Generate field name entries with cfg guards for named fields
let named_field_names: Vec<_> = named_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! { stringify!(#ident), }
} else {
quote! {
#(#cfg_attrs)*
{ stringify!(#ident) },
}
}
})
.collect();

// Generate field name entries with cfg guards for skipped fields
let skipped_field_names: Vec<_> = skipped_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! { stringify!(#ident), }
} else {
quote! {
#(#cfg_attrs)*
{ stringify!(#ident) },
}
}
})
.collect();

// Generate into_tuple items with cfg guards
let visible_tuple_items: Vec<_> = visible_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! {
::rustpython_vm::convert::ToPyObject::to_pyobject(self.#ident, vm),
}
} else {
quote! {
#(#cfg_attrs)*
{ ::rustpython_vm::convert::ToPyObject::to_pyobject(self.#ident, vm) },
}
}
})
.collect();

let skipped_tuple_items: Vec<_> = skipped_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! {
::rustpython_vm::convert::ToPyObject::to_pyobject(self.#ident, vm),
}
} else {
quote! {
#(#cfg_attrs)*
{ ::rustpython_vm::convert::ToPyObject::to_pyobject(self.#ident, vm) },
}
}
})
.collect();

// Generate TryFromObject impl only when try_from_object=true
let try_from_object_impl = if try_from_object {
let n_required = visible_fields.len();
Expand Down Expand Up @@ -233,15 +316,17 @@ pub(crate) fn impl_pystruct_sequence_data(

// Generate try_from_elements trait override only when try_from_object=true
let try_from_elements_trait_override = if try_from_object {
let visible_field_idents: Vec<_> = visible_fields.iter().map(|f| &f.ident).collect();
let skipped_field_idents: Vec<_> = skipped_fields.iter().map(|f| &f.ident).collect();
quote! {
fn try_from_elements(
elements: Vec<::rustpython_vm::PyObjectRef>,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<Self> {
let mut iter = elements.into_iter();
Ok(Self {
#(#visible_fields: iter.next().unwrap().clone().try_into_value(vm)?,)*
#(#skipped_fields: match iter.next() {
#(#visible_field_idents: iter.next().unwrap().clone().try_into_value(vm)?,)*
#(#skipped_field_idents: match iter.next() {
Some(v) => v.clone().try_into_value(vm)?,
None => vm.ctx.none(),
},)*
Expand All @@ -259,20 +344,14 @@ pub(crate) fn impl_pystruct_sequence_data(

// PyStructSequenceData trait impl
impl ::rustpython_vm::types::PyStructSequenceData for #data_ident {
const REQUIRED_FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#named_fields),)*];
const OPTIONAL_FIELD_NAMES: &'static [&'static str] = &[#(stringify!(#skipped_fields),)*];
const REQUIRED_FIELD_NAMES: &'static [&'static str] = &[#(#named_field_names)*];
const OPTIONAL_FIELD_NAMES: &'static [&'static str] = &[#(#skipped_field_names)*];
const UNNAMED_FIELDS_LEN: usize = #n_unnamed_fields;

fn into_tuple(self, vm: &::rustpython_vm::VirtualMachine) -> ::rustpython_vm::builtins::PyTuple {
let items = vec![
#(::rustpython_vm::convert::ToPyObject::to_pyobject(
self.#visible_fields,
vm,
),)*
#(::rustpython_vm::convert::ToPyObject::to_pyobject(
self.#skipped_fields,
vm,
),)*
#(#visible_tuple_items)*
#(#skipped_tuple_items)*
];
::rustpython_vm::builtins::PyTuple::new_unchecked(items.into_boxed_slice())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/stdlib/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4657,7 +4657,7 @@ mod fileio {
closefd: AtomicCell::new(true),
mode: AtomicCell::new(Mode::empty()),
seekable: AtomicCell::new(None),
blksize: AtomicCell::new(0),
blksize: AtomicCell::new(8 * 1024), // DEFAULT_BUFFER_SIZE
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions crates/vm/src/stdlib/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,9 +894,11 @@ pub(super) mod _os {
#[pystruct_sequence(skip)]
pub st_ctime_ns: i128,
// Unix-specific attributes
#[cfg(not(windows))]
#[pyarg(any, default)]
#[pystruct_sequence(skip)]
pub st_blksize: i64,
#[cfg(not(windows))]
#[pyarg(any, default)]
#[pystruct_sequence(skip)]
pub st_blocks: i64,
Expand Down Expand Up @@ -952,11 +954,12 @@ pub(super) mod _os {
#[cfg(not(windows))]
let st_ino = stat.st_ino;

#[cfg(any(unix, target_os = "wasi"))]
#[cfg(not(windows))]
#[allow(clippy::useless_conversion)] // needed for 32-bit platforms
let (st_blksize, st_blocks) = (i64::from(stat.st_blksize), i64::from(stat.st_blocks));
#[cfg(windows)]
let (st_blksize, st_blocks) = (0i64, 0i64);
let st_blksize = i64::from(stat.st_blksize);
#[cfg(not(windows))]
#[allow(clippy::useless_conversion)] // needed for 32-bit platforms
let st_blocks = i64::from(stat.st_blocks);

Self {
st_mode: vm.ctx.new_pyref(stat.st_mode),
Expand All @@ -975,7 +978,9 @@ pub(super) mod _os {
st_atime_ns: to_ns(atime),
st_mtime_ns: to_ns(mtime),
st_ctime_ns: to_ns(ctime),
#[cfg(not(windows))]
st_blksize,
#[cfg(not(windows))]
st_blocks,
st_reparse_tag,
st_file_attributes,
Expand Down
Loading