Question

I have a function that I cannot edit and it prints results onto the console. Is it possible to calls that function but pipes the console output into a file without changing the internal code of the function?

For example, I have:

def somefunc(x):
  print "Hello World", x**x

def pipeout(func, outfilename)
  with open('outfilename', 'w') as fout:
    # somehow pipes out the output...

I couldn't use logger because I couldn't edit the somefunc().

I have tried @Aशwini चhaudhary 's solution but i couldn't the files were empty other than the first outputfile and also it overwrites that file again and again.:

def redirect_output(file_name):
  def decorator(func):
    def wrapper(*args):
      with open(file_name, 'w') as f:
        original_stdout = sys.stdout
        sys.stdout = f
        func(*args)
      sys.stdout = original_stdout
    return wrapper
  return decorator

def somefunc(x):
  print x*x

xs = [1,2,3,4]

for i in xs:
  outputfilename = 'results/'+str(i)
  somefunc = redirect_output(outputfilename)(somefunc)
  somefunc(i)
Was it helpful?

Solution 2

You can use a decorator to redirect the sys.stdout to a file object when the function gets called and later restores it back to original STDOUT.

import sys

def redirect_output(file_name):

    def decorator(func):
        def wrapper(*args):
            with open(file_name, 'w') as f:
                original_stdout = sys.stdout
                sys.stdout = f
                func(*args)
            sys.stdout = original_stdout
        return wrapper
    return decorator

@redirect_output('file.txt')
def somefunc(x):
  print "Hello World", x**x


somefunc(2)

print 'Hello to console.'

Output:

>>> %run so.py
Hello to console.
>>> !cat file.txt
Hello World 4

Update:

Working version of your latest code:

for i in xs:
  outputfilename = 'results/'+str(i)
  new_func = redirect_output(outputfilename)(somefunc)
  #or call it directly
  #redirect_output(outputfilename)(somefunc)(i)
  new_func(i)

OTHER TIPS

Yes, you can set the value of sys.stdout to another open file with write access. Then you can set it back with sys.__stdout__

I had this need a while ago and made a context manager:

import sys                                                                                                                          
from StringIO import StringIO 

class OutStreamCapture(object):
    """
    A context manager to replace stdout and stderr with StringIO objects and
    cache all output.
    """

    def __init__(self):
        self._stdout = None
        self._stderr = None
        self.stdout = None
        self.stderr = None

    def __enter__(self):
        self._stdout = sys.stdout
        self._stderr = sys.stderr
        sys.stdout = StringIO()
        sys.stderr = StringIO()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        Restore original values of stderr and stdout.
        The captured contents are stored as strings in the stdout and stderr
        members.
        """
        self.stdout = sys.stdout.getvalue()
        self.stderr = sys.stderr.getvalue()
        sys.stdout = self._stdout
        sys.stderr = self._stderr

You can use it like this:

with OutStreamCapture() as osc:
    somefunc(x)

And then is osc.stdout and osc.stderr you have two strings with anything the function put in stdout and stderr, respectively.

This could be modified to use arbitrary files instead.

Please note that here I am caching the current value of sys.stdout in the context manager rather than using sys.__stdout__ to restore. This is because at the point we enter this context, stdout might already be redirected, and we want to put it back to what it was.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top