Skip to content

Commit ede9a98

Browse files
committed
Fix BytesIO and add bytearray.resize
- Add bytearray.resize() method - BytesIO: support keyword arg for initial_bytes - BytesIO.seek: clamp negative positions to 0 - BytesIO.close: raise BufferError on active exports - BytesIO.getbuffer: check closed state - Set UnsupportedOperation.__module__ to "io"
1 parent fd88749 commit ede9a98

2 files changed

Lines changed: 50 additions & 8 deletions

File tree

crates/vm/src/builtins/bytearray.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,15 @@ impl PyByteArray {
538538
self.borrow_buf_mut().reverse();
539539
}
540540

541+
#[pymethod]
542+
fn resize(&self, size: isize, vm: &VirtualMachine) -> PyResult<()> {
543+
if size < 0 {
544+
return Err(vm.new_value_error("bytearray.resize(): new size must be >= 0".to_owned()));
545+
}
546+
self.try_resizable(vm)?.elements.resize(size as usize, 0);
547+
Ok(())
548+
}
549+
541550
// TODO: Uncomment when Python adds __class_getitem__ to bytearray
542551
// #[pyclassmethod]
543552
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {

crates/vm/src/stdlib/io.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4393,6 +4393,12 @@ mod _io {
43934393
}
43944394
}
43954395

4396+
#[derive(FromArgs)]
4397+
struct BytesIOArgs {
4398+
#[pyarg(any, optional)]
4399+
initial_bytes: OptionalArg<Option<ArgBytesLike>>,
4400+
}
4401+
43964402
#[pyattr]
43974403
#[pyclass(name = "BytesIO", base = _BufferedIOBase)]
43984404
#[derive(Debug)]
@@ -4417,15 +4423,17 @@ mod _io {
44174423
}
44184424

44194425
impl Initializer for BytesIO {
4420-
type Args = OptionalArg<Option<ArgBytesLike>>;
4426+
type Args = BytesIOArgs;
44214427

4422-
fn init(zelf: PyRef<Self>, object: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
4428+
fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
44234429
if zelf.exports.load() > 0 {
44244430
return Err(vm.new_buffer_error(
44254431
"Existing exports of data: object cannot be re-sized".to_owned(),
44264432
));
44274433
}
4428-
let raw_bytes = object
4434+
4435+
let raw_bytes = args
4436+
.initial_bytes
44294437
.flatten()
44304438
.map_or_else(Vec::new, |input| input.borrow_buf().to_vec());
44314439
*zelf.buffer.write() = BufferedIO::new(Cursor::new(raw_bytes));
@@ -4503,9 +4511,20 @@ mod _io {
45034511
how: OptionalArg<i32>,
45044512
vm: &VirtualMachine,
45054513
) -> PyResult<u64> {
4506-
self.buffer(vm)?
4507-
.seek(seekfrom(vm, offset, how)?)
4508-
.map_err(|err| os_err(vm, err))
4514+
let seek_from = seekfrom(vm, offset, how)?;
4515+
let mut buffer = self.buffer(vm)?;
4516+
4517+
// Handle negative positions by clamping to 0
4518+
match seek_from {
4519+
SeekFrom::Current(offset) if offset < 0 => {
4520+
let current = buffer.tell();
4521+
let new_pos = current.saturating_add_signed(offset);
4522+
buffer
4523+
.seek(SeekFrom::Start(new_pos))
4524+
.map_err(|err| os_err(vm, err))
4525+
}
4526+
_ => buffer.seek(seek_from).map_err(|err| os_err(vm, err)),
4527+
}
45094528
}
45104529

45114530
#[pymethod]
@@ -4534,8 +4553,14 @@ mod _io {
45344553
}
45354554

45364555
#[pymethod]
4537-
fn close(&self) {
4556+
fn close(&self, vm: &VirtualMachine) -> PyResult<()> {
4557+
if self.exports.load() > 0 {
4558+
return Err(vm.new_buffer_error(
4559+
"Existing exports of data: object cannot be closed".to_owned(),
4560+
));
4561+
}
45384562
self.closed.store(true);
4563+
Ok(())
45394564
}
45404565

45414566
#[pymethod]
@@ -4602,6 +4627,9 @@ mod _io {
46024627
impl PyRef<BytesIO> {
46034628
#[pymethod]
46044629
fn getbuffer(self, vm: &VirtualMachine) -> PyResult<PyMemoryView> {
4630+
if self.closed.load() {
4631+
return Err(vm.new_value_error("I/O operation on closed file.".to_owned()));
4632+
}
46054633
let len = self.buffer.read().cursor.get_ref().len();
46064634
let buffer = PyBuffer::new(
46074635
self.into(),
@@ -4931,14 +4959,19 @@ mod _io {
49314959
}
49324960

49334961
fn create_unsupported_operation(ctx: &Context) -> PyTypeRef {
4962+
use crate::builtins::type_::PyAttributes;
49344963
use crate::types::PyTypeSlots;
4964+
4965+
let mut attrs = PyAttributes::default();
4966+
attrs.insert(identifier!(ctx, __module__), ctx.new_str("io").into());
4967+
49354968
PyType::new_heap(
49364969
"UnsupportedOperation",
49374970
vec![
49384971
ctx.exceptions.os_error.to_owned(),
49394972
ctx.exceptions.value_error.to_owned(),
49404973
],
4941-
Default::default(),
4974+
attrs,
49424975
PyTypeSlots::heap_default(),
49434976
ctx.types.type_type.to_owned(),
49444977
ctx,

0 commit comments

Comments
 (0)