diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 19027370dcd..0142208427b 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -780,6 +780,7 @@ def test_closefd_attr(self): file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False) self.assertEqual(file.buffer.raw.closefd, False) + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; cyclic GC not supported, causes file locking') @unittest.expectedFailure # TODO: RUSTPYTHON def test_garbage_collection(self): # FileIO objects are collected, and collecting them flushes @@ -1795,6 +1796,7 @@ def test_misbehaved_io_read(self): # checking this is not so easy. self.assertRaises(OSError, bufio.read, 10) + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; cyclic GC not supported, causes file locking') @unittest.expectedFailure # TODO: RUSTPYTHON def test_garbage_collection(self): # C BufferedReader objects are collected. @@ -2166,6 +2168,7 @@ def test_initialization(self): self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) self.assertRaises(ValueError, bufio.write, b"def") + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; cyclic GC not supported, causes file locking') @unittest.expectedFailure # TODO: RUSTPYTHON def test_garbage_collection(self): # C BufferedWriter objects are collected, and collecting them flushes @@ -2677,6 +2680,7 @@ def test_interleaved_readline_write(self): class CBufferedRandomTest(BufferedRandomTest, SizeofTest): tp = io.BufferedRandom + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; cyclic GC not supported, causes file locking') @unittest.expectedFailure # TODO: RUSTPYTHON def test_garbage_collection(self): CBufferedReaderTest.test_garbage_collection(self) @@ -4122,6 +4126,7 @@ def test_initialization(self): t = self.TextIOWrapper.__new__(self.TextIOWrapper) self.assertRaises(Exception, repr, t) + @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; cyclic GC not supported, causes file locking') @unittest.expectedFailure # TODO: RUSTPYTHON def test_garbage_collection(self): # C TextIOWrapper objects are collected, and collecting them flushes @@ -4271,10 +4276,6 @@ def test_reconfigure_write_through(self): def test_repr(self): return super().test_repr() - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_telling(self): - return super().test_telling() - @unittest.expectedFailure # TODO: RUSTPYTHON def test_uninitialized(self): return super().test_uninitialized() diff --git a/crates/vm/src/stdlib/io.rs b/crates/vm/src/stdlib/io.rs index 7cf21cc77e4..c373f88d20b 100644 --- a/crates/vm/src/stdlib/io.rs +++ b/crates/vm/src/stdlib/io.rs @@ -1753,11 +1753,23 @@ mod _io { } #[pyclass( - with(Constructor, BufferedMixin, BufferedReadable), + with(Constructor, BufferedMixin, BufferedReadable, Destructor), flags(BASETYPE, HAS_DICT) )] impl BufferedReader {} + impl Destructor for BufferedReader { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } + impl DefaultConstructor for BufferedReader {} #[pyclass] @@ -1810,11 +1822,23 @@ mod _io { } #[pyclass( - with(Constructor, BufferedMixin, BufferedWritable), + with(Constructor, BufferedMixin, BufferedWritable, Destructor), flags(BASETYPE, HAS_DICT) )] impl BufferedWriter {} + impl Destructor for BufferedWriter { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } + impl DefaultConstructor for BufferedWriter {} #[pyattr] @@ -1852,11 +1876,29 @@ mod _io { } #[pyclass( - with(Constructor, BufferedMixin, BufferedReadable, BufferedWritable), + with( + Constructor, + BufferedMixin, + BufferedReadable, + BufferedWritable, + Destructor + ), flags(BASETYPE, HAS_DICT) )] impl BufferedRandom {} + impl Destructor for BufferedRandom { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } + impl DefaultConstructor for BufferedRandom {} #[pyattr] @@ -1900,7 +1942,13 @@ mod _io { } #[pyclass( - with(Constructor, Initializer, BufferedReadable, BufferedWritable), + with( + Constructor, + Initializer, + BufferedReadable, + BufferedWritable, + Destructor + ), flags(BASETYPE, HAS_DICT) )] impl BufferedRWPair { @@ -1942,6 +1990,18 @@ mod _io { } } + impl Destructor for BufferedRWPair { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } + #[derive(FromArgs)] struct TextIOWrapperArgs { #[pyarg(any, default)] @@ -2413,7 +2473,10 @@ mod _io { vm.call_method(&textio.buffer, "flush", ()) } - #[pyclass(with(Constructor, Initializer), flags(BASETYPE))] + #[pyclass( + with(Constructor, Initializer, Destructor, Iterable, IterNext), + flags(BASETYPE) + )] impl TextIOWrapper { #[pymethod] fn reconfigure(&self, args: TextIOWrapperArgs, vm: &VirtualMachine) -> PyResult<()> { @@ -3276,6 +3339,57 @@ mod _io { } } + impl Destructor for TextIOWrapper { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } + + impl Iterable for TextIOWrapper { + fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + check_closed(&zelf, vm)?; + Ok(zelf) + } + + fn iter(_zelf: PyRef, _vm: &VirtualMachine) -> PyResult { + unreachable!("slot_iter is implemented") + } + } + + impl IterNext for TextIOWrapper { + fn slot_iternext(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { + // Set telling = false during iteration (matches CPython behavior) + let textio_ref: PyRef = + zelf.downcast_ref::().unwrap().to_owned(); + { + let mut textio = textio_ref.lock(vm)?; + textio.telling = false; + } + + let line = vm.call_method(zelf, "readline", ())?; + + if !line.clone().try_to_bool(vm)? { + // Restore telling on StopIteration + let mut textio = textio_ref.lock(vm)?; + textio.snapshot = None; + textio.telling = textio.seekable; + Ok(PyIterReturn::StopIteration(None)) + } else { + Ok(PyIterReturn::Return(line)) + } + } + + fn next(_zelf: &Py, _vm: &VirtualMachine) -> PyResult { + unreachable!("slot_iternext is implemented") + } + } + #[pyattr] #[pyclass(name)] #[derive(Debug, PyPayload, Default)] @@ -4166,14 +4280,15 @@ mod _io { mod fileio { use super::{_io::*, Offset}; use crate::{ - AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, + AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, + VirtualMachine, builtins::{PyBaseExceptionRef, PyUtf8Str, PyUtf8StrRef}, common::crt_fd, convert::{IntoPyException, ToPyException}, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, ospath::{IOErrorBuilder, OsPath, OsPathOrFd}, stdlib::os, - types::{Constructor, DefaultConstructor, Initializer, Representable}, + types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable}, }; use crossbeam_utils::atomic::AtomicCell; use std::io::{Read, Write}; @@ -4433,7 +4548,7 @@ mod fileio { } #[pyclass( - with(Constructor, Initializer, Representable), + with(Constructor, Initializer, Representable, Destructor), flags(BASETYPE, HAS_DICT) )] impl FileIO { @@ -4649,4 +4764,16 @@ mod fileio { Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name()))) } } + + impl Destructor for FileIO { + fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { + let _ = vm.call_method(zelf, "close", ()); + Ok(()) + } + + #[cold] + fn del(_zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + unreachable!("slot_del is implemented") + } + } }