我只想查看进程的状态,是否可以将控制台附加到进程中,因此我可以调用进程内的函数并查看一些全局变量。
这个过程运行得更好而不会受到影响(当然性能会有所下降)
答案 0 :(得分:55)
这会中断你的进程(除非你在一个线程中启动它),但你可以使用code
模块启动一个Python控制台:
import code
code.interact()
这将阻止,直到用户通过执行exit()
退出交互式控制台。
code
模块至少可以在Python v2.6中使用,可能还有其他模块。
我倾向于将这种方法与我的Linux工作的信号结合使用(对于Windows,见下文)。我把它放在Python脚本的顶部:
import code
import signal
signal.signal(signal.SIGUSR2, lambda sig, frame: code.interact())
然后从kill -SIGUSR2 <PID>
的shell中触发它,其中<PID>
是进程ID。然后,该过程将停止正在执行的操作并显示控制台:
Python 2.6.2 (r262:71600, Oct 9 2009, 17:53:52)
[GCC 3.4.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
通常从那里我将加载远程调试器的服务器端组件,如优秀的WinPDB。
Windows不是POSIX兼容的操作系统,因此不提供与Linux相同的信号。但是,Python v2.2 and above expose a Windows-specific signal SIGBREAK
(按CTRL
+ Pause/Break
触发)。这不干扰正常CTRL
+ C
(SIGINT
)操作,因此是一个方便的替代方案。
因此,以上的便携式但略显丑陋的版本是:
import code
import signal
signal.signal(
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR2"),
lambda sig, frame: code.interact()
)
这种方法的优点:
这是我在生产环境中使用的代码,它将加载WinPDB的服务器端(如果可用)并回退到打开Python控制台。
# Break into a Python console upon SIGUSR1 (Linux) or SIGBREAK (Windows:
# CTRL+Pause/Break). To be included in all production code, just in case.
def debug_signal_handler(signal, frame):
del signal
del frame
try:
import rpdb2
print
print
print "Starting embedded RPDB2 debugger. Password is 'foobar'"
print
print
rpdb2.start_embedded_debugger("foobar", True, True)
rpdb2.setbreak(depth=1)
return
except StandardError:
pass
try:
import code
code.interact()
except StandardError as ex:
print "%r, returning to normal program flow" % ex
import signal
try:
signal.signal(
vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
debug_signal_handler
)
except ValueError:
# Typically: ValueError: signal only works in main thread
pass
答案 1 :(得分:40)
如果您可以访问该程序的源代码,则可以相对轻松地添加此功能。
请参阅Recipe 576515:Debugging a running python process by interrupting and providing an interactive prompt (Python)
引用:
这提供了允许任何python的代码 使用它的程序 在当前点中断,并且 通过普通的python进行交流 互动控制台。这允许 本地人,全局和相关的程序 国家待调查,以及 调用任意函数和 类。
要使用,进程应导入 模块,并在任何时候调用listen() 在启动期间。打断这个 进程,脚本可以运行 直接给出过程的ID 调试的过程作为参数。
rconsole提供了大致相同概念的另一种实现方式。来自文档:
rconsole是一个远程Python控制台 自动完成,可以 用来检查和修改 正在运行的脚本的命名空间。
要在脚本中调用,请执行以下操作:
from rfoo.utils import rconsole
rconsole.spawn_server()
要从shell附加,请执行以下操作:
$ rconsole
安全说明:rconsole侦听器 以spawn_server()开头 接受任何本地连接,并可能 因此在共享中使用是不安全的 托管或类似的环境!
答案 2 :(得分:21)
使用pyrasite-shell。我无法相信它运作良好,但确实如此。 “给它一个pid,获得一个shell ”。
$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # If YAMA activated, see below.
$ pyrasite-shell 16262
Pyrasite Shell 2.0
Connected to 'python my_script.py'
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> globals()
>>> print(db_session)
>>> run_some_local_function()
>>> some_existing_local_variable = 'new value'
这将启动python shell,可以访问运行python进程的globals()和locals()变量,以及其他很棒的东西。
仅在Ubuntu上亲自测试过,但似乎也适合OSX。
改编自this answer。
注意:关闭ptrace_scope
属性的行仅对于使用CONFIG_SECURITY_YAMA
构建的内核/系统是必需的。在敏感环境中小心使用ptrace_scope,因为它可能会引入某些安全漏洞。有关详细信息,请参阅here。
答案 3 :(得分:6)
为什么不简单地使用pdb模块?它允许您停止脚本,检查元素值,并逐行执行代码。由于它是基于Python解释器构建的,因此它还提供了经典解释器提供的功能。要使用它,只需将这两行放在您希望停止并检查它的代码中:
import pdb
pdb.set_trace()
答案 4 :(得分:4)
此处描述了另一种可能,即不向python脚本添加内容:
https://wiki.python.org/moin/DebuggingWithGdb
不幸的是,这个解决方案还需要一些预先考虑,至少在某种程度上你需要使用带有调试符号的python版本。
答案 5 :(得分:1)
使用PyCharm,我在Ubuntu中无法连接到进程。解决此问题的方法是禁用YAMA。有关详细信息,请参阅askubuntu
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
答案 6 :(得分:1)
pdb_attach 非常适合我们将 Python 调试器附加到长时间运行的进程。
作者描述如下: 这个包是为了应对调试长时间运行的进程的挫败感而制作的。将 pdb 附加到正在运行的 python 程序并查看发生了什么不是很好吗?嗯,这正是 pdb-attach 所做的。
在您的主模块中进行如下设置:
pdb_attach.listen()
当程序运行时,通过从命令行调用 pdb_attach 将程序的 PID 和传递给 $ python -m pdb_attach <PID> 50000
(Pdb) # Interact with pdb as you normally would
的端口附加到它:
def top_3_words(text):
new_list = []
new_text = text.lower().split()
for word in new_text:
for char in word:
if char.isalpha():
pass
else:
new_text.remove(char)
Count = Counter(new_text)
most_occur = Count.most_common(3)
for l in most_occur:
new_list.append(l[0])
return new_list