I am using a context manager to wrap the text which would show in terminal and write to file at the same time.
I faced this problem and got the solution, please check
Writing terminal output to terminal and to a file?
Cannot change the functions (etc. func1 and func2) problem is after the 'with' statement any output as sys.stdout.write its showing value error: I/O operation in closed file
import sys, datetime
def __init__(self,log_file_name, stdout):
self.log_file_name = log_file_name
self.stdout = stdout
self.log_file = open(self.log_file_name, 'a', 0)
def __exit__(self, exc_type, exc_value, exc_traceback):
def write(self, data):
with FileWrite(..........., sys.stdout) as sys.stdout:
# both output A and B is showing in terminal and writing in file
# writing 'test' only in terminal......
I/O operation in closed file
You don't really need (or want) to use
as here. If the goal is to convert any write to
sys.stdout to a write to both
sys.stdout and your log file, you need to backup
__enter__ and restore it on
__exit__, but don't explicitly pass
sys.stdout to the constructor, and don't use the
__enter__ return to replace
sys.stdout, because that bypasses the
__exit__ code. Instead, have
__exit__ do the replacing work for you:
class tee_stdout(object): def __init__(self, log_file_name): self.log_file_name = log_file_name self.stdout = None def __enter__(self): self.log_file = open(self.log_file_name, 'a', 0) # Replace sys.stdout while backing it up self.stdout, sys.stdout = sys.stdout, self def __exit__(self, exc_type, exc_value, exc_traceback): sys.stdout = self.stdout # Restore original sys.stdout self.log_file.close() def write(self, data): self.log_file.write(data) self.stdout.write(data) self.stdout.flush()
Now, usage is just:
with tee_stdout(logfilename): ... do stuff that uses sys.stdout, explicitly or implicitly ... ... when block exits, sys.stdout restored, so normal behavior resumes ...
Note: If you're targeting Python 3.4 or higher, I'd recommend implementing the class with just
write, and then using
contextlib.redirect_stdout to avoid reinventing the wheel:
from contextlib import redirect_stdout class tee_output: def __init__(self, *targets): self.targets = targets def write(self, data): for tgt in self.targets: tgt.write(data) tgt.flush() with open(logfilename, 'a') as log, redirect_stdout(tee_output(log, sys.stdout)): ... logs to logfilename and sys.stdout when sys.stdout written to ... ... undoes redirection ...
Note: All the above aside, usually, you want to just use the
logging module and logger methods for stuff like this. You can pre-configure different loggers, some that go to
sys.stdout, some that go to a log file and
sys.stdout, some that go just to log files, and use the appropriate one when needed.