diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2010-10-29 10:38:18 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2010-10-29 10:38:18 (GMT) |
commit | e033e06db077d5abcb4bc3729d03f8a4a09b2486 (patch) | |
tree | 04445ffa669d4d0df240d680249c7d7a7f661bd4 /Lib | |
parent | 9cbdd75ec5deda8f55edd7caab42dff65d009da2 (diff) | |
download | cpython-e033e06db077d5abcb4bc3729d03f8a4a09b2486.zip cpython-e033e06db077d5abcb4bc3729d03f8a4a09b2486.tar.gz cpython-e033e06db077d5abcb4bc3729d03f8a4a09b2486.tar.bz2 |
Issue #10093: ResourceWarnings are now issued when files and sockets are
deallocated without explicit closing. These warnings are silenced by
default, except in pydebug mode.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/socket.py | 2 | ||||
-rw-r--r-- | Lib/test/test_io.py | 41 | ||||
-rw-r--r-- | Lib/test/test_socket.py | 17 | ||||
-rw-r--r-- | Lib/xml/etree/ElementTree.py | 33 |
4 files changed, 81 insertions, 12 deletions
diff --git a/Lib/socket.py b/Lib/socket.py index 6af1964..2dc9736 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -108,7 +108,7 @@ class socket(_socket.socket): if s.startswith("<socket object"): s = "<%s.%s%s%s" % (self.__class__.__module__, self.__class__.__name__, - (self._closed and " [closed] ") or "", + getattr(self, '_closed', False) and " [closed] " or "", s[7:]) return s diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 8784e34..7ce9753 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -29,6 +29,7 @@ import weakref import abc import signal import errno +import warnings from itertools import cycle, count from collections import deque from test import support @@ -2525,6 +2526,46 @@ class MiscIOTest(unittest.TestCase): # baseline "io" module. self._check_abc_inheritance(io) + def _check_warn_on_dealloc(self, *args, **kwargs): + f = open(*args, **kwargs) + r = repr(f) + with self.assertWarns(ResourceWarning) as cm: + f = None + support.gc_collect() + self.assertIn(r, str(cm.warning.args[0])) + + def test_warn_on_dealloc(self): + self._check_warn_on_dealloc(support.TESTFN, "wb", buffering=0) + self._check_warn_on_dealloc(support.TESTFN, "wb") + self._check_warn_on_dealloc(support.TESTFN, "w") + + def _check_warn_on_dealloc_fd(self, *args, **kwargs): + fds = [] + try: + r, w = os.pipe() + fds += r, w + self._check_warn_on_dealloc(r, *args, **kwargs) + # When using closefd=False, there's no warning + r, w = os.pipe() + fds += r, w + with warnings.catch_warnings(record=True) as recorded: + open(r, *args, closefd=False, **kwargs) + support.gc_collect() + self.assertEqual(recorded, []) + finally: + for fd in fds: + try: + os.close(fd) + except EnvironmentError as e: + if e.errno != errno.EBADF: + raise + + def test_warn_on_dealloc_fd(self): + self._check_warn_on_dealloc_fd("rb", buffering=0) + self._check_warn_on_dealloc_fd("rb") + self._check_warn_on_dealloc_fd("r") + + class CMiscIOTest(MiscIOTest): io = io diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 699efc0..e20364d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -706,6 +706,23 @@ class GeneralModuleTests(unittest.TestCase): def test_sendall_interrupted_with_timeout(self): self.check_sendall_interrupted(True) + def test_dealloc_warn(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + r = repr(sock) + with self.assertWarns(ResourceWarning) as cm: + sock = None + support.gc_collect() + self.assertIn(r, str(cm.warning.args[0])) + # An open socket file object gets dereferenced after the socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + f = sock.makefile('rb') + r = repr(sock) + sock = None + support.gc_collect() + with self.assertWarns(ResourceWarning): + f = None + support.gc_collect() + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index ecc8ea7..9f5717e 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -662,17 +662,23 @@ class ElementTree: # @exception ParseError If the parser fails to parse the document. def parse(self, source, parser=None): + close_source = False if not hasattr(source, "read"): source = open(source, "rb") - if not parser: - parser = XMLParser(target=TreeBuilder()) - while 1: - data = source.read(65536) - if not data: - break - parser.feed(data) - self._root = parser.close() - return self._root + close_source = True + try: + if not parser: + parser = XMLParser(target=TreeBuilder()) + while 1: + data = source.read(65536) + if not data: + break + parser.feed(data) + self._root = parser.close() + return self._root + finally: + if close_source: + source.close() ## # Creates a tree iterator for the root element. The iterator loops @@ -1226,16 +1232,19 @@ def parse(source, parser=None): # @return A (event, elem) iterator. def iterparse(source, events=None, parser=None): + close_source = False if not hasattr(source, "read"): source = open(source, "rb") + close_source = True if not parser: parser = XMLParser(target=TreeBuilder()) - return _IterParseIterator(source, events, parser) + return _IterParseIterator(source, events, parser, close_source) class _IterParseIterator: - def __init__(self, source, events, parser): + def __init__(self, source, events, parser, close_source=False): self._file = source + self._close_file = close_source self._events = [] self._index = 0 self.root = self._root = None @@ -1282,6 +1291,8 @@ class _IterParseIterator: except IndexError: if self._parser is None: self.root = self._root + if self._close_file: + self._file.close() raise StopIteration # load event buffer del self._events[:] |