|
| 1 | +DEF _FUT_PENDING = 1 |
| 2 | +DEF _FUT_CANCELLED = 2 |
| 3 | +DEF _FUT_FINISHED = 3 |
| 4 | + |
| 5 | + |
| 6 | +cdef class BaseFuture: |
| 7 | + cdef: |
| 8 | + int _state |
| 9 | + |
| 10 | + readonly Loop _loop |
| 11 | + readonly list _callbacks |
| 12 | + readonly object _exception |
| 13 | + readonly object _result |
| 14 | + public bint _blocking |
| 15 | + readonly object _source_traceback |
| 16 | + readonly bint _log_traceback |
| 17 | + |
| 18 | + def __init__(self, Loop loop): |
| 19 | + if loop is None: |
| 20 | + loop = aio_get_event_loop() |
| 21 | + if not isinstance(loop, Loop): |
| 22 | + raise TypeError('uvloop.Future supports only uvloop.Loop') |
| 23 | + |
| 24 | + self._state = _FUT_PENDING |
| 25 | + self._loop = loop |
| 26 | + self._callbacks = [] |
| 27 | + self._result = None |
| 28 | + self._exception = None |
| 29 | + self._blocking = False |
| 30 | + self._log_traceback = False |
| 31 | + |
| 32 | + if loop._debug: |
| 33 | + self._source_traceback = tb_extract_stack(sys_getframe(0)) |
| 34 | + else: |
| 35 | + self._source_traceback = None |
| 36 | + |
| 37 | + cdef _schedule_callbacks(self): |
| 38 | + cdef: |
| 39 | + list callbacks |
| 40 | + size_t cb_len = len(self._callbacks) |
| 41 | + size_t i |
| 42 | + |
| 43 | + if cb_len == 0: |
| 44 | + return |
| 45 | + |
| 46 | + callbacks = self._callbacks |
| 47 | + self._callbacks = [] |
| 48 | + |
| 49 | + for i from 0 <= i < cb_len: |
| 50 | + self._loop.call_soon(callbacks[i], self) |
| 51 | + |
| 52 | + cdef _add_done_callback(self, fn): |
| 53 | + if self._state != _FUT_PENDING: |
| 54 | + self._loop.call_soon(fn, self) |
| 55 | + else: |
| 56 | + self._callbacks.append(fn) |
| 57 | + |
| 58 | + cdef _done(self): |
| 59 | + return self._state != _FUT_PENDING |
| 60 | + |
| 61 | + cdef _cancel(self): |
| 62 | + if self._done(): |
| 63 | + return False |
| 64 | + self._state = _FUT_CANCELLED |
| 65 | + self._schedule_callbacks() |
| 66 | + return True |
| 67 | + |
| 68 | + # _result would shadow the "_result" property |
| 69 | + cdef _result_impl(self): |
| 70 | + if self._state == _FUT_CANCELLED: |
| 71 | + raise aio_CancelledError |
| 72 | + if self._state != _FUT_FINISHED: |
| 73 | + raise aio_InvalidStateError('Result is not ready.') |
| 74 | + self._log_traceback = False |
| 75 | + if self._exception is not None: |
| 76 | + raise self._exception |
| 77 | + return self._result |
| 78 | + |
| 79 | + cdef _str_state(self): |
| 80 | + if self._state == _FUT_PENDING: |
| 81 | + return 'PENDING' |
| 82 | + elif self._state == _FUT_CANCELLED: |
| 83 | + return 'CANCELLED' |
| 84 | + elif self._state == _FUT_FINISHED: |
| 85 | + return 'FINISHED' |
| 86 | + else: |
| 87 | + raise RuntimeError('unknown Future state') |
| 88 | + |
| 89 | + property _state: |
| 90 | + def __get__(self): |
| 91 | + return self._str_state() |
| 92 | + |
| 93 | + def cancel(self): |
| 94 | + """Cancel the future and schedule callbacks. |
| 95 | +
|
| 96 | + If the future is already done or cancelled, return False. Otherwise, |
| 97 | + change the future's state to cancelled, schedule the callbacks and |
| 98 | + return True. |
| 99 | + """ |
| 100 | + return self._cancel() |
| 101 | + |
| 102 | + def cancelled(self): |
| 103 | + """Return True if the future was cancelled.""" |
| 104 | + return self._state == _FUT_CANCELLED |
| 105 | + |
| 106 | + def done(self): |
| 107 | + """Return True if the future is done. |
| 108 | +
|
| 109 | + Done means either that a result / exception are available, or that the |
| 110 | + future was cancelled. |
| 111 | + """ |
| 112 | + return self._state != _FUT_PENDING |
| 113 | + |
| 114 | + def result(self): |
| 115 | + """Return the result this future represents. |
| 116 | +
|
| 117 | + If the future has been cancelled, raises CancelledError. If the |
| 118 | + future's result isn't yet available, raises InvalidStateError. If |
| 119 | + the future is done and has an exception set, this exception is raised. |
| 120 | + """ |
| 121 | + return self._result_impl() |
| 122 | + |
| 123 | + def exception(self): |
| 124 | + """Return the exception that was set on this future. |
| 125 | +
|
| 126 | + The exception (or None if no exception was set) is returned only if |
| 127 | + the future is done. If the future has been cancelled, raises |
| 128 | + CancelledError. If the future isn't done yet, raises |
| 129 | + InvalidStateError. |
| 130 | + """ |
| 131 | + if self._state == _FUT_CANCELLED: |
| 132 | + raise aio_CancelledError |
| 133 | + if self._state != _FUT_FINISHED: |
| 134 | + raise aio_InvalidStateError('Exception is not set.') |
| 135 | + self._log_traceback = False |
| 136 | + return self._exception |
| 137 | + |
| 138 | + def add_done_callback(self, fn): |
| 139 | + """Add a callback to be run when the future becomes done. |
| 140 | +
|
| 141 | + The callback is called with a single argument - the future object. If |
| 142 | + the future is already done when this is called, the callback is |
| 143 | + scheduled with call_soon. |
| 144 | + """ |
| 145 | + self._add_done_callback(fn) |
| 146 | + |
| 147 | + def remove_done_callback(self, fn): |
| 148 | + """Remove all instances of a callback from the "call when done" list. |
| 149 | +
|
| 150 | + Returns the number of callbacks removed. |
| 151 | + """ |
| 152 | + cdef: |
| 153 | + size_t clen = len(self._callbacks) |
| 154 | + size_t i |
| 155 | + size_t ni = 0 |
| 156 | + object cb |
| 157 | + |
| 158 | + for i from 0 <= i < clen: |
| 159 | + cb = self._callbacks[i] |
| 160 | + if cb != fn: |
| 161 | + self._callbacks[ni] = cb |
| 162 | + ni += 1 |
| 163 | + |
| 164 | + if ni != clen: |
| 165 | + del self._callbacks[ni:] |
| 166 | + |
| 167 | + return clen - ni |
| 168 | + |
| 169 | + cpdef set_result(self, result): |
| 170 | + """Mark the future done and set its result. |
| 171 | +
|
| 172 | + If the future is already done when this method is called, raises |
| 173 | + InvalidStateError. |
| 174 | + """ |
| 175 | + if self._state != _FUT_PENDING: |
| 176 | + raise aio_InvalidStateError('{}: {!r}'.format( |
| 177 | + self._str_state(), self)) |
| 178 | + self._result = result |
| 179 | + self._state = _FUT_FINISHED |
| 180 | + self._schedule_callbacks() |
| 181 | + |
| 182 | + cpdef set_exception(self, exception): |
| 183 | + """Mark the future done and set an exception. |
| 184 | +
|
| 185 | + If the future is already done when this method is called, raises |
| 186 | + InvalidStateError. |
| 187 | + """ |
| 188 | + if self._state != _FUT_PENDING: |
| 189 | + raise aio_InvalidStateError('{}: {!r}'.format( |
| 190 | + self._str_state(), self)) |
| 191 | + if isinstance(exception, type): |
| 192 | + exception = exception() |
| 193 | + if type(exception) is StopIteration: |
| 194 | + raise TypeError("StopIteration interacts badly with generators " |
| 195 | + "and cannot be raised into a Future") |
| 196 | + self._exception = exception |
| 197 | + self._state = _FUT_FINISHED |
| 198 | + self._schedule_callbacks() |
| 199 | + self._log_traceback = True |
| 200 | + |
| 201 | + # Copy of __await__ |
| 202 | + def __iter__(self): |
| 203 | + if self._state == _FUT_PENDING: |
| 204 | + self._blocking = True |
| 205 | + yield self # This tells Task to wait for completion. |
| 206 | + |
| 207 | + if self._state == _FUT_PENDING: |
| 208 | + raise AssertionError("yield from wasn't used with future") |
| 209 | + |
| 210 | + return self._result_impl() # May raise too. |
| 211 | + |
| 212 | + # Copy of __iter__ |
| 213 | + def __await__(self): |
| 214 | + if self._state == _FUT_PENDING: |
| 215 | + self._blocking = True |
| 216 | + yield self # This tells Task to wait for completion. |
| 217 | + |
| 218 | + if self._state == _FUT_PENDING: |
| 219 | + raise AssertionError("yield from wasn't used with future") |
| 220 | + |
| 221 | + return self._result_impl() # May raise too. |
| 222 | + |
| 223 | + |
| 224 | +class Future(BaseFuture, aio_Future): |
| 225 | + # Inherit asyncio.Future.__del__ and __repr__ |
| 226 | + pass |
| 227 | + |
| 228 | + |
| 229 | +cdef uvloop_Future = Future |
0 commit comments