Skip to content

Commit cd3b196

Browse files
committed
pythongh-133982: Run unclosed file test on all io implementations
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.
1 parent 009e7b3 commit cd3b196

File tree

3 files changed

+22
-4
lines changed

3 files changed

+22
-4
lines changed

Lib/_pyio.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ def __del__(self):
407407
if closed:
408408
return
409409

410+
if dealloc_warn := getattr(self, "_dealloc_warn", None):
411+
dealloc_warn(self)
412+
410413
# If close() fails, the caller logs the exception with
411414
# sys.unraisablehook. close() must be called at the end at __del__().
412415
self.close()
@@ -853,6 +856,10 @@ def __repr__(self):
853856
else:
854857
return "<{}.{} name={!r}>".format(modname, clsname, name)
855858

859+
def _dealloc_warn(self, source):
860+
if dealloc_warn := getattr(self.raw, "_dealloc_warn", None):
861+
dealloc_warn(source)
862+
856863
### Lower-level APIs ###
857864

858865
def fileno(self):
@@ -1600,11 +1607,15 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
16001607
raise
16011608
self._fd = fd
16021609

1603-
def __del__(self):
1610+
def _dealloc_warn(self, source):
16041611
if self._fd >= 0 and self._closefd and not self.closed:
16051612
import warnings
1606-
warnings.warn('unclosed file %r' % (self,), ResourceWarning,
1613+
warnings.warn(f'unclosed file {source!r}', ResourceWarning,
16071614
stacklevel=2, source=self)
1615+
1616+
def __del__(self):
1617+
if self._fd >= 0 and self._closefd and not self.closed:
1618+
self._dealloc_warn(self)
16081619
self.close()
16091620

16101621
def __getstate__(self):
@@ -2689,6 +2700,10 @@ def readline(self, size=None):
26892700
def newlines(self):
26902701
return self._decoder.newlines if self._decoder else None
26912702

2703+
def _dealloc_warn(self, source):
2704+
if dealloc_warn := getattr(self.buffer, "_dealloc_warn", None):
2705+
dealloc_warn(source)
2706+
26922707

26932708
class StringIO(TextIOWrapper):
26942709
"""Text I/O implementation using an in-memory buffer.

Lib/test/test_io.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4417,7 +4417,7 @@ def test_abc_inheritance_official(self):
44174417
self._check_abc_inheritance(io)
44184418

44194419
def _check_warn_on_dealloc(self, *args, **kwargs):
4420-
f = open(*args, **kwargs)
4420+
f = self.open(*args, **kwargs)
44214421
r = repr(f)
44224422
with self.assertWarns(ResourceWarning) as cm:
44234423
f = None
@@ -4446,7 +4446,7 @@ def cleanup_fds():
44464446
r, w = os.pipe()
44474447
fds += r, w
44484448
with warnings_helper.check_no_resource_warning(self):
4449-
open(r, *args, closefd=False, **kwargs)
4449+
self.open(r, *args, closefd=False, **kwargs)
44504450

44514451
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
44524452
def test_warn_on_dealloc_fd(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Emit :exc:`RuntimeWarning` in the Python implementation of :mod:`io` when
2+
the :term:`file-like object <file object>` is not closed explicitly in the
3+
presence of multiple I/O layers.

0 commit comments

Comments
 (0)