小能豆

如何在 Pycharm 中使用 Popen 调试运行的代码

py

我正在运行从其他人那里继承的代码库,该代码库大量使用用户输入。因此,我使用 来运行它subprocess.Popen。以下是示例。以下脚本 ( caller.py) 调用第三方代码。

from subprocess import Popen, PIPE, STDOUT
import sys
user_input = ['John', '555']

communicate_argument = '\n'.join(user_input)
p = Popen([sys.executable, 'example2.py'], stdout=PIPE, stdin=PIPE, stderr=STDOUT, encoding='utf-8')

stdout, stderr = p.communicate(communicate_argument)

print(stdout)

以下脚本(example.py)通过接受来自用户的几个输入参数来模拟我提供的源代码的行为:

name = input('What is your name\n')
age = input('What is your age\n')

print('You are {}, and you are {} years old'.format(name, age))

运行代码一切正常,我得到了预期的输出。

调试代码部分有效,但部分无效。调试器成功附加到子进程,p因此放置的任何断点都将起作用。但是,调试控制台example.py似乎没有成功附加到子进程。当我尝试在调试控制台中输入一些变量时,它们不会打印出来,即使它们在我的调试会话中显示为活动变量。


阅读 18

收藏
2024-12-30

共1个答案

小能豆

你遇到的问题是因为 subprocess.Popen 启动的子进程和主进程之间的输入/输出流被重定向,从而导致调试控制台无法正常附加到子进程。以下是一些方法来调试子进程代码并解决问题。


方法 1: 使用 Pdb 直接在子进程中调试

可以在 example.py 中插入 Python 的内置调试器 pdb,如下所示:

import pdb

name = input('What is your name\n')
pdb.set_trace()  # 设置断点
age = input('What is your age\n')

print('You are {}, and you are {} years old'.format(name, age))

这样,在运行 caller.py 时,调试器会启动,你可以通过控制台输入变量查看和调试子进程中的代码。


方法 2: 使用 subprocess.run 并传递 stderrstdout

如果你希望调试更简单,可以用 subprocess.run 替代 Popen,并将子进程的输出直接显示在主进程的终端中。

修改 caller.py 如下:

from subprocess import run
import sys

user_input = ['John', '555']

communicate_argument = '\n'.join(user_input)
result = run(
    [sys.executable, 'example2.py'],
    input=communicate_argument,
    text=True  # 等价于 encoding='utf-8'
)

print(result.stdout)

这样,调试器可以正确附加到子进程,并且输出会直接显示在主终端中。


方法 3: 使用调试工具支持的调试器

如果你使用的是 IDE(如 PyCharm 或 VS Code),可以配置调试器以附加到子进程:

  1. VS Code:
  2. 打开 launch.json 文件。
  3. 添加以下配置:

    json { "name": "Python: Attach to Subprocess", "type": "python", "request": "launch", "program": "caller.py", "console": "integratedTerminal", "subProcess": true }

  4. 启动调试器,子进程也会被附加到。

  5. PyCharm:

  6. 启用 “Attach to subprocess automatically while debugging” 选项(在 Preferences > Build, Execution, Deployment > Python Debugger 中)。
  7. 运行调试器,它会自动附加到子进程。

方法 4: 模拟直接交互的环境

如果你必须在调试会话中直接控制子进程的输入/输出,可以避免将 stdinstdout 重定向,允许子进程直接与用户交互。

修改 caller.py

from subprocess import Popen
import sys

# 不重定向输入输出
p = Popen([sys.executable, 'example2.py'])
p.wait()  # 等待子进程完成

这种方法将允许 example2.py 的调试控制台正常工作,但用户输入需要手动提供。


总结

  • 简单调试:在子进程代码中使用 pdb
  • IDE 调试:配置 IDE 支持附加到子进程。
  • 交互式调试:避免重定向 stdinstdout
  • 更友好的运行:使用 subprocess.run 替代 Popen

选择适合你需求的方法即可。

2024-12-30