我无法通过编译后的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"
答案 0 :(得分:1)
此答案适用于Linux和Python3(Python 3.7),其基本思想与Windows / MacOS相同,但某些细节可能有所不同。
由于使用了venv
,因此我们具有以下选项:
<..>/sometest/lib/python3.5/site-packages
-environment变量将sometest
(sys.path
是虚拟环境的根文件夹)添加到PYTHONPATH
。sometest
的子目录中(例如,bin
或创建一个自己的目录)将带有嵌入式python的可执行文件放入其中。virtualenv
代替venv
。注意:对于具有嵌入式python的可执行文件,无论是否激活了虚拟环境(或哪个虚拟环境),它都不起作用。
为什么以上情况在您的情况下有效?
问题是,(嵌入式)Python解释器需要弄清楚以下内容在哪里:
,
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.prefix
,sys.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
用于将解释器指向正确的位置。