inducer / pudb

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

tab completion in internal shell causes escape mode mess #336

Open rofl0r opened 5 years ago

rofl0r commented 5 years ago

admittedly, in sabotage linux we have a quite unusual python setup:

nonetheless, i'm reporting the issue i experienced with pudb 2018-1 here for the record.

when the internal shell is selected, and one switched there using '!', as soon as TAB is pressed for the first time for completion (there needs to be something written, for example "a", which would then be completed to e.g. "abs" or "assert"), escapes are broken and every subsequent keypress is displayed as eg ^CC or similar, but not recognized as such. makes it impossible to exit the shell via CTRL-X. or use backspace, etc. only way out is to kill pudb.

fortunately, i noticed that if i used e.g. the "classic" shell once, then switched to "internal" using preferences, everything behaves as exactly as it should. this lead me to produce the following patch, which fixes the issue for me. apparently the readline completer needs to be "initialized" with the code in the patch at least once.

--- pudb-2018.1/pudb/debugger.py
+++ pudb-2018.1.org/pudb/debugger.py
@@ -1831,7 +1831,13 @@

         def run_cmdline(w, size, key):
             if CONFIG["shell"] == "internal":
-                return toggle_cmdline_focus(w, size, key)
+                curframe = self.debugger.curframe
+                self.screen.stop()
+                import pudb.shell as shell
+                shell.setup_readline(curframe.f_globals, curframe.f_locals)
+                ret = toggle_cmdline_focus(w, size, key)
+                self.screen.start()
+                return ret
             else:
                 return run_external_cmdline(w, size, key)

--- pudb-2018.1/pudb/shell.py
+++ pudb-2018.1.org/pudb/shell.py
@@ -72,14 +72,7 @@

 custom_shell_dict = {}

-
-def run_classic_shell(globals, locals, first_time=[True]):
-    if first_time:
-        banner = "Hit Ctrl-D to return to PuDB."
-        first_time.pop()
-    else:
-        banner = ""
-
+def setup_readline(globals, locals):
     ns = SetPropagatingDict([locals, globals], locals)

     from pudb.settings import get_save_config_path
@@ -97,6 +90,18 @@
             readline.read_history_file(hist_file)
         except IOError:
             pass
+
+    return hist_file
+
+
+def run_classic_shell(globals, locals, first_time=[True]):
+    if first_time:
+        banner = "Hit Ctrl-D to return to PuDB."
+        first_time.pop()
+    else:
+        banner = ""
+
+    hist_file = setup_readline(globals, locals)

     from code import InteractiveConsole
     cons = InteractiveConsole(ns)

(the self.screen.stop/start is probably not necessary, it's a leftover from my first attempts at fixing)

asmeurer commented 5 years ago

libedit is known to be quite buggy (it's also included by default on OS X). I believe you can install readline with https://pypi.org/project/gnureadline/ (though I'm not sure how that package works if PuDB needs to be modified to support it).

Can you send your diff as a pull request? We would need to test it to make sure that it doesn't break the normal readline environments.

rofl0r commented 5 years ago

I believe you can install readline with https://pypi.org/project/gnureadline/ (though I'm not sure how that package works if PuDB needs to be modified to support it).

i'm aware that i could install GNU readline instead, but the whole point of using libedit is to use less bloated components. that way we can have a python 2.7 package that extracts to a mere 22.2 MB. using a static-linked readline just for python would add even more bloat than simply using GNU readline distro-wide ;)

Can you send your diff as a pull request?

thanks, i will accept your invitation and give it a shot

inducer commented 5 years ago

I don't understand this at all--neither the bug nor the patch, sorry. The internal shell is implemented purely in Urwid, with no code path that goes through readline. So introducing readline into that code path seems like a weird thing to do.

My read on what's happening is that your Curses library fails to properly set up the terminal to receive the Tab key, resulting in the breakage you observe. Rather than calling into readline to (mostly, by voodoo) fix that bit of terminal state, I feel like there are two options that would be strongly preferable:

rofl0r commented 5 years ago

is that so ?

i've seen this snippet and assumed it was used:

        def cmdline_tab_complete(w, size, key):
            from rlcompleter import Completer

although i didn't step through it

inducer commented 5 years ago

That's true--but rlcompleter is independent of readline. All it does is, given a command line fragment and a scope, produce some options for Tab completion.

inducer commented 5 years ago

Hmm, now that I've said that: It does import readline and set itself as the completer there. So if importing readline has some side effect in your setup (it shouldn't!), that's probably what's causing the issue.