Как сделать уже открытый файл доступным для чтения (например, sys.stdout)? - PullRequest
2 голосов
/ 07 апреля 2020

Я пытался получить содержимое sys.stdout в строке. Я попробовал очевидное:

def get_stdout():
    import sys

    print('a')
    print('b')
    print('c')

    repr(sys.stdout)

    contents = ""
    #with open('some_file.txt','r') as f:
    #with open(sys.stdout) as f:
    for line in sys.stdout.readlines():
        contents += line
    print(contents)

, но это выдает ошибку:

Exception has occurred: UnsupportedOperation
not readable

Так как мне просто изменить разрешения для этого уже открытого файла?

I пробовал:

    sys.stdout.mode = 'r'

но это все равно дает ту же ошибку ...

Другие вещи, которые могли бы работать, это просто получить мне имя / путь stdout в аппаратно-независимом способ.


Еще одна вещь, которая будет работать, - это разрешить мне помещать содержимое sys.stdout после запуска основного сценария в строку.


Вопросы Я читал, что не помогло:

Ответы [ 4 ]

1 голос
/ 07 апреля 2020

Я хочу поделиться кодом, который я использую, исходя из принятого ответа:

import sys 
from pathlib import Path
def my_print(*args, filepath='~/my_stdout.txt'):
    filepath = Path(filepath).expanduser()
    # do normal print
    __builtins__['print'](*args, file=sys.__stdout__) #prints to terminal
    # open my stdout file in update mode
    with open(filepath, "a+") as f:
        # save the content we are trying to print
        __builtins__['print'](*args, file=f) #saves in a file

def collect_content_from_file(filepath):
    filepath = Path(filepath).expanduser()
    contents = ''
    with open(filepath,'r') as f:
        for line in f.readlines():
            contents = contents + line
    return contents

Обратите внимание на a+, чтобы иметь возможность создать файл, если он уже НЕ существует.

Обратите внимание, что если вы хотите удалить старое содержимое вашего пользовательского my_stdout.txt, вам нужно удалить файл и проверить, существует ли он:

    # remove my stdout if it exists
    os.remove(Path('~/my_stdout.txt').expanduser()) if os.path.isfile(Path('~/my_stdout.txt').expanduser()) else None

Я думаю, что это должно быть все.

1 голос
/ 07 апреля 2020

Вы можете временно перенаправить stdout на выбранный вами объект. В приведенном ниже примере хранятся напечатанные данные в экземпляре StringIO. Как только блок диспетчера контекста заканчивается, нормальная печать возобновляется и позволяет отображать некоторую отладочную информацию:

#! /usr/bin/env python3
import contextlib
import io


def main():
    file = io.StringIO()
    with contextlib.redirect_stdout(file):
        print('a')
        print('b')
        print('c')
    print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')


if __name__ == '__main__':
    main()

Приложение:

Если вы используете sh для использования stdout Как обычно и по-прежнему захватывать то, что напечатано на нем, вы можете вместо этого использовать следующий пример. Класс Apply может обернуть несколько экземпляров и дублировать вызовы методов для всех них. Поэтому вызов redirect_stdout был немного изменен:

#! /usr/bin/env python3
import contextlib
import io
import sys


def main():
    file = io.StringIO()
    with contextlib.redirect_stdout(Apply(sys.stdout, file)):
        print('a')
        print('b')
        print('c')
    print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')


class Apply:

    def __init__(self, *args):
        self.__objects = args

    def __getattr__(self, name):
        attr = _Attribute(getattr(obj, name) for obj in self.__objects)
        setattr(self, name, attr)
        return attr


class _Attribute:

    def __init__(self, iterable):
        self.__attributes = tuple(filter(callable, iterable))

    def __call__(self, *args, **kwargs):
        return [attr(*args, **kwargs) for attr in self.__attributes]


if __name__ == '__main__':
    main()
1 голос
/ 07 апреля 2020

Вы можете использовать следующий код:

import sys
from builtins import print as builtin_print
myfile = "output.txt"
def print(*args):
    builtin_print(*args, file=sys.__stdout__)    # prints to terminal
    with open(myfile, "a+") as f:
        builtin_print(*args, file=f)    # saves in a file

Это должно переопределить функцию print, чтобы она печаталась в stdout и в ваш файл. Затем вы можете прочитать из файла.

0 голосов
/ 16 апреля 2020

Мой предыдущий ответ на этот вопрос не так хорош, как я думал ({ ссылка }). Я думаю, что реальный ответ на этот вопрос - просто использовать регистратор. До недавнего времени я не знал, что такое регистраторы, но они намного лучше.

Гораздо лучше создать объект регистратора, который отправляет ваши строки в файл журнала И на стандартный вывод. Он даже позволяет более точно маршрутизировать сообщения в соответствии с пороговым уровнем. Вот код:

def logger_SO_print_and_write_to_my_stdout():
    """My sample logger code to print to screen and write to file (the same thing).

    Note: trying to replace this old answer of mine using a logger: 
    - https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced

    Credit: 
    - https://www.youtube.com/watch?v=jxmzY9soFXg&t=468s
    - https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
    - https://stackoverflow.com/questions/21494468/about-notset-in-python-logging/21494716#21494716

    Other resources:
    - https://docs.python-guide.org/writing/logging/
    - https://docs.python.org/3/howto/logging.html#logging-basic-tutorial
    """
    from pathlib import Path
    import logging
    import os
    import sys
    from datetime import datetime

    ## create directory (& its parents) if it does not exist otherwise do nothing :)
    # get current time
    current_time = datetime.now().strftime('%b%d_%H-%M-%S') 
    logs_dirpath = Path(f'~/logs/python_playground_logs_{current_time}/').expanduser()
    logs_dirpath.mkdir(parents=True, exist_ok=True)
    my_stdout_filename = logs_dirpath / Path('my_stdout.log')
    # remove my_stdout if it exists (note you can also just create a new log dir/file each time or append to the end of the log file your using)
    #os.remove(my_stdout_filename) if os.path.isfile(my_stdout_filename) else None

    ## create top logger
    logger = logging.getLogger(__name__) # loggers are created in hierarchy using dot notation, thus __name__ ensures no name collisions.
    logger.setLevel(logging.DEBUG) # note: use logging.DEBUG, CAREFUL with logging.UNSET: https://stackoverflow.com/questions/21494468/about-notset-in-python-logging/21494716#21494716

    ## log to my_stdout.log file
    file_handler = logging.FileHandler(filename=my_stdout_filename)
    #file_handler.setLevel(logging.INFO) # not setting it means it inherits the logger. It will log everything from DEBUG upwards in severity to this handler.
    log_format = "{asctime}:{levelname}:{lineno}:{name}:{message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
    formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
    file_handler.setFormatter(fmt=formatter)

    ## log to stdout/screen
    stdout_stream_handler = logging.StreamHandler(stream=sys.stdout) # default stderr, though not sure the advatages of logging to one or the other
    #stdout_stream_handler.setLevel(logging.INFO) # Note: having different set levels means that we can route using a threshold what gets logged to this handler
    log_format = "{name}:{levelname}:-> {message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
    formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
    stdout_stream_handler.setFormatter(fmt=formatter)

    logger.addHandler(hdlr=file_handler) # add this file handler to top logger
    logger.addHandler(hdlr=stdout_stream_handler) # add this file handler to top logger

    logger.log(logging.NOTSET, 'notset')
    logger.debug('debug')
    logger.info('info')
    logger.warning('warning')
    logger.error('error')
    logger.critical('critical')

содержимое журнала:

2020-04-16 11:28:24,987:DEBUG:154:__main__:debug
2020-04-16 11:28:24,988:INFO:155:__main__:info
2020-04-16 11:28:24,988:WARNING:156:__main__:warning
2020-04-16 11:28:24,988:ERROR:157:__main__:error
2020-04-16 11:28:24,988:CRITICAL:158:__main__:critical

вывод вывода терминала:

__main__:DEBUG:-> debug
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical

Мне кажется, это очень важный вопрос / ответ на ссылка на тот случай, если у вас возникнут проблемы с UNSET: О NOTSET в python logging Слава Богу за ответ и вопрос.

...