如何在 gdb 中使用 IPython 交互式 shell?||如何让制表符完成在 gdb 的 Python 交互式 (pi) shell 中工作?
问题描述
通常,在 Python shell 中,我可以按两次 Tab 键来获取提示列表.
Normally, in Python shells I can press Tab twice to get a list of prompts.
另一方面,在 gdb 的 Python shell(pi
或 python-interactive
命令)中,只有 gdb 样式的补全.
On the other hand, in gdb's Python shell (pi
or python-interactive
command), there's only gdb-style completion.
示例会话:
$ gdb -q
(gdb) pi
>>> gdb
<module 'gdb' from '/usr/share/gdb/python/gdb/__init__.py'>
>>> gdb.TabTab
... nothing ...
>>> show TabTab
Display all 148 possibilities? (y or n)
ada exec-direction record
agent exec-done-display remote
annotate exec-file-mismatch remoteaddresssize
[...]
Python 自动补全应该至少是这样的.
Python auto complete should be at least like this.
$ python
Python 3.X.Y ...
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.TabTab
sys.abiflags sys.hash_info
sys.addaudithook( sys.hexversion
sys.api_version sys.implementation
[...]
如何在 gdb 中获得相同/相似的东西?特别是带有制表符完成的 IPython shell 很好.
How can I get the same/similar thing in gdb? in particular IPython shell with tab completion is fine.
尝试失败:
这个解决方案
import readline
import rlcompleter
readline.parse_and_bind("tab: complete")
当在 sys.
或类似之后按下 Tab 时,使 shell 输出一个文字选项卡.
makes the shell output a literal tab when Tab is pressed after sys.
or similar.
至少它确实适用于标识符选项卡完成(aTabTab
)确实列出了一些条目)
At least it does work for identifier tab completion (aTabTab
) does list some entries)
看起来这是因为与 gdb 的一些交互——get_completer_delims
每次都会重置为某个值,如果运行上面的代码,则 tab 补全 outsidegdb 切换到Python 模式";也是.
Looks like this is because of some interaction with gdb -- get_completer_delims
get reset to some value every time, and if the code above is run then the tab completion outside gdb switches to "Python mode" as well.
使用background_zmq_ipython
会导致分段错误,因为某些gdb API(如gdb.Value
)无法从主线程外读取.
Use background_zmq_ipython
causes segmentation fault, because some gdb API (such as gdb.Value
) cannot be read from outside the main thread.
使用 IPython.embed()
还可以使 Tab 输出文字制表符.
Use IPython.embed()
also make Tab output a literal tab character.
gdb 官方文档 https://sourceware.org/gdb/current/onlinedocs/gdb/Completion.html 没有提到任何关于 Python 的内容.
The official gdb documentation https://sourceware.org/gdb/current/onlinedocs/gdb/Completion.html doesn't mention anything about Python.
解决方案
我想出了几种方法.
我想不出任何使用内置 readline
库的方法.有关详细信息,请参阅问题中的失败尝试.
I can't figure out any way to use the built-in readline
library.
See the failed attempts in the question for more details.
在调用
IPython.embed
之前重置 stdout 和 stderr.
Reset stdout and stderr before calling
IPython.embed
.
import sys
sys.stdout=sys.__stdout__
sys.stderr=sys.__stderr__
import IPython
IPython.embed(colors="neutral")
记得在之后重置 stdout 和 stderr 以避免可能出现的问题.
Remember to reset stdout and stderr afterwards to avoid possible issues.
参考:
- 如何识别脚本是否是在 tty 上运行?
- IPython.embed() 不使用终端颜色
当所有 stdin、stdout 和 stderr 都是 tty 设备时,IPython 只会使用制表符补全和颜色.默认情况下 gdb sys.stdout 和 sys.stderr 是 gdb 包装器(因此 gdb 可以按 Enter 继续")超过分页限制时)
IPython will only use tab-completion and color when all of stdin, stdout and stderr are tty devices. By default gdb sys.stdout and sys.stderr are gdb wrappers (so that gdb can do "press enter to continue" when pagination limit is exceeded)
启动内核,单独启动控制台.
Start a kernel, and start a console separately.
import IPython
IPython.embed_kernel()
阅读控制台输出,了解如何连接,以及如何从远程控制台退出终端.
Read the console output on how to connect, and how to exit the terminal from the remote console.
使用我的其他答案也可以通过编程方式远程退出终端.
Using my other answer it's also possible to exit the terminal remotely programmatically.
启动内核(复杂方式)
阅读IPython的源码,了解如何手动启动内核,并在过程中获取连接文件路径.
Read the source code of IPython to figure out how to start a kernel manually, and get the connection file path in the process.
import threading
import subprocess
import IPython
from ipykernel.kernelapp import IPKernelApp
import sys
app = IPKernelApp.instance()
app.initialize([])
app.kernel.user_module = sys.modules[__name__]
app.kernel.user_ns = locals()
app.shell.set_completer_frame()
def console_thread_run():
subprocess.run(["jupyter-console", "--no-confirm-exit", "--existing",
app.abs_connection_file
])
app.kernel.do_shutdown(restart=False)
console_thread=threading.Thread(target=console_thread_run)
console_thread.start()
app.start()
使用 background_zmq_ipython
启动内核(访问内部属性,随时可能中断).
Start a kernel using background_zmq_ipython
(accesses internal property, might break at any time).
主要区别在于sys.stdin
、sys.stdout
等不受影响.见 background_zmq_ipython
文档和 ipython - 为 Python 脚本提供远程 shell - 堆栈内存溢出了解更多详细信息.
The main difference is that sys.stdin
, sys.stdout
etc. are not affected. See background_zmq_ipython
documentation and ipython - Provide remote shell for Python script - Stack Overflow for more details.
import subprocess
import logging
import threading
from background_zmq_ipython import IPythonBackgroundKernelWrapper
kernel_wrapper = IPythonBackgroundKernelWrapper(
banner="", # default value is "Hello from background-zmq-ipython."
user_ns=globals(),
logger=logging.Logger("IPython", level=logging.INFO)
# no handler
# otherwise it will print "To connect another client to this IPython kernel" ...
)
kernel_wrapper.thread=threading.main_thread() # workaround for assertions
subprocess.Popen(["python", "-c",
(
"from jupyter_console.app import ZMQTerminalIPythonApp;"
"app = ZMQTerminalIPythonApp();"
"app.initialize();"
"app.shell.own_kernel=True;"
"app.start();"
),
"--no-confirm-exit",
"--existing", kernel_wrapper.connection_filename
])
kernel_wrapper._thread_loop()
还展示了如何更改消息保持内核活动";到关闭内核".
Also show how to change the message "keeping kernel alive" to "Shutting down kernel".
相关文章