Portable code: __import__ demands different string types between 2 and 3

Ben Finney ben+python at benfinney.id.au
Mon Dec 15 02:29:19 EST 2014


Howdy all,

What should I do, in a world where all text literals are Unicode by
default, to make ‘__import__’ work in both Python 2 and 3?


I'm slowly learning about making Python code that will run under both
Python 2 (version 2.6 or above) and Python 3 (version 3.2 or above).

This entails, I believe, the admonition to ensure text literals are
Unicode by default::

    from __future__ import unicode_literals

and to specify bytes literals explicitly with ‘b'wibble'’ if needed.


The ‘__import__’ built-in function, though, is tripping up.

A contrived, trivial project layout::

    $ mkdir fooproject/
    $ cd fooproject/

    $ mkdir foo/
    $ printf "" > foo/__init__.py
    $ mkdir foo/bar/
    $ printf "" > foo/bar/__init__.py

Here's a simple ‘fooproject/setup.py’::

    from __future__ import unicode_literals

    main_module_name = 'foo'
    main_module = __import__(main_module_name, fromlist=['bar'])

    assert main_module.bar

That fails under Python 2, but runs fine under Python 3::

    $ python2 ./setup.py
    Traceback (most recent call last):
      File "./setup.py", line 4, in <module>
        main_module = __import__(main_module_name, fromlist=['bar'])
    TypeError: Item in ``from list'' not a string

    $ python3 ./setup.py

We've deliberately made unadorned strings Unicode by default. By “not a
string”, I presume Python 2 means “not a ‘bytes’ object”.


Okay, so we'll explicitly set that to a ‘bytes’ literal::

    from __future__ import unicode_literals

    main_module_name = 'foo'
    main_module = __import__(main_module_name, fromlist=[b'bar'])

    assert main_module.bar

Now Python 2 is satisfied, but Python 3 complains::

    $ python2 ./setup.py

    $ python3 ./setup.py
    Traceback (most recent call last):
      File "./setup.py", line 4, in <module>
        main_module = __import__(main_module_name, fromlist=[b'bar'])
      File "<frozen importlib._bootstrap>", line 2281, in
        _handle_fromlist
    TypeError: hasattr(): attribute name must be string


How can I get that ‘__import__’ call, complete with its ‘fromlist’
parameter, working correctly under both Python 2 and Python 3, keeping
the ‘unicode_literals’ setting?

If some kind of kludge is needed to make it work between versions, is
this a bug which should be fixed so “use Unicode for text” remains
applicable advice?

-- 
 \           “I do not believe in immortality of the individual, and I |
  `\        consider ethics to be an exclusively human concern with no |
_o__)  superhuman authority behind it.” —Albert Einstein, letter, 1953 |
Ben Finney




More information about the Python-list mailing list