[3.14] gh-133982: Run unclosed file test on all io implementations (gh-134165) (gh-134433)

Update `test_io` `_check_warn_on_dealloc` to use `self.` to dispatch to
different I/O implementations.

Update the `_pyio` implementation to match expected behavior, using the
same `_dealloc_warn` design as the C implementation uses to report the
topmost `__del__` object.

The FileIO one now matches all the others, so can use IOBase. There was
a missing check on closing (self._fd must be valid), add that check
(cherry picked from commit 5b0e827521)

Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
This commit is contained in:
Miss Islington (bot) 2025-05-21 17:22:44 +02:00 committed by GitHub
parent 74dde92903
commit fade04e0f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 19 additions and 6 deletions

View file

@ -407,6 +407,9 @@ class IOBase(metaclass=abc.ABCMeta):
if closed:
return
if dealloc_warn := getattr(self, "_dealloc_warn", None):
dealloc_warn(self)
# If close() fails, the caller logs the exception with
# sys.unraisablehook. close() must be called at the end at __del__().
self.close()
@ -853,6 +856,10 @@ class _BufferedIOMixin(BufferedIOBase):
else:
return "<{}.{} name={!r}>".format(modname, clsname, name)
def _dealloc_warn(self, source):
if dealloc_warn := getattr(self.raw, "_dealloc_warn", None):
dealloc_warn(source)
### Lower-level APIs ###
def fileno(self):
@ -1601,12 +1608,11 @@ class FileIO(RawIOBase):
raise
self._fd = fd
def __del__(self):
def _dealloc_warn(self, source):
if self._fd >= 0 and self._closefd and not self.closed:
import warnings
warnings.warn('unclosed file %r' % (self,), ResourceWarning,
warnings.warn(f'unclosed file {source!r}', ResourceWarning,
stacklevel=2, source=self)
self.close()
def __getstate__(self):
raise TypeError(f"cannot pickle {self.__class__.__name__!r} object")
@ -1781,7 +1787,7 @@ class FileIO(RawIOBase):
if not self.closed:
self._stat_atopen = None
try:
if self._closefd:
if self._closefd and self._fd >= 0:
os.close(self._fd)
finally:
super().close()
@ -2690,6 +2696,10 @@ class TextIOWrapper(TextIOBase):
def newlines(self):
return self._decoder.newlines if self._decoder else None
def _dealloc_warn(self, source):
if dealloc_warn := getattr(self.buffer, "_dealloc_warn", None):
dealloc_warn(source)
class StringIO(TextIOWrapper):
"""Text I/O implementation using an in-memory buffer.