Перенаправление FORTRAN (вызванного через F2PY) вывода в Python
Я пытаюсь выяснить, как перенаправить вывод из некоторого кода FORTRAN, для которого я создал интерфейс Python, используя F2PY. Я пробовал:
from fortran_code import fortran_function
stdout_holder = sys.stdout
stderr_holder = sys.stderr
sys.stdout = file("/dev/null","w")
fortran_function()
sys.stdout.close()
sys.stderr.close()
sys.stdout = stdout_holder
sys.stderr = stderr_holder
Это де-факто метод перенаправления вывода в Python, но в этом случае он не работает (т.е. вывод все равно отображается).
Я нашел почтовую рассылку с 2002 года, в которой говорилось: "Можно читать сообщения с устройств pts, например, ttysnoop делает это". Информация о ttysnoop кажется довольно сложной для поиска в Интернете (я не думаю, что она была обновлена через несколько лет, например, первый результат в Google для "ttysnoop" имеет только мертвые ссылки на tarballs, RPM и .deb), и этот запрос для порта в OS X получил ответ "Не повезло, для этого требуются некоторые специфичные для Linux функции utmp, которые я не могу создать".
Я открыт для любых предложений о том, как перенаправить вывод (ему не нужно использовать ttysnoop).
Спасибо!
Ответы
Ответ 1
Фреймы stdin и stdout наследуются совместно используемой библиотекой C.
from fortran_code import fortran_function
import os
print "will run fortran function!"
# open 2 fds
null_fds = [os.open(os.devnull, os.O_RDWR) for x in xrange(2)]
# save the current file descriptors to a tuple
save = os.dup(1), os.dup(2)
# put /dev/null fds on 1 and 2
os.dup2(null_fds[0], 1)
os.dup2(null_fds[1], 2)
# *** run the function ***
fortran_function()
# restore file descriptors so I can print the results
os.dup2(save[0], 1)
os.dup2(save[1], 2)
# close the temporary fds
os.close(null_fds[0])
os.close(null_fds[1])
print "done!"
Ответ 2
Здесь контекстный менеджер, который я недавно написал и нашел полезным, потому что у меня была аналогичная проблема с distutils.ccompiler.CCompiler.has_function
во время работы над pymssql. Я также использовал подход дескриптора файла, но я использовал контекстный менеджер. Вот что я придумал:
import contextlib
@contextlib.contextmanager
def stdchannel_redirected(stdchannel, dest_filename):
"""
A context manager to temporarily redirect stdout or stderr
e.g.:
with stdchannel_redirected(sys.stderr, os.devnull):
if compiler.has_function('clock_gettime', libraries=['rt']):
libraries.append('rt')
"""
try:
oldstdchannel = os.dup(stdchannel.fileno())
dest_file = open(dest_filename, 'w')
os.dup2(dest_file.fileno(), stdchannel.fileno())
yield
finally:
if oldstdchannel is not None:
os.dup2(oldstdchannel, stdchannel.fileno())
if dest_file is not None:
dest_file.close()
Контекст, почему я создал это, находится в этом сообщении в блоге. Похоже на твое мнение.
Я использую его так: setup.py
:
with stdchannel_redirected(sys.stderr, os.devnull):
if compiler.has_function('clock_gettime', libraries=['rt']):
libraries.append('rt')