summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-01-29 17:32:39 +0100
committerVictor Stinner <victor.stinner@gmail.com>2015-01-29 17:32:39 +0100
commit190e78603a4ce58a2f248fdf8a3472fa1fc6c064 (patch)
tree0990f66b83b12d6466ba5042bfbb0b02442ab5e7
parenta1611f147decd003b0ffe5d10eb011d0b8c2725e (diff)
downloadtrollius-master.tar.gz
Python issue #23243: On Python 3.4 and newer, emit a ResourceWarning when anHEADmaster
event loop or a transport is not explicitly closed
-rw-r--r--asyncio/base_events.py11
-rw-r--r--asyncio/base_subprocess.py19
-rw-r--r--asyncio/futures.py6
-rw-r--r--asyncio/proactor_events.py11
-rw-r--r--asyncio/selector_events.py16
-rw-r--r--asyncio/sslproto.py13
-rw-r--r--asyncio/unix_events.py19
-rw-r--r--asyncio/windows_utils.py6
-rw-r--r--tests/test_proactor_events.py6
9 files changed, 101 insertions, 6 deletions
diff --git a/asyncio/base_events.py b/asyncio/base_events.py
index e40d3ad..7108f25 100644
--- a/asyncio/base_events.py
+++ b/asyncio/base_events.py
@@ -26,6 +26,7 @@ import threading
import time
import traceback
import sys
+import warnings
from . import coroutines
from . import events
@@ -333,6 +334,16 @@ class BaseEventLoop(events.AbstractEventLoop):
"""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/asyncio/base_subprocess.py b/asyncio/base_subprocess.py
index 81c6f1a..651a9a2 100644
--- a/asyncio/base_subprocess.py
+++ b/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 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
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 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
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 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
raise NotImplementedError
def close(self):
+ self._closed = True
for proto in self._pipes.values():
if proto is None:
continue
@@ -77,6 +84,15 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
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 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
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/asyncio/futures.py b/asyncio/futures.py
index 19212a9..2c741fd 100644
--- a/asyncio/futures.py
+++ b/asyncio/futures.py
@@ -195,9 +195,9 @@ class Future:
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/asyncio/proactor_events.py b/asyncio/proactor_events.py
index 0f533a5..65de926 100644
--- a/asyncio/proactor_events.py
+++ b/asyncio/proactor_events.py
@@ -7,6 +7,8 @@ proactor is only implemented on Windows with IOCP.
__all__ = ['BaseProactorEventLoop']
import socket
+import sys
+import warnings
from . import base_events
from . import constants
@@ -74,6 +76,15 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin,
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/asyncio/selector_events.py b/asyncio/selector_events.py
index 9147832..4bd6dc8 100644
--- a/asyncio/selector_events.py
+++ b/asyncio/selector_events.py
@@ -10,6 +10,8 @@ import collections
import errno
import functools
import socket
+import sys
+import warnings
try:
import ssl
except ImportError: # pragma: no cover
@@ -499,6 +501,11 @@ class _SelectorTransport(transports._FlowControlMixin,
_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 @@ class _SelectorTransport(transports._FlowControlMixin,
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/asyncio/sslproto.py b/asyncio/sslproto.py
index fc809b9..235855e 100644
--- a/asyncio/sslproto.py
+++ b/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 @@ class _SSLProtocolTransport(transports._FlowControlMixin,
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 @@ class _SSLProtocolTransport(transports._FlowControlMixin,
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/asyncio/unix_events.py b/asyncio/unix_events.py
index 7e1265a..b06f1b2 100644
--- a/asyncio/unix_events.py
+++ b/asyncio/unix_events.py
@@ -8,6 +8,7 @@ import stat
import subprocess
import sys
import threading
+import warnings
from . import base_events
@@ -353,6 +354,15 @@ class _UnixReadPipeTransport(transports.ReadTransport):
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 @@ class _UnixWritePipeTransport(transports._FlowControlMixin,
# 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/asyncio/windows_utils.py b/asyncio/windows_utils.py
index 5f8327e..870cd13 100644
--- a/asyncio/windows_utils.py
+++ b/asyncio/windows_utils.py
@@ -14,6 +14,7 @@ import os
import socket
import subprocess
import tempfile
+import warnings
__all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
@@ -156,7 +157,10 @@ class PipeHandle:
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/tests/test_proactor_events.py b/tests/test_proactor_events.py
index 33a8a67..fcd9ab1 100644
--- a/tests/test_proactor_events.py
+++ b/tests/test_proactor_events.py
@@ -499,8 +499,12 @@ class BaseProactorEventLoopTests(test_utils.TestCase):
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())