【问题标题】:How can I use IPython interactive shell in gdb? || How can I get tab-completion to work in gdb's Python-interactive (pi) shell?如何在 gdb 中使用 IPython 交互式 shell? ||如何让制表符完成在 gdb 的 Python 交互式 (pi) shell 中工作?
【发布时间】:2022-01-10 19:44:45
【问题描述】:

通常,在 Python shell 中,我可以按两次 Tab 键来获取提示列表。

另一方面,在 gdb 的 Python shell(pipython-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 很好。


尝试失败:

  • This solution

    import readline
    import rlcompleter
    readline.parse_and_bind("tab: complete")
    

    当在sys. 或类似之后按下Tab 时,使shell 输出一个文字标签。

    至少它确实适用于标识符选项卡完成(a<kbd>Tab</kbd><kbd>Tab</kbd>)确实列出了一些条目)

    看起来这是因为与 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 的内容。

【问题讨论】:

    标签: python gdb


    【解决方案1】:

    我想出了几种方法。

    我想不出任何使用内置readline 库的方法。 有关详细信息,请参阅问题中的失败尝试。


    1. 在调用IPython.embed之前重置stdout和stderr。

      import sys
      sys.stdout=sys.__stdout__
      sys.stderr=sys.__stderr__
      
      import IPython
      IPython.embed(colors="neutral")
      

      记得在之后重置 stdout 和 stderr 以避免可能出现的问题。

      参考:

      所有 stdin、stdout 和 stderr 都是 tty 设备时,IPython 只会使用制表符补全和颜色。 默认情况下 gdb sys.stdout 和 sys.stderr 是 gdb 包装器(这样 gdb 可以“按 Enter 继续” 超过分页限制时)

    2. 启动内核,单独启动控制台。

      import IPython
      IPython.embed_kernel()
      

      阅读控制台输出,了解如何连接,以及如何从远程控制台退出终端。

      使用my other answer 也可以通过编程方式远程退出终端。

    3. 启动内核(复杂方式)

      阅读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()
      
    4. 使用background_zmq_ipython 启动内核(访问内部属性,随时可能中断)。

      主要区别在于sys.stdinsys.stdout等不受影响。见background_zmq_ipython 文档和ipython - Provide remote shell for Python script - 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()
      

      还展示了如何将消息“保持内核活动”更改为“关闭内核”。

    【讨论】:

    • 还回答:“我如何在 gdb 中使用 IPython?” ■ 同样为方便起见,还可以从 IPython 调用 gdb 命令:from IPython.core.magic import register_line_magic; register_line_magic("!")(lambda line: gdb.execute(line)) — 参考:@987654325 @
    • 要自动执行此操作,请在适当的位置添加app.shell.client.execute('from ...');
    • 要自动执行此操作,请在适当的位置添加 app.shell.client.execute('from ...', silent=True, store_history=False);
    猜你喜欢
    • 2012-06-01
    • 2011-03-12
    • 2010-10-31
    • 2011-07-24
    • 2017-11-24
    • 2017-06-16
    • 2013-01-25
    • 1970-01-01
    • 2013-03-23
    相关资源
    最近更新 更多