Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPython integration 😎 #306

Open
wants to merge 1 commit into
base: ctx_result_consumption
Choose a base branch
from

Conversation

goodboy
Copy link
Owner

@goodboy goodboy commented Mar 20, 2022

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 gists for this:

TODO:

@goodboy goodboy added experiment Exploratory design and testing debugging labels Mar 20, 2022
@goodboy goodboy removed the debugging label Aug 1, 2022
@goodboy goodboy force-pushed the ipython_integration branch 4 times, most recently from 329ea3b to 206a523 Compare September 16, 2022 03:50
@goodboy goodboy force-pushed the ipython_integration branch 2 times, most recently from d0106b8 to a5c6941 Compare October 12, 2022 17:09
@goodboy goodboy mentioned this pull request Oct 27, 2022
8 tasks
@goodboy goodboy force-pushed the ctx_result_consumption branch 2 times, most recently from 8b93a1b to 2fe9a1a Compare December 12, 2022 20:26
@goodboy goodboy force-pushed the ipython_integration branch 3 times, most recently from af47834 to 8c9f1c4 Compare December 13, 2022 20:20
@goodboy
Copy link
Owner Author

goodboy commented Jan 14, 2023

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
>>>

@smurfix
Copy link

smurfix commented Jan 15, 2023

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.

@goodboy
Copy link
Owner Author

goodboy commented Jan 16, 2023

@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:

  • handling task-level crashes with a breakpoint() and engaging the REPL in that task's frame,
  • overriding the global hook to always allow using 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.

This code is originally written (with much thanks) by
@mikenerone:matrix.org. Adds a `tractor.trionics.ipython_embed()` which
is `trio` compatible and allows straight up `await async_func()` calls
in the REPL with expected default blocking semantics. More refinements
to come including user config loading and eventually a foundation for
what will be a console REPL + %magics for shipping work off to actor
clusters and manual respawn controls and thus probably eventually
obsoleting all the "parallel" stuff built into `ipython` B)

Probably pertains to #130
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
experiment Exploratory design and testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants