2

I'm porting a Python 2 application to Python 3. Currently running Python2.7 but, updating code to pass pylint --py3k tests, I have run into a problem with this:

def json_resource_file(baseurl, jsondata, resource_info):
    """
    Return a file object that reads out a JSON version of the supplied entity values data. 
    """
    response_file = StringIO()
    json.dump(jsondata, response_file, indent=2, separators=(',', ': '), sort_keys=True)
    response_file.seek(0)
    return response_file

It works fine with:

from StringIO import StringIO

but StringIO.StringIO doesn't exist in Python3 (according to pylint), so using:

from io import StringIO

I get an error: "TypeError: unicode argument expected, got 'str'" (This is running under Python 2 - I'm still preparing the ground, so to speak, and am not planning to use Python 3 until I have done as much preparation and testing as I can under Python 2.)

By way of some experimentation, I tried using BytesIO from io, but that gives a different error "TypeError: 'unicode' does not have the buffer interface".

Clearly, the Python2 version of json.dump is writing str values to the supplied file object. I think the Python3 version of json.dump also writes str (i.e. Unicode) values.

So my question is: is there an easy way to dump JSON to a StringIO memory buffer that works with Python 2 and 3?

Notes

  1. I realize I could use json.dumps and coerce the result to whatever type StringIO expects, but that all seems rather heavy-handed.

  2. I'm also using the following future imports as part of my porting process:

    from __future__ import (unicode_literals, absolute_import, division, print_function)
    
  3. Currently, a solution I'm thinking of is to test the python version and import a different version of StringIO accordingly, but that would violate the "Use feature detection instead of version detection" principle.

  4. In most of my code, using from io import StringIO appears to work fine for situations that I use StringIO. It's the specific case of using a StringIO with json.dump that has caused me a few problems.

Graham Klyne
  • 816
  • 10
  • 16
  • 1
    The errors occurr on python2 or python3 or on both? If the issue is with python2 then the fix is simply `try: from StringIO import StringIO except ImportError: from io import StringIO` or use `six.StringIO` – Giacomo Alzetta Aug 23 '18 at 08:24
  • `"TypeError: unicode argument expected, got 'str'"` was raised by Python 2, not Python 3. – L3viathan Aug 23 '18 at 08:55
  • 1
    Sorry, I was't clear - the errors are all under Python 2. I haven't started actually using Python 3 yet. – Graham Klyne Aug 23 '18 at 09:30
  • @Giacomo Alzetta - Thanks! I wondered about the import/exception approach, and maybe I'll end up doing that. But I have a gripe that it treats the (eventually) normal case of using Python3 as an exception, which feels not-so-future-proof to me. (E.g. suppose a new module "StringIO" comes available in the future?). And I can't construct it the other way round because Python2 has io.StringIO. I think using `six.StringIO` is a better option, but so far I've been able to avoid this kind of external library dependency. – Graham Klyne Aug 23 '18 at 09:35
  • Python2 will be dead soon. Port it over and fuggedaboutit. – sureshvv Aug 23 '18 at 09:43
  • @GrahamKlyne There aren't many alternatives. anyway `six` isn't really an issue. You can use it just in the imports. When in the future you want to drop python2 support you should be able to remove it by simply changing the imports from `from six import XXX` to `from built-package import XXX` and you are done. (Well, not in 100% of the cases but for most simple stuff it's like this). – Giacomo Alzetta Aug 23 '18 at 09:52
  • I've just used "from six import StringIO", and all is well (under Python2), and I assume it will continue to be fine under Python3. I would happily accept that as an answer :) – Graham Klyne Aug 23 '18 at 10:01

2 Answers2

2

Use six's StringIO

from six.moves import StringIO

Tom Hennen
  • 4,746
  • 7
  • 34
  • 45
0

If your problem is the import statement, just wrap it in a try statement:

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

The rest of your code should work as is, at least the code provided in your question works just fine for me.

alkanen
  • 636
  • 6
  • 16