5

I'm trying to store the output of a Function into a file in Python, what I am trying to do is something like this:

def test():
        print("This is a Test")
file=open('Log','a')
file.write(test())
file.close()

But when I do this I get this error:

TypeError: argument 1 must be string or read-only character buffer, not None

PD: I'm trying to do this for a function I can't modify.

Saul Solis
  • 85
  • 1
  • 5

5 Answers5

14

Whenever any operation would need to be performed in pairs, use a context manager.

In this case, use contextlib.redirect_stdout:

with open('Log','a') as f:
    with contextlib.redirect_stdout(f):
        test()

Edit: if you want it as a string, use io.StringIO:

f = io.StringIO()
with contextlib.redirect_stdout(f):
    test()
s = f.getvalue()
o11c
  • 15,265
  • 4
  • 50
  • 75
  • Excellent answer, BTW, to avoid of using nested WITH Statement, we can use ExitStack – Menglong Li Aug 25 '17 at 16:51
  • @AntoineFontaine question is tagged python3, and nobody uses 3.2 or 3.3 anymore. – o11c Aug 25 '17 at 16:54
  • To be fair it is also tagged just `python` and there are a lot of people still using python 2.n despite what your preference may be. – Rolf of Saxony Aug 25 '17 at 17:02
  • Well, there's always the `contextlib2` module on Pypi ... really, anyone still using Python2 should be used to hunting down backports. – o11c Aug 25 '17 at 17:08
  • perfect! Was searching for this for a while := – p0w3r Nov 12 '20 at 08:51
  • Note, this solution will most likely not work if your called function is using python's `logging` library to print to console. The logger seems to override the request to redirect to file. Also, from contextlib's documentation, it says the following about redirect_stdout: "Note that the global side effect on sys.stdout means that this context manager is not suitable for use in library code and most threaded applications. It also has no effect on the output of subprocesses. However, it is still a useful approach for many utility scripts." – Clint Chelak Apr 01 '21 at 21:50
1

Solution 1:

Instead of using print, you should use logging to do this:

import logging

logger = logging.getLogger('myapp')
hdlr = logging.FileHandler('/tmp/myapp.log')
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)

def test():
    logger.info("This is a Test")

test()

the codes above works fine, you can use it. PS: check the output in file /tmp/myapp.log

Solution 2:

just run your codes in command line, and store all the output into a file 'text.log'

python main.py >> text.log

Solution 3:

import contextlib
from contextlib import ExitStack


def test():
    print('hello world')


with ExitStack() as stack:
    f = stack.enter_context(open('Log', 'a'))
    stack.enter_context(contextlib.redirect_stdout(f))
    test()

print("I'm not logged")
Menglong Li
  • 2,177
  • 14
  • 19
1

You need to redirect the standard output by assigning it an object implementing the write method, like a file.

import sys

def test():
    print("This is a Test")

stdout_backup = sys.stdout

with open('Log', 'a') as f:
    sys.stdout = f
    test()

sys.stdout = stdout_backup
Antoine Fontaine
  • 792
  • 7
  • 16
1

you can define a decorator based on @o11c answer:

def redirect_output_to_file(fname, overwrite=False):
    def real_redirect_output_to_file(func):
        def wrapper(*args, **kwargs):
            import contextlib
            with open(fname, 'w' if overwrite else 'a') as f:
                with contextlib.redirect_stdout(f):
                    retval = func(*args, **kwargs)
            return retval
        return wrapper
    return real_redirect_output_to_file

And then use it on any function:

@redirect_output_to_file('test_output.log')
def test():
   print('Hi')
Noam Peled
  • 4,484
  • 5
  • 43
  • 48
0

Is the function in the same file? becuase it is was you could do something like this

Solution 1:

import sys

def test():
        print("This is a test") 

out = sys.stdout
with open("Log", "a") as output:
    sys.stdout = output
    test()
sys.stdout = out 

gives

[Program finished]

contents in file,

This is a test
This is a test

The issue is that the function only prints, but doesn't return anything. Python 3.4 added https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout which allows you to redirect stdout to a file:

Solution 2:

PS C:\Users\tan\Desktop> cat stdout.py
def test():
  print("This is a Test")

from contextlib import redirect_stdout

with open('stdout.txt', 'w') as f:
  with redirect_stdout(f):
    test()

print('stdout in console again')
PS C:\Users\tan\Desktop> python .\stdout.py
stdout in console again
PS C:\Users\tan\Desktop> cat stdout.txt
This is a Test 
Subham
  • 397
  • 1
  • 6
  • 14