通常,在 Python shell 中,我可以按两次 Tab 键来获取提示列表。
另一方面,在 gdb 的 Python shell(
pi
或 python-interactive
命令)中,只有 gdb 风格的补全。
示例会话:
$ 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
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 就很好。
失败的尝试:
import readline
import rlcompleter
readline.parse_and_bind("tab: complete")
当在 sys.
或类似操作之后按下 Tab
时,使 shell 输出文本制表符。
至少它确实可以完成标识符选项卡(
aTabTab
)确实列出了一些条目)
看起来这是因为与 gdb 的一些交互 -
get_completer_delims
每次都会重置为某个值,如果运行上面的代码,则制表符补全 outside gdb 也会切换到“Python 模式”。
使用
background_zmq_ipython
会导致分段错误,因为某些gdb API(例如gdb.Value
)无法从主线程外部读取。
使用
IPython.embed()
还可以使 Tab 输出文字制表符。
官方gdb文档https://sourceware.org/gdb/current/onlinedocs/gdb/Completion.html没有提及任何有关Python的内容。
我想出了一些方法。
我不知道如何使用内置的
readline
库。
有关更多详细信息,请参阅问题中的失败尝试。
在调用
IPython.embed
之前重置 stdout 和 stderr。
import sys
sys.stdout=sys.__stdout__
sys.stderr=sys.__stderr__
import IPython
IPython.embed(colors="neutral")
记住之后重置 stdout 和 stderr 以避免可能的问题。
参考:
IPython 仅当 stdin、stdout 和 stderr 的 all 为 tty 设备时才使用制表符补全和颜色。 默认情况下,gdb sys.stdout 和 sys.stderr 是 gdb 包装器(以便 gdb 可以执行“按 Enter 继续” 当超过分页限制时)
启动一个内核,并单独启动一个控制台。
import IPython
IPython.embed_kernel()
阅读控制台输出,了解如何连接以及如何从远程控制台退出终端。
使用我的其他答案也可以通过编程方式远程退出终端。
启动内核(复杂的方式)
阅读IPython源码,了解如何手动启动内核,并获取进程中的连接文件路径。
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
启动内核(访问内部属性,可能随时中断)。
主要区别是
sys.stdin
、sys.stdout
等不受影响。参见background_zmq_ipython
文档和 ipython - 为 Python 脚本提供远程 shell - Stack Overflow 了解更多详细信息。
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()
还展示了如何将消息“保持内核活动”更改为“关闭内核”。
所有方法对我来说都不是开箱即用的。
这就是我在 Debian 13 上的做法:
$ gdb
GNU gdb (Debian 13.1-3) 13.1 ...
(gdb) set $foo = 123
(gdb) print $foo
$1 = 123
(gdb) pi
>>> import IPython
>>> import sys
>>> sys.stderr=sys.__stderr__
>>> sys.stdout=sys.__stdout__
>>> IPython.embed_kernel()
0.00s - Debugger warning: It seems that frozen modules are being used, which may...
To connect another client to this kernel, use:
--existing kernel-713400.json
在另一个外壳中:
$ jupyter console --existing kernel-713400.json
Jupyter console 6.4.4
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0]
IPython 8.5.0 -- An enhanced Interactive Python. Type '?' for help.
# gdb.exec<TAB> - works
In [1]: gdb.execute("print $foo")
$2 = 123