我有一个嵌入到C ++ MPI应用程序中的Python 3解释器。此应用程序加载脚本并将其传递给解释器。
当我在没有MPI启动器的情况下执行程序时(简单地调用./myprogram),脚本正确执行并且其“print”语句输出到终端。当脚本出错时,我使用PyErr_Print()在C ++端打印它。
但是,当我通过mpirun(甚至在单个进程中)执行程序时,我没有从python代码中的“print”获得任何输出。当我的脚本出错时,我也从PyErr_Print()中得不到任何东西。
我猜Python中处理标准输出的方式与MPI(实际上是Mpich)处理将进程的输出重定向到启动器并最终重定向到终端的方式不一致。
关于如何解决这个问题的任何想法?
我最终遇到了同样的问题(PyErr_Print
没有在mpirun工作)。追溯(涉及python3的一些gdb)并比较工作的东西(./myprogram)和非工作的东西(mpirun -np 1 ./myprogram),我最后在_io_TextIOWrapper_write_impl
的./Modules/_io/textio.c:1277
(顺便说一下python-3.6.0) )。
两次运行之间的唯一区别是self->line_buffering
是1对0(此时self
代表sys.stderr
)。然后,在pylifecycle.c:1128
中,我们可以看到谁决定了这个值:
if (isatty || Py_UnbufferedStdioFlag)
line_buffering = Py_True;
所以似乎MPI在启动程序之前对stderr做了一些事情,这使得它不是一个tty。我没有调查过mpirun中是否有一个选项可以将stty标志保留在stderr上...如果有人知道,那就很有意思了(尽管第二个想法mpi可能有充分的理由把他的文件描述符放在stdout和stderr的位置,例如,它的--output-filename。
有了这些信息,我可以提出2个快速解决方案:
1 /在启动python解释器的C代码中,在创建sys.stderr之前设置缓冲标志。代码变成:
Py_UnbufferedStdioFlag = 1; // force line_buffering for _all_ I/O
Py_Initialize();
这会在所有情况下将Python的追溯带回屏幕;但可能会给灾难性的I / O ......所以在调试模式下只能是一个可接受的解决方案。
2 /在python(嵌入式)脚本中,在最开始添加:
import sys
#sys.stderr.line_buffering = True # would be nice, but readonly attribute !
sys.stderr = open("error.log", 'w', buffering=1 )
然后,该脚本将回溯转储到此文件error.log。
我也尝试在PyErr_Print()之后立即添加对fflush(stderr)或fflush(NULL)的调用...但这不起作用(不知道为什么)。那是最好的解决方案。
[编辑]
经过多一点挖掘后,我在Python/pythonrun.c:57:static void flush_io(void);
找到了完美的功能。事实上它在此文件中的每个PyErr_Print之后调用。不幸的是它是静态的(仅存在于该文件中,在Python.h中没有引用它,至少在3.6.0中)。我将该函数从此文件复制到myprogram,结果确实完成了这项工作。
至于没有工作的fflush(stderr),那是因为sys.stderr有自己的内部缓冲。