1

I created a python program, test.py, below:

import subprocess
import sys, os
FolderPath = subprocess.getoutput("cd . && pwd")
ProgramName = sys.argv[0]
LogName = ProgramName[:-3]+'_printout.txt'
ProgramFile = FolderPath+'/'+ProgramName
LogFile = FolderPath+'/'+LogName

_stdin = sys.stdin
_stdout = sys.stdout
_stderr = sys.stderr

sys.stdin = open(LogFile, 'w')
sys.stdout = open(LogFile, 'a')
sys.stderr = open(LogFile, 'a')

Prog = open(ProgramFile, 'r')
print(Prog.read())

TEST = str(input("Enter the name: \n TEST_NAME: "))
print(TEST)

sys.stdin = _stdin.flush()
sys.stdout = _stdout.flush()
sys.stderr = _stderr.flush()

After I executed on linux with command python test.py, I got the error in test_printout.txt.

Enter the name: 
 TEST_NAME: Traceback (most recent call last):
  File "test.py", line 21, in <module>
    TEST = str(input("Enter the name: \n TEST_NAME: "))
io.UnsupportedOperation: not readable

I modified the code:

import subprocess
import sys, os
FolderPath = subprocess.getoutput("cd . && pwd")
ProgramName = sys.argv[0]
LogName = ProgramName[:-3]+'_printout.txt'
ProgramFile = FolderPath+'/'+ProgramName
LogFile = FolderPath+'/'+LogName

_stdin = sys.stdin
_stdout = sys.stdout
_stderr = sys.stderr

sys.stdin = open(LogFile, 'w+')
sys.stdout = open(LogFile, 'a')
sys.stderr = open(LogFile, 'a')

Prog = open(ProgramFile, 'r')
print(Prog.read())

TEST = str(input("Enter the name: \n TEST_NAME: "))
print(TEST)

sys.stdin = _stdin.flush()
sys.stdout = _stdout.flush()
sys.stderr = _stderr.flush()

But got:

Enter the name: 
 TEST_NAME: import subprocess

It did not let me type anything. What I want is to let me type string and it also save to test_printout.txt.

Enter the name: 
 TEST_NAME: This Is What I Type And Save!

Does anyone know how to fix it?
Also, if I use w+ instead of w mode, it will take longer time to write to the test_printout.txt if I changed the program to import pandas.DataFrame and manipulate data.

Is there a way to only write simple print words to test_printout.txt without reading entire thing?

UPDATE
I modified the code as below:

import subprocess, sys, os
FolderPath = subprocess.getoutput("cd . && pwd")
ProgramName = sys.argv[0]
LogName = ProgramName[:-3]+'_printout.txt'
ProgramFile = FolderPath+'/'+ProgramName
LogFile = FolderPath+'/'+LogName

_stdin = sys.stdin
_stdout = sys.stdout
_stderr = sys.stderr


class stdout_Logger(object):
    def __init__(self):
        self.stdout = sys.stdout
        self.log = open(LogFile, "a")

    def write(self, message):
        self.stdout.write(message)
        self.log.write(message)  

    def flush(self):
        #this flush method is needed for python 3 compatibility.
        #this handles the flush command by doing nothing.
        #you might want to specify some extra behavior here.
        pass    

sys.stdout = stdout_Logger()


class stderr_Logger(object):
    def __init__(self):
        self.stderr = sys.stderr
        self.log = open("test_printout.txt", "a")

    def write(self, message):
        self.stderr.write(message)
        self.log.write(message)  

    def flush(self):
        #this flush method is needed for python 3 compatibility.
        #this handles the flush command by doing nothing.
        #you might want to specify some extra behavior here.
        pass    

sys.stderr = stderr_Logger()

Prog = open(ProgramFile, 'r')
print(Prog.read())

##START Program

TEST = str(input("Enter the name: \n TEST_NAME: "))
print(TEST)

#END Program

sys.stdin = _stdin.flush()
sys.stdout = _stdout.flush()
sys.stderr = _stderr.flush()

This got almost what I want. This also save my program to test_printout.txt at the top and do print(TEST) in the bottom.
However, it also prints all program to the linux terminal console which is not I desire. I only want it to print "Enter the name: \n TEST_NAME: " in linux terminal and I can type string instead of printing entire program.
I think the issue came from sys.stdin.

Peter Chen
  • 1,464
  • 3
  • 21
  • 48

1 Answers1

0

I think I figured it out. The problem is that when you substitute input with a file-handle in write mode you ban input() from reading it. You can get the same error if you tried this:

file = open("foo.txt",'w')
content = file.read()

The way to go around it is to log streams without redirecting them. So either you dump your console to file with python test.py > test_printout.txt or create a logger class to wrap around the streams (check out this answer: How to redirect stdout to both file and console with scripting?).

Perhaps its worth for you to look into the logging module, as I believe it handles these issues rather neatly.

EDIT:

From what you laid out in the comments, this is what you want:

import subprocess, sys, os
FolderPath = subprocess.getoutput("cd . && pwd")
ProgramName = sys.argv[0]
LogName = ProgramName[:-3]+'_printout.txt'
ProgramFile = FolderPath+'/'+ProgramName
LogFile = FolderPath+'/'+LogName

Prog = open(ProgramFile, 'r')
with open(LogFile, 'w') as logfile:
    logfile.write(Prog.read())

_stdin = sys.stdin
_stdout = sys.stdout
_stderr = sys.stderr

class stdout_Logger(object):
    def __init__(self):
        self.stdout = sys.stdout
        self.log = open(LogFile, "a")

    def write(self, message):
        self.stdout.write(message)
        self.log.write(message)  

    def flush(self):
        #this flush method is needed for python 3 compatibility.
        #this handles the flush command by doing nothing.
        #you might want to specify some extra behavior here.
        pass    


class stderr_Logger(object):
    def __init__(self):
        self.stderr = sys.stderr
        self.log = open("test_printout.txt", "a")

    def write(self, message):
        self.stderr.write(message)
        self.log.write(message)  

    def flush(self):
        #this flush method is needed for python 3 compatibility.
        #this handles the flush command by doing nothing.
        #you might want to specify some extra behavior here.
        pass    


sys.stdout = stdout_Logger()
sys.stderr = stderr_Logger()

##START Program

TEST = str(input("Enter the name: \n TEST_NAME: "))
print(TEST)

#END Program

sys.stdin = _stdin.flush()
sys.stdout = _stdout.flush()
sys.stderr = _stderr.flush()
Olaf Sikorski
  • 81
  • 1
  • 6
  • I also need to print my `test.py` program in `test_printout.txt`... this way fails – Peter Chen Apr 01 '21 at 17:13
  • Looking at your edited code, you are printing your entire program to console, because you have the line `print(Prog.read())` in your program, and since you are redirecting `stdout` to `LogFile` you effectively save it there. If you want to avoid it just remove that line. Alternatively, if you do wanna print it for some reason, do it before you redirect the stream, so before you do `sys.stdout = stdout_Logger()`. – Olaf Sikorski Apr 01 '21 at 18:13
  • After deleting `print(Prog.read())`, the `test_printout.txt` does not include full code of `test.py` – Peter Chen Apr 01 '21 at 18:46
  • I think the issue came from after using `sys.stdin = open(LogFile, 'w')`. Got error when adding this line. `Traceback (most recent call last): File "test.py", line 13, in TEST = str(input("Enter the name: \n TEST_NAME: ")) io.UnsupportedOperation: not readable` – Peter Chen Apr 01 '21 at 18:52
  • Well,, then you can just manually write the code into the file, no? Replace `print(Prog.read())` with `with open(LogFile, 'w') as logfile: logfile.write(Prog.read())`. You have to move it to the beginning of your code (check my edit), so you don't mess up the streams. – Olaf Sikorski Apr 01 '21 at 19:11
  • Btw, this discussion will likely be deleted if we continue, but if my answer solved at least part of your issues I would really appreciate if you gave an upvote or even accepted the answer :) – Olaf Sikorski Apr 01 '21 at 19:22