如何让macports的python优先使用自己的包而非其他安装的包?

1 投票
1 回答
1694 浏览
提问于 2025-04-18 05:29

我花了好几天时间在Mac OS Lion上折腾,想要搭建一个科学计算的Python环境。我试过SciPack Superpack,也尝试过用pip和easy_install手动安装,但在导入或使用各种模块时还是遇到了错误。根据这个Stackoverflow的讨论,我决定用MacPorts重新安装一遍。

但是,当我运行MacPorts的Python时,它却忽略了MacPorts安装的包,反而试图从旧的安装中加载不兼容的包。我非常确定我在运行新安装的MacPorts Python。我检查了符号链接,也查看了python_select,并且是直接输入新安装的路径来启动Python的。但是当我尝试导入,比如说statsmodels时,它却从另一个目录拉取了旧版本。

这是sys.path的内容(为了简洁已做编辑):

    ['',
 '/Library/Python/2.7/site-packages/mrjob-0.4.3_dev-py2.7.egg',
 '/Library/Python/2.7/site-packages/statsmodels-0.6.0-py2.7-macosx-10.9-intel.egg',
    ...
 '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python',
 '/Library/Python/2.7/site-packages/twilio-3.6.6-py2.7.egg',
 '/Library/Python/2.7/site-packages/six-1.6.1-py2.7.egg',
 '/Library/Python/2.7/site-packages/httplib2-0.9-py2.7.egg',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/readline',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PyObjC',
 '/Library/Python/2.7/site-packages']

MacPorts的安装在/opt/local,而旧的安装在/Library/Python。正如你所看到的,旧的包在列表中排得更靠前,这意味着它们的优先级更高。

环境变量PYTHONPATH是空的。如果我在PYTHONPATH中放入任何东西,它会在/Library条目之后但在/opt条目之前出现在sys.path中。所以这并没有解决问题。

如果我用-S选项启动新的Python,sys.path变成:

    ['',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload']

这样可以成功去掉那些外来的站点包条目,但同时也会删除MacPorts的站点包条目,这样我就无法加载任何东西了。

我认为问题的根源在于一个叫做/Library/Python/2.7/site-packages/easy-install.pth的文件,里面的内容如下(同样为了简洁已做编辑):

import sys; sys.__plen = len(sys.path)
./mrjob-0.4.3_dev-py2.7.egg
    ...
./statsmodels-0.6.0-py2.7-macosx-10.9-intel.egg
    ...
/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python
./twilio-3.6.6-py2.7.egg
./six-1.6.1-py2.7.egg
./httplib2-0.9-py2.7.egg
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

如果我在启动MacPorts Python之前重命名这个文件,Python就不会再把这些外来的包添加到它的sys.path中了。现在sys.path看起来和使用"python -S"选项时很相似,只是最后有这些额外的条目:

 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages',
 '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PyObjC',
 '/Library/Python/2.7/site-packages'

现在我可以加载MacPorts自己的包,比如说statsmodels。通过检查statsmodels.__file__,我确认它导入的是本地包,而不是外来的包。

不过,我觉得这只是一个变通办法,而不是一个真正的解决方案。真正的解决方案应该让它按预期工作,也就是:从/opt/local/...安装启动的MacPorts Python,应该优先考虑在/opt/local/...目录树中通过MacPorts安装的包,只有在本地没有模块时才去其他地方查找。所以我希望/opt/local的条目在sys.path中排在前面,而/Library的条目排在后面。

这似乎应该是默认行为,我在Stackoverflow上看到很多评论都说MacPorts Python就是这样工作的。那么……我该怎么做才能实现这个呢?

1 个回答

3

你遇到的问题有两个主要原因。第一个原因是,非系统的 OS X 框架版本的 Python 2.7,比如 MacPorts 的 Python 2.7,故意把系统 Python 的 site-packages 位置,也就是 /Library/Python/2.7/site-packages,放到了 sys.path 中,通常是在 sys.path 的最后面,也就是在这个版本自己的 site-packages 目录之后。这在 Python 的开发中一直有争论,现在 MacPorts 也有一个问题在讨论要把它去掉,理由是最好把系统 Python 和 MacPorts 的 Python 完全分开。

第二个原因是,原来的 setuptools 包和它的 easy_install 命令在 OS X 中的表现。正如你发现的,它通过一些魔法手段,利用 .pth 文件(包括 easy-install.pth),对 sys.path 进行了一些复杂的操作,确保通过 easy_install 安装的包会优先出现在 sys.path 中,从而覆盖其他已安装的同类包。你也发现,删除 /Library/Python/2.7/site-packages 中的 easy-install.pth 文件是去掉这种行为的一种方法,而且是最简单的方法。这是基于你不想使用系统 Python 中安装的那些包。

为了避免将来再出现这个问题,长期的策略是确保你不使用 Apple 提供的 easy_installeasy_install-2.7 命令,这些命令在 /usr/bin 中。它们会把包安装到 /Library/Python,供系统 Python 使用,并会创建或更新 easy-install.pth。一般来说,你应该尽量避免使用 easy_install。它的现代替代品是 pip,它提供了更好的控制,并避免了与 .pth 文件的复杂操作。如果 MacPorts 还没有提供你想安装的 Python 包,通常是 py27-xxxx 这种形式,你可以安装并使用 MacPorts 的 py27-pip,而不是使用 easy_install

/opt/local/bin/pip-2.7 install xxxx

你还可以使用常见的 MacPorts 功能,比如 port select pip pip27

撰写回答