7C00.ME/houmu 2015-12-30

Python秘技之sitecustomize.py

最近在读一个项目代码的时候,发现这样一个现象:代码执行效果是 A-B-C,但是源代码里面A、C相关的代码紧邻,中间并没有B的启动代码。那么B的代码是如何被执行的呢?

这段代码运行在virtualenv中没有其他关联的代码,也不是并发的,也没有找到hook或类似的东西。这让我纳闷了很久,直到突然发现,B代码所在文件叫做sitecustomize.py,这个名字似乎有点意思,就google了一下,于是就发现了这个Python秘技。

说是秘技,其实也没有多么高深,因为 Python标准库文档的 28.14 节 Site-specific configuration hook 说明这个功能。简单说来,python解释器在启动的时候会自动import 位于 $PREFIX/lib/pythonX.Y 目录下的sitecustomize.py文件(如果这个文件存在的话)。PREFIX是python的默认安装位置,标准安装情况下一般是/usr,可以通过 python -c 'import sys ; print(sys.prefix)' 查询。pythonX.Y取决于实际python版本Y,如python2.7。可以简单做一个实验:

PREFIX=$(python -c 'from sys import prefix; print(prefix)') # /usr
VERSION=$(python -c 'from sys import version_info as i; print("%d.%d" % (i.major, i.minor))') # 2.7
FILENAME=$PREFIX/lib/python$VERSION/sitecutomize.py # /usr/lib/python2.8/sitecustomize.py
echo 'print("Hello, Python")' > $FILENAME

这样以后每次执行python命令都会首先输出一行‘Hello, Python’。

除了在$PREFIX/lib/pythonX.Y目录添加sitecustomize.py,还可以利用PYTHONPATH在任意位置添加sitecustomize.py,比如这个实验:

mkdir foo
touch foo/__init__.py
echo 'print("Hello, Foo")' > foo/sitecustomize.py
export PYTHONPATH=`pwd`/foo
# test
python -c 'print("Hello, World")'

结果是在‘Hello, World’之前还有一行‘Hello, Foo’。不过当设置PYTHONPATH中多个module带有sitecustomize.py时,实际只会执行一个。

利用sitecustomize.py能做什么呢?比输出Hello,Python更多一些,可以实现类似Ubuntu的MOTD功能;在运行实际代码之前先加载一些专门的模块,从而实现代码监控工具,这样对于普通使用者来说,就不用特地写钩子函数之类的东西了。