1

How to force csv.writer() to accept stdout inside with block?

I this works ok:

with (open(output, "w")) if output != 'stdout' else stdout as file:

this is also fine:

file = csv.writer(open(output, "w") if output != 'stdout' else stdout)

but when I try this:

with csv.writer(open(output, "w") if output != 'stdout' else stdout) as file:

I get AttributeError: __exit__

How to make this with block work please? I am looking for solution like ruby solution but for python.

PapeK24
  • 988
  • 1
  • 10
  • 26
  • 1
    `csv.writer` doesn't have to close the handle on exit. Hence no `__exit__` method, and it makes no sense to put `csv.writer` in a `with` statement. – Jean-François Fabre Apr 29 '18 at 20:59

2 Answers2

3

Refactored slightly:

import csv
from sys import stdout

#output = "temp.txt"
output = "stdout"

with (open(output, "w") if output != 'stdout' else stdout) as file:
    writer = csv.writer(file)
    writer.writerow(["A","B","C"])

works on Python 3.6, at least.

But... this will close stdout after the with block completes.

For example:

with (open(output, "w") if output != 'stdout' else stdout) as file:
    writer = csv.writer(file)
    writer.writerow(["A","B","C"])

print("AFTER")  # Raises "ValueError: I/O operation on closed file."

I think the best approach is just to handle the file closing directly. You do lose the benefit of the context manager auto-closing the file on exceptions, but without doing some additional work creating a pseduo-stdout that doesn't actually close on __exit__, your best bet might be:

import csv
from sys import stdout

#output = "temp.txt"
output = "stdout"

file = (open(output, "w") if output != 'stdout' else stdout)

writer = csv.writer(file)
writer.writerow(["A","B","C"])

if output != "stdout": file.close()

print("AFTER")
jedwards
  • 29,432
  • 3
  • 65
  • 92
2
  • csv.writer doesn't have to close the handle on exit. Hence no __exit__ method, and it makes no sense to put csv.writer in a with statement.
  • With my python version, your original with open code doesn't work, I get a AttributeError: __exit__
  • And even if it worked (seems to work on Python 3.6 as the other answer has tested it), it closes stdout. Not what you want.

I've created a "fake" file object with __enter__ and __exit__ (does nothing) methods. this object complies with the with constraints.

import csv,sys

output="stdout"
class FakeStdout:
    def write(self,data):
        sys.stdout.write(data)
    def __exit__(self,*args,**kwargs):
        pass
    def __enter__(self):
        return self

with (open(output, "w") if output != 'stdout' else FakeStdout()) as file:
    cw = csv.writer(file)
    cw.writerow([1,2,3])

print("lll")

This code prints:

1,2,3
lll

it works with standard output and doesn't close it when exiting

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219