inducer / pudb

Full-screen console debugger for Python
https://documen.tician.de/pudb/
Other
2.93k stars 225 forks source link

Variable ListBox crashes for long variables repr #652

Open alexfikl opened 2 months ago

alexfikl commented 2 months ago

Describe the bug

When using stringifier = repr and there are several variables with a long repr in the local scope, pudb crashes somewhere in urwid/widget/listbox.py::755 with an error like

urwid.widget.listbox.ListBoxError: Widget <VariableWidget selectable flow widget> at position 1 within listbox calculated 111 rows but rendered 110!

This started happening in the last 2-3 months, I think, but I haven't had time to track it down.

Full traceback ``` Traceback (most recent call last): File "/usr/lib/python3.12/site-packages/pudb/__init__.py", line 162, in runscript dbg._runscript(mainpyfile) File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 534, in _runscript self.run(statement) File "/usr/lib/python3.12/bdb.py", line 600, in run exec(cmd, globals, locals) File "", line 1, in File "pudb-bug.py", line 9, in r = np.sqrt(x ** 2 + y ** 2 + z ** 2) ^ File "/usr/lib/python3.12/bdb.py", line 94, in trace_dispatch return self.dispatch_return(frame, arg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/bdb.py", line 153, in dispatch_return self.user_return(frame, arg) File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 479, in user_return self.interaction(frame) File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 436, in interaction self.ui.call_with_ui(self.ui.interaction, exc_tuple, File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 2556, in call_with_ui return f(*args, **kwargs) ^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 2879, in interaction self.event_loop() File "/usr/lib/python3.12/site-packages/pudb/debugger.py", line 2834, in event_loop canvas = toplevel.render(self.size, focus=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 711, in render canv = get_delegate(self).render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/frame.py", line 481, in render body = self.body.render((maxcol, maxrow - ftrim - htrim), focus and self.focus_part == "body") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/attr_map.py", line 158, in render canv = self._original_widget.render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/columns.py", line 1026, in render w.render(w_size, focus=focus and self.focus_position == i), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 711, in render canv = get_delegate(self).render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/pile.py", line 873, in render canv = w.render(w_size, focus=focus and item_focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/attr_map.py", line 158, in render canv = self._original_widget.render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/pile.py", line 873, in render canv = w.render(w_size, focus=focus and item_focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/attr_map.py", line 158, in render canv = self._original_widget.render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 711, in render canv = get_delegate(self).render(size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/widget.py", line 112, in cached_render canv = fn(self, size, focus=focus) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/site-packages/urwid/widget/listbox.py", line 755, in render raise ListBoxError( urwid.widget.listbox.ListBoxError: Widget at position 1 within listbox calculated 111 rows but rendered 110! ```

To Reproduce

For a simple reproducer

import numpy as np

rng = np.random.default_rng()

x = rng.random(512)
y = rng.random(512)
z = rng.random(512)

r = np.sqrt(x ** 2 + y ** 2 + z ** 2)

and then running python -m pudb reproducer.py and pressing n a few times will cause the crash above.

Versions

mvanderkamp commented 2 months ago

I managed to reproduce, but with the following additions:

mvanderkamp commented 2 months ago

Anyway this looks more like an urwid than pudb bug, and I suspect it's got something to do with the way numpy formats its strings, since I can't reproduce just with a long regular python list of floating point numbers.

mvanderkamp commented 2 months ago

If it helps, just a single one of those numpy arrays is sufficient to trigger the traceback for me.

mvanderkamp commented 2 months ago

Ooh actually to be even more precise it's something to do with the newline characters. If I had to guess I'd say that urwid has a bug with how it handles special characters.

What I did:

import random
floats = [random.random() for _ in range(512)]
columns = [floats[i::5] for i in range(5)]
rows = list(zip(*columns))
strings = '\n'.join(', '.join(str(x) for x in row) for row in rows)

This skips the numpy dependency and, by comparing how the different variables behave, you can see that it's really only when you enable line wrapping and use the str stringifier on the strings variable that urwid hits a traceback.

I'm not sure why str is the one to trigger it instead of repr. Probably something to do with how I included the newlines.