tanggang1740 发表于 2017-5-1 13:23:35

[Python源码学习]之模块路径

接前面Python源码笔记之Py_InitializeEx,尝试看看Python中用到的一些Path
(这部分东西太乱了...具体见PC/getpathp.c 和 Modules/getpath.c 中的注释)。
一个Python程序要有运行,必须要能找到 .py/.pyc/.pyo/.pyd/.so 等,如何找到这些模块,还真是个问题:





sys.executable




可执行程序完整路径(其他函数可据此查找python运行时需要的库)







sys.prefix




平台无关文件




Python安装路径,用来生成标准库搜索路径







sys.exec_prefix




平台相关文件







sys.path




模块搜索路径







prefix 和 exec_prefix,平时用,几乎总是一样的,应该不用细分。









常见默认值







Unix (pure)




prefix/lib/pythonX.Y




/usr/local/lib/pythonX.Y







Unix (non-pure)




exec-prefix/lib/pythonX.Y/




/usr/local/lib/pythonX.Y







Windows




prefix\Lib




C:\PythonXY\Lib\








何处初始化?


这4个均在Py_InitializeEx进行初始化:







初始值







executable




Py_GetProgramFullPath()







prefix




Py_GetPrefix()







exec_prefix




Py_GetExecPrefix()







path




Py_GetPath()







[*]

_PySys_Init()



PyObject *
_PySys_Init(void)
{
    SET_SYS_FROM_STRING("executable",
                        PyUnicode_FromWideChar(
                               Py_GetProgramFullPath(), -1));
    SET_SYS_FROM_STRING("prefix",
                        PyUnicode_FromWideChar(Py_GetPrefix(), -1));
    SET_SYS_FROM_STRING("exec_prefix",
                        PyUnicode_FromWideChar(Py_GetExecPrefix(), -1));
...


[*]

PySys_SetPath(Py_GetPath());



void
PySys_SetPath(const wchar_t *path)
{
    PyObject *v;
    if ((v = makepathobject(path, DELIM)) == NULL)
      Py_FatalError("can't create sys.path");
    if (PySys_SetObject("path", v) != 0)
      Py_FatalError("can't assign sys.path");
    Py_DECREF(v);
}


环境变量


与这些path相关的有两个环境变量非常关键:


[*]PYTHONHOME

用来指定 prefix 和 exec_prefix 的值,格式:"ThePrefixPath" 或 "ThePrefixPath:TheExec_prefixPath"。注意,指定两个路径时,用冒号分割。
如果使用Py_SetPythonHome()设置了home值,则不会使用环境变量指定的值!


[*]PYTHONPATH

设置模块的默认搜索路径,路径间分隔符和平台相关:Windows下分号,其他平台冒号。


3个函数


除了前面的环境变量,下面3个函数也很重要(在Py_InitializeEx之前调用)


[*]

Py_SetProgramName()

[*]

Py_SetPythonHome()

[*]

Py_SetPath()


注意:使用Py_SetPath()以后,prefix和exec_prefix都将为空。Manual中说多个路径采用分号分割!这是错的,应该是和平台相关,Windows下用分号,其他平台冒号。


路径确定


大致分两步:


[*]首先确定 prefix 和 exec_prefix
[*]

然后,在prefix和exec_prefix上面追加libpath(比如lib/pythonXX/)以及宏PYTHONPATH中指定的路径(比如在linux下,可能是L":plat-linux2")



prefix


prefix 的确定(linux下):


[*]

如果PYTHONHOME被设置(无论是环境变量,还是Py_SetPythonHome()),则无条件使用它。

[*]

否则,按照可执行程序所在路径(Py_SetProgramName)依次往上搜索(准则是:lib/pythonXX/os.py(os.pyc、os.pyo)文件)

[*]否则,使用configure时设置的值

Windows下,prefix和exec_prefix没多大意思了,主要就是确定Pyathon的主目录:


[*]如何PYTHONHOME被设置,则使用
[*]否则,按照可执行程序所在目录向上搜索 (lib/os.py)
[*]否则,查找注册表


path


即 sys.path 的初始值:


[*]

如果使用Py_SetPath()设置了路径,则直接使用这些路径

[*]反之:按下面的规则生成

[*]首先添加:环境变量 PYTHONPATH 设置的路径
[*]其次:默认的zip文件路径(和prefix相关),比如:prefix/lib/pythonXX.zip
[*]

再次:编译期设置的一些路径(通过宏PYTHONPATH),和prefix合并成得到的路径,比如:

[*]prefix/lib/python3.2
[*]prefix/lib/python3.2/plat-linux2


[*]最后:动态加载模块的路径(和exec_prefix相关),比如:exec_prefix/lib/python3.2/lib-dynload



于是,在linux下,可以见到:





'/usr/lib/python3.2'




.py/.pyc/.pyo







'/usr/lib/python3.2/plat-linux2'




平台相关的 .py/.pyc/.pyo







'/usr/lib/python3.2/lib-dynload'




动态库的 .so








参考



[*]

http://docs.python.org/py3k/using/cmdline.html#envvar-PYTHONHOME

[*]

http://docs.python.org/py3k/c-api/init.html

[*]

http://docs.python.org/py3k/install/index.html

页: [1]
查看完整版本: [Python源码学习]之模块路径