47

Some code:

import cStringIO

def f():
    buffer = cStringIO.StringIO()
    buffer.write('something')
    return buffer.getvalue()

The documentation says:

StringIO.close(): Free the memory buffer. Attempting to do further operations with a closed StringIO object will raise a ValueError.

Do I have to do buffer.close(), or it will happen automatically when buffer goes out of scope and is garbage collected?

UPDATE:

I did a test:

import StringIO, weakref

def handler(ref):
    print 'Buffer died!'

def f():
    buffer = StringIO.StringIO()
    ref = weakref.ref(buffer, handler)
    buffer.write('something')
    return buffer.getvalue()

print 'before f()'
f()
print 'after f()'

Result:

vic@wic:~/projects$ python test.py 
before f()
Buffer died!
after f()
vic@wic:~/projects$
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
warvariuc
  • 57,116
  • 41
  • 173
  • 227

4 Answers4

23

From the source:

class StringIO:
    ...
    def close(self):
        """Free the memory buffer.
        """
        if not self.closed:
            self.closed = True
            del self.buf, self.pos

So StringIO.close just frees the memory buffer deleting references to StringIO.buf and StringIO.pos. But if self is garbage collected, its attributes will also be garbage collected, having the same effect as StringIO.close.

warvariuc
  • 57,116
  • 41
  • 173
  • 227
23

Generally it's still better to call close() or use the with statement, because there may be some unexpected behaviour in special circumstances. For example, the expat-IncrementalParser seems to expect a file to be closed, or it won't return the last tidbit of parsed xml until a timeout occurs in some rare circumstances.

But for the with-statement, which handles the closing for you, you have to use the StringIO class from the io-Modules, as stated in the comment of Ivc.

This was a major headache in some legacy sax-parser script we solved by closing the StringIO manually.

The "out-of-scope" close didn't work. It just waited for the timeout-limit.

Don Question
  • 11,227
  • 5
  • 36
  • 54
  • 20
    Except note that StringIO and cStringIO in Py2 don't implement the context manager protocol - so, to use it in a `with` statement you need to do `with contextlib.closing(StringIO()) as buffer:`. Py3's `io.StringIO`, on the other hand, *can* be used in a `with` statement directly. – lvc Mar 15 '12 at 12:27
  • 4
    `io.StringIO` does implement the context-manager protocol, but not prior 2.6 see: http://docs.python.org/release/2.6.7/library/io.htmlhighlight=io.stringio#io.IOBase` – Don Question Mar 15 '12 at 12:46
  • 2
    Ah, I hadn't realised that the `io` module existed that far back. Thanks for the pointer. However, it remains that the `StringIO.StringIO` and `cStringIO.StringIO` modules used in the OP don't. I'm actually slightly surprised they're not marked as deprecated in 2.6/2.7, and that there isn't even the usual note in the 2.7 docs saying "these don't exist anymore in 3.x". – lvc Mar 15 '12 at 12:57
  • You're right and quite frankly i didn't really pay attention to the fact, the OP didn't use the io Module! Thanks for pointing that out! ;-) – Don Question Mar 15 '12 at 13:05
13

StringIO.close() is merely a convenience for routines that take a file-like and eventually attempt to close them. There is no need to do so yourself.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
3

I wound up using a try block to handle it.

import cStringIO

def f():
    buffer = cStringIO.StringIO()
    try:
        buffer.write('something')
        return buffer.getvalue()
    finally:
        buffer.close()
DrRobotNinja
  • 1,381
  • 12
  • 14