5

I have a program that outputs many calculations and results to the console through the print statement. I want to write some code to export (or save) all the contents of the console to a simple text file.

I searched StackOverflow and other sites but I found some methods to redirect the print statement to print to a file directly, but I want the program to work normally, to display outputs to the console, then to save its contents AFTER all operations of the program done.

I am using PyCharm with Python2.7 if it matters

Mohammad ElNesr
  • 2,477
  • 4
  • 27
  • 44

7 Answers7

3

Ok, so normally to get it done, you have to rewrite python print built-in function. But... There is ipython, which provides some hooks.

First you need to have ipython installed:

#bash
sudo pip install ipython

(I'm using sudo to simple locate then folder I need to reach, read further)

After ipython installation you'll have ipython extensions folder available, so get to it:

#bash
cd ~/.ipython/extensions/

and create there let's say a file called print_to_file.py, here is its content:

#python
class PrintWatcher(object):
    def __init__(self, ip):
        self.shell = ip

    def post_execute(self):
        with open('/home/turkus/shell.txt', 'a+') as f:
            in_len = len(self.shell.user_ns['In'])
            i = in_len - 1

            in_ = self.shell.user_ns['In'][i]
            out = self.shell.user_ns['Out'].get(i, '')
            # you can edit this line if you want different input in shell.txt
            f.write('{}\n{}\n'.format(in_, out))


def load_ipython_extension(ip):
    pw = PrintWatcher(ip)
    ip.events.register('post_run_cell', pw.post_execute)

After saving a file just run:

#bash
ipython profile create 

# you will get something like that:
[ProfileCreate] Generating default config file: u'/home/turkus/.ipython/profile_default/ipython_config.py'

Now get back to setting up our hook. We must open ipython_config.py created under path above and put there some magic (there is a lot of stuff there, so go to the end of file):

# some commented lines here
c = get_config()
c.InteractiveShellApp.extensions = [
    'print_to_file'
]

After saving it, you can run ipython and write your code. Every your input will be written in a file under path you provided above, in my case it was:

/home/turkus/shell.txt

Notes

You can avoid loading your extension every time ipython fires up, by just delete 'print_to_file' from c.InteractiveShellApp.extensions list in ipython_config.py. But remember that you can load it anytime you need, just by typing in ipython console:

➜  ~ ipython
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: %load_ext print_to_file

Any change in print_to_file.py is being reflected in open ipython shell after using %reload_ext print_to_file command, so you don't have to exit from and fire up it again.

turkus
  • 4,637
  • 2
  • 24
  • 28
  • Thanks @turkus once more for your contribution, however, it will take too long to apply it through all Python 2.7 print statements. – Mohammad ElNesr Aug 25 '16 at 11:56
  • 1
    the command line solution does not show the output in the shell, but only in the file. You will need "tee" command – Glostas Aug 26 '16 at 12:26
  • Many thanks @turkus for this huge effort. I will try it tomorrow morning at office, and will return back to you. – Mohammad ElNesr Aug 27 '16 at 18:14
  • OK, I'll be waiting. – turkus Aug 27 '16 at 18:17
  • @turkus you are a genius, but this solution is too hard to follow! Anyway, I want to ask you is this solution will make the print statement prints both on the console and the file, or only the file? please refer to Glostas comment up here. – Mohammad ElNesr Aug 28 '16 at 07:34
  • @Mohammad ElNesr both. Glostas was referring to something different, which I've deleted already from my answer, it was this command: ``python script.py >> shell.txt``, which puts every print to a file, but doesn't display it in the console. – turkus Aug 28 '16 at 09:31
  • OK, @turkus, is this solution applicable through PyCharm environment? I have installed iPython, but can't go further, can you simplify your solution a little bit? – Mohammad ElNesr Aug 28 '16 at 10:10
  • https://www.jetbrains.com/help/pycharm/2016.1/ipython.html here's something about. – turkus Aug 28 '16 at 10:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122037/discussion-between-turkus-and-mohammad-elnesr). – turkus Aug 28 '16 at 14:37
1

I am unsure how you could receive the contents of a console for any editor however this can be achieved quite simply by replacing your print() statements with .write

class Writer(object):
    def __init__(self, out_file, overwrite=False):
        self.file_name = out_file
        self.overwrite = overwrite
        self.history = []

    def write(self, statement):
        self.history.append(statement)
        print statement

    def close(self):
        if self.overwrite:
            self.out_file = open(self.file_name, 'wb')
        else:
            self.out_file = open(self.file_name, 'ab')
        for x in self.history:
            self.out_file.write(x+'/n')
        self.out_file.close()
        self.history = []

p = Writer('my_output_file.txt')
p.write('my string to print and save!') 
p.close() #close the writer to save the contents to a file before exiting
TheLazyScripter
  • 2,541
  • 1
  • 10
  • 19
  • Your answer is awesome, but it needs to rewrite hundreds of print statements in the program. I still wondering if it is possible to grab the console contents after all outputs? – Mohammad ElNesr Aug 25 '16 at 11:54
  • I understand the problem you are having and will revise my answer to supplement your current issue! – TheLazyScripter Aug 26 '16 at 14:36
1

After I know understood your question I think you search the tee command

python your_program | tee output.txt

This will show you the output both, in the console and in output.txt

PS: Since you did not answer to my comment which OS you use I assumed that you use either Linux or MACOS. Should work on both. I don't know how to do this on windows...

Glostas
  • 1,090
  • 2
  • 11
  • 21
  • I am so sorry not answering you comment in a timely manner, aqs I was off-desk for some time. However, I answered it today, and I use Windows 10 OS. Many thanks @Glostas – Mohammad ElNesr Aug 27 '16 at 18:07
  • Is this possible at Windows 10? – Mohammad ElNesr Aug 27 '16 at 18:14
  • Sry, I dont have much experience with the dos command, but maybe you find something here: http://stackoverflow.com/questions/11239924/windows-batch-tee-command – Glostas Aug 29 '16 at 07:48
1

You could override the print function which will still be accessible through the builtins module

import builtins

f = open("logs.txt", "w")

def print(*args, sep=' ', end='\n', **kwargs):
    builtins.print(*args, sep=sep, end=end, **kwargs)
    f.write(sep.join(*args) + end)

EDIT: A similar solution for Python 2

from __future__ import print_function

class Print:

    def __init__(self, print_function, filename='test', mode='w'):
        self.print_function = print_function
        self.file = open(filename, 'w')

    def __call__(self, *args, **kwargs):
        self.print_function(*args, **kwargs)
        kwargs['file'] = self.file
        self.print_function(*args, **kwargs)

print = Print(print, 'logs.txt')

This creates a print function that you use exactly as the function you import from __future__.
To close the file when everything is done you have to run:

print.file.close()
odrling
  • 134
  • 1
  • 6
  • This seems to be very nice and brief solution, I will try it tomorrow morning and return to you. Many thanks @odrling – Mohammad ElNesr Aug 27 '16 at 18:13
  • 1
    But you have to remember to close a file at the end. – turkus Aug 27 '16 at 18:20
  • With all respect @odrling. I tried this solution, but it did not work! First, the builtins module is not found (I use Python 2.7.x), so I used `import future_builtins as builtins` then I wrote your `def print...` but several errors occur! I don't know how to apply your solution. Many thanks to explain more... – Mohammad ElNesr Aug 28 '16 at 08:02
  • Sorry I didn't notice that you used Python 2.7. the `future_builtins` module doesn't include the print function. I think the only solution in python 2 is to create a function and call this function every time you would want to use `print` – odrling Aug 28 '16 at 21:30
  • Well I added a solution for python 2. But it would imply to change every print statement to a function call, so I guess you'll still prefer yours – odrling Aug 28 '16 at 22:41
0

Maybe you should create a variable that will log the outputs and then put it into a file.

For ex:

print statement
logger += statement+"\n" #a new line char so each statement is on a new line

with open('file.txt', 'a') as f:
   f.write(statement)
Ultcyber
  • 396
  • 1
  • 6
0

With all thanks and respect to all who contributed to this question. I have finally found a solution to this problem with minimal modifications to my original code. The solution is provided by the member @Status and here is its link .

Although I searched a lot before posting my question, but the answers of the respected members enlightened my mind to a precise search especially the contributions of @turkus, who performs an exceptional work, and @Glostas who opened my eyes to the "tee" which guided me to find the solution I posted (although it does not contain "tee").

The solution, as of the mentioned post with slight modifications:

1- Put the following Class in the program:

class Logger(object):
"""
Lumberjack class - duplicates sys.stdout to a log file and it's okay
source: https://stackoverflow.com/a/24583265/5820024
"""
def __init__(self, filename="Red.Wood", mode="a", buff=0):
    self.stdout = sys.stdout
    self.file = open(filename, mode, buff)
    sys.stdout = self

def __del__(self):
    self.close()

def __enter__(self):
    pass

def __exit__(self, *args):
    pass

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

def flush(self):
    self.stdout.flush()
    self.file.flush()
    os.fsync(self.file.fileno())

def close(self):
    if self.stdout != None:
        sys.stdout = self.stdout
        self.stdout = None

    if self.file != None:
        self.file.close()
        self.file = None

2- At the beginning of the program, before any print statements, put this line:

my_console = Logger('my_console_file.txt')  # you can change the file's name

3- At the end of the program, after all of the print statements, put this line:

my_console.close()

I tested this, and It works perfectly, and finally I have a clone of the console's output after the program ends.

With best regards to everybody, and Many thanks to all contributors.

Mohammad ElNesr
  • 2,477
  • 4
  • 27
  • 44
  • 1
    After two years I came back to this question and now I see, that you mentioned me :P but with a typo, maybe that's why I didn't notice this mention before. Great thanks for your answer anyway. – turkus Dec 02 '18 at 18:29
  • Sorry @turkus for this typo, I have changed your mention from `turcus` to `turkus`. Best regards – Mohammad ElNesr Dec 04 '18 at 10:30
  • It was void comment, but thanks for your response ;) – turkus Dec 04 '18 at 11:40
-1

There is a very obvious but not very elegant solution.

instead of:

print statement 1
calculation
print statement 2

you can make something like

sexport =''
calculation
print statement 1
sexport += statement1 + "\n"
calculaztion
print statement 2
sexport += statement 2 

finally just save sexport to a file

Glostas
  • 1,090
  • 2
  • 11
  • 21