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

Skip to content

Commit 00b39ff

Browse files
committed
(Merge 3.4) asyncio: sync with Tulip
* _WaitHandleFuture.cancel() now notify IocpProactor through the overlapped object that the wait was cancelled. * Optimize IocpProactor.wait_for_handle() gets the result if the wait is signaled immediatly. * Enhance representation of Future and Future subclasses - Add "created at filename:lineno" in the representation - Add Future._repr_info() method which can be more easily overriden than Future.__repr__(). It should now be more easy to enhance Future representation without having to modify each subclass. For example, _OverlappedFuture and _WaitHandleFuture get the new "created at" information. - Use reprlib to format Future result, and function arguments when formatting a callback, to limit the length of the representation. * Fix repr(_WaitHandleFuture) * _WaitHandleFuture and _OverlappedFuture: hide frames of internal calls in the source traceback. * Cleanup ProactorIocp._poll(): set the timeout to 0 after the first call to GetQueuedCompletionStatus() * test_locks: close the temporary event loop and check the condition lock * Remove workaround in test_futures, no more needed
2 parents 205c226 + 313a980 commit 00b39ff

7 files changed

Lines changed: 108 additions & 72 deletions

File tree

Lib/asyncio/events.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
import functools
1212
import inspect
13-
import subprocess
14-
import traceback
15-
import threading
13+
import reprlib
1614
import socket
15+
import subprocess
1716
import sys
17+
import threading
18+
import traceback
1819

1920

2021
_PY34 = sys.version_info >= (3, 4)
@@ -36,8 +37,12 @@ def _get_function_source(func):
3637

3738

3839
def _format_args(args):
39-
# function formatting ('hello',) as ('hello')
40-
args_repr = repr(args)
40+
"""Format function arguments.
41+
42+
Special case for a single parameter: ('hello',) is formatted as ('hello').
43+
"""
44+
# use reprlib to limit the length of the output
45+
args_repr = reprlib.repr(args)
4146
if len(args) == 1 and args_repr.endswith(',)'):
4247
args_repr = args_repr[:-2] + ')'
4348
return args_repr

Lib/asyncio/futures.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import concurrent.futures._base
99
import logging
10+
import reprlib
1011
import sys
1112
import traceback
1213

@@ -175,20 +176,25 @@ def format_cb(callback):
175176
format_cb(cb[-1]))
176177
return 'cb=[%s]' % cb
177178

178-
def _format_result(self):
179-
if self._state != _FINISHED:
180-
return None
181-
elif self._exception is not None:
182-
return 'exception={!r}'.format(self._exception)
183-
else:
184-
return 'result={!r}'.format(self._result)
185-
186-
def __repr__(self):
179+
def _repr_info(self):
187180
info = [self._state.lower()]
188181
if self._state == _FINISHED:
189-
info.append(self._format_result())
182+
if self._exception is not None:
183+
info.append('exception={!r}'.format(self._exception))
184+
else:
185+
# use reprlib to limit the length of the output, especially
186+
# for very long strings
187+
result = reprlib.repr(self._result)
188+
info.append('result={}'.format(result))
190189
if self._callbacks:
191190
info.append(self._format_callbacks())
191+
if self._source_traceback:
192+
frame = self._source_traceback[-1]
193+
info.append('created at %s:%s' % (frame[0], frame[1]))
194+
return info
195+
196+
def __repr__(self):
197+
info = self._repr_info()
192198
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
193199

194200
# On Python 3.3 or older, objects with a destructor part of a reference

Lib/asyncio/tasks.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,30 +92,19 @@ def __del__(self):
9292
self._loop.call_exception_handler(context)
9393
futures.Future.__del__(self)
9494

95-
def __repr__(self):
96-
info = []
95+
def _repr_info(self):
96+
info = super()._repr_info()
97+
9798
if self._must_cancel:
98-
info.append('cancelling')
99-
else:
100-
info.append(self._state.lower())
99+
# replace status
100+
info[0] = 'cancelling'
101101

102102
coro = coroutines._format_coroutine(self._coro)
103-
info.append('coro=<%s>' % coro)
104-
105-
if self._source_traceback:
106-
frame = self._source_traceback[-1]
107-
info.append('created at %s:%s' % (frame[0], frame[1]))
108-
109-
if self._state == futures._FINISHED:
110-
info.append(self._format_result())
111-
112-
if self._callbacks:
113-
info.append(self._format_callbacks())
103+
info.insert(1, 'coro=<%s>' % coro)
114104

115105
if self._fut_waiter is not None:
116-
info.append('wait_for=%r' % self._fut_waiter)
117-
118-
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
106+
info.insert(2, 'wait_for=%r' % self._fut_waiter)
107+
return info
119108

120109
def get_stack(self, *, limit=None):
121110
"""Return the list of stack frames for this task's coroutine.

Lib/asyncio/windows_events.py

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,12 @@ def __init__(self, ov, *, loop=None):
4242
del self._source_traceback[-1]
4343
self._ov = ov
4444

45-
def __repr__(self):
46-
info = [self._state.lower()]
45+
def _repr_info(self):
46+
info = super()._repr_info()
4747
if self._ov is not None:
4848
state = 'pending' if self._ov.pending else 'completed'
49-
info.append('overlapped=<%s, %#x>' % (state, self._ov.address))
50-
if self._state == futures._FINISHED:
51-
info.append(self._format_result())
52-
if self._callbacks:
53-
info.append(self._format_callbacks())
54-
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
49+
info.insert(1, 'overlapped=<%s, %#x>' % (state, self._ov.address))
50+
return info
5551

5652
def _cancel_overlapped(self):
5753
if self._ov is None:
@@ -85,8 +81,14 @@ def set_result(self, result):
8581
class _WaitHandleFuture(futures.Future):
8682
"""Subclass of Future which represents a wait handle."""
8783

88-
def __init__(self, handle, wait_handle, *, loop=None):
84+
def __init__(self, iocp, ov, handle, wait_handle, *, loop=None):
8985
super().__init__(loop=loop)
86+
if self._source_traceback:
87+
del self._source_traceback[-1]
88+
# iocp and ov are only used by cancel() to notify IocpProactor
89+
# that the wait was cancelled
90+
self._iocp = iocp
91+
self._ov = ov
9092
self._handle = handle
9193
self._wait_handle = wait_handle
9294

@@ -95,19 +97,16 @@ def _poll(self):
9597
return (_winapi.WaitForSingleObject(self._handle, 0) ==
9698
_winapi.WAIT_OBJECT_0)
9799

98-
def __repr__(self):
99-
info = [self._state.lower()]
100+
def _repr_info(self):
101+
info = super()._repr_info()
102+
info.insert(1, 'handle=%#x' % self._handle)
100103
if self._wait_handle:
101-
state = 'pending' if self._poll() else 'completed'
102-
info.append('wait_handle=<%s, %#x>' % (state, self._wait_handle))
103-
info.append('handle=<%#x>' % self._handle)
104-
if self._state == futures._FINISHED:
105-
info.append(self._format_result())
106-
if self._callbacks:
107-
info.append(self._format_callbacks())
108-
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
109-
110-
def _unregister(self):
104+
state = 'signaled' if self._poll() else 'waiting'
105+
info.insert(1, 'wait_handle=<%s, %#x>'
106+
% (state, self._wait_handle))
107+
return info
108+
109+
def _unregister_wait(self):
111110
if self._wait_handle is None:
112111
return
113112
try:
@@ -117,10 +116,25 @@ def _unregister(self):
117116
raise
118117
# ERROR_IO_PENDING is not an error, the wait was unregistered
119118
self._wait_handle = None
119+
self._iocp = None
120+
self._ov = None
120121

121122
def cancel(self):
122-
self._unregister()
123-
return super().cancel()
123+
result = super().cancel()
124+
if self._ov is not None:
125+
# signal the cancellation to the overlapped object
126+
_overlapped.PostQueuedCompletionStatus(self._iocp, True,
127+
0, self._ov.address)
128+
self._unregister_wait()
129+
return result
130+
131+
def set_exception(self, exception):
132+
super().set_exception(exception)
133+
self._unregister_wait()
134+
135+
def set_result(self, result):
136+
super().set_result(result)
137+
self._unregister_wait()
124138

125139

126140
class PipeServer(object):
@@ -405,7 +419,9 @@ def wait_for_handle(self, handle, timeout=None):
405419
ov = _overlapped.Overlapped(NULL)
406420
wh = _overlapped.RegisterWaitWithQueue(
407421
handle, self._iocp, ov.address, ms)
408-
f = _WaitHandleFuture(handle, wh, loop=self._loop)
422+
f = _WaitHandleFuture(self._iocp, ov, handle, wh, loop=self._loop)
423+
if f._source_traceback:
424+
del f._source_traceback[-1]
409425

410426
def finish_wait_for_handle(trans, key, ov):
411427
# Note that this second wait means that we should only use
@@ -414,12 +430,17 @@ def finish_wait_for_handle(trans, key, ov):
414430
# or semaphores are not. Also note if the handle is
415431
# signalled and then quickly reset, then we may return
416432
# False even though we have not timed out.
433+
return f._poll()
434+
435+
if f._poll():
417436
try:
418-
return f._poll()
419-
finally:
420-
f._unregister()
437+
result = f._poll()
438+
except OSError as exc:
439+
f.set_exception(exc)
440+
else:
441+
f.set_result(result)
421442

422-
self._cache[ov.address] = (f, ov, None, finish_wait_for_handle)
443+
self._cache[ov.address] = (f, ov, 0, finish_wait_for_handle)
423444
return f
424445

425446
def _register_with_iocp(self, obj):
@@ -438,6 +459,8 @@ def _register(self, ov, obj, callback,
438459
# operation when it completes. The future's value is actually
439460
# the value returned by callback().
440461
f = _OverlappedFuture(ov, loop=self._loop)
462+
if f._source_traceback:
463+
del f._source_traceback[-1]
441464
if not ov.pending and not wait_for_post:
442465
# The operation has completed, so no need to postpone the
443466
# work. We cannot take this short cut if we need the
@@ -484,10 +507,13 @@ def _poll(self, timeout=None):
484507
ms = math.ceil(timeout * 1e3)
485508
if ms >= INFINITE:
486509
raise ValueError("timeout too big")
510+
487511
while True:
488512
status = _overlapped.GetQueuedCompletionStatus(self._iocp, ms)
489513
if status is None:
490514
return
515+
ms = 0
516+
491517
err, transferred, key, address = status
492518
try:
493519
f, ov, obj, callback = self._cache.pop(address)
@@ -504,7 +530,6 @@ def _poll(self, timeout=None):
504530
# handle which should be closed to avoid a leak.
505531
if key not in (0, _overlapped.INVALID_HANDLE_VALUE):
506532
_winapi.CloseHandle(key)
507-
ms = 0
508533
continue
509534

510535
if obj in self._stopped_serving:
@@ -520,7 +545,6 @@ def _poll(self, timeout=None):
520545
else:
521546
f.set_result(value)
522547
self._results.append(f)
523-
ms = 0
524548

525549
def _stop_serving(self, obj):
526550
# obj is a socket or pipe handle. It will be closed in

Lib/test/test_asyncio/test_futures.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ def fixture():
105105
self.assertEqual(next(g), ('C', 42)) # yield 'C', y.
106106

107107
def test_future_repr(self):
108+
self.loop.set_debug(True)
109+
f_pending_debug = asyncio.Future(loop=self.loop)
110+
frame = f_pending_debug._source_traceback[-1]
111+
self.assertEqual(repr(f_pending_debug),
112+
'<Future pending created at %s:%s>'
113+
% (frame[0], frame[1]))
114+
f_pending_debug.cancel()
115+
116+
self.loop.set_debug(False)
108117
f_pending = asyncio.Future(loop=self.loop)
109118
self.assertEqual(repr(f_pending), '<Future pending>')
110119
f_pending.cancel()
@@ -299,12 +308,6 @@ def test_future_source_traceback(self):
299308

300309
@mock.patch('asyncio.base_events.logger')
301310
def test_future_exception_never_retrieved(self, m_log):
302-
# FIXME: Python issue #21163, other tests may "leak" pending task which
303-
# emit a warning when they are destroyed by the GC
304-
support.gc_collect()
305-
m_log.error.reset_mock()
306-
# ---
307-
308311
self.loop.set_debug(True)
309312

310313
def memory_error():
@@ -324,7 +327,7 @@ def memory_error():
324327
if sys.version_info >= (3, 4):
325328
frame = source_traceback[-1]
326329
regex = (r'^Future exception was never retrieved\n'
327-
r'future: <Future finished exception=MemoryError\(\)>\n'
330+
r'future: <Future finished exception=MemoryError\(\) created at {filename}:{lineno}>\n'
328331
r'source_traceback: Object created at \(most recent call last\):\n'
329332
r' File'
330333
r'.*\n'

Lib/test/test_asyncio/test_locks.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,13 @@ def test_explicit_lock(self):
660660
lock = asyncio.Lock(loop=self.loop)
661661
cond = asyncio.Condition(lock, loop=self.loop)
662662

663-
self.assertIs(lock._loop, cond._loop)
663+
self.assertIs(cond._lock, lock)
664+
self.assertIs(cond._loop, lock._loop)
664665

665666
def test_ambiguous_loops(self):
666667
loop = self.new_test_loop()
668+
self.addCleanup(loop.close)
669+
667670
lock = asyncio.Lock(loop=self.loop)
668671
with self.assertRaises(ValueError):
669672
asyncio.Condition(lock, loop=loop)

Lib/test/test_asyncio/test_tasks.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ def test_async_neither(self):
132132
asyncio.async('ok')
133133

134134
def test_task_repr(self):
135+
self.loop.set_debug(False)
136+
135137
@asyncio.coroutine
136138
def notmuch():
137139
yield from []
@@ -189,6 +191,8 @@ def notmuch():
189191
"<Task finished %s result='abc'>" % coro)
190192

191193
def test_task_repr_coro_decorator(self):
194+
self.loop.set_debug(False)
195+
192196
@asyncio.coroutine
193197
def notmuch():
194198
# notmuch() function doesn't use yield from: it will be wrapped by
@@ -252,6 +256,8 @@ def notmuch():
252256
self.loop.run_until_complete(t)
253257

254258
def test_task_repr_wait_for(self):
259+
self.loop.set_debug(False)
260+
255261
@asyncio.coroutine
256262
def wait_for(fut):
257263
return (yield from fut)

0 commit comments

Comments
 (0)