Python issue #23243: On Python 3.4 and newer, emit a ResourceWarning when an
event loop or a transport is not explicitly closed
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user