From 5d7e3b6cd208dffdfe8530664081b62e0c7e3092 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 17 Nov 2015 12:19:41 -0500 Subject: [PATCH] asyncio: Cleanup Future API See https://github.com/python/asyncio/pull/292 for details. --- Lib/asyncio/futures.py | 60 +++++++++++++-------------- Lib/asyncio/proactor_events.py | 3 +- Lib/asyncio/selector_events.py | 6 ++- Lib/asyncio/tasks.py | 3 +- Lib/asyncio/unix_events.py | 6 ++- Lib/test/test_asyncio/test_futures.py | 11 +++-- 6 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 166bc8047bf..4dcb6546be0 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -154,7 +154,7 @@ class Future: if self._loop.get_debug(): self._source_traceback = traceback.extract_stack(sys._getframe(1)) - def _format_callbacks(self): + def __format_callbacks(self): cb = self._callbacks size = len(cb) if not size: @@ -184,7 +184,7 @@ class Future: result = reprlib.repr(self._result) info.append('result={}'.format(result)) if self._callbacks: - info.append(self._format_callbacks()) + info.append(self.__format_callbacks()) if self._source_traceback: frame = self._source_traceback[-1] info.append('created at %s:%s' % (frame[0], frame[1])) @@ -319,12 +319,6 @@ class Future: # So-called internal methods (note: no set_running_or_notify_cancel()). - def _set_result_unless_cancelled(self, result): - """Helper setting the result only if the future was not cancelled.""" - if self.cancelled(): - return - self.set_result(result) - def set_result(self, result): """Mark the future done and set its result. @@ -358,27 +352,6 @@ class Future: # have had a chance to call result() or exception(). self._loop.call_soon(self._tb_logger.activate) - # Truly internal methods. - - def _copy_state(self, other): - """Internal helper to copy state from another Future. - - The other Future may be a concurrent.futures.Future. - """ - assert other.done() - if self.cancelled(): - return - assert not self.done() - if other.cancelled(): - self.cancel() - else: - exception = other.exception() - if exception is not None: - self.set_exception(exception) - else: - result = other.result() - self.set_result(result) - def __iter__(self): if not self.done(): self._blocking = True @@ -390,6 +363,13 @@ class Future: __await__ = __iter__ # make compatible with 'await' expression +def _set_result_unless_cancelled(fut, result): + """Helper setting the result only if the future was not cancelled.""" + if fut.cancelled(): + return + fut.set_result(result) + + def _set_concurrent_future_state(concurrent, source): """Copy state from a future to a concurrent.futures.Future.""" assert source.done() @@ -405,6 +385,26 @@ def _set_concurrent_future_state(concurrent, source): concurrent.set_result(result) +def _copy_future_state(source, dest): + """Internal helper to copy state from another Future. + + The other Future may be a concurrent.futures.Future. + """ + assert source.done() + if dest.cancelled(): + return + assert not dest.done() + if source.cancelled(): + dest.cancel() + else: + exception = source.exception() + if exception is not None: + dest.set_exception(exception) + else: + result = source.result() + dest.set_result(result) + + def _chain_future(source, destination): """Chain two futures so that when one completes, so does the other. @@ -421,7 +421,7 @@ def _chain_future(source, destination): def _set_state(future, other): if isinstance(future, Future): - future._copy_state(other) + _copy_future_state(other, future) else: _set_concurrent_future_state(future, other) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 9c514c8345d..7eac41eec02 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -41,7 +41,8 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin, self._loop.call_soon(self._protocol.connection_made, self) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(waiter._set_result_unless_cancelled, None) + self._loop.call_soon(futures._set_result_unless_cancelled, + waiter, None) def __repr__(self): info = [self.__class__.__name__] diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 236f7b36a8d..a05f81cd9de 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -636,7 +636,8 @@ class _SelectorSocketTransport(_SelectorTransport): self._sock_fd, self._read_ready) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(waiter._set_result_unless_cancelled, None) + self._loop.call_soon(futures._set_result_unless_cancelled, + waiter, None) def pause_reading(self): if self._closing: @@ -990,7 +991,8 @@ class _SelectorDatagramTransport(_SelectorTransport): self._sock_fd, self._read_ready) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(waiter._set_result_unless_cancelled, None) + self._loop.call_soon(futures._set_result_unless_cancelled, + waiter, None) def get_write_buffer_size(self): return sum(len(data) for data, _ in self._buffer) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 61f0645f97f..63cbcda32fe 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -500,7 +500,8 @@ def sleep(delay, result=None, *, loop=None): future = futures.Future(loop=loop) h = future._loop.call_later(delay, - future._set_result_unless_cancelled, result) + futures._set_result_unless_cancelled, + future, result) try: return (yield from future) finally: diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index f75e89f3175..7747ff41bb8 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -319,7 +319,8 @@ class _UnixReadPipeTransport(transports.ReadTransport): self._fileno, self._read_ready) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(waiter._set_result_unless_cancelled, None) + self._loop.call_soon(futures._set_result_unless_cancelled, + waiter, None) def __repr__(self): info = [self.__class__.__name__] @@ -442,7 +443,8 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(waiter._set_result_unless_cancelled, None) + self._loop.call_soon(futures._set_result_unless_cancelled, + waiter, None) def __repr__(self): info = [self.__class__.__name__] diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 0bc0581d281..55fdff3f8d5 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -174,11 +174,13 @@ class FutureTests(test_utils.TestCase): '') def test_copy_state(self): + from asyncio.futures import _copy_future_state + f = asyncio.Future(loop=self.loop) f.set_result(10) newf = asyncio.Future(loop=self.loop) - newf._copy_state(f) + _copy_future_state(f, newf) self.assertTrue(newf.done()) self.assertEqual(newf.result(), 10) @@ -186,7 +188,7 @@ class FutureTests(test_utils.TestCase): f_exception.set_exception(RuntimeError()) newf_exception = asyncio.Future(loop=self.loop) - newf_exception._copy_state(f_exception) + _copy_future_state(f_exception, newf_exception) self.assertTrue(newf_exception.done()) self.assertRaises(RuntimeError, newf_exception.result) @@ -194,7 +196,7 @@ class FutureTests(test_utils.TestCase): f_cancelled.cancel() newf_cancelled = asyncio.Future(loop=self.loop) - newf_cancelled._copy_state(f_cancelled) + _copy_future_state(f_cancelled, newf_cancelled) self.assertTrue(newf_cancelled.cancelled()) def test_iter(self): @@ -382,9 +384,10 @@ class FutureTests(test_utils.TestCase): self.check_future_exception_never_retrieved(True) def test_set_result_unless_cancelled(self): + from asyncio import futures fut = asyncio.Future(loop=self.loop) fut.cancel() - fut._set_result_unless_cancelled(2) + futures._set_result_unless_cancelled(fut, 2) self.assertTrue(fut.cancelled())