mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
[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:
parent
74dde92903
commit
fade04e0f4
3 changed files with 19 additions and 6 deletions
18
Lib/_pyio.py
18
Lib/_pyio.py
|
@ -407,6 +407,9 @@ class IOBase(metaclass=abc.ABCMeta):
|
||||||
if closed:
|
if closed:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if dealloc_warn := getattr(self, "_dealloc_warn", None):
|
||||||
|
dealloc_warn(self)
|
||||||
|
|
||||||
# If close() fails, the caller logs the exception with
|
# If close() fails, the caller logs the exception with
|
||||||
# sys.unraisablehook. close() must be called at the end at __del__().
|
# sys.unraisablehook. close() must be called at the end at __del__().
|
||||||
self.close()
|
self.close()
|
||||||
|
@ -853,6 +856,10 @@ class _BufferedIOMixin(BufferedIOBase):
|
||||||
else:
|
else:
|
||||||
return "<{}.{} name={!r}>".format(modname, clsname, name)
|
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 ###
|
### Lower-level APIs ###
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
|
@ -1601,12 +1608,11 @@ class FileIO(RawIOBase):
|
||||||
raise
|
raise
|
||||||
self._fd = fd
|
self._fd = fd
|
||||||
|
|
||||||
def __del__(self):
|
def _dealloc_warn(self, source):
|
||||||
if self._fd >= 0 and self._closefd and not self.closed:
|
if self._fd >= 0 and self._closefd and not self.closed:
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn('unclosed file %r' % (self,), ResourceWarning,
|
warnings.warn(f'unclosed file {source!r}', ResourceWarning,
|
||||||
stacklevel=2, source=self)
|
stacklevel=2, source=self)
|
||||||
self.close()
|
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
raise TypeError(f"cannot pickle {self.__class__.__name__!r} object")
|
raise TypeError(f"cannot pickle {self.__class__.__name__!r} object")
|
||||||
|
@ -1781,7 +1787,7 @@ class FileIO(RawIOBase):
|
||||||
if not self.closed:
|
if not self.closed:
|
||||||
self._stat_atopen = None
|
self._stat_atopen = None
|
||||||
try:
|
try:
|
||||||
if self._closefd:
|
if self._closefd and self._fd >= 0:
|
||||||
os.close(self._fd)
|
os.close(self._fd)
|
||||||
finally:
|
finally:
|
||||||
super().close()
|
super().close()
|
||||||
|
@ -2690,6 +2696,10 @@ class TextIOWrapper(TextIOBase):
|
||||||
def newlines(self):
|
def newlines(self):
|
||||||
return self._decoder.newlines if self._decoder else None
|
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):
|
class StringIO(TextIOWrapper):
|
||||||
"""Text I/O implementation using an in-memory buffer.
|
"""Text I/O implementation using an in-memory buffer.
|
||||||
|
|
|
@ -4417,7 +4417,7 @@ class MiscIOTest(unittest.TestCase):
|
||||||
self._check_abc_inheritance(io)
|
self._check_abc_inheritance(io)
|
||||||
|
|
||||||
def _check_warn_on_dealloc(self, *args, **kwargs):
|
def _check_warn_on_dealloc(self, *args, **kwargs):
|
||||||
f = open(*args, **kwargs)
|
f = self.open(*args, **kwargs)
|
||||||
r = repr(f)
|
r = repr(f)
|
||||||
with self.assertWarns(ResourceWarning) as cm:
|
with self.assertWarns(ResourceWarning) as cm:
|
||||||
f = None
|
f = None
|
||||||
|
@ -4446,7 +4446,7 @@ class MiscIOTest(unittest.TestCase):
|
||||||
r, w = os.pipe()
|
r, w = os.pipe()
|
||||||
fds += r, w
|
fds += r, w
|
||||||
with warnings_helper.check_no_resource_warning(self):
|
with warnings_helper.check_no_resource_warning(self):
|
||||||
open(r, *args, closefd=False, **kwargs)
|
self.open(r, *args, closefd=False, **kwargs)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
|
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
|
||||||
def test_warn_on_dealloc_fd(self):
|
def test_warn_on_dealloc_fd(self):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Emit :exc:`RuntimeWarning` in the Python implementation of :mod:`io` when
|
||||||
|
the :term:`file-like object <file object>` is not closed explicitly in the
|
||||||
|
presence of multiple I/O layers.
|
Loading…
Add table
Add a link
Reference in a new issue