summaryrefslogtreecommitdiffstats
path: root/Lib/asyncio/futures.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/asyncio/futures.py')
-rw-r--r--Lib/asyncio/futures.py48
1 files changed, 37 insertions, 11 deletions
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 4dcb654..9ca8d84 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -2,7 +2,7 @@
__all__ = ['CancelledError', 'TimeoutError',
'InvalidStateError',
- 'Future', 'wrap_future',
+ 'Future', 'wrap_future', 'isfuture',
]
import concurrent.futures._base
@@ -110,6 +110,17 @@ class _TracebackLogger:
self.loop.call_exception_handler({'message': msg})
+def isfuture(obj):
+ """Check for a Future.
+
+ This returns True when obj is a Future instance or is advertising
+ itself as duck-type compatible by setting _asyncio_future_blocking.
+ See comment in Future for more details.
+ """
+ return (hasattr(obj.__class__, '_asyncio_future_blocking') and
+ obj._asyncio_future_blocking is not None)
+
+
class Future:
"""This class is *almost* compatible with concurrent.futures.Future.
@@ -134,7 +145,15 @@ class Future:
_loop = None
_source_traceback = None
- _blocking = False # proper use of future (yield vs yield from)
+ # This field is used for a dual purpose:
+ # - Its presence is a marker to declare that a class implements
+ # the Future protocol (i.e. is intended to be duck-type compatible).
+ # The value must also be not-None, to enable a subclass to declare
+ # that it is not compatible by setting this to None.
+ # - It is set by __iter__() below so that Task._step() can tell
+ # the difference between `yield from Future()` (correct) vs.
+ # `yield Future()` (incorrect).
+ _asyncio_future_blocking = False
_log_traceback = False # Used for Python 3.4 and later
_tb_logger = None # Used for Python 3.3 only
@@ -142,7 +161,7 @@ class Future:
def __init__(self, *, loop=None):
"""Initialize the future.
- The optional event_loop argument allows to explicitly set the event
+ The optional event_loop argument allows explicitly setting the event
loop object used by the future. If it's not provided, the future uses
the default event loop.
"""
@@ -341,6 +360,9 @@ class Future:
raise InvalidStateError('{}: {!r}'.format(self._state, self))
if isinstance(exception, type):
exception = exception()
+ if type(exception) is StopIteration:
+ raise TypeError("StopIteration interacts badly with generators "
+ "and cannot be raised into a Future")
self._exception = exception
self._state = _FINISHED
self._schedule_callbacks()
@@ -354,7 +376,7 @@ class Future:
def __iter__(self):
if not self.done():
- self._blocking = True
+ self._asyncio_future_blocking = True
yield self # This tells Task to wait for completion.
assert self.done(), "yield from wasn't used with future"
return self.result() # May raise too.
@@ -412,15 +434,17 @@ def _chain_future(source, destination):
If destination is cancelled, source gets cancelled too.
Compatible with both asyncio.Future and concurrent.futures.Future.
"""
- if not isinstance(source, (Future, concurrent.futures.Future)):
+ if not isfuture(source) and not isinstance(source,
+ concurrent.futures.Future):
raise TypeError('A future is required for source argument')
- if not isinstance(destination, (Future, concurrent.futures.Future)):
+ if not isfuture(destination) and not isinstance(destination,
+ concurrent.futures.Future):
raise TypeError('A future is required for destination argument')
- source_loop = source._loop if isinstance(source, Future) else None
- dest_loop = destination._loop if isinstance(destination, Future) else None
+ source_loop = source._loop if isfuture(source) else None
+ dest_loop = destination._loop if isfuture(destination) else None
def _set_state(future, other):
- if isinstance(future, Future):
+ if isfuture(future):
_copy_future_state(other, future)
else:
_set_concurrent_future_state(future, other)
@@ -444,10 +468,12 @@ def _chain_future(source, destination):
def wrap_future(future, *, loop=None):
"""Wrap concurrent.futures.Future object."""
- if isinstance(future, Future):
+ if isfuture(future):
return future
assert isinstance(future, concurrent.futures.Future), \
'concurrent.futures.Future is expected, got {!r}'.format(future)
- new_future = Future(loop=loop)
+ if loop is None:
+ loop = events.get_event_loop()
+ new_future = loop.create_future()
_chain_future(future, new_future)
return new_future