Last active
February 7, 2020 09:25
-
-
Save kousu/5a83e248b230cdafad076c0c682888ec to your computer and use it in GitHub Desktop.
Better python breakpoint()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Try 'python test.py' with the above breakpoint hook loaded. Try adding/removing/editing the variables. | |
def g(a,b,c): | |
print(locals()) | |
print() | |
print(globals()) | |
print() | |
a+=b+c | |
print("Before the breakpoint", locals()) | |
breakpoint() | |
print("After the breakpoint", locals()) | |
return c | |
def h(): | |
y = 4 | |
print("g = ", g(2,y,6)) | |
return y*3 | |
h() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I bequeth any rights I might have in this to the public domain | |
# Put this in ~/.local/lib/python3.7/site-packages/usercustomize.py | |
# and then breakpoint() will be plainer -- it will be a standard python REPL. | |
# (*caveat: tab completion isn't fully the same as cpython's built-in thing) | |
print("REMEMBER YOU HAVE THE WEIRD BREAKPOINT LOADED") | |
def breakpoint(*args, **kwargs): | |
""" | |
Set breakpoint() to drop into the interactive interpreter | |
the default breakpoint() drops to Pdb, which can then get to the interpreter via 'interact', but without tab completion, so it's not really the same | |
Bug: it doesn't know to restrict tab-completing imports to only modules, or how to tab-complete dots. | |
and maybe other quirks | |
""" | |
import sys | |
frame = sys._getframe(1) # experimentally, 1 frame back is the right number of frames | |
# https://stackoverflow.com/questions/35115208/is-there-any-way-to-combine-readline-rlcompleter-and-interactiveconsole-in-pytho | |
import readline | |
from rlcompleter import Completer | |
readline.parse_and_bind("tab: complete") | |
readline.set_completer(Completer(frame.f_locals).complete) # TODO: expand the set of completions | |
# stackoverflow.com/a/1396386/2898673 | |
import code | |
# asdsfgsd InteractiveConsole is broken. it claims to take 'locals' but it actually passes them through to exec() as the *globals* argument | |
# and exec() has a bunch of automatic things it needs to do (for one: fill in __builtins__ if not otherwise there, otherwise no code will work at all) | |
f_locals = frame.f_locals # from pdb.py: | |
# The f_locals dictionary is updated from the actual frame | |
# locals whenever the .f_locals accessor is called, so we | |
# cache it here to ensure that modifications are not overwritten. | |
class InteractiveConsole(code.InteractiveConsole): | |
def runcode(self, code): | |
try: | |
exec(code, frame.f_globals, f_locals) | |
except SystemExit: | |
raise | |
except: | |
self.showtraceback() | |
InteractiveConsole(locals=None).interact(banner="", exitmsg="") # Note: we've made locals irrelevant | |
# Next level: re-export the variables, if changed | |
# Pdb can't even do this; can ipython? | |
# based on https://stackoverflow.com/questions/34650744/modify-existing-variable-in-locals-or-frame-f-locals / http://pydev.blogspot.com/2014/02/changing-locals-of-frame-frameflocals.html | |
# This is cpython-specific!! | |
# Every access frame.f_locals *is a getter function* secretly, and every access to it refreshes the contents from the internal cpython data "fast" cache | |
# to go the other way you need to call PyFrame_LocalsToFast *before* each write | |
# ...or..something? | |
# Surprise feature: `del` gets passed through immediately to the underlying storage | |
import ctypes | |
# LocalsToFast() syncs the state of f_locals with the internal cpython VM | |
# To take effect, it seems to need to be called before *each* write | |
# it also needs to be called at least once, to handle synchronize when all variables were deleted, | |
# hence this weird do-while mess. | |
ctypes.pythonapi.PyFrame_LocalsToFast( | |
ctypes.py_object(frame), | |
ctypes.c_int(1)) | |
for k, v in f_locals.items(): | |
frame.f_locals[k] = v | |
ctypes.pythonapi.PyFrame_LocalsToFast( | |
ctypes.py_object(frame), | |
ctypes.c_int(1)) | |
import sys | |
sys.breakpointhook = breakpoint | |
del sys | |
del breakpoint |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment