cython嵌入后的ImportError

时间:2019-07-02 17:28:42

标签: python module cython

我无法通过编译后的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'

我认为从cython嵌入式编译脚本中使用venv并不是问题:该脚本在没有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 个答案:

答案 0 :(得分:1)

此答案适用于Linux和Python3(Python 3.7),其基本思想与Windows / MacOS相同,但某些细节可能有所不同。

由于使用了venv,因此我们具有以下选项:

  • 在pyx文件中以编程方式或通过在启动前设置<..>/sometest/lib/python3.5/site-packages-environment变量将sometestsys.path是虚拟环境的根文件夹)添加到PYTHONPATH
  • sometest的子目录中(例如,bin或创建一个自己的目录)将带有嵌入式python的可执行文件放入其中。
  • 使用virtualenv代替venv

注意:对于具有嵌入式python的可执行文件,无论是否激活了虚拟环境(或哪个虚拟环境),它都不起作用。


为什么以上情况在您的情况下有效?

问题是,(嵌入式)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 prefix / lib / pythonX.Y`)。
  • 依赖平台的目录/文件,例如共享库。给定sys.exec_prefix,解释器可以找出在哪里找到它们(例如,共享库可以在exec_prefix/lib/pythonX.Y/lib-dynload中找到)。

执行found here时,算法可以为Py_Initialize并执行搜索。找到这些目录后,即可构建“ sys.path”。

但是,在使用venv时,有一个pyvenv.cfg文件next to exe or in the parent directory,可确保找到正确的Python-Home-一个很好的起点是{{1} }键输入此文件。

使用site.py时(由于已知home,解释器可以找到它)将虚拟环境的副程序包添加到sys.prefixsys.path寻找site.py并解析。但是,仅在以下情况下,将本地pyvenv.cfg添加到python-path中:

  

如果名为“ pyvenv.cfg”的文件位于上面的一个目录中   sys.executable,sys.prefix和sys.exec_prefix设置为   目录,并且还会检查它是否包含站点软件包(sys.base_prefix   和sys.base_exec_prefix将始终是   Python安装)。

在您的情况下,side-packages不在上面的目录中,而是与exe相同-因此,不包括通过pip安装库的本地站点软件包。不包含全局站点程序包,因为pyvenv.cfg具有密钥pyvenv.cfg。因此,不允许使用任何侧包,并且找不到已安装的库。

但是,将exe下移一个目录会导致将本地站点程序包包含到路径中。


还有其他可能的情况,重要的是可执行文件的位置,而不是激活的环境。

A:可执行文件在某处,但不在虚拟环境中

此搜索试探法对于已安装的python解释器而言或多或少可靠,但对于嵌入式解释器或虚拟环境可能有所帮助(有关更多信息,请参见this issue)。

如果使用常规的include-system-site-packages = false或类似工具安装了python,则会找到它(由于搜索算法中的4. step),并且嵌入式解释器将使用系统安装。

但是,如果文件到处移动或从源代码构建python但未安装python,则嵌入的interperter无法启动:

apt install

在这种情况下,可以使用Py_SetPythonHome或设置环境变量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'

B:可在虚拟环境中执行,由virtualenv创建

假设虚拟环境使用的是相同的Python版本,并且嵌入了python(否则,我们遇到了上述情况),那么exe将使用本地包。归因于this rule,归位搜索算法将始终找到本地归宿:

  

第3步。尝试找到相对于argv0_path的前缀和exec_prefix,回溯路径,直到耗尽为止。这是成功的最常见步骤。请注意,如果prefix和exec_prefix为   不同的是,更有可能找到exec_prefix;但是如果   exec_prefix是前缀的子目录,两个都可以找到。

在这种情况下,$PYTHONHOME是exe(没有argv0_path文件!)和"landmarks"(lib / python $ VERSION / os.py和lib /将会找到python $ VERSION / lib-dynload),因为它们以符号链接的形式显示在exe上方的本地首页中。

C:在pyvenv.cfg环境内部深处可执行两个文件夹

venv环境中向下移动两个文件夹(而不是在其中工作的文件夹)会导致在以下情况下搜索A:在搜索主目录(上方位置太远)时未读取venv文件,“ venv`-environments缺少与“地标”的符号链接(仅存在本地软件包),第3步将失败,4. step是唯一的希望。


推论:除非正确安装Python,否则嵌入式Python将无法正常工作,

  • 所需的文件被打包到嵌入可执行文件旁边或上方的pyvenv.cfg中(并且周围没有lib\pythonX.Y\*会使搜索混乱)。

  • pyvenv.cfg用于将解释器指向正确的位置。