2014-12-15 2 views
1

Что делать, в мире, где все текстовые литералы по умолчанию имеют Unicode, чтобы сделать __import__ работать как на Python 2, так и на 3?Портативный код: __import__ тип строки параметров между Python 2 и Python 3

Я медленно узнаю о создании кода Python, который будет работать как под Python 2 (версия 2.6, так и выше) и Python 3 (версия 3.2 или выше).

Это влечет за собой, я считаю, увещевание для обеспечения текстовых литералов являются Unicode по умолчанию:

from __future__ import unicode_literals 

и указать байты литералов явно с b'wibble', если это необходимо.

__import__ встроенная функция, однако, срабатывание.

Хитрый, тривиальный макет проекта:

$ mkdir fooproject/ 
$ cd fooproject/ 

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

Вот простой fooproject/setup.py для этого проекта:

from __future__ import unicode_literals 

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

assert main_module.bar 

Это терпит неудачу под Python 2, но прекрасно работает под 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 

Мы намеренно сделали unadorned строки Unicode по умолчанию. «Не строка », я предполагаю, что Python 2 означает «не« объект байтов ».

Итак, мы явно установить, что к bytes буквальным:

from __future__ import unicode_literals 

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

assert main_module.bar 

Теперь Python 2 выполняется, но Python 3 жалуется:

$ 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 

Так что я намеренно прикрас строки по умолчанию для Unicode, как и я; но это, очевидно, нарушая ожидания __import__ между Python 2 и Python 3.

Как я могу получить, что __import__ вызов, в комплекте с параметром fromlist, правильно работает под как Python 2 и Python 3, сохраняя настройки unicode_literals?

+0

Вы могли бы написать функцию, вы передаете строку и он проверяет версию и возвращает версию unicode или bytestring. – BrenBarn

+0

@BrenBarn, вот как ведет себя 'str()'! –

ответ

2

отзыв str работает по-разному для python2 и Python3 (см @ комментарий BrenBarn в), так что:

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

или вообще

main_module = __import__(main_module_name, fromlist=list(map(str, ['bar']))) 
+0

Колд для чего-то, что не нужно; но намного опрятно, чем то, что у меня было раньше. Благодарю. – bignose

1

Лучшее, что я могу придумать до сих пор является функцией обертка для преобразования типа в зависимости от версии Python:

from __future__ import unicode_literals 

import sys 

fromlist_expects_type = str 
if sys.version_info < (3, 0): 
    fromlist_expects_type = bytes 

def import_module(
     name, globals=None, locals=None, fromlist=(), level=0): 
    """ Import specified module, together with options used by __import__. 

     :param module_name: Text string of the module name to import. 
     :param fromlist: List of names of attributes in the module to 
      also import. 
     :return: The module object. 

     The built-in ``__import__`` function accepts a ``fromlist`` 
     parameter, but expects different string types between Python 2 
     and Python 3. Python 2 only allows text string items; Python 3 
     only allows byte string items. 

     This function's ``fromlist`` parameter must have items of text 
     string (Unicode) type only; the items are converted depending 
     on the running Python version. 

     """ 
    module_fromlist =() 
    if fromlist: 
     module_fromlist = [ 
       fromlist_expects_type(attr_name) for attr_name in fromlist] 
    module = __import__(
      name=name, 
      globals=globals, 
      locals=locals, 
      fromlist=module_fromlist, 
      level=level) 

    return module 

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

Это довольно громоздким, и я, вероятно, сделал несколько ошибок. Наверняка это ошибка в Python, что мне нужно сделать это?