The reason you get a "bad file descriptor" is because the garbage collector closes the stdout FD file for you. Consider these two lines:
sys.stdout = os.fdopen(1, 'w', 0) # from first part of your script ... sys.stdout = os.fdopen(new, 'w', 0) # from second part of your script
Now, when the second of these two is executed, the first instance of the object object reference drops to zero, and the garbage collector destroys it. File objects close their associated fd upon destruction, and fd - 1 = stdout. Therefore, you need to be very careful how you destroy objects created with os.fdopen .
Here is a small example to show the problem. os.fstat simply used as an example function that os.fstat "Bad file descriptor" error when passing a private fd.
import os whatever = os.fdopen(1, 'w', 0) os.fstat(1) del whatever os.fstat(1)
Actually, I have a context manager that I think does exactly (or almost at least in my case I need a named tempfile) that you are looking for. You can see that it reuses the original sys.stdout object to avoid problems with the problem.
import sys import tempfile import os class captured_stdout: def __init__(self): self.prevfd = None self.prev = None def __enter__(self): F = tempfile.NamedTemporaryFile() self.prevfd = os.dup(sys.stdout.fileno()) os.dup2(F.fileno(), sys.stdout.fileno()) self.prev = sys.stdout sys.stdout = os.fdopen(self.prevfd, "w") return F def __exit__(self, exc_type, exc_value, traceback): os.dup2(self.prevfd, self.prev.fileno()) sys.stdout = self.prev
source share