Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 582 – Python local packages directory

Author:
Kushal Das <mail at kushaldas.in>, Steve Dower <steve.dower at python.org>, Donald Stufft <donald at stufft.io>, Nick Coghlan <ncoghlan at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Packaging
Created:
16-May-2018
Python-Version:
3.12

Table of Contents

摘要

此 PEP 提议向 Python 添加一种自动识别 __pypackages__ 目录的机制,并优先导入安装在此位置的包,而不是用户或全局站点包。这将避免创建、激活或禁用“虚拟环境”的步骤。当出现时,Python 将使用脚本基目录中的 __pypackages__

动机

Python 虚拟环境已经成为社区中开发和教学工作流程的重要组成部分,但与此同时,它们也为许多人创造了进入的障碍。以下是人们在接触 Python(或第一次编程)时遇到的一些问题。

  • 对于新手来说,虚拟环境是如何工作的是很多信息。解释它们需要花费大量额外的时间和精力。
  • 不同的平台和 shell 环境需要不同的命令集来激活虚拟环境。任何研讨会或教学环境,如果人们的笔记本电脑上安装了不同的操作系统,都会在参与者中造成很多困惑。
  • 虚拟环境需要在每个打开的终端上被激活。如果有人创建/打开新终端,默认情况下,该终端不会获得与之前激活的虚拟环境相同的环境。

基本原理

Python 是初学者友好的编程语言。但是,到目前为止,虚拟环境是新人学习过程中的主要时间。这个 PEP 并不是试图解决每个打包问题,而是专注于 90% 在学习过程中与虚拟环境作斗争的新人。创建新目录仍然比在不同平台上了解虚拟环境的细节容易得多。

PEP 的主要观点是,它并没有试图取代虚拟环境。如果需要虚拟环境的所有功能,他们应该使用合适的虚拟环境(例如,使用 venv 模块创建)。

规范

当 Python 二进制文件执行时,它尝试确定其前缀(存储在 sys.prefix 中),然后使用该前缀查找标准库和其他关键文件,并由 site 模块确定 site-package 目录的位置。目前要找到前缀(假设 PYTHONHOME 未设置),首先要遍历文件系统树,寻找表示标准库存在的标记文件(os.py),如果没有找到,则返回到二进制文件中硬编码的构建时前缀。这个过程的结果是 sys.path 文件的内容 - Python 导入系统将搜索模块的位置列表。

这个 PEP 提议在这个过程中增加一个新步骤。如果在当前工作目录中找到 __pypackages__ 目录,那么它将包含在 sys.path 目录中,位于当前工作目录之后,在系统 site-packages 之前。这样,如果 Python 可执行文件在给定的项目目录中启动,它将自动找到 __pypackages__ 中的所有依赖项。

对于 Python 脚本,Python 将尝试在与脚本相同的目录中找到 __pypackages__。如果找到了(以及其中的当前 Python 版本目录),那么它将被使用,否则 Python 将像当前一样运行。

如果任何包管理工具在当前工作目录中发现相同的 __pypackages__ 目录,它将在那里安装任何包,并根据 Python 版本创建它。

使用源代码管理系统的项目可以包含 __pypackages__ 目录(空目录或带有例如 .gitignore 这样的文件)。在重新检查源代码之后,可以使用像 pip 这样的工具将所需的依赖项直接安装到这个目录中。

但是,这并不能以类似的方式启用虚拟环境的所有功能。例如,如果项目有多个脚本,或者在不同的目录中有用于构建项目的辅助脚本,则应该优先使用普通的虚拟环境而不是 __pypackages__

示例

下面显示了项目目录结构示例,以及 Python 可执行文件和任何脚本的不同行为方式。

foo
    __pypackages__
        lib
            python3.10
                       site-packages
                                     bottle
    myscript.py

/> python foo/myscript.py
sys.path[0] == 'foo'
sys.path[1] == 'foo/__pypackages__/lib/python3.10/site-packages/'


cd foo

foo> /usr/bin/ansible
    #! /usr/bin/env python3
foo> python /usr/bin/ansible

foo> python myscript.py

foo> python
sys.path[0] == '.'
sys.path[1] == './__pypackages__/lib/python3.10/site-packages'

foo> python -m bottle

我们有名为 foo 的项目目录,其中有 __pypackages__。将 bottle 安装在 __pypackages__/lib/python3.10/stie-packages/ 中,并在项目目录中有 myscript.py 文件。通常使用的任何工具来安装 bottle 在那个位置。正如 sysconfig._INSTALL_SCHEMES['posix_prefix'] 字典中提到的,这个实际的内部路径将依赖于 Python 实现名。

为了调用脚本,Python 将尝试在脚本所在的目录 [1]/usr/bin 中找到 __pypackages__。在最后一个例子中,在 foo 目录中执行 /usr/bin/ansible。在这两种情况下,它都不会在当前工作目录中使用 __pypackages__

类似地,如果调用第一个例子中的 myscript.py,它将使用 foo 目录中的 __pypackages__ 目录。

如果进入 foo 目录并启动 Python 可执行文件(解释器),它将在当前工作目录中找到 __pypackages__ 目录,并在 sys.path 中使用它。如果我们尝试使用 -m 并使用模块,也会发生同样的情况。在我们的例子中,bottle 模块将在 __pypackages__ 目录中找到。

以上两个例子只是使用当前工作目录中的 __pypackages__ 的情况。

在另一个例子中,Python 类的 trainer 会说“今天我们要学习如何使用 Twisted!开始,请签出我们的示例项目,进入该目录,然后运行 python3 -m pip install twisted。”

这将把 Twisted 安装到与 python3 分开的目录中。没有必要讨论虚拟环境、全局安装和用户安装等问题,因为默认情况下安装将是本地的。然后,trainer 可以一直告诉他们使用 python3,而不需要任何激活步骤,等等。

安全注意事项

在执行 Python 脚本时,它不会考虑当前目录中的 __pypackages__,相反,如果在脚本的同一路径中存在 __pypackages__ 目录,则将使用该目录。

例如,如果从 /tmp 目录中执行 python /usr/share/myproject/fancy.py,如果 /usr/share/myproject/ 目录中有 __pypackages__ 目录,它将被使用。/tmp 中任何潜在的 __pypackages__ 目录都将被忽略。

这也意味着在执行脚本时不会扫描任何父目录。如果想要在 ~/bin/ 目录中执行脚本,那么 __pypackages__ 目录必须在 ~/bin/ 目录中。

向后兼容性

这不会影响任何旧版本的 Python 实现。

对其他 Python 实现的影响

其他 Python 实现将需要复制解释器引导的新行为,包括定位 __pypackages__ 目录并将其添加到 sys.path 刚好在站点包之前,如果它存在的话。

参考实现

pep582 是一个小脚本,它将启用 CpythonPyPy 中的实现。

拒绝的想法

__pylocal__python_modules 作为目录名。我们也不会重新实现虚拟环境的所有功能。


Source: https://github.com/python/peps/blob/main/pep-0582.rst

Last modified: 2023-01-29 19:18:01 GMT