ImportError после вставки Cython - PullRequest
1 голос
/ 02 июля 2019

Я не могу получить другие доступные модули, видимые скомпилированным скриптом Python.Как мне нужно изменить описанный ниже процесс, чтобы принимать либо модули на основе venv, либо глобальные модули?

Шаги:

$ python3 -m venv sometest
$ cd sometest
$ . bin/activate
(sometest) $ pip3 install PyCrypto Cython

Базовый скрипт с использованием нестандартного модуля Crypto:

# hello.py
from Crypto.Cipher import AES
import base64
obj = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
msg = "The answer is no"
ciphertext = obj.encrypt(msg)
print(msg)
print(base64.b64encode(ciphertext))
(sometest) $ python3 hello.py
The answer is no
b'1oONZCFWVJKqYEEF4JuL8Q=='

Компиляция:

(sometest) $ cython -3 --embed hello.py
(sometest) $ gcc -Os -I /usr/include/python3.5m -o hello hello.c -lpython3.5m -lpthread -lm -lutil -ldl
(sometest) $ $ ./hello
Traceback (most recent call last):
  File "hello.py", line 1, in init hello
    from Crypto.Cipher import AES
ImportError: No module named 'Crypto'

Я не думаю, что это проблема с использованием venv из встроенного в cthon скрипта: скрипт работаетв других местах системы без venv (то есть python3 -c 'from Crypto.Cipher import AES' не дает сбоя).

В противном случае процесс работает нормально:

(sometest) $ echo 'print("hello world")' > hello2.py
(sometest) $ cython -3 --embed hello2.py
(sometest) $ gcc -Os -I /usr/include/python3.5m -o hello2 hello2.c -lpython3.5m -lpthread -lm -lutil -ldl
(sometest) $ ./hello2
hello world

Система:

(sometest) $ python3 --version
Python 3.5.2
(sometest) $ pip3 freeze
Cython==0.29.11
pkg-resources==0.0.0
pycrypto==2.6.1

(sometest) $ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"

1 Ответ

1 голос
/ 03 июля 2019

Этот ответ предназначен для Linux и Python3 (Python 3.7), основная идея та же для Windows / MacOS, но некоторые детали могут отличаться.

Поскольку venv используется, у нас есть следующие опции:

  • добавление <..>/sometest/lib/python3.5/site-packages (sometest в качестве корневой папки виртуальной среды) к sys.path либо программно в pyx-файле, либо путем установки переменной PYTHONPATH -environment перед запуском.
  • размещение исполняемого файла с внедренным python в подкаталоге sometest (например, bin или создание собственного).
  • с использованием virtualenv вместо venv.

Примечание. Для исполняемого файла с внедренным питоном он не играет никакой роли, независимо от того, активирована виртуальная среда (или какая) или нет.


Почему вышеописанное работает в вашем сценарии?

Проблема в том, что (встроенный) Python-интерпретатор должен выяснить, где находятся следующие вещи:

  • независимый от платформы каталог / файлы, например, «Os.py , argparse.py (mostly everything *.py/*.pyc). Given [ sys.prefix ][1], the interpreter can figure out where to find them (i.e. in префикс / Lib / pythonX.Y`).
  • каталог / файлы, зависящие от платформы, например, общие библиотеки. Учитывая sys.exec_prefix, интерпретатор может выяснить, где их найти (например, общие библиотеки можно найти в exec_prefix/lib/pythonX.Y/lib-dynload).

Алгоритм может быть найден здесь , и поиск выполняется, когда выполняется Py_Initialize. Когда эти каталоги найдены, можно создать `sys.path '.

Однако при использовании venv рядом с exe или в родительском каталоге имеется pyvenv.cfg -файл *1052*, который гарантирует, что найден правильный Python-Home - хорошая отправная точка home ключ в этом файле.

Когда используется site.py (он может быть найден интерпретатором, поскольку известно sys.prefix) для добавления дополнительных пакетов виртуальной среды к sys.path, site.py ищет pyvenv.cfg и разбирает. Тем не менее, локальные side-packages добавляются в путь Python только тогда, когда:

Если файл с именем "pyvenv.cfg" существует на один каталог выше sys.executable, sys.prefix и sys.exec_prefix установлены на это каталог и он также проверяется на наличие пакетов сайта (sys.base_prefix и sys.base_exec_prefix всегда будут «настоящими» префиксами Установка Python).

В вашем случае pyvenv.cfg находится не в указанном выше каталоге, а в том же, что и exe - поэтому локальные пакеты сайта, где библиотеки были установлены через pip, не включены. Глобальные пакеты сайта не включены, потому что pyvenv.cfg имеет ключ include-system-site-packages = false. Таким образом, не допускаются дополнительные пакеты, и установленные библиотеки не могут быть найдены.

Однако перемещение каталога exe на один вниз приведет к включению локальных пакетов сайта в путь.


Возможны и другие сценарии, в которых учитывается местоположение исполняемого файла, а не какая среда активирована.

A: Исполняемый файл находится где-то, но не внутри виртуальной среды

Эта эвристика поиска работает более или менее надежно для установленных интерпретаторов Python, но может относиться к встроенным интерпретаторам или виртуальным средам (см. эту проблему для получения дополнительной информации).

Если python был установлен с использованием обычного apt install или подобного, то он будет найден (из-за 4. Step в алгоритме поиска), и установка системы будет использоваться встроенным интерпретатором.

Однако, если файлы были перемещены или Python был собран из источника, но не установлен, встроенный интерпретатор не может запуститься:

Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: initfsencoding: unable to load the file system codec
ModuleNotFoundError: No module named 'encodings'

В этом случае Py_SetPythonHome или установка переменной среды $PYTHONHOME являются возможными решениями.

B: Исполняемый файл в виртуальной среде, созданный с помощью virtualenv

Предполагая, что это одна и та же версия Python для виртуальной среды и встраиваемого Python (в противном случае мы имеем вышеупомянутый случай), emebeded exe будет использовать локальные сторонние пакеты. Алгоритм поиска домов всегда найдет местный дом, благодаря этому правилу :

Шаг 3. Попробуйте найти префикс и exec_prefix относительно argv0_path, возвращая путь до тех пор, пока он не будет исчерпан.Это самый распространенный шаг к успеху.Обратите внимание, что если префикс и exec_prefix различаются, exec_prefix с большей вероятностью будет найден;однако, если exec_prefix является подкаталогом префикса, будут найдены оба.

В этом случае argv0_path - это путь к exe (файла pyvenv.cfg нет!), а "landmarks" (lib / python $ VERSION / os.py и lib / python $ VERSION / lib-dynload) будут найдены, потому что они представлены в виде символических ссылок в local-home над exe.

C: Исполняемые две папки в глубине venv -окружения

Если две, а не одна папка (там, где она работает) в venv -среде, приведет кA: pyvenv.cfg файл не читается при поиске дома (слишком далеко выше), в средах 'venv` отсутствуют символические ссылки на "ориентиры" (присутствуют только сторонние пакеты), и такой шаг 3 завершится неудачно, с 4.step является единственной надеждой.


Следствие: Embeded Python не будет работать без правильной установки Python, если только среди других возможностей:

  • необходимые файлы упакованы в lib\pythonX.Y\* рядом с исполняющим файлом для встраивания или где-то выше (и нет pyvenv.cfg вокруг, чтобы испортить поиск).

  • или pyvenv.cfg используется для указания переводчика в нужное место.

...