| 209 | |
| 210 | @functools.wraps(func) |
| 211 | def wrapper(*args, **kwargs): |
| 212 | # type: (*Any, **Any) -> Future[_T] |
| 213 | # This function is type-annotated with a comment to work around |
| 214 | # https://bitbucket.org/pypy/pypy/issues/2868/segfault-with-args-type-annotation-in |
| 215 | future = _create_future() |
| 216 | if contextvars is not None: |
| 217 | ctx_run = contextvars.copy_context().run # type: Callable |
| 218 | else: |
| 219 | ctx_run = _fake_ctx_run |
| 220 | try: |
| 221 | result = ctx_run(func, *args, **kwargs) |
| 222 | except (Return, StopIteration) as e: |
| 223 | result = _value_from_stopiteration(e) |
| 224 | except Exception: |
| 225 | future_set_exc_info(future, sys.exc_info()) |
| 226 | try: |
| 227 | return future |
| 228 | finally: |
| 229 | # Avoid circular references |
| 230 | future = None # type: ignore |
| 231 | else: |
| 232 | if isinstance(result, Generator): |
| 233 | # Inline the first iteration of Runner.run. This lets us |
| 234 | # avoid the cost of creating a Runner when the coroutine |
| 235 | # never actually yields, which in turn allows us to |
| 236 | # use "optional" coroutines in critical path code without |
| 237 | # performance penalty for the synchronous case. |
| 238 | try: |
| 239 | yielded = ctx_run(next, result) |
| 240 | except (StopIteration, Return) as e: |
| 241 | future_set_result_unless_cancelled( |
| 242 | future, _value_from_stopiteration(e) |
| 243 | ) |
| 244 | except Exception: |
| 245 | future_set_exc_info(future, sys.exc_info()) |
| 246 | else: |
| 247 | # Provide strong references to Runner objects as long |
| 248 | # as their result future objects also have strong |
| 249 | # references (typically from the parent coroutine's |
| 250 | # Runner). This keeps the coroutine's Runner alive. |
| 251 | # We do this by exploiting the public API |
| 252 | # add_done_callback() instead of putting a private |
| 253 | # attribute on the Future. |
| 254 | # (GitHub issues #1769, #2229). |
| 255 | runner = Runner(ctx_run, result, future, yielded) |
| 256 | future.add_done_callback(lambda _: runner) |
| 257 | yielded = None |
| 258 | try: |
| 259 | return future |
| 260 | finally: |
| 261 | # Subtle memory optimization: if next() raised an exception, |
| 262 | # the future's exc_info contains a traceback which |
| 263 | # includes this stack frame. This creates a cycle, |
| 264 | # which will be collected at the next full GC but has |
| 265 | # been shown to greatly increase memory usage of |
| 266 | # benchmarks (relative to the refcount-based scheme |
| 267 | # used in the absence of cycles). We can avoid the |
| 268 | # cycle by clearing the local variable after we return it. |