python: lib/site-packages/site.py与lib/site.py的关系
由于我解决了一个特定的问题,今天大部分时间我都在研究site.py是怎么工作的。不过有一点我不太明白。
根据我的理解,当Python被加载时,首先会运行lib/python2.7/site-packages/site.py
。这个文件会查看PYTHONPATH
,寻找lib/python2.7/site.py
,然后把它导入。这个文件里有一个addsitedir
的方法,它不仅会把一个路径添加到sys.path
中,还会处理这个路径下的*.pth
文件。到这一步时,lib/python2.7/site.py
里的main()
会被执行,addsitedir
会在site-packages和用户的site-packages上运行。
接下来就是奇怪的地方了。我们又回到lib/python2.7/site-packages/site.py
,它会遍历pythonpath中的每一个路径,并对每个路径运行addsitedir
。我觉得这很奇怪,主要有两个原因:
addsitedir
在lib/python2.7/site-packages
上被执行了两次。- 这本身并不算太糟糕(因为同一个路径不会被添加到
sys.path
两次),但似乎lib/python2.7/site.py
有一个机制,允许用户通过实现一个usercustomize
模块来操控sys.path
(嘿,这在文档里都有提到)。显然,当你实现这样的机制时,你希望用户的代码最后执行,这样他就能控制添加到sys.path
的所有内容。但在这里并不是这样(我很沮丧地发现了这一点)。很可能第二次对lib/python2.7/site-packages
的调用会覆盖usercustomize
中做的所有事情。
我知道这很糟糕,但我在addsitedir
中加了一个打印语句,打印它接收到的路径,以便我能展示发生了什么。这些是处理过的路径:
/home/user/.local/lib/python2.7/site-packages #lib/python2.7/site.py
/home/user/py/lib/python2.7/site-packages #lib/python2.7/site.py
#This is where your usercustomize runs
#Followin calls are from lib/python2.7/site-packages/site.py
/home/user/py/lib/python2.7/site-packages/numpy-1.9.0-py2.7-linux-x86_64.egg
/home/user/Develop/Python/myproject
/home/user/lmfit-0.7.2
/home/user/py/lib/python2.7/site-packages #NOTE: this runs a second time
那么我在这里想问什么呢? :)
A. 我希望能得到一些关于为什么需要第二次调用site-packages的见解。
B. usercustomize
是否真的像我认为的那样受到限制?考虑到这一点,你会如何理论上实现一个移除路径的功能,从sys.path
中?
请求的调试输出:
:genie39:~ ;-) python2.7 -v
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# /home/user/py/lib/python2.7/site-packages/site.pyc matches /home/user/py/lib/python2.7/site-packages/site.py
import site # precompiled from /home/user/py/lib/python2.7/site-packages/site.pyc
# /home/user/py/lib/python2.7/os.pyc matches /home/user/py/lib/python2.7/os.py
import os # precompiled from /home/user/py/lib/python2.7/os.pyc
import errno # builtin
import posix # builtin
# /home/user/py/lib/python2.7/posixpath.pyc matches /home/user/py/lib/python2.7/posixpath.py
import posixpath # precompiled from /home/user/py/lib/python2.7/posixpath.pyc
# /home/user/py/lib/python2.7/stat.pyc matches /home/user/py/lib/python2.7/stat.py
import stat # precompiled from /home/user/py/lib/python2.7/stat.pyc
# /home/user/py/lib/python2.7/genericpath.pyc matches /home/user/py/lib/python2.7/genericpath.py
import genericpath # precompiled from /home/user/py/lib/python2.7/genericpath.pyc
# /home/user/py/lib/python2.7/warnings.pyc matches /home/user/py/lib/python2.7/warnings.py
import warnings # precompiled from /home/user/py/lib/python2.7/warnings.pyc
# /home/user/py/lib/python2.7/linecache.pyc matches /home/user/py/lib/python2.7/linecache.py
import linecache # precompiled from /home/user/py/lib/python2.7/linecache.pyc
# /home/user/py/lib/python2.7/types.pyc matches /home/user/py/lib/python2.7/types.py
import types # precompiled from /home/user/py/lib/python2.7/types.pyc
# /home/user/py/lib/python2.7/UserDict.pyc matches /home/user/py/lib/python2.7/UserDict.py
import UserDict # precompiled from /home/user/py/lib/python2.7/UserDict.pyc
# /home/user/py/lib/python2.7/_abcoll.pyc matches /home/user/py/lib/python2.7/_abcoll.py
import _abcoll # precompiled from /home/user/py/lib/python2.7/_abcoll.pyc
# /home/user/py/lib/python2.7/abc.pyc matches /home/user/py/lib/python2.7/abc.py
import abc # precompiled from /home/user/py/lib/python2.7/abc.pyc
# /home/user/py/lib/python2.7/_weakrefset.pyc matches /home/user/py/lib/python2.7/_weakrefset.py
import _weakrefset # precompiled from /home/user/py/lib/python2.7/_weakrefset.pyc
import _weakref # builtin
# /home/user/py/lib/python2.7/copy_reg.pyc matches /home/user/py/lib/python2.7/copy_reg.py
import copy_reg # precompiled from /home/user/py/lib/python2.7/copy_reg.pyc
import imp # builtin
# /home/user/py/lib/python2.7/site.pyc matches /home/user/py/lib/python2.7/site.py
import site # precompiled from /home/user/py/lib/python2.7/site.pyc
# /home/user/py/lib/python2.7/traceback.pyc matches /home/user/py/lib/python2.7/traceback.py
import traceback # precompiled from /home/user/py/lib/python2.7/traceback.pyc
# /home/user/py/lib/python2.7/sysconfig.pyc matches /home/user/py/lib/python2.7/sysconfig.py
import sysconfig # precompiled from /home/user/py/lib/python2.7/sysconfig.pyc
# /home/user/py/lib/python2.7/re.pyc matches /home/user/py/lib/python2.7/re.py
import re # precompiled from /home/user/py/lib/python2.7/re.pyc
# /home/user/py/lib/python2.7/sre_compile.pyc matches /home/user/py/lib/python2.7/sre_compile.py
import sre_compile # precompiled from /home/user/py/lib/python2.7/sre_compile.pyc
import _sre # builtin
# /home/user/py/lib/python2.7/sre_parse.pyc matches /home/user/py/lib/python2.7/sre_parse.py
import sre_parse # precompiled from /home/user/py/lib/python2.7/sre_parse.pyc
# /home/user/py/lib/python2.7/sre_constants.pyc matches /home/user/py/lib/python2.7/sre_constants.py
import sre_constants # precompiled from /home/user/py/lib/python2.7/sre_constants.pyc
# /home/user/py/lib/python2.7/_sysconfigdata.pyc matches /home/user/py/lib/python2.7/_sysconfigdata.py
import _sysconfigdata # precompiled from /home/user/py/lib/python2.7/_sysconfigdata.pyc
# zipimport: found 604 names in /home/user/py/lib/python2.7/site-packages/pytz-2014.7-py2.7.egg
# zipimport: found 20 names in /home/user/py/lib/python2.7/site-packages/hashlib-20081119-py2.7-linux-x86_64.egg
# zipimport: found 40 names in /home/user/py/lib/python2.7/site-packages/pysqlite-2.6.3-py2.7-linux-x86_64.egg
# zipimport: found 7 names in /home/user/py/lib/python2.7/site-packages/mock-1.0.1-py2.7.egg
import encodings # directory /home/user/py/lib/python2.7/encodings
# /home/user/py/lib/python2.7/encodings/__init__.pyc matches /home/user/py/lib/python2.7/encodings/__init__.py
import encodings # precompiled from /home/user/py/lib/python2.7/encodings/__init__.pyc
# /home/user/py/lib/python2.7/codecs.pyc matches /home/user/py/lib/python2.7/codecs.py
import codecs # precompiled from /home/user/py/lib/python2.7/codecs.pyc
import _codecs # builtin
# /home/user/py/lib/python2.7/encodings/aliases.pyc matches /home/user/py/lib/python2.7/encodings/aliases.py
import encodings.aliases # precompiled from /home/user/py/lib/python2.7/encodings/aliases.pyc
# /home/user/py/lib/python2.7/encodings/utf_8.pyc matches /home/user/py/lib/python2.7/encodings/utf_8.py
import encodings.utf_8 # precompiled from /home/user/py/lib/python2.7/encodings/utf_8.pyc
Python 2.7.8 (default, Sep 7 2014, 12:14:33)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
dlopen("/home/user/py/lib/python2.7/site-packages/readline-6.2.4.1-py2.7-linux-x86_64.egg/readline.so", 2);
import readline # dynamically loaded from /home/user/py/lib/python2.7/site-packages/readline-6.2.4.1-py2.7-linux-x86_64.egg/readline.so
>>>
运行python -vv
的输出在这里
1 个回答
lib/python2.7/site-packages/site.py
这个文件通常不会被加载。这是因为它的任务是将 site-packages
的路径添加到 sys.path
中,而 site-packages
中的 site.py
是不可见的。如果你在 site-packages
中找到了 site.py
,那就是个错误,那里不应该有这个文件。
在没有修补的 Python 中,会发生以下情况:
- Python 启动时,
sys.path
的内容很有限。site-packages
不在这个列表中,除非你设置了包含它的PYTHONPATH
变量。 - 当 Python 导入
site.py
时,它会导入sys.path
中第一个列出的那个。 - 找到并加载
lib/python2.7/site.py
site.py
将site-packages
添加到sys.path
中。
就这样,没有进一步的 site.py
模块被加载。即使你尝试加载,也只会找到已经 被导入 的模块;sys.modules['site']
存在,并保存了从 lib/python2.7/site.py
加载的对象。
不过,你的安装中有一个旧版本的 setuptools
,它包含一个特殊版本的 site.py
,这个 easy_install
命令会将其安装到 site-packages
中,如果那里还没有的话。它会通过明确扫描原始的 sys.path
来加载原始的 site.py
,忽略任何 PYTHONPATH
提供的路径,并手动加载原始的 site.py
模块,使用imp.find_module()
和imp.load_module()
这些低级函数,从而绕过正常的模块缓存。
它的目的是改变 sys.path
的顺序,使得 PYTHONPATH
列出的 .pth
文件具有更高的优先级,具体可以查看添加补丁的原始提交:
注意:这个版本包含一个修改过的 'site.py',以支持处理在 site-packages 之前的目录中的
.pth
文件。
这个补丁在更新的 setuptools
版本中已经完全移除,早在 2006 年的原始 setuptools
中就已经如此。
所以,要么你的 Linux 发行版已经设置好将 lib/python2.7/site-packages
添加到你的 PYTHONPATH
中,要么你的 shell 为你设置了这个,或者你的 Python 已经被修补以包含它,并且 你在 site-packages
中有旧的 setuptools
'补丁'。
删除这个文件是完全安全的。