| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b96ef55 commit eb63645
27 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,10 @@ PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); | |||
| 25 | 25 | PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *); | |
| 26 | 26 | PyAPI_FUNC(void) _PyEval_SetCoroutineWrapper(PyObject *); | |
| 27 | 27 | PyAPI_FUNC(PyObject *) _PyEval_GetCoroutineWrapper(void); | |
| 28 | + PyAPI_FUNC(void) _PyEval_SetAsyncGenFirstiter(PyObject *); | ||
| 29 | + PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFirstiter(void); | ||
| 30 | + PyAPI_FUNC(void) _PyEval_SetAsyncGenFinalizer(PyObject *); | ||
| 31 | + PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFinalizer(void); | ||
| 28 | 32 | #endif | |
| 29 | 33 | ||
| 30 | 34 | struct _frame; /* Avoid including frameobject.h */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,7 @@ typedef struct { | |||
| 59 | 59 | ``async def`` keywords) */ | |
| 60 | 60 | #define CO_COROUTINE 0x0080 | |
| 61 | 61 | #define CO_ITERABLE_COROUTINE 0x0100 | |
| 62 | + #define CO_ASYNC_GENERATOR 0x0200 | ||
| 62 | 63 | ||
| 63 | 64 | /* These are no longer used. */ | |
| 64 | 65 | #if 0 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -61,6 +61,37 @@ PyObject *_PyAIterWrapper_New(PyObject *aiter); | |||
| 61 | 61 | PyObject *_PyCoro_GetAwaitableIter(PyObject *o); | |
| 62 | 62 | PyAPI_FUNC(PyObject *) PyCoro_New(struct _frame *, | |
| 63 | 63 | PyObject *name, PyObject *qualname); | |
| 64 | + | ||
| 65 | + /* Asynchronous Generators */ | ||
| 66 | + | ||
| 67 | + typedef struct { | ||
| 68 | + _PyGenObject_HEAD(ag) | ||
| 69 | + PyObject *ag_finalizer; | ||
| 70 | + | ||
| 71 | + /* Flag is set to 1 when hooks set up by sys.set_asyncgen_hooks | ||
| 72 | + were called on the generator, to avoid calling them more | ||
| 73 | + than once. */ | ||
| 74 | + int ag_hooks_inited; | ||
| 75 | + | ||
| 76 | + /* Flag is set to 1 when aclose() is called for the first time, or | ||
| 77 | + when a StopAsyncIteration exception is raised. */ | ||
| 78 | + int ag_closed; | ||
| 79 | + } PyAsyncGenObject; | ||
| 80 | + | ||
| 81 | + PyAPI_DATA(PyTypeObject) PyAsyncGen_Type; | ||
| 82 | + PyAPI_DATA(PyTypeObject) _PyAsyncGenASend_Type; | ||
| 83 | + PyAPI_DATA(PyTypeObject) _PyAsyncGenWrappedValue_Type; | ||
| 84 | + PyAPI_DATA(PyTypeObject) _PyAsyncGenAThrow_Type; | ||
| 85 | + | ||
| 86 | + PyAPI_FUNC(PyObject *) PyAsyncGen_New(struct _frame *, | ||
| 87 | + PyObject *name, PyObject *qualname); | ||
| 88 | + | ||
| 89 | + #define PyAsyncGen_CheckExact(op) (Py_TYPE(op) == &PyAsyncGen_Type) | ||
| 90 | + | ||
| 91 | + PyObject *_PyAsyncGenValueWrapperNew(PyObject *); | ||
| 92 | + | ||
| 93 | + int PyAsyncGen_ClearFreeLists(void); | ||
| 94 | + | ||
| 64 | 95 | #endif | |
| 65 | 96 | ||
| 66 | 97 | #undef _PyGenObject_HEAD | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -107,6 +107,7 @@ PyAPI_FUNC(void) _PyGC_Fini(void); | |||
| 107 | 107 | PyAPI_FUNC(void) PySlice_Fini(void); | |
| 108 | 108 | PyAPI_FUNC(void) _PyType_Fini(void); | |
| 109 | 109 | PyAPI_FUNC(void) _PyRandom_Fini(void); | |
| 110 | + PyAPI_FUNC(void) PyAsyncGen_Fini(void); | ||
| 110 | 111 | ||
| 111 | 112 | PyAPI_DATA(PyThreadState *) _Py_Finalizing; | |
| 112 | 113 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -148,6 +148,9 @@ typedef struct _ts { | |||
| 148 | 148 | Py_ssize_t co_extra_user_count; | |
| 149 | 149 | freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; | |
| 150 | 150 | ||
| 151 | + PyObject *async_gen_firstiter; | ||
| 152 | + PyObject *async_gen_finalizer; | ||
| 153 | + | ||
| 151 | 154 | /* XXX signal handlers should also be here */ | |
| 152 | 155 | ||
| 153 | 156 | } PyThreadState; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,6 +48,7 @@ typedef struct _symtable_entry { | |||
| 48 | 48 | unsigned ste_child_free : 1; /* true if a child block has free vars, | |
| 49 | 49 | including free refs to globals */ | |
| 50 | 50 | unsigned ste_generator : 1; /* true if namespace is a generator */ | |
| 51 | + unsigned ste_coroutine : 1; /* true if namespace is a coroutine */ | ||
| 51 | 52 | unsigned ste_varargs : 1; /* true if block has varargs */ | |
| 52 | 53 | unsigned ste_varkeywords : 1; /* true if block has varkeywords */ | |
| 53 | 54 | unsigned ste_returns_value : 1; /* true if namespace uses return with | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,7 +13,6 @@ | |||
| 13 | 13 | to modify the meaning of the API call itself. | |
| 14 | 14 | """ | |
| 15 | 15 | ||
| 16 | - | ||
| 17 | 16 | import collections | |
| 18 | 17 | import concurrent.futures | |
| 19 | 18 | import heapq | |
@@ -28,6 +27,7 @@ | |||
| 28 | 27 | import traceback | |
| 29 | 28 | import sys | |
| 30 | 29 | import warnings | |
| 30 | + import weakref | ||
| 31 | 31 | ||
| 32 | 32 | from . import compat | |
| 33 | 33 | from . import coroutines | |
@@ -242,6 +242,13 @@ def __init__(self): | |||
| 242 | 242 | self._task_factory = None | |
| 243 | 243 | self._coroutine_wrapper_set = False | |
| 244 | 244 | ||
| 245 | + # A weak set of all asynchronous generators that are being iterated | ||
| 246 | + # by the loop. | ||
| 247 | + self._asyncgens = weakref.WeakSet() | ||
| 248 | + | ||
| 249 | + # Set to True when `loop.shutdown_asyncgens` is called. | ||
| 250 | + self._asyncgens_shutdown_called = False | ||
| 251 | + | ||
| 245 | 252 | def __repr__(self): | |
| 246 | 253 | return ('<%s running=%s closed=%s debug=%s>' | |
| 247 | 254 | % (self.__class__.__name__, self.is_running(), | |
@@ -333,13 +340,56 @@ def _check_closed(self): | |||
| 333 | 340 | if self._closed: | |
| 334 | 341 | raise RuntimeError('Event loop is closed') | |
| 335 | 342 | ||
| 343 | + def _asyncgen_finalizer_hook(self, agen): | ||
| 344 | + self._asyncgens.discard(agen) | ||
| 345 | + if not self.is_closed(): | ||
| 346 | + self.create_task(agen.aclose()) | ||
| 347 | + | ||
| 348 | + def _asyncgen_firstiter_hook(self, agen): | ||
| 349 | + if self._asyncgens_shutdown_called: | ||
| 350 | + warnings.warn( | ||
| 351 | + "asynchronous generator {!r} was scheduled after " | ||
| 352 | + "loop.shutdown_asyncgens() call".format(agen), | ||
| 353 | + ResourceWarning, source=self) | ||
| 354 | + | ||
| 355 | + self._asyncgens.add(agen) | ||
| 356 | + | ||
| 357 | + @coroutine | ||
| 358 | + def shutdown_asyncgens(self): | ||
| 359 | + """Shutdown all active asynchronous generators.""" | ||
| 360 | + self._asyncgens_shutdown_called = True | ||
| 361 | + | ||
| 362 | + if not len(self._asyncgens): | ||
| 363 | + return | ||
| 364 | + | ||
| 365 | + closing_agens = list(self._asyncgens) | ||
| 366 | + self._asyncgens.clear() | ||
| 367 | + | ||
| 368 | + shutdown_coro = tasks.gather( | ||
| 369 | + *[ag.aclose() for ag in closing_agens], | ||
| 370 | + return_exceptions=True, | ||
| 371 | + loop=self) | ||
| 372 | + | ||
| 373 | + results = yield from shutdown_coro | ||
| 374 | + for result, agen in zip(results, closing_agens): | ||
| 375 | + if isinstance(result, Exception): | ||
| 376 | + self.call_exception_handler({ | ||
| 377 | + 'message': 'an error occurred during closing of ' | ||
| 378 | + 'asynchronous generator {!r}'.format(agen), | ||
| 379 | + 'exception': result, | ||
| 380 | + 'asyncgen': agen | ||
| 381 | + }) | ||
| 382 | + | ||
| 336 | 383 | def run_forever(self): | |
| 337 | 384 | """Run until stop() is called.""" | |
| 338 | 385 | self._check_closed() | |
| 339 | 386 | if self.is_running(): | |
| 340 | 387 | raise RuntimeError('Event loop is running.') | |
| 341 | 388 | self._set_coroutine_wrapper(self._debug) | |
| 342 | 389 | self._thread_id = threading.get_ident() | |
| 390 | + old_agen_hooks = sys.get_asyncgen_hooks() | ||
| 391 | + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, | ||
| 392 | + finalizer=self._asyncgen_finalizer_hook) | ||
| 343 | 393 | try: | |
| 344 | 394 | while True: | |
| 345 | 395 | self._run_once() | |
@@ -349,6 +399,7 @@ def run_forever(self): | |||
| 349 | 399 | self._stopping = False | |
| 350 | 400 | self._thread_id = None | |
| 351 | 401 | self._set_coroutine_wrapper(False) | |
| 402 | + sys.set_asyncgen_hooks(*old_agen_hooks) | ||
| 352 | 403 | ||
| 353 | 404 | def run_until_complete(self, future): | |
| 354 | 405 | """Run until the Future is done. | |
@@ -1179,7 +1230,9 @@ def call_exception_handler(self, context): | |||
| 1179 | 1230 | - 'handle' (optional): Handle instance; | |
| 1180 | 1231 | - 'protocol' (optional): Protocol instance; | |
| 1181 | 1232 | - 'transport' (optional): Transport instance; | |
| 1182 | - - 'socket' (optional): Socket instance. | ||
| 1233 | + - 'socket' (optional): Socket instance; | ||
| 1234 | + - 'asyncgen' (optional): Asynchronous generator that caused | ||
| 1235 | + the exception. | ||
| 1183 | 1236 | ||
| 1184 | 1237 | New keys maybe introduced in the future. | |
| 1185 | 1238 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -276,7 +276,10 @@ def _format_coroutine(coro): | |||
| 276 | 276 | try: | |
| 277 | 277 | coro_code = coro.gi_code | |
| 278 | 278 | except AttributeError: | |
| 279 | - coro_code = coro.cr_code | ||
| 279 | + try: | ||
| 280 | + coro_code = coro.cr_code | ||
| 281 | + except AttributeError: | ||
| 282 | + return repr(coro) | ||
| 280 | 283 | ||
| 281 | 284 | try: | |
| 282 | 285 | coro_frame = coro.gi_frame | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -248,6 +248,10 @@ def close(self): | |||
| 248 | 248 | """ | |
| 249 | 249 | raise NotImplementedError | |
| 250 | 250 | ||
| 251 | + def shutdown_asyncgens(self): | ||
| 252 | + """Shutdown all active asynchronous generators.""" | ||
| 253 | + raise NotImplementedError | ||
| 254 | + | ||
| 251 | 255 | # Methods scheduling callbacks. All these return Handles. | |
| 252 | 256 | ||
| 253 | 257 | def _timer_handle_cancelled(self, handle): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -87,6 +87,7 @@ def distb(tb=None, *, file=None): | |||
| 87 | 87 | 64: "NOFREE", | |
| 88 | 88 | 128: "COROUTINE", | |
| 89 | 89 | 256: "ITERABLE_COROUTINE", | |
| 90 | + 512: "ASYNC_GENERATOR", | ||
| 90 | 91 | } | |
| 91 | 92 | ||
| 92 | 93 | def pretty_flags(flags): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments