Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 978a9af

Browse files
committed
Issue #23243, asyncio: Emit a ResourceWarning when an event loop or a transport
is not explicitly closed. Close also explicitly transports in test_sslproto.
1 parent 3c0cf05 commit 978a9af

10 files changed

Lines changed: 104 additions & 10 deletions

File tree

Lib/asyncio/base_events.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import time
2727
import traceback
2828
import sys
29+
import warnings
2930

3031
from . import coroutines
3132
from . import events
@@ -333,6 +334,16 @@ def is_closed(self):
333334
"""Returns True if the event loop was closed."""
334335
return self._closed
335336

337+
# On Python 3.3 and older, objects with a destructor part of a reference
338+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
339+
# to the PEP 442.
340+
if sys.version_info >= (3, 4):
341+
def __del__(self):
342+
if not self.is_closed():
343+
warnings.warn("unclosed event loop %r" % self, ResourceWarning)
344+
if not self.is_running():
345+
self.close()
346+
336347
def is_running(self):
337348
"""Returns True if the event loop is running."""
338349
return (self._owner is not None)

Lib/asyncio/base_subprocess.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import collections
22
import subprocess
3+
import sys
4+
import warnings
35

46
from . import protocols
57
from . import transports
@@ -13,6 +15,7 @@ def __init__(self, loop, protocol, args, shell,
1315
stdin, stdout, stderr, bufsize,
1416
extra=None, **kwargs):
1517
super().__init__(extra)
18+
self._closed = False
1619
self._protocol = protocol
1720
self._loop = loop
1821
self._pid = None
@@ -40,7 +43,10 @@ def __init__(self, loop, protocol, args, shell,
4043
program, self._pid)
4144

4245
def __repr__(self):
43-
info = [self.__class__.__name__, 'pid=%s' % self._pid]
46+
info = [self.__class__.__name__]
47+
if self._closed:
48+
info.append('closed')
49+
info.append('pid=%s' % self._pid)
4450
if self._returncode is not None:
4551
info.append('returncode=%s' % self._returncode)
4652

@@ -70,13 +76,23 @@ def _make_read_subprocess_pipe_proto(self, fd):
7076
raise NotImplementedError
7177

7278
def close(self):
79+
self._closed = True
7380
for proto in self._pipes.values():
7481
if proto is None:
7582
continue
7683
proto.pipe.close()
7784
if self._returncode is None:
7885
self.terminate()
7986

87+
# On Python 3.3 and older, objects with a destructor part of a reference
88+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
89+
# to the PEP 442.
90+
if sys.version_info >= (3, 4):
91+
def __del__(self):
92+
if not self._closed:
93+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
94+
self.close()
95+
8096
def get_pid(self):
8197
return self._pid
8298

@@ -104,6 +120,7 @@ def _kill_wait(self):
104120
Function called when an exception is raised during the creation
105121
of a subprocess.
106122
"""
123+
self._closed = True
107124
if self._loop.get_debug():
108125
logger.warning('Exception during subprocess creation, '
109126
'kill the subprocess %r',

Lib/asyncio/futures.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ def __repr__(self):
195195
info = self._repr_info()
196196
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
197197

198-
# On Python 3.3 or older, objects with a destructor part of a reference
199-
# cycle are never destroyed. It's not more the case on Python 3.4 thanks to
200-
# the PEP 442.
198+
# On Python 3.3 and older, objects with a destructor part of a reference
199+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
200+
# to the PEP 442.
201201
if _PY34:
202202
def __del__(self):
203203
if not self._log_traceback:

Lib/asyncio/proactor_events.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
__all__ = ['BaseProactorEventLoop']
88

99
import socket
10+
import sys
11+
import warnings
1012

1113
from . import base_events
1214
from . import constants
@@ -74,6 +76,15 @@ def close(self):
7476
self._read_fut.cancel()
7577
self._read_fut = None
7678

79+
# On Python 3.3 and older, objects with a destructor part of a reference
80+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
81+
# to the PEP 442.
82+
if sys.version_info >= (3, 4):
83+
def __del__(self):
84+
if self._sock is not None:
85+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
86+
self.close()
87+
7788
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
7889
if isinstance(exc, (BrokenPipeError, ConnectionResetError)):
7990
if self._loop.get_debug():

Lib/asyncio/selector_events.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import errno
1111
import functools
1212
import socket
13+
import sys
14+
import warnings
1315
try:
1416
import ssl
1517
except ImportError: # pragma: no cover
@@ -499,6 +501,11 @@ class _SelectorTransport(transports._FlowControlMixin,
499501

500502
_buffer_factory = bytearray # Constructs initial value for self._buffer.
501503

504+
# Attribute used in the destructor: it must be set even if the constructor
505+
# is not called (see _SelectorSslTransport which may start by raising an
506+
# exception)
507+
_sock = None
508+
502509
def __init__(self, loop, sock, protocol, extra=None, server=None):
503510
super().__init__(extra, loop)
504511
self._extra['socket'] = sock
@@ -559,6 +566,15 @@ def close(self):
559566
self._conn_lost += 1
560567
self._loop.call_soon(self._call_connection_lost, None)
561568

569+
# On Python 3.3 and older, objects with a destructor part of a reference
570+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
571+
# to the PEP 442.
572+
if sys.version_info >= (3, 4):
573+
def __del__(self):
574+
if self._sock is not None:
575+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
576+
self._sock.close()
577+
562578
def _fatal_error(self, exc, message='Fatal error on transport'):
563579
# Should be called from exception handler only.
564580
if isinstance(exc, (BrokenPipeError,

Lib/asyncio/sslproto.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import collections
2+
import sys
3+
import warnings
24
try:
35
import ssl
46
except ImportError: # pragma: no cover
@@ -295,6 +297,7 @@ def __init__(self, loop, ssl_protocol, app_protocol):
295297
self._loop = loop
296298
self._ssl_protocol = ssl_protocol
297299
self._app_protocol = app_protocol
300+
self._closed = False
298301

299302
def get_extra_info(self, name, default=None):
300303
"""Get optional transport information."""
@@ -308,8 +311,18 @@ def close(self):
308311
protocol's connection_lost() method will (eventually) called
309312
with None as its argument.
310313
"""
314+
self._closed = True
311315
self._ssl_protocol._start_shutdown()
312316

317+
# On Python 3.3 and older, objects with a destructor part of a reference
318+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
319+
# to the PEP 442.
320+
if sys.version_info >= (3, 4):
321+
def __del__(self):
322+
if not self._closed:
323+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
324+
self.close()
325+
313326
def pause_reading(self):
314327
"""Pause the receiving end.
315328

Lib/asyncio/unix_events.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import subprocess
99
import sys
1010
import threading
11+
import warnings
1112

1213

1314
from . import base_events
@@ -353,6 +354,15 @@ def close(self):
353354
if not self._closing:
354355
self._close(None)
355356

357+
# On Python 3.3 and older, objects with a destructor part of a reference
358+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
359+
# to the PEP 442.
360+
if sys.version_info >= (3, 4):
361+
def __del__(self):
362+
if self._pipe is not None:
363+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
364+
self._pipe.close()
365+
356366
def _fatal_error(self, exc, message='Fatal error on pipe transport'):
357367
# should be called by exception handler only
358368
if (isinstance(exc, OSError) and exc.errno == errno.EIO):
@@ -529,6 +539,15 @@ def close(self):
529539
# write_eof is all what we needed to close the write pipe
530540
self.write_eof()
531541

542+
# On Python 3.3 and older, objects with a destructor part of a reference
543+
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
544+
# to the PEP 442.
545+
if sys.version_info >= (3, 4):
546+
def __del__(self):
547+
if self._pipe is not None:
548+
warnings.warn("unclosed transport %r" % self, ResourceWarning)
549+
self._pipe.close()
550+
532551
def abort(self):
533552
self._close(None)
534553

Lib/asyncio/windows_utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import socket
1515
import subprocess
1616
import tempfile
17+
import warnings
1718

1819

1920
__all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
@@ -156,7 +157,10 @@ def close(self, *, CloseHandle=_winapi.CloseHandle):
156157
CloseHandle(self._handle)
157158
self._handle = None
158159

159-
__del__ = close
160+
def __del__(self):
161+
if self._handle is not None:
162+
warnings.warn("unclosed %r" % self, ResourceWarning)
163+
self.close()
160164

161165
def __enter__(self):
162166
return self

Lib/test/test_asyncio/test_proactor_events.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,12 @@ def test_sock_accept(self):
499499
self.proactor.accept.assert_called_with(self.sock)
500500

501501
def test_socketpair(self):
502+
class EventLoop(BaseProactorEventLoop):
503+
# override the destructor to not log a ResourceWarning
504+
def __del__(self):
505+
pass
502506
self.assertRaises(
503-
NotImplementedError, BaseProactorEventLoop, self.proactor)
507+
NotImplementedError, EventLoop, self.proactor)
504508

505509
def test_make_socket_transport(self):
506510
tr = self.loop._make_socket_transport(self.sock, asyncio.Protocol())

Lib/test/test_asyncio/test_sslproto.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ def setUp(self):
2222
def ssl_protocol(self, waiter=None):
2323
sslcontext = test_utils.dummy_ssl_context()
2424
app_proto = asyncio.Protocol()
25-
return sslproto.SSLProtocol(self.loop, app_proto, sslcontext, waiter)
25+
proto = sslproto.SSLProtocol(self.loop, app_proto, sslcontext, waiter)
26+
self.addCleanup(proto._app_transport.close)
27+
return proto
2628

2729
def connection_made(self, ssl_proto, do_handshake=None):
2830
transport = mock.Mock()
@@ -56,9 +58,6 @@ def do_handshake(callback):
5658
with test_utils.disable_logger():
5759
self.loop.run_until_complete(handshake_fut)
5860

59-
# Close the transport
60-
ssl_proto._app_transport.close()
61-
6261
def test_eof_received_waiter(self):
6362
waiter = asyncio.Future(loop=self.loop)
6463
ssl_proto = self.ssl_protocol(waiter)

0 commit comments

Comments
 (0)