Python 2 и 3 совместимый писатель строкового файла - PullRequest
0 голосов
/ 01 мая 2018

Я создал менеджер контекста python, который захватывает все выходные данные sys.stdout, например, с помощью print (), и записывает их в файл.

Проблема в том, что я не могу заставить это работать как для Python 2.7, так и для 3.6.

Менеджер контекста внутренне использует

self.file_writer = open(self.log_file, 'w', encoding='utf8')

но когда я запускаю его в Python 2.7, тогда

print(u"a test string")

приводит к сообщению об ошибке:

write() argument 1 must be unicode, not str

Даже если строка явно в кодировке Unicode.

Если я изменю файл на

self.file_writer = open(self.log_file, 'wb')

тогда это работает в Python 2.7, но не в 3.6.

Что мне нужно сделать, чтобы заставить его работать на любой версии Python?

Ниже приводится выписка менеджера:

PATH_PREFIX = "some/path/"
class manager:
    def __init__(self):
        self.log_file = os.path.join(PATH_PREFIX, 'log.txt')
    def __enter__(self):
        # create a file for logging
        self.log_file_stream = open(self.log_file, 'w', encoding='utf8')
        self.log_file_stream.__enter__()
        # redirect stdout to this file
        self.previous_stdout = sys.stdout
        sys.stdout = self.log_file_stream
        return self
    def __exit__(self, etype, value, exception_traceback):
        # stop redirecting stdout to the log file
        sys.stdout = self.previous_stdout
        # close the log file
        self.log_file_stream.__exit__()

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Прочитав другие ответы и узнав, что это невозможно с существующими библиотеками python, я написал менеджер контекста, который заменил open (). Это немного глупо, но это делает работу.

Вероятно, это можно улучшить, явно проверив версию Python вместо использования hasattr (), но я не знал об этом, когда писал следующее:

class open_properly(object):
    """
    This is a file writer that doesn't complain about unicode/string mismatches.
    It works for both python 2.7 and 3+.
    """
    def __init__(self, file_path):
        super(open_properly, self).__init__()
        self.file_path = file_path
    def __enter__(self):
        self.file_writer = open(self.file_path, 'w', encoding='utf8')
        self.file_writer.__enter__()
        return self
    def write(self, s):
        if not isinstance(s, string_types):
            raise ValueError("the text to print must be a valid string type")
        # make sure it's unicode (important for python 2)
        if isinstance(s, str):
            if hasattr(s, 'decode'):
                s = s.decode('utf-8')
        # write unicode to file
        self.file_writer.write(s)
    def __exit__(self, exc_type, exc_val, exc_tb):
        return self.file_writer.__exit__(exc_type, exc_val, exc_tb)
0 голосов
/ 01 мая 2018

sys.stdout ожидается, что это будет поток байтов в Python 2, но поток Unicode в Python 3. print для Python 2 кодирует строки Unicode в строки байтов перед записью в стандартный вывод, но вы переопределили sys.stdout быть потоком Unicode как в Python 2, так и в Python 3.

При переопределении sys.stdout вам необходимо предоставить поток байтов для Python 2, но поток Unicode для Python 3. Вы можете использовать sys.version_info.major, чтобы решить, что поддерживать.

...