在Linux上默认会有系统的python路径,例如:/usr/lib/python3.7/,python在查询库时,会先查PYTHONHOME环境变量,如果没找到包再查系统默认路径,所以通常下是不需要有PYTHONHOME,而virtualenv创建的各种python版本的环境,走的是VIRTUAL_ENV环境变量。总之,默认系统里是没有定义PYTHONHOME环境变量的。
通常我们通过apt安装的软件,如果依赖python,比如:hg,那么它是会写#!/usr/bin/env python,来指定走系统python,这样就不受virtual env的影响,同时也不应该定义PYTHONHOME到virtual env环境,而应该使用对应的activate命令加载环境。否则就会出现:可能你安装的hg依赖系统的python2.7,但是PYTHONHOME指向了python3.7环境,结果导致无法正常运行。
但是在使用cython进行python的二进制打包编译时,如果使用了cython_freeze,cython_freeze是一个简单的python脚本,用于生成main.c,内容中定义了python模块加载,默认情况会指定第一个包为__main__运行的包。举例:
./cython_freeze main uvloop msgpack ujson brotli > main.c
这个时候就有个问题了:一方面,我们不希望定义PYTHONHOME,避免对终端环境产生干扰,当然我们可以在程序前加env PYTHONHOME=$VIRTUAL_ENV,但有些麻烦;另一方面,我们希望编译出的程序能够自动识别VIRTUAL_ENV环境变量。
否则就会遇到如下错误:
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 get the locale encoding
ModuleNotFoundError: No module named 'encodings'
Current thread 0x00007fe324f01740 (most recent call first):
Aborted (core dumped)
显然改main.pyx模块是不错的,因为python环境是先加载,后执行main.pyx的,所以要改cython_freeze这个代码生成工具。
/* 当有VIRTUAL_ENV时,设置为PYTHONHOME,但不覆盖 */
const char* virtual_home = getenv("VIRTUAL_ENV");
const char* python_home = getenv("PYTHONHOME");
if (virtual_home && !python_home) {
setenv("PYTHONHOME", virtual_home, 0);
}
Py_Initialize();
PySys_SetArgv(argc, argv);
位置在Py_Initialize()之前,进行环境变量的设置,这里setenv第三个参数用0,表示不覆盖,其实感觉也可以考虑覆盖。
这样,编译出的c模块就能支持VIRTUAL_ENV环境变量了。