Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StringIO and compatibility with 'with' statement (context manager)

I have some legacy code with a legacy function that takes a filename as an argument and processes the file contents. A working facsimile of the code is below.

What I want to do is not have to write to disk with some content that I generate in order to use this legacy function, so I though I could use StringIO to create an object in place of the physical filename. However, this does not work, as you can see below.

I thought StringIO was the way to go with this. Can anyone tell me if there is a way to use this legacy function and pass it something in the argument that isn't a file on disk but can be treated as such by the legacy function? The legacy function does have the with context manager doing work on the filename parameter value.

The one thing I came across in google was: http://bugs.python.org/issue1286, but that didn't help me...

Code

from pprint import pprint import StringIO      # Legacy Function def processFile(filename):     with open(filename, 'r') as fh:         return fh.readlines()      # This works print 'This is the output of FileOnDisk.txt' pprint(processFile('c:/temp/FileOnDisk.txt')) print      # This fails plink_data = StringIO.StringIO('StringIO data.') print 'This is the error.' pprint(processFile(plink_data)) 

Output

This is the output in FileOnDisk.txt:

['This file is on disk.\n'] 

This is the error:

Traceback (most recent call last):   File "C:\temp\test.py", line 20, in <module>     pprint(processFile(plink_data))   File "C:\temp\test.py", line 6, in processFile     with open(filename, 'r') as fh: TypeError: coercing to Unicode: need string or buffer, instance found 
like image 907
mpettis Avatar asked Aug 09 '12 22:08

mpettis


People also ask

What is StringIO used for?

The StringIO module is an in-memory file-like object. This object can be used as input or output to the most function that would expect a standard file object. When the StringIO object is created it is initialized by passing a string to the constructor. If no string is passed the StringIO will start empty.

How do I write a StringIO file?

Python – Write String to Text FileOpen the text file in write mode using open() function. The function returns a file object. Call write() function on the file object, and pass the string to write() function as argument. Once all the writing is done, close the file using close() function.

What does IO BytesIO do?

StringIO and BytesIO are methods that manipulate string and bytes data in memory. StringIO is used for string data and BytesIO is used for binary data. This classes create file like object that operate on string data. The StringIO and BytesIO classes are most useful in scenarios where you need to mimic a normal file.


1 Answers

A StringIO instance is an open file already. The open command, on the other hand, only takes filenames, to return an open file. A StringIO instance is not suitable as a filename.

Also, you don't need to close a StringIO instance, so there is no need to use it as a context manager either. While closing an instance frees the memory allocated, so does simply letting the garbage collector reap the object. At any rate, the contextlib.closing() context manager could take care of closing the object if you want to ensure freeing the memory while still holding a reference to the object.

If all your legacy code can take is a filename, then a StringIO instance is not the way to go. Use the tempfile module to generate a temporary filename instead.

Here is an example using a contextmanager to ensure the temp file is cleaned up afterwards:

import os import tempfile from contextlib import contextmanager  @contextmanager def tempinput(data):     temp = tempfile.NamedTemporaryFile(delete=False)     temp.write(data)     temp.close()     try:         yield temp.name     finally:         os.unlink(temp.name)  with tempinput('Some data.\nSome more data.') as tempfilename:     processFile(tempfilename) 

You can also switch to the newer Python 3 infrastructure offered by the io module (available in Python 2 and 3), where io.BytesIO is the more robust replacement for StringIO.StringIO / cStringIO.StringIO. This object does support being used as a context manager (but still can't be passed to open()).

like image 187
Martijn Pieters Avatar answered Sep 28 '22 08:09

Martijn Pieters