[Python-checkins] cpython (3.4): Issue #23243, asyncio: Emit a ResourceWarning when an event loop or a transport

victor.stinner python-checkins at python.org
Thu Jan 29 17:54:06 CET 2015


https://hg.python.org/cpython/rev/543f770f62f0
changeset: 94377:543f770f62f0
branch: 3.4
parent: 94375:9a4af12dcc9d
user: Victor Stinner <victor.stinner at gmail.com>
date: Thu Jan 29 17:50:58 2015 +0100
summary:
 Issue #23243, asyncio: Emit a ResourceWarning when an event loop or a transport
is not explicitly closed. Close also explicitly transports in test_sslproto.
files:
 Lib/asyncio/base_events.py | 11 +++++
 Lib/asyncio/base_subprocess.py | 19 +++++++++-
 Lib/asyncio/futures.py | 6 +-
 Lib/asyncio/proactor_events.py | 11 +++++
 Lib/asyncio/selector_events.py | 16 ++++++++
 Lib/asyncio/sslproto.py | 13 ++++++
 Lib/asyncio/unix_events.py | 19 ++++++++++
 Lib/asyncio/windows_utils.py | 6 ++-
 Lib/test/test_asyncio/test_proactor_events.py | 6 ++-
 Lib/test/test_asyncio/test_sslproto.py | 7 +--
 10 files changed, 104 insertions(+), 10 deletions(-)
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -26,6 +26,7 @@
 import time
 import traceback
 import sys
+import warnings
 
 from . import coroutines
 from . import events
@@ -333,6 +334,16 @@
 """Returns True if the event loop was closed."""
 return self._closed
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if not self.is_closed():
+ warnings.warn("unclosed event loop %r" % self, ResourceWarning)
+ if not self.is_running():
+ self.close()
+
 def is_running(self):
 """Returns True if the event loop is running."""
 return (self._owner is not None)
diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py
--- a/Lib/asyncio/base_subprocess.py
+++ b/Lib/asyncio/base_subprocess.py
@@ -1,5 +1,7 @@
 import collections
 import subprocess
+import sys
+import warnings
 
 from . import protocols
 from . import transports
@@ -13,6 +15,7 @@
 stdin, stdout, stderr, bufsize,
 extra=None, **kwargs):
 super().__init__(extra)
+ self._closed = False
 self._protocol = protocol
 self._loop = loop
 self._pid = None
@@ -40,7 +43,10 @@
 program, self._pid)
 
 def __repr__(self):
- info = [self.__class__.__name__, 'pid=%s' % self._pid]
+ info = [self.__class__.__name__]
+ if self._closed:
+ info.append('closed')
+ info.append('pid=%s' % self._pid)
 if self._returncode is not None:
 info.append('returncode=%s' % self._returncode)
 
@@ -70,6 +76,7 @@
 raise NotImplementedError
 
 def close(self):
+ self._closed = True
 for proto in self._pipes.values():
 if proto is None:
 continue
@@ -77,6 +84,15 @@
 if self._returncode is None:
 self.terminate()
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if not self._closed:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self.close()
+
 def get_pid(self):
 return self._pid
 
@@ -104,6 +120,7 @@
 Function called when an exception is raised during the creation
 of a subprocess.
 """
+ self._closed = True
 if self._loop.get_debug():
 logger.warning('Exception during subprocess creation, '
 'kill the subprocess %r',
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -195,9 +195,9 @@
 info = self._repr_info()
 return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
 
- # On Python 3.3 or older, objects with a destructor part of a reference
- # cycle are never destroyed. It's not more the case on Python 3.4 thanks to
- # the PEP 442.
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
 if _PY34:
 def __del__(self):
 if not self._log_traceback:
diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
--- a/Lib/asyncio/proactor_events.py
+++ b/Lib/asyncio/proactor_events.py
@@ -7,6 +7,8 @@
 __all__ = ['BaseProactorEventLoop']
 
 import socket
+import sys
+import warnings
 
 from . import base_events
 from . import constants
@@ -74,6 +76,15 @@
 self._read_fut.cancel()
 self._read_fut = None
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if self._sock is not None:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self.close()
+
 def _fatal_error(self, exc, message='Fatal error on pipe transport'):
 if isinstance(exc, (BrokenPipeError, ConnectionResetError)):
 if self._loop.get_debug():
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -10,6 +10,8 @@
 import errno
 import functools
 import socket
+import sys
+import warnings
 try:
 import ssl
 except ImportError: # pragma: no cover
@@ -499,6 +501,11 @@
 
 _buffer_factory = bytearray # Constructs initial value for self._buffer.
 
+ # Attribute used in the destructor: it must be set even if the constructor
+ # is not called (see _SelectorSslTransport which may start by raising an
+ # exception)
+ _sock = None
+
 def __init__(self, loop, sock, protocol, extra=None, server=None):
 super().__init__(extra, loop)
 self._extra['socket'] = sock
@@ -559,6 +566,15 @@
 self._conn_lost += 1
 self._loop.call_soon(self._call_connection_lost, None)
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if self._sock is not None:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self._sock.close()
+
 def _fatal_error(self, exc, message='Fatal error on transport'):
 # Should be called from exception handler only.
 if isinstance(exc, (BrokenPipeError,
diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py
--- a/Lib/asyncio/sslproto.py
+++ b/Lib/asyncio/sslproto.py
@@ -1,4 +1,6 @@
 import collections
+import sys
+import warnings
 try:
 import ssl
 except ImportError: # pragma: no cover
@@ -295,6 +297,7 @@
 self._loop = loop
 self._ssl_protocol = ssl_protocol
 self._app_protocol = app_protocol
+ self._closed = False
 
 def get_extra_info(self, name, default=None):
 """Get optional transport information."""
@@ -308,8 +311,18 @@
 protocol's connection_lost() method will (eventually) called
 with None as its argument.
 """
+ self._closed = True
 self._ssl_protocol._start_shutdown()
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if not self._closed:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self.close()
+
 def pause_reading(self):
 """Pause the receiving end.
 
diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py
--- a/Lib/asyncio/unix_events.py
+++ b/Lib/asyncio/unix_events.py
@@ -8,6 +8,7 @@
 import subprocess
 import sys
 import threading
+import warnings
 
 
 from . import base_events
@@ -353,6 +354,15 @@
 if not self._closing:
 self._close(None)
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if self._pipe is not None:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self._pipe.close()
+
 def _fatal_error(self, exc, message='Fatal error on pipe transport'):
 # should be called by exception handler only
 if (isinstance(exc, OSError) and exc.errno == errno.EIO):
@@ -529,6 +539,15 @@
 # write_eof is all what we needed to close the write pipe
 self.write_eof()
 
+ # On Python 3.3 and older, objects with a destructor part of a reference
+ # cycle are never destroyed. It's not more the case on Python 3.4 thanks
+ # to the PEP 442.
+ if sys.version_info >= (3, 4):
+ def __del__(self):
+ if self._pipe is not None:
+ warnings.warn("unclosed transport %r" % self, ResourceWarning)
+ self._pipe.close()
+
 def abort(self):
 self._close(None)
 
diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py
--- a/Lib/asyncio/windows_utils.py
+++ b/Lib/asyncio/windows_utils.py
@@ -14,6 +14,7 @@
 import socket
 import subprocess
 import tempfile
+import warnings
 
 
 __all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
@@ -156,7 +157,10 @@
 CloseHandle(self._handle)
 self._handle = None
 
- __del__ = close
+ def __del__(self):
+ if self._handle is not None:
+ warnings.warn("unclosed %r" % self, ResourceWarning)
+ self.close()
 
 def __enter__(self):
 return self
diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py
--- a/Lib/test/test_asyncio/test_proactor_events.py
+++ b/Lib/test/test_asyncio/test_proactor_events.py
@@ -499,8 +499,12 @@
 self.proactor.accept.assert_called_with(self.sock)
 
 def test_socketpair(self):
+ class EventLoop(BaseProactorEventLoop):
+ # override the destructor to not log a ResourceWarning
+ def __del__(self):
+ pass
 self.assertRaises(
- NotImplementedError, BaseProactorEventLoop, self.proactor)
+ NotImplementedError, EventLoop, self.proactor)
 
 def test_make_socket_transport(self):
 tr = self.loop._make_socket_transport(self.sock, asyncio.Protocol())
diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -22,7 +22,9 @@
 def ssl_protocol(self, waiter=None):
 sslcontext = test_utils.dummy_ssl_context()
 app_proto = asyncio.Protocol()
- return sslproto.SSLProtocol(self.loop, app_proto, sslcontext, waiter)
+ proto = sslproto.SSLProtocol(self.loop, app_proto, sslcontext, waiter)
+ self.addCleanup(proto._app_transport.close)
+ return proto
 
 def connection_made(self, ssl_proto, do_handshake=None):
 transport = mock.Mock()
@@ -56,9 +58,6 @@
 with test_utils.disable_logger():
 self.loop.run_until_complete(handshake_fut)
 
- # Close the transport
- ssl_proto._app_transport.close()
-
 def test_eof_received_waiter(self):
 waiter = asyncio.Future(loop=self.loop)
 ssl_proto = self.ssl_protocol(waiter)
-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /