How does one make an already opened file readable (e.g. sys.stdout)?

安稳与你 提交于 2020-04-15 06:53:15

问题


I was trying to get the contents of sys.stdout in a string. I tried the obvious:

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)

but that gives the error:

Exception has occurred: UnsupportedOperation
not readable

So how do I just change the permissions of that already opened file?

I tried:

    sys.stdout.mode = 'r'

but that still gives the same error...

Other things that would work would be to just get me the name/path of stdout in a hardware independent way.


Another thing that would just work is letting me to put the contents of sys.stdout after I've run my main script in a string.


Questions I've read that did not help:

  • Making File Writable and Readable in Python
  • TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper
  • Send the contents from unmodified print statement by e-mail in python

回答1:


You can use the following code:

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

This should redefine the print function so that it prints to stdout and to your file. You can then read from the file.




回答2:


I want to share the code that I am using, as inspired by accepted answer:

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

Note the a+ to be able to create the file if it already does NOT exist.

Note that if you want to delete the old contents of your custom my_stdout.txt you need to delete the file and check if it exists:

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

I think that should be all.




回答3:


You can temporarily redirect stdout to an object of your choosing. The example shown below stores printed data in a StringIO instance. Once the context manager block ends, normal printing resumes and allows showing some debugging information:

#! /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()

Addendum:

If you wish to use stdout like normal and still capture what is printed to it, you might want to use the following example instead. The Apply class can wrap several instances and duplicate method calls across all of them. Therefore, the call to redirect_stdout has been slightly modified:

#! /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()


来源:https://stackoverflow.com/questions/61084916/how-does-one-make-an-already-opened-file-readable-e-g-sys-stdout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!