Как перенаправить stderr в Python? - PullRequest
16 голосов
/ 24 декабря 2009

Я хотел бы записать все выходные данные скрипта Python. Я попробовал:

import sys

log = []

class writer(object):
    def write(self, data):
        log.append(data)

sys.stdout = writer()
sys.stderr = writer()

Теперь, если я "напечатаю" что-нибудь ", он регистрируется. Но если я сделаю, например, какую-нибудь синтаксическую ошибку, скажем «напечатать что-то #», она не будет зарегистрирована - вместо этого она войдет в консоль.

Как мне перехватить ошибки интерпретатора Python?

Я видел возможное решение здесь:

http://www.velocityreviews.com/forums/showpost.php?p=1868822&postcount=3

но второй пример входит в / dev / null - это не то, что я хочу. Я хотел бы войти в список, как мой пример выше или StringIO или что-то подобное ...

Кроме того, желательно, чтобы я не хотел создавать подпроцесс (и читать его stdout и stderr в отдельном потоке).

Ответы [ 8 ]

19 голосов
/ 24 декабря 2009

У меня есть программа, которую я написал для работы, которая записывает stderr в файл примерно так:

import sys
sys.stderr = open('C:\\err.txt', 'w')

так что это определенно возможно.

Я считаю, что ваша проблема в том, что вы создаете два экземпляра писателя.

Может быть, что-то вроде:

import sys

class writer(object):
    log = []

    def write(self, data):
        self.log.append(data)

logger = writer()
sys.stdout = logger
sys.stderr = logger
6 голосов
/ 24 декабря 2009

Я не могу придумать легкий путь. Стандартная ошибка процесса Python находится на более низком уровне, чем объект файла Python (C против Python).

Вы можете обернуть скрипт python во второй скрипт python и использовать subprocess.Popen. Также возможно, что вы можете использовать магию в одном сценарии:

import os
import subprocess
import sys

cat = subprocess.Popen("/bin/cat", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
os.close(sys.stderr.fileno())
os.dup2(cat.stdin.fileno(), sys.stderr.fileno())

А затем с помощью select.poll () регулярно проверяйте cat.stdout для поиска выходных данных.

Да, похоже, работает.

Проблема, которую я предвижу, заключается в том, что в большинстве случаев что-то, выводимое на stderr python, указывает, что оно собирается завершиться. Более обычный способ справиться с этим - использовать исключения.

--------- Edit

Каким-то образом я пропустил функцию os.pipe ().

import os, sys
r, w = os.pipe()
os.close(sys.stderr.fileno())
os.dup2(w, sys.stderr.fileno())

Тогда читайте из r

5 голосов
/ 10 января 2017

На самом деле, если вы используете Linux / Mac OS, вы можете просто использовать перенаправление файлов, чтобы сделать это. Например, если вы собираетесь запустить «a.py» и записать все сгенерированные им сообщения в файл «a.out», это будет просто

python a.py 2>&1 > a.out

Первая часть перенаправляет stderr в stdout, а вторая - в файл с именем a.out.

Более подробный список операторов перенаправления в Linux / Unix см. В https://askubuntu.com/questions/420981/how-do-i-save-terminal-output-to-a-file

5 голосов
/ 24 декабря 2009

Вы не можете делать в коде Python ничего, что могло бы фиксировать ошибки во время компиляции того же кода. Как это могло? Если компилятор не может завершить компиляцию кода, он не запустит код, поэтому ваше перенаправление еще даже не вступило в силу.

Вот тут и появляется ваш (нежелательный) подпроцесс. Вы можете написать код Python, который перенаправляет стандартный вывод, а затем вызывает интерпретатор Python для компиляции некоторого другого фрагмента кода.

1 голос
/ 27 августа 2018

Начиная с Python 3.5 вы можете использовать contextlib.redirect_stderr

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        help(pow)
0 голосов
/ 16 октября 2018

Чтобы добавить к ответу Неда, трудно во время компиляции фиксировать ошибки на лету.

Вы можете написать несколько операторов печати в своем скрипте и вывести stdout в файл, он прекратит запись в файл при возникновении ошибки. Для отладки кода вы можете проверить последний зарегистрированный вывод и проверить ваш сценарий после этой точки.


Примерно так:

# Add to the beginning of the script execution(eg: if __name__ == "__main__":).
from datetime import datetime
dt = datetime.now()
script_dir = os.path.dirname(os.path.abspath(__file__))      # gets the path of the script
stdout_file = script_dir+r'\logs\log'+('').join(str(dt.date()).split("-"))+r'.log'
sys.stdout = open(stdout_file, 'w')

Это создаст файл журнала и передаст операторы печати в файл.

1012 *
*

Примечание: Остерегайтесь escape-символов в вашем пути к файлу, объединяясь с script_dir во второй строке последней в коде. Вы можете захотеть что-то похожее на необработанную строку. Вы можете проверить эту тему для этого.

0 голосов
/ 17 сентября 2018

Python не выполнит ваш код в случае ошибки. Но вы можете импортировать ваш сценарий в другой сценарий исключения. Пример:

Script.py

print 'something#

FinalScript.py

from importlib.machinery import SourceFileLoader

try:
    SourceFileLoader("main", "<SCRIPT PATH>").load_module()
except Exception as e:
    # Handle the exception here
0 голосов
/ 08 февраля 2016
import sys
import tkinter

# ********************************************

def mklistenconsswitch(*printf: callable) -> callable:
    def wrapper(*fcs: callable) -> callable:
        def newf(data):
            [prf(data) for prf in fcs]
        return newf
    stdoutw, stderrw = sys.stdout.write, sys.stderr.write
    funcs = [(wrapper(sys.stdout.write, *printf), wrapper(sys.stderr.write, *printf)), (stdoutw, stderrw)]
    def switch():
        sys.stdout.write, sys.stderr.write = dummy = funcs[0]
        funcs[0] = funcs[1]
        funcs[1] = dummy
    return switch

# ********************************************

def datasupplier():
    i = 5.5
    while i > 0:
        yield i
        i -= .5

def testloop():
    print(supplier.__next__())
    svvitch()
    root.after(500, testloop)

root = tkinter.Tk()
cons = tkinter.Text(root)
cons.pack(fill='both', expand=True)
supplier = datasupplier()
svvitch = mklistenconsswitch(lambda text: cons.insert('end', text))
testloop()
root.mainloop()
...