Open goodboy opened 2 years ago
Turns out there might be a better solution for what we actually want (await <blah>
inside any REPL, but mostly our debugger UX).
from @smurfix on gitter:
import trio
import greenback as gb
AW=gb.await_
async def wat():
await trio.sleep(0.1)
return "yes"
async def run():
await gb.ensure_portal()
breakpoint()
pass
trio.run(run)
which can be called from repl like:
(Pdb) AW(wat())
'yes'
with follow up from @oremanj actually using pdb++
💥 :
$ python3.8 -c "import trio, pdb, greenback; trio.run(greenback.with_portal_run_sync, lambda: breakpoint())"
(Pdb++) greenback.await_(trio.sleep(1))
<1sec delay>
also a further extended example 🏄🏼
$ python3.8 -c "import trio, greenback, code; trio.run(greenback.with_portal_run_sync, code.interact)"
>>> import trio
>>> from greenback import await_ as aw, async_context as acm
>>> async def task(interval, msg):
... while True:
... await trio.sleep(interval)
... print(msg)
...
>>> with acm(trio.open_nursery()) as nursery:
... nursery.start_soon(task, 2, "every two seconds")
... nursery.start_soon(task, 0.75, "every 3/4 second")
... aw(task(1.7, "every 1.7 seconds"))
...
every 3/4 second
every 3/4 second
every 1.7 seconds
every two seconds
every 3/4 second
every 3/4 second
every 1.7 seconds
every 3/4 second
every two seconds
^CTraceback (most recent call last):
File "/usr/lib/python3.8/code.py", line 90, in runcode
exec(code, self.locals)
File "<console>", line 4, in <module>
File "/usr/lib/python3.8/site-packages/greenback/_impl.py", line 653, in await_
raise exception_from_greenbacked_function
File "<console>", line 3, in task
File "/usr/lib/python3.8/site-packages/trio/_timeouts.py", line 75, in sleep
await sleep_until(trio.current_time() + seconds)
File "/usr/lib/python3.8/site-packages/trio/_timeouts.py", line 56, in sleep_until
await sleep_forever()
File "/usr/lib/python3.8/site-packages/trio/_timeouts.py", line 40, in sleep_forever
await trio.lowlevel.wait_task_rescheduled(lambda _: trio.lowlevel.Abort.SUCCEEDED)
File "/usr/lib/python3.8/site-packages/trio/_core/_traps.py", line 166, in wait_task_rescheduled
return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
File "/usr/lib/python3.8/site-packages/outcome/_sync.py", line 111, in unwrap
raise captured_error
File "/usr/lib/python3.8/site-packages/trio/_core/_run.py", line 1178, in raise_cancel
raise KeyboardInterrupt
KeyboardInterrupt
>>>
To make this work anywhere we should teach Trio's task creation code to unconditionally create a Greenback portal in every new task; otherwise debugging at random breakpoints won't work.
@smurfix agreed, this is already an issue with debugging single task crashes within a nursery as well.
Currently on a task crash, you can't breakpoint()
at the (specific task's) error stack frame and instead always end up at the nursery exit; so, an extended nursery is needed for both of these cases:
breakpoint()
and engaging the REPL in that task's frame, await
in the REPL (and obviously making the particular REPL plug-gable in the longer run)Further, this probably plays best with the idea of a OCONursery
(one-cancels-one) style nursery where every task has an associated individual cancel scope that can be managed independently of other tasks such that respawns can happen on a per-task-failure scenario.
Like it sounds, with more refinements to come!
This is a POC and seems to do about what I'd like; would appreciate lurker feedback 😉
ping @mikenerone, original author of the base
gist
s for this:TODO: