xdev.embeding module

Minor quality of life improvements over IPython.embed

For a full featured debugger see [PypiIPDB], although note that this still suffers from issues like [IpythonIssue62], which this code contains workarounds for.

The latest workaround is the “xdev.snapshot” function (new in 1.5.0).

Either when calling xdev.embed() or at any point in your code you can call the xdev.snapshot() function. This allows you to pickle your interpreter state to disk, and startup a new IPython shell (where [IpythonIssue62] doesnt apply) and essentially simulates the embedded stack frame.

export PYTHONBREAKPOINT=xdev.embed

python -c “if 1:

def foo(arg1):

a = 2 b = 3 + arg1 breakpoint()

foo(432)

echo "if 1:

    def foo(arg1):
        a = 2
        b = 3 + arg1
        import xdev
        xdev.embed()

" > mymod.py

# This new features does not handle __main__ modules yet, so we use this
# workaround to demo the feature.
python -c "import mymod; mymod.foo(23)"

This will embed you in an IPython session with the prompt:

================
____ _  _ ___  ____ ___  ___  _ _  _ ____
|___ |\/| |__] |___ |  \ |  \ | |\ | | __
|___ |  | |__] |___ |__/ |__/ | | \| |__]


================
[xdev.embed] embedding
[xdev.embed] use xdev.fix_embed_globals() to address https://github.com/ipython/ipython/issues/62
[xdev.embed] to debug in a fresh IPython context, run:

xdev.snapshot()

             and then follow instructions
[xdev.embed] set EXIT_NOW or qqq=1 to hard exit on unembed
[util] calling IPython.embed()
In [1]:

And calling xdev.snapshot() will result in:

In [1]: xdev.snapshot()
   ...:
not_pickleable = [
    '__builtins__',
]
Could not pickle 1 variables
# To debug in a fresh IPython session run:

ipython -i -c "if 1:
    fpath = '/home/joncrall/.cache/xdev/states/state_2023-10-23T120433-5.pkl'
    from xdev.embeding import load_snapshot
    load_snapshot(fpath, globals())
"

Now, executing that code in a new terminal will startup a fresh IPython session (where issue 62 no longer applies because IPython was the entry point), and it loads your entired state into memory as long as it is pickleable. This is very handy for interactive development, but like all workaround it does still have limitations - namely everything needs to be pickleable. However, it has several advantages:

  • Quickly restart from the breakpoint state because it is saved in a pickle file.

  • Have more than one independent interpreter session looking at the same state.

References

xdev.embeding._stop_rich_live_contexts()[source]
xdev.embeding.embed(parent_locals=None, parent_globals=None, exec_lines=None, remove_pyqt_hook=True, n=0)[source]

Starts interactive session. Similar to keyboard command in matlab. Wrapper around IPython.embed.

Note

Contains helper logic to allow the developer to more easilly exit the program if embed is called in a loop. Specifically if you want to quit and not embed again, then set qqq=1 before exiting the embed session.

SeeAlso:

embed_on_exception()

xdev.embeding.breakpoint()[source]
xdev.embeding._devcheck_frames()[source]
xdev.embeding.load_snapshot(fpath, parent_globals=None)[source]

Loads a snapshot of a local state from disk.

Parameters:
  • fpath (str | PathLike) – the path to the snapshot file to load

  • parent_globals (dict | None) – The state dictionary to update. Should be given as globals(). If unspecified, it is inferred via frame inspection.

xdev.embeding.snapshot(parent_ns=None, n=0)[source]

Save a snapshot of the local state to disk.

Serialize all names in scope to a pickle and save to disk. Also print the command that will let the user start an IPython session with this namespace.

Parameters:
  • parent_ns (dict) – A dictionary containing all of the available names in the scope to export.

  • n (int) – if parent_ns is unspecified, infer locals and globals from the frame n stack levels above the namespace this function is called in.

Todo

  • [ ] need to handle __main__

References

xdev.embeding.embed_if_requested(n=0)[source]

Calls xdev.embed conditionally based on the environment.

Useful in cases where you want to leave the embed call around, but you dont want it to trigger in normal circumstances.

Specifically, embed is only called if the environment variable XDEV_EMBED exists or if –xdev-embed is in sys.argv.

class xdev.embeding.EmbedOnException(before_embed=None)[source]

Bases: object

Context manager which embeds in ipython if an exception is thrown

SeeAlso:

embed()

xdev.embeding.fix_embed_globals()[source]

HACK adds current locals() to globals(). Can be dangerous.

References

https://github.com/ipython/ipython/issues/62

Solves the following issue:

def foo():

x = 5 # You embed here import xdev xdev.embed()

‘’’ Now you try and run this line manually but you get a NameError

result = [x + i for i in range(10)]

No problem, just use. It changes all local variables to globals xdev.fix_embed_globals()

‘’’ result = [x + i for i in range(10)]

foo()