从 ipython 捕获库 f2py 调用标准输出

Capturing library f2py call stdout from ipython

我正在使用带有 Python 3 内核的 Jupyter notebook。

如果我运行:

import scipy.optimize
scipy.optimize.minimize(
    lambda _: 1,
    0,
    method='COBYLA',
    options={'iprint': 1, 'disp': True, 'maxiter': 2})

我希望将诊断优化信息打印到 ipython 笔记本上。但是,这会打印到控制台。我怀疑是这种情况,因为优化例程是在 Fortran 中实现的,并通过 f2py 在 scipy 中进行接口。 COBYLA Fortran 文件进行实际打印。

如何将 Fortran 子例程输出通过管道传输到 ipython 笔记本?据我了解,它应该与调用已编译的 C 函数相同 - 那么为什么不共享标准输出?

简短的回答是你不能。不容易。以下 enhancement proposal 到 IPython/Jupyter 协议可以涵盖的用例之一。虽然它还没有被接受,也不会很快发生。

(挥手)的原因是,当使用 Python 时,你可以 monkeypatch sys.stdin/sys.stdout/sys.stderr 并写入一个类似文件的界面,重定向来做"The Right Thing"™,虽然当它是一个fortran/c/...函数时,它们通常会直接打开对应于原始流的文件句柄,并且事后无法更改。

唯一的解决方案是控制进程的启动方式,并提前更改文件描述符,因此建议 "kernel nany"。


让我们开发(在 OP 进一步问题之后)。

Python print 是一个不直接打印到标准输出的函数,它实际上写入 sys.stdout 除非另有说明。如果你签入正常 python shell:

>>> import sys
>>> sys.stdout
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>

你可以看到它是一个文件句柄的直接包装器。

如果您在笔记本中执行相同操作(而不是在 IPython 终端中,那是另一回事),您将看到 <ipykernel.iostream.OutStream at 0x104602be0> 这是 ZMQ 协议周围的代理对象。在 IPython 内核中,前一个流存储在 sys.__stdout__ 中,因此您可以尝试使用

sys.__stdout__.write('Hello Whosebug\n')

这将在您的笔记本服务器的终端中打印 "Hello Whosebug"。 不要忘记触发流被刷新的 \n

并不是说这不是 Jupyter 行为,而是 IPython 行为。 Jupyter 端不关心你是怎么做的,只要你通过 ZMQ 发送标准输出。 Haskell 内核可能通过提供它自己的 io 模块来做同样的事情。

捕获进程 stdout 是一种解决方案(内核保姆提案涵盖)但它有其自身的缺点。在 Python 级别重定向更简单,因为 sys.stdout 就是为此而设计的。

这种行为既不是错误也不是 "Feature",有人可能会争辩说 subprocess/f2py/pyc,等等...应该能够处理非标准的 stdout/stderr,作为论据, nanny 是帮助解决这些情况的解决方法,即