|
16 | 16 | from newrelic.core.trace_cache import trace_cache |
17 | 17 |
|
18 | 18 |
|
19 | | -def remove_from_cache(task): |
| 19 | +def remove_from_cache_callback(task): |
20 | 20 | cache = trace_cache() |
21 | 21 | cache.task_stop(task) |
22 | 22 |
|
23 | 23 |
|
24 | | -def propagate_task_context(task): |
| 24 | +def wrap_create_task(task): |
25 | 25 | trace_cache().task_start(task) |
26 | | - task.add_done_callback(remove_from_cache) |
| 26 | + task.add_done_callback(remove_from_cache_callback) |
27 | 27 | return task |
28 | 28 |
|
29 | 29 |
|
30 | | -def _bind_loop(loop, *args, **kwargs): |
| 30 | +def _instrument_event_loop(loop): |
| 31 | + if loop and hasattr(loop, "create_task") and not hasattr(loop.create_task, "__wrapped__"): |
| 32 | + wrap_out_function(loop, "create_task", wrap_create_task) |
| 33 | + |
| 34 | + |
| 35 | +def _bind_set_event_loop(loop, *args, **kwargs): |
31 | 36 | return loop |
32 | 37 |
|
33 | 38 |
|
34 | | -def wrap_create_task(wrapped, instance, args, kwargs): |
35 | | - loop = _bind_loop(*args, **kwargs) |
| 39 | +def wrap_set_event_loop(wrapped, instance, args, kwargs): |
| 40 | + loop = _bind_set_event_loop(*args, **kwargs) |
36 | 41 |
|
37 | | - if loop and not hasattr(loop.create_task, "__wrapped__"): |
38 | | - wrap_out_function(loop, "create_task", propagate_task_context) |
| 42 | + _instrument_event_loop(loop) |
39 | 43 |
|
40 | 44 | return wrapped(*args, **kwargs) |
41 | 45 |
|
42 | 46 |
|
| 47 | +def wrap__lazy_init(wrapped, instance, args, kwargs): |
| 48 | + result = wrapped(*args, **kwargs) |
| 49 | + # This logic can be used for uvloop, but should |
| 50 | + # work for any valid custom loop factory. |
| 51 | + |
| 52 | + # A custom loop_factory will be used to create |
| 53 | + # a new event loop instance. It will then run |
| 54 | + # the main() coroutine on this event loop. Once |
| 55 | + # this coroutine is complete, the event loop will |
| 56 | + # be stopped and closed. |
| 57 | + |
| 58 | + # The new loop that is created and set as the |
| 59 | + # running loop of the duration of the run() call. |
| 60 | + # When the coroutine starts, it runs in the context |
| 61 | + # that was active when run() was called. Any tasks |
| 62 | + # created within this coroutine on this new event |
| 63 | + # loop will inherit that context. |
| 64 | + |
| 65 | + # Note: The loop created by loop_factory is never |
| 66 | + # set as the global current loop for the thread, |
| 67 | + # even while it is running. |
| 68 | + loop = instance._loop |
| 69 | + _instrument_event_loop(loop) |
| 70 | + |
| 71 | + return result |
| 72 | + |
| 73 | + |
43 | 74 | def instrument_asyncio_base_events(module): |
44 | | - wrap_out_function(module, "BaseEventLoop.create_task", propagate_task_context) |
| 75 | + wrap_out_function(module, "BaseEventLoop.create_task", wrap_create_task) |
45 | 76 |
|
46 | 77 |
|
47 | 78 | def instrument_asyncio_events(module): |
48 | 79 | if hasattr(module, "_BaseDefaultEventLoopPolicy"): # Python >= 3.14 |
49 | | - wrap_function_wrapper(module, "_BaseDefaultEventLoopPolicy.set_event_loop", wrap_create_task) |
50 | | - else: # Python <= 3.13 |
51 | | - wrap_function_wrapper(module, "BaseDefaultEventLoopPolicy.set_event_loop", wrap_create_task) |
| 80 | + wrap_function_wrapper(module, "_BaseDefaultEventLoopPolicy.set_event_loop", wrap_set_event_loop) |
| 81 | + elif hasattr(module, "BaseDefaultEventLoopPolicy"): # Python <= 3.13 |
| 82 | + wrap_function_wrapper(module, "BaseDefaultEventLoopPolicy.set_event_loop", wrap_set_event_loop) |
| 83 | + |
| 84 | + |
| 85 | +# For Python >= 3.11 |
| 86 | +def instrument_asyncio_runners(module): |
| 87 | + if hasattr(module, "Runner") and hasattr(module.Runner, "_lazy_init"): |
| 88 | + wrap_function_wrapper(module, "Runner._lazy_init", wrap__lazy_init) |
0 commit comments