Невозможно импортировать расширение C ++, если в каталоге пакета root - PullRequest
5 голосов
/ 03 апреля 2020

Примечание : С тех пор как я задал этот вопрос, позже я обнаружил, что python -m pip install -e . установит расширение к cmod с .venv/lib/python3.8/site-packages/hello-c-extension.egg-link, указывающим на проект в текущем каталоге. Я также переключился на макет src в последующих коммитах и ​​обнаружил, что https://pythonwheels.com/ является отличным справочником для высококачественных пакетов, которые раздают колеса. Тем не менее, мне все еще интересно узнать о поведении подкоманд setup.py.


В рамках некоторых исследований many linux я использую игрушечный проект для создания различных колес платформы для модуля расширения C ++.

Кажется, что при сборке и установке локально я не могу импортировать модуль расширения C ++, если мой текущий каталог является каталогом проекта root. Это мешает мне запускать юнит-тесты, между прочим. Я полагаю, что причина этого в том, что . становится первым компонентом sys.path, и поэтому берется чистая версия Python, а скомпилированное расширение - нет.

Как это можно исправить ? Я правильно запускаю локальную сборку / установку?

Структура пакета выглядит следующим образом:

$ tree hello-c-extension/
hello-c-extension/
├── LICENSE
├── Makefile
├── README.md
├── cmod
│   ├── __init__.py
│   ├── _cmodule.cc
│   └── pymod.py
├── setup.py
└── tests
    ├── __init__.py
    └── test_cext.py

У меня также есть проект на GitHub; Я задал этот вопрос начиная с 29fef5b .

. Для сборки / установки я использую:

cd hello-c-extension
python -m venv .venv
source ./.venv/bin/activate
python -m pip install -U pip wheel setuptools
python setup.py build install

Теперь из текущего каталога я могу импортировать Python модуль, но не соответствующий модуль расширения. Модуль Python выбирается как модуль в текущем каталоге, а не как в site-packages:

$ python -c 'from cmod import pymod; print(pymod)'
<module 'cmod.pymod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/cmod/pymod.py'>
$ python -c 'from cmod import _cmod; print(_cmod)'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: cannot import name '_cmod' from 'cmod' (/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/cmod/__init__.py)

Удачное удаление элемента PWD sys.path исправляет это:

>>> import sys
>>> sys.path
['', '/Users/brad/.pyenv/versions/3.8.1/lib/python38.zip', '/Users/brad/.pyenv/versions/3.8.1/lib/python3.8', '/Users/brad/.pyenv/versions/3.8.1/lib/python3.8/lib-dynload', '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages', '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg']
>>> del sys.path[0]
>>> from cmod import _cmod; print(_cmod)
<module 'cmod._cmod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg/cmod/_cmod.cpython-38-darwin.so'>

И, наконец, смена каталога также устраняет проблему go:

$ cd ..
$ python -c 'from cmod import _cmod; print(_cmod)'
<module 'cmod._cmod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg/cmod/_cmod.cpython-38-darwin.so'>

Это действительно ... как это должно работать? Каков будет правильный способ запуска модульных тестов для модуля расширения в этом случае?

Информация о системе:

$ python -V
Python 3.8.1
$ uname -mrsv
Darwin 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64

1 Ответ

2 голосов
/ 07 апреля 2020

причина проблем

Это действительно ... как это должно работать? Каков будет правильный способ запуска модульных тестов для модуля расширения в этом случае?

Python импорт из sys.path. Это означает, что он будет искать все каталоги для импорта, от индекса 0 до последнего элемента в sys.path.

Обратите внимание, что хакерский подход к исправлению заключается в добавлении следующего перед импортированием: sys.path.append(sys.path.pop(0)). Это по-прежнему разрешает локальный импорт, но означает, что сначала выполняется поиск site-packages и стандартная библиотека.

Как это исправить? Я правильно запускаю локальную сборку / установку?

Я отвечу на первый вопрос ниже.

Да, с вашей сборкой / установкой все в порядке, но для исправления проблемы с импортом вам нужно немного ее настроить.

Устранение проблемы

Переключиться на src/cmod макет. Отличная ссылка - на которую вы сами указали в комментариях к вопросам - это здесь .

Этот подход обсуждается в комментариях, и после того, как вы задали вопрос, вы фактически реализовали это. ( 60093f1 ).

Я сейчас процитирую часть этой статьи; это объясняет это лучше, чем я мог.

Каталог sr c - лучший подход, потому что:

  • Вы получаете импортную четность. Текущий каталог неявно включен в sys.path; но не так при установке и импорте из пакетов сайтов. Пользователи никогда не будут иметь тот же текущий рабочий каталог, что и вы.

Это ограничение имеет полезные последствия как для тестирования, так и для упаковки:

  • Вы будете быть вынужденным проверить установленный код (например, путем установки в virtualenv). Это гарантирует, что развернутый код работает (он правильно упакован), иначе ваши тесты не пройдут. Рано. Прежде чем вы сможете опубликовать sh сломанный дистрибутив.

  • Вы будете вынуждены установить дистрибутив. Если вы когда-либо загружали дистрибутив на PyPI с отсутствующими модулями или нарушенными зависимостями, это потому, что вы не тестировали установку. Простая возможность успешно собрать sdist не гарантирует его фактической установки!

И pip install -e . не будет больше не испортить вещи .

...