Как сохранить цветную отдачу python при использовании тройника на Windows - PullRequest
1 голос
/ 10 февраля 2020

В моем сценарии Windows 10 я хочу напечатать произвольный вывод консоли python (print(), sys.exit(), et c) как в консоль, так и в файл журнала. У меня нет контроля над некоторыми частями кода (внешние python пакеты), поэтому я не могу использовать какой-то специальный механизм ведения журнала.

После некоторых исследований я нашел инструмент tee.exe в UnxUtils, которая выполняет эту задачу почти так, как я хочу.

Моя проблема - сохранить цвет, генерируемый python colorama пакетом. Есть ли способ сделать это? В настоящее время tee.exe удаляет цвет.

Ответ, который я ищу, не должен полагаться на tee.exe, он просто ближе всего подходит к реальному решению. То, что я ищу, должно делать следующее:

  • любой вывод командной строки появляется как в командной строке, так и в файле журнала (STOUT и STDERR)
  • вывод появляется на командная строка в реальном времени. Бонусные баллы, если это также верно для файла журнала.
  • цвет сохраняется в командной строке. Бонусные баллы, если файл журнала не содержит артефактов, связанных с цветом.

Пока у меня есть следующее:

Python file teetest.py :

import sys
import colorama

print("Test")
print("2nd Test")

colorama.init(autoreset=True)
print(colorama.Fore.RED + 'Color Test')
colorama.deinit(autoreset=True)

sys.exit("Error_Test")

Пакетный файл teetest.bat :

@echo off
python teetest.py 2>&1 | tee log.txt
pause

Мой вывод выглядит следующим образом (командная строка и файл журнала идентичны, без цвета):

Test
2nd Test
Color Test
Error_Test

Решение, которое я ищу, выведет приведенное выше в командную строку, поэтому слова Color Test будут красного цвета.

Редактировать:

Кажется, что tee.exe не виноват. Вместо этого, colorama удаляет символы ANSI для контроля цвета, поэтому цвет теряется при прохождении через tee.exe.

Из руководства по colorama:

Colorama делает эту работу на Windows также, оборачивая stdout, удаляя найденные им последовательности ANSI (которые будут отображаться как gobbledygook на выходе), и конвертируя их в соответствующие вызовы win32 для изменения состояния терминала.

Функция Colorama init() предлагает параметр strip, который, если False, заставляет colorama не удалять символы ANSI. Это, в свою очередь, позволяет написать пользовательский tee.py, который делает то же самое, что и tee.exe, как указано пользователем @martineau ниже. В нем мы можем вызывать колораму и правильно обрабатывать цвет.

Это может быть работоспособным решением, но у него все еще есть недостаток, что мне придется заменить все вызовы колорамы init() на init(strip=False) в моем исходный код python, что, в свою очередь, приведет к тому, что символы ANSI появятся в выходных данных, если код был вызван без перенаправления через tee.py.

На самом деле это может быть наиболее близким к правильному решению здесь. , Если кто-то может предложить другие идеи, я весь слух, но боюсь, что шансы невелики.

1 Ответ

1 голос
/ 10 февраля 2020

Я не знаю, как это будет работать в отношении colorama, но, будучи неудовлетворенным несколькими tee утилитами для Windows, которые я нашел в Интернете, я написал свою собственную в Python (3 .Икс).

Возможно, вам придется изменить его в соответствии со своими потребностями, но это должно быть хорошим началом.

mytee.py:

"""
    Copies stdin to stdout (screen) *and* the specified file.
"""
import fileinput
import os
from pathlib import Path
import sys

SHOW_FULL_PATH = False
DIVIDER = True
DIV_CH = ' '

if len(sys.argv) != 2:
    raise SystemExit('Usage: mytee <filepath>')

try:
    inp = fileinput.input(())  # Read from stdin.
    path = Path(sys.argv[1])
    stdout_write = sys.stdout.write
    stdout_flush = sys.stdout.flush
    # Assumes .py in same dir as output file.
    script = (f'{path.parent/path.stem}{path.suffix}' if SHOW_FULL_PATH else
              f'{path.stem}{path.suffix}')

    with open(path, 'w') as outp:  # Write to specified file.
        outp_write = outp.write
        outp_flush = outp.flush

        def write(line):
            stdout_write(line)
            outp_write(line)

        def writeln(line):
            write(line + '\n')

        banner = f'"{script}"' if ' ' in script else f'-[{script}]-'
        writeln(f'{banner}')
        if DIVIDER:
            writeln(f'{DIV_CH * len(banner)}')
        for line in inp:
            write(line)
        if DIVIDER:
            writeln(f'{DIV_CH * len(banner)}')
        writeln('-[done]-')
finally:
    inp.close()  # Not sure this is really necessary.

sys.exit(0)
...