mpounsett
mpounsett

Reputation: 1204

How do I get compatible type() behaviour in python 2 & 3 with unicode_literals?

This question looks strikingly similar to this one, however the suggestion in the comments there doesn't work (anymore?) as demonstrated below.

I'm trying to write a python2-3 compatible package, and one of my methods has a class generator in it, and type() is giving me problems in the python-2.7 tests:

Python 2.7.13 (default, Mar 18 2017, 17:03:32) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import unicode_literals
>>> from builtins import str
>>> type('MyClass', (object,), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type() argument 1 must be string, not unicode
>>> type(str('MyClass'), (object,), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type() argument 1 must be string, not newstr

The Python-Future overview page says:

# Compatible output from isinstance() across Py2/3:
assert isinstance(2**64, int)        # long integers
assert isinstance(u'blah', str)
assert isinstance('blah', str)       # only if unicode_literals is in effect

I expected this would give me consistent behaviour anywhere that strings are required, but apparently not.

What's the correct, version-independent, way to do this? The other question I linked to was asked in the era of python-2.6, and it seems like the behaviour has changed since then. I don't think I can just dump unicode_literals, since I run into portability problems (elsewhere) with calls to hashlib if I don't have it.

Upvotes: 3

Views: 2068

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123480

Don't use builtins.str(), use the plain str that comes with your Python version:

>>> from __future__ import unicode_literals
>>> type(str('MyClass'), (object,), {})
<class '__main__.MyClass'>

This works both in Python 2 and 3. If the future.builtins module replaces the str built-in type by default, use the __builtin__ module:

try:
    # Python 2
    from __builtin__ import str as builtin_str
except ImportError:
    # Python 3
    from builtins import str as builtin_str

MyClass = type(builtin_str('MyClass'), (object,), {})

Upvotes: 5

Related Questions