@@ -67,6 +67,212 @@ except NameError:
67
67
}.memoize()
68
68
69
69
val PYDEVD_ASYNC_PLUGIN = """
70
+ import asyncio
71
+ import asyncio.events as events
72
+ import os
73
+ import sys
74
+ import threading
75
+ from contextlib import contextmanager
76
+ from heapq import heappop
77
+
78
+
79
+ def apply(loop=None):
80
+ '''Patch asyncio to make its event loop reentrant.'''
81
+ loop = loop or asyncio.get_event_loop()
82
+ if not isinstance(loop, asyncio.BaseEventLoop):
83
+ raise ValueError('Can\'t patch loop of type %s' % type(loop))
84
+ if getattr(loop, '_nest_patched', None):
85
+ # already patched
86
+ return
87
+ _patch_asyncio()
88
+ _patch_loop(loop)
89
+ _patch_task()
90
+ _patch_tornado()
91
+
92
+
93
+ def _patch_asyncio():
94
+ '''
95
+ Patch asyncio module to use pure Python tasks and futures,
96
+ use module level _current_tasks, all_tasks and patch run method.
97
+ '''
98
+ def run(future, *, debug=False):
99
+ loop = asyncio.get_event_loop()
100
+ loop.set_debug(debug)
101
+ return loop.run_until_complete(future)
102
+
103
+ if sys.version_info >= (3, 6, 0):
104
+ asyncio.Task = asyncio.tasks._CTask = asyncio.tasks.Task = \
105
+ asyncio.tasks._PyTask
106
+ asyncio.Future = asyncio.futures._CFuture = asyncio.futures.Future = \
107
+ asyncio.futures._PyFuture
108
+ if sys.version_info < (3, 7, 0):
109
+ asyncio.tasks._current_tasks = asyncio.tasks.Task._current_tasks # noqa
110
+ asyncio.all_tasks = asyncio.tasks.Task.all_tasks # noqa
111
+ if not hasattr(asyncio, '_run_orig'):
112
+ asyncio._run_orig = getattr(asyncio, 'run', None)
113
+ asyncio.run = run
114
+
115
+
116
+ def _patch_loop(loop):
117
+ '''Patch loop to make it reentrant.'''
118
+
119
+ def run_forever(self):
120
+ with manage_run(self), manage_asyncgens(self):
121
+ while True:
122
+ self._run_once()
123
+ if self._stopping:
124
+ break
125
+ self._stopping = False
126
+
127
+ def run_until_complete(self, future):
128
+ with manage_run(self):
129
+ f = asyncio.ensure_future(future, loop=self)
130
+ if f is not future:
131
+ f._log_destroy_pending = False
132
+ while not f.done():
133
+ self._run_once()
134
+ if self._stopping:
135
+ break
136
+ if not f.done():
137
+ raise RuntimeError(
138
+ 'Event loop stopped before Future completed.')
139
+ return f.result()
140
+
141
+ def _run_once(self):
142
+ '''
143
+ Simplified re-implementation of asyncio's _run_once that
144
+ runs handles as they become ready.
145
+ '''
146
+ now = self.time()
147
+ ready = self._ready
148
+ scheduled = self._scheduled
149
+ while scheduled and scheduled[0]._cancelled:
150
+ heappop(scheduled)
151
+
152
+ timeout = (
153
+ 0 if ready or self._stopping
154
+ else min(max(scheduled[0]._when - now, 0), 86400) if scheduled
155
+ else None)
156
+ event_list = self._selector.select(timeout)
157
+ self._process_events(event_list)
158
+
159
+ end_time = self.time() + self._clock_resolution
160
+ while scheduled and scheduled[0]._when < end_time:
161
+ handle = heappop(scheduled)
162
+ ready.append(handle)
163
+
164
+ for _ in range(len(ready)):
165
+ if not ready:
166
+ break
167
+ handle = ready.popleft()
168
+ if not handle._cancelled:
169
+ handle._run()
170
+ handle = None
171
+
172
+ @contextmanager
173
+ def manage_run(self):
174
+ '''Set up the loop for running.'''
175
+ self._check_closed()
176
+ old_thread_id = self._thread_id
177
+ old_running_loop = events._get_running_loop()
178
+ try:
179
+ self._thread_id = threading.get_ident()
180
+ events._set_running_loop(self)
181
+ self._num_runs_pending += 1
182
+ if self._is_proactorloop:
183
+ if self._self_reading_future is None:
184
+ self.call_soon(self._loop_self_reading)
185
+ yield
186
+ finally:
187
+ self._thread_id = old_thread_id
188
+ events._set_running_loop(old_running_loop)
189
+ self._num_runs_pending -= 1
190
+ if self._is_proactorloop:
191
+ if (self._num_runs_pending == 0
192
+ and self._self_reading_future is not None):
193
+ ov = self._self_reading_future._ov
194
+ self._self_reading_future.cancel()
195
+ if ov is not None:
196
+ self._proactor._unregister(ov)
197
+ self._self_reading_future = None
198
+
199
+ @contextmanager
200
+ def manage_asyncgens(self):
201
+ old_agen_hooks = sys.get_asyncgen_hooks()
202
+ try:
203
+ self._set_coroutine_origin_tracking(self._debug)
204
+ if self._asyncgens is not None:
205
+ sys.set_asyncgen_hooks(
206
+ firstiter=self._asyncgen_firstiter_hook,
207
+ finalizer=self._asyncgen_finalizer_hook)
208
+ yield
209
+ finally:
210
+ self._set_coroutine_origin_tracking(False)
211
+ if self._asyncgens is not None:
212
+ sys.set_asyncgen_hooks(*old_agen_hooks)
213
+
214
+ def _check_running(self):
215
+ '''Do not throw exception if loop is already running.'''
216
+ pass
217
+
218
+ cls = loop.__class__
219
+ cls.run_forever = run_forever
220
+ cls.run_until_complete = run_until_complete
221
+ cls._run_once = _run_once
222
+ cls._check_running = _check_running
223
+ cls._check_runnung = _check_running # typo in Python 3.7 source
224
+ cls._nest_patched = True
225
+ cls._num_runs_pending = 0
226
+ cls._is_proactorloop = (
227
+ os.name == 'nt' and issubclass(cls, asyncio.ProactorEventLoop))
228
+ if sys.version_info < (3, 7, 0):
229
+ cls._set_coroutine_origin_tracking = cls._set_coroutine_wrapper
230
+
231
+
232
+ def _patch_task():
233
+ '''Patch the Task's step and enter/leave methods to make it reentrant.'''
234
+
235
+ def step(task, exc=None):
236
+ curr_task = curr_tasks.get(task._loop)
237
+ try:
238
+ step_orig(task, exc)
239
+ finally:
240
+ if curr_task is None:
241
+ curr_tasks.pop(task._loop, None)
242
+ else:
243
+ curr_tasks[task._loop] = curr_task
244
+
245
+ Task = asyncio.Task
246
+ if sys.version_info >= (3, 7, 0):
247
+
248
+ def enter_task(loop, task):
249
+ curr_tasks[loop] = task
250
+
251
+ def leave_task(loop, task):
252
+ curr_tasks.pop(loop, None)
253
+
254
+ asyncio.tasks._enter_task = enter_task
255
+ asyncio.tasks._leave_task = leave_task
256
+ curr_tasks = asyncio.tasks._current_tasks
257
+ step_orig = Task._Task__step
258
+ Task._Task__step = step
259
+ else:
260
+ curr_tasks = Task._current_tasks
261
+ step_orig = Task._step
262
+ Task._step = step
263
+
264
+
265
+ def _patch_tornado():
266
+ '''
267
+ If tornado is imported before nest_asyncio, make tornado aware of
268
+ the pure-Python asyncio Future.
269
+ '''
270
+ if 'tornado' in sys.modules:
271
+ import tornado.concurrent as tc
272
+ tc.Future = asyncio.Future
273
+ if asyncio.Future not in tc.FUTURES:
274
+ tc.FUTURES += (asyncio.Future,)
275
+
70
276
import asyncio
71
277
import functools
72
278
import sys
0 commit comments