Skip to content

Commit 69abca3

Browse files
committed
🔧 Update async_eval version
1 parent d212261 commit 69abca3

File tree

2 files changed

+209
-3
lines changed

2 files changed

+209
-3
lines changed

helpers/poetry.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/java/com/uriyyo/evaluate_async_code/AsyncPyDebugUtils.kt

+206
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,212 @@ except NameError:
6767
}.memoize()
6868

6969
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+
70276
import asyncio
71277
import functools
72278
import sys

0 commit comments

Comments
 (0)