15

I always open and write into files using with statement:

with open('file_path', 'w') as handle:
    print >>handle, my_stuff

However, there is one instance where I need to be able to be more flexible, and write to sys.stdout (or other types of streams), if that is provided instead of file path:

So, my question is this: Is there a way for using with statement both with real files and with sys.stdout?

Note that I can use the following code, but I think this defeats the purpose of using with:

if file_path != None:
    outputHandle = open(file_path, 'w')
else:
    outputHandle = sys.stdout

with outputHandle as handle:
    print >>handle, my_stuff
farmir
  • 1,259
  • 1
  • 10
  • 14

4 Answers4

14

You can create a context manager and use it like this

import contextlib, sys

@contextlib.contextmanager
def file_writer(file_name = None):
    # Create writer object based on file_name
    writer = open(file_name, "w") if file_name is not None else sys.stdout
    # yield the writer object for the actual use
    yield writer
    # If it is file, then close the writer object
    if file_name != None: writer.close()

with file_writer("Output.txt") as output:
    print >>output, "Welcome"

with file_writer() as output:
    print >>output, "Welcome"

If you don't pass any input to file_writer it will use sys.stdout.

Anthon
  • 69,918
  • 32
  • 186
  • 246
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
4

Thing is, you don't need to use a context processor with stdout, because you're not opening or closing it. A less fancy way of abstracting this is:

def do_stuff(file):
    # Your real code goes here. It works both with files or stdout
    return file.readline()

def do_to_stdout():
    return do_stuff(sys.stdout)

def do_to_file(filename):
    with open(filename) as f:
        return do_stuff(f)


print do_to_file(filename) if filename else do_to_stdout()
salezica
  • 74,081
  • 25
  • 105
  • 166
2

With python3 optional closefd argument is recognized. If set to False, resulting IO object won't close underlying fd:

if file_path != None:
    outputHandle = open(file_path, 'w')
else:
    outputHandle = open(sys.stdout.fileno(), 'w', closefd=False)

with outputHandle as handle:
    print(my_stuff, file=handle)
reddot
  • 764
  • 7
  • 15
1

The simplest way is to simply use "old school" streamed filenames, that way your code doesn't have to change. In Unix this is "/dev/tty" or in Windows this is "con" (although there are other choices for both platforms).

if default_filename is None:
    default_filename = "/dev/tty"

with open(default_filename, 'w') as handle:
    handle.write("%s\n" % my_stuff)

This code tested in Python 2.7.3 and 3.3.5