在 Linux 系统目录结构中,由系统包管理程序管理的文件一般存放在 /、/usr 中,/usr/local 则存放不受系统包管理程序管理的文件,例如通过 make install 手动安装的程序、使用 pip 等其他程序安装的程序。Debian/Ubuntu 等发行版对 python 包进行了一定的修改,由 pip 安装的包默认会安装在 /usr/local 中。而 Arch/Manjaro 等发行版保持了上游的“原汁原味”,对上游的代码做了最小的修改,因此保留了 Python 默认的行为。在这些发行版中,pip 安装的包会安装在 /usr/lib/pythonX.Y/site-packages 中。为了防止 pip 和系统包管理冲突,最好通过配置使 pip 像 Debian/Ubuntu 一样把包安装到 /usr/local 中。
目标¶
- 让
pip管理pip自身和其他包 - 不和系统包管理冲突
pip自身和其他包都装在/usr/local- 所有用户都能使用安装的库
- 一次设置,平时不需要加特殊的参数
设置方法¶
假设系统是全新安装的,只安装了 python 和 python2 等包,没有安装 pip,没有用 pip 安装其他包。
按步骤进行:
在所有版本 Python 的默认 site-packages 目录中创建 .pth 文件。例如,对于 Python 3.8 版本,在 /usr/lib/python3.8/site-packages/ 目录中创建 usr-local.pth 文件,内容为:
/usr/local/lib/python3.8/site-packages
注意:pythonX.Y 需要和实际情况一致。
编辑 root 用户的 ~/.config/pip/pip.conf 文件,设置 install.prefix 配置项。同时可以顺便换一下源。编辑的结果为:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
prefix = /usr/local
然后即可按照官方文档的方法,使用 root 用户下载 get-pip.py,正常安装 pip,使用 pip 安装其他包。
注:洒家使用的是 Manjaro,理论上 Arch 也能用。
其他方案¶
如果自己的使用场景目标和前述 5 个目标不完全重合,还有以下几种替代方案。
--user 参数¶
如果只有一个用户运行 Python,可以使用 pip 和 get-pip.py 都接受的参数 --user,把 pip 和其他包安装在 ~/.local/lib/ 中。
缺点是只安装在了自己的家目录中,并且每次安装都需要加参数。
使用虚拟环境¶
使用 pyenv、virtualenv 等,不再赘述。
解释¶
对比 Arch 2020-4-8 3.8.2-2 版本、Ubuntu 2020-4-30 3.8.2-1 版本和 Python 原版的 site.py 文件,可以发现,Debian/Ubuntu 等发行版对 python 包进行了一定的修改,而 Arch/Manjaro 等发行版保持了上游的“原汁原味”,对上游的代码做了最小的修改。
Ubuntu 的头部增加了一段注释:
For Debian and derivatives, this sys.path is augmented with directories
for packages distributed within the distribution. Local addons go
into /usr/local/lib/python<version>/dist-packages, Debian addons
install into /usr/lib/python3/dist-packages.
/usr/lib/python<version>/site-packages is not used.
getsitepackages 函数有区别,Ubuntu 版为:
def getsitepackages(prefixes=None):
"""Returns a list containing all global site-packages directories.
For each directory present in ``prefixes`` (or the global ``PREFIXES``),
this function will find its `site-packages` subdirectory depending on the
system environment, and will return a list of full paths.
"""
sitepackages = []
seen = set()
if prefixes is None:
prefixes = PREFIXES
for prefix in prefixes:
if not prefix or prefix in seen:
continue
seen.add(prefix)
if os.sep == '/':
if 'VIRTUAL_ENV' in os.environ or sys.base_prefix != sys.prefix:
sitepackages.append(os.path.join(prefix, "lib",
"python" + sys.version[:3],
"site-packages"))
sitepackages.append(os.path.join(prefix, "local/lib",
"python" + sys.version[:3],
"dist-packages"))
sitepackages.append(os.path.join(prefix, "lib",
"python3",
"dist-packages"))
# this one is deprecated for Debian
sitepackages.append(os.path.join(prefix, "lib",
"python" + sys.version[:3],
"dist-packages"))
else:
sitepackages.append(prefix)
sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
return sitepackages
而 Arch 版为:
def getsitepackages(prefixes=None):
"""Returns a list containing all global site-packages directories.
For each directory present in ``prefixes`` (or the global ``PREFIXES``),
this function will find its `site-packages` subdirectory depending on the
system environment, and will return a list of full paths.
"""
sitepackages = []
seen = set()
if prefixes is None:
prefixes = PREFIXES
for prefix in prefixes:
if not prefix or prefix in seen:
continue
seen.add(prefix)
if os.sep == '/':
sitepackages.append(os.path.join(prefix, "lib",
"python%d.%d" % sys.version_info[:2],
"site-packages"))
else:
sitepackages.append(prefix)
sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
return sitepackages
实际运行命令也有区别,在 Ubuntu 20.04 中:
Python 3.8.2 (default, Apr 27 2020, 15:53:34)
[GCC 9.3.0] on linux
>>> site.getsitepackages()
['/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.8/dist-packages']
>>> sys.path
['', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']
在 Manjaro 中:
Python 3.8.3 (default, May 17 2020, 18:15:42)
[GCC 10.1.0] on linux
>>> site.getsitepackages()
['/usr/lib/python3.8/site-packages']
>>> sys.path
['', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/usr/lib/python3.8/site-packages']
解决方法就是,配置 pip,修改其默认 prefix,使 pip 安装到 /usr/local 中。由于 sys.path 不包含 /usr/local 中的安装目录,还需要增加一个 .pth 文件,从而保证安装的包能被正常加载运行。
参考和扩展阅读¶
原版源代码:
官方文档:
相关讨论: