10

Arch 系 Linux pip 安装到 /usr/local 的方法

 3 years ago
source link: https://phuker.github.io/arch-pip-usr-local.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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
  • 所有用户都能使用安装的库
  • 一次设置,平时不需要加特殊的参数

设置方法

假设系统是全新安装的,只安装了 pythonpython2 等包,没有安装 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,可以使用 pipget-pip.py 都接受的参数 --user,把 pip 和其他包安装在 ~/.local/lib/ 中。

缺点是只安装在了自己的家目录中,并且每次安装都需要加参数。

使用虚拟环境

使用 pyenvvirtualenv 等,不再赘述。

解释

对比 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 文件,从而保证安装的包能被正常加载运行。

参考和扩展阅读

原版源代码:

官方文档:

相关讨论:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK