Пакет Cython с __init__.pyx: возможно?

Можно ли создать пакет Python 2.7, используя __init__.pyx (скомпилированный в __init__.so)? Если да, то как? Мне не повезло заставить его работать.

Вот что я пробовал:

  • setup.py:

    #!/usr/bin/env python
    
    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    
    foo = Extension(name='foo.__init__', sources=['foo/__init__.pyx'])
    bar = Extension(name='foo.bar', sources=['foo/bar.pyx'])
    
    setup(name='foo',
          packages = ['foo'],
          cmdclass={'build_ext':build_ext},
          ext_modules = [foo, bar])
    
  • foo/__init__.pyx:

    import foo.bar
    
    cpdef hello_world():
        print "hello world"
        foo.bar.blah()
    
  • foo/bar.pyx:

    cpdef blah():
        print "blah"
    

Вышеприведенное имеет следующее поведение:

$ python -c 'import foo; foo.hello_world()'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named foo

Я видел ошибку Python №15576, которая была исправлена ​​эта фиксация Hg. Глядя на эквивалентную фиксацию Git в Git зеркало репозитория Python Hg, я вижу, что фиксация доступна из тега Python v2.7.5 (а также всех последующих версий v2.7.x). Был ли регресс?


person Richard Hansen    schedule 01.02.2015    source источник
comment
Просто из любопытства: зачем тебе это?   -  person Dschoni    schedule 18.08.2015


Ответы (2)


Согласно этому очень старому сообщению списка рассылки, это работает, если у вас также есть __init__.py файл (файл __init__.py не используется, но, по-видимому, необходим для того, чтобы каталог рассматривался как модуль, и, следовательно, файл __init__.so должен быть загружен).

Если я добавлю __init__.py:

# an exception just to confirm that the .so file is loaded instead of the .py file
raise ImportError("__init__.py loaded when __init__.so should have been loaded")

тогда ваш пример работает на Linux Python 2.7.3:

$ python -c 'import foo; foo.hello_world()'
hello world
blah

Это имеет все признаки глючного углового корпуса, поэтому, вероятно, не рекомендуется. Обратите внимание, что в Windows это, похоже, не работает для меня,

ImportError: DLL load failed: %1 is not a valid Win32 application.

Дополнение (для небольшого дополнительного контекста):

Такое поведение явно не задокументировано. В исходном описании пакетов эпохи Python 1.5 говорится:

без __init__.py каталог не распознается как пакет

а также

Совет: порядок поиска определяется списком суффиксов, возвращаемым функцией imp.get_suffixes(). Обычно суффиксы ищутся в следующем порядке: ".so", "module.so", ".py", ".pyc". Каталоги явно не встречаются в этом списке, но предшествуют всем его записям.

Наблюдаемое поведение, безусловно, согласуется с этим __init__.py, необходимым для обработки каталога как пакета, но файл .so загружается вместо файла .py, но вряд ли это однозначно.

С точки зрения Cython такое поведение, по-видимому, использовалось для компиляции стандартной библиотеки (в этом случае __init__.py всегда присутствовало бы) или в тестовых примерах, заданных https://github.com/cython/cython/blob/master/tests/build/package_compilation.srctree (а также несколько других примеров) . В них файл «srctree» выглядит расширенным во множество папок, содержащих __init__.py (и другие файлы), а затем компилируется. Возможно, что только наличие __init__.so просто никогда не проверялось.

person DavidW    schedule 18.08.2015
comment
Упоминается ли этот трюк в официальной документации? (по-видимому, этого не было во время этой ветки списка рассылки, но, возможно, с тех пор что-то изменилось) - person Richard Hansen; 19.08.2015
comment
Я так не думаю. (Я нашел его случайно, а затем чуть позже наткнулся на сообщение группы новостей и понял, что я сделал.) Я немного посмотрю и обновлю свой ответ, если смогу найти лучший источник. Кажется, он также работает с файлом __init__.pyc. - person DavidW; 19.08.2015
comment
Предложения по улучшению: вместо assert False может быть лучше поднять ImportError. Или, может быть, есть какие-то низкоуровневые вещи, которые можно сделать с модулем imp в качестве запасного варианта на случай, если этот трюк перестанет работать в будущей версии Python. - person Richard Hansen; 19.08.2015
comment
@RichardHansen Я обновил его, чтобы поднять ImportError (согласен - это явно лучше!) И добавил ссылку на небольшую документацию, которая, как я могу найти, поддерживает это. Я думаю, что это в основном недокументировано - person DavidW; 19.08.2015

Попробуйте использовать относительный импорт.

in __init__:

from . import bar

Также может быть from . import foo. Давно не использовал python 2 cython.

person Berserker    schedule 14.08.2015