在 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
文件,从而保证安装的包能被正常加载运行。
参考和扩展阅读¶
原版源代码:
官方文档:
相关讨论: