我无法通过编译的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"
最佳答案
通常,Python解释器不是“独立的”,并且要运行,它需要其标准库(例如ctypes
(已编译)或site.py
(已解释))以及其他站点包的路径(例如numpy
)必须设置。
尽管可以通过冻结py模块并将所有c扩展名(例如参见this SO-post)合并到生成的可执行文件中来使Python插入器完全独立,但为嵌入的插入器提供所需的安装更为容易。
有时找不到标准的模块/站点软件包不是开箱即用的:必须通过设置Python路径来帮助解释器,即通过在<..>/sometest/lib/python3.5/site-packages
中添加sometest
(作为虚拟环境根文件夹的sys.path
)来帮助解释器。在pyx文件中以编程方式或在启动前通过设置PYTHONPATH
-environment变量进行设置。
继续阅读以获取更多详细信息和替代解决方案。
这个答案适用于Linux和Python3(Python 3.7),基本思想与Windows / MacOS相同,但是某些细节可能有所不同。
因为使用了venv
,所以我们有以下替代方法可以解决此问题:
在pyx文件中以编程方式将<..>/sometest/lib/python3.5/site-packages
(sometest
是虚拟环境的根文件夹)添加到sys.path
或通过在启动前设置PYTHONPATH
-environment变量。
将带有嵌入式python的可执行文件放在sometest
的子目录中(例如bin
或创建一个自己的目录)。
使用virtualenv
代替venv
。
注意:对于带有嵌入式python的可执行文件,无论是否激活了虚拟环境(或哪个虚拟环境),它均不起作用。
为什么以上解决了您的情况下的问题?
问题是,(嵌入式)Python解释器需要弄清楚以下几点:
平台无关的目录/文件,例如os.py
,argparse.py
(大多数都是* .py / * .pyc)。给定sys.prefix
,解释器可以找出在哪里找到它们(即在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-一个好的起点是此文件中的home
键。
如果未设置Py_NoSiteFlag
,则Py_Initialize
将利用site.py
(可以通过解释器找到它,因为sys.prefix
是已知的)或更精确的site.main()
来将虚拟环境的站点程序包添加到sys.path
。这样做时,site.py
查找pyvenv.cfg
并对其进行解析。但是,仅在以下情况下将本地site-packages
添加到python-path中:
如果名为“ 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下移一个目录会导致将本地站点程序包包含到路径中。
还有其他可能的情况,最重要的是可执行文件的位置,而不是激活哪个环境。
答:可执行文件在某处,但不在虚拟环境中
这种搜索启发式方法对于已安装的python解释器而言或多或少可靠,但对于嵌入式解释器或虚拟环境可能有所帮助(有关更多信息,请参见this issue)。
如果使用通常的apt install
或类似方法安装了python,则会找到它(由于搜索算法中的4. step),并且嵌入式解释器将使用系统安装。
但是,如果文件被移动或python是从源代码构建而未安装的,则嵌入的interperter无法启动:
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版本(否则我们有上述情况),则exe嵌入的exe将使用本地包。归因于this rule,归位搜索算法将始终找到本地归宿:
步骤3.尝试找到相对于argv0_path的前缀和exec_prefix,回溯路径,直到耗尽为止。这是成功的最常见步骤。请注意,如果prefix和exec_prefix为
不同的是,更有可能找到exec_prefix;但是如果
exec_prefix是前缀的子目录,两个都可以找到。
在这种情况下,
argv0_path
是exe的路径(没有pyvenv.cfg
文件!),而"landmarks"(lib / python $ VERSION / os.py和lib / python $ VERSION / lib-dynload)将被发现,因为它们在exe上方的本地首页中以符号链接的形式呈现。C:在
venv
环境内部的可执行两个文件夹在以下情况下,在
venv
环境中向下移动两个文件夹而不是一个文件夹(在该文件夹中可以工作):情况A:在搜索主目录(太远)时未读取pyvenv.cfg
文件,'venv`环境缺少指向以下链接的符号链接“地标”(仅在本地提供侧包),第3步将失败,只有4. step是希望。结论:如果没有正确的Python安装,嵌入式Python将无法工作,除非有其他可能性:
所需的文件打包在嵌入可执行文件旁边或上方的
lib\pythonX.Y\*
中(并且周围没有pyvenv.cfg
会使搜索混乱)。或
pyvenv.cfg
用于将解释器指向正确的位置。
关于python - cython嵌入后的ImportError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56857449/