问题
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