using Python 'with' statement with sys.stdout
Asked Answered
C

4

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
Carbonyl answered 8/3, 2014 at 3:9 Comment(0)
G
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.

Goraud answered 8/3, 2014 at 3:21 Comment(4)
I'd replace != with is not.Wrinkle
@Wrinkle I was thinking about swapping the if and else part and simply doing if file_name :) Anyway, fixed it with is not :)Goraud
this deserves more upvotes! it provided a great start for me, the context manager approach is non-obtrusiveGalloping
I think it should be writer = open(file_name, "w") instead of writer = open("Output.txt", "w"), but good answer!Pesek
N
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()
Nika answered 8/3, 2014 at 3:14 Comment(0)
S
3

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)
Statistician answered 19/8, 2022 at 12:45 Comment(0)
G
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

Greening answered 13/12, 2016 at 14:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.