Skip to content

使用 PEP 582#

PEP 582 已被拒绝

这是一个被拒绝的 PEP。然而,由于这个功能是 PDM 诞生的原因,PDM 将保留对其的支持。 我们建议使用 virtual environments

使用 PEP 582, 依赖项将安装到项目根目录下的 __pypackages__ 目录中。启用 全局启用 PEP 582 后,您还可以使用项目解释器直接运行脚本。

当项目解释器是普通 Python 时,此模式已启用。

此外,在您的计算机上首次使用的项目上,如果它包含一个空的 __pypackages__ 目录,则 PEP 582 将自动启用,并且不会创建虚拟环境。

全局启用 PEP 582#

要使 Python 解释器了解 PEP 582 包, 需要将 pdm/pep582/sitecustomize.py 添加到 Python 库搜索路径中。

只需执行 pdm --pep582,然后环境变量将自动更改。 不要忘记重新启动终端会话以生效。

更改环境变量的命令可以通过 pdm --pep582 [<SHELL>] 打印。 如果没有给出 <SHELL>,PDM 将根据某些猜测选择一个。 您可以运行 eval "$(pdm --pep582)" 来执行该命令。

您可能希望在登录时使其生效,因此在您的 .bash_profile(或类似的配置文件)中写入一行。 例如,在 bash 中可以这样做:

1
pdm --pep582 >> ~/.bash_profile

再次强调,不要忘记重新启动终端会话以生效。

它是如何实现的?

这要归功于 Python 启动时的 site 包加载。 通过执行随 PDM 一起提供的 sitecustomize.py,可以对 sys.path 进行修补。 解释器可以搜索最近的 __pypackage__ 文件夹,并将其附加到 sys.path 变量中。

配置 IDE 以支持 PEP 582#

现在大多数 IDE 中都没有针对 PEP 582 的内置支持或插件,您必须手动配置工具。

PyCharm#

__pypackages__/<major.minor>/lib 标记为 Sources Root。 然后,选择具有相同 <major.minor> 版本的 Python 安装作为 Python interpreter

此外,如果您想要使用环境中的工具(例如 pytest), 您必须在相应的 运行/调试 配置中将 __pypackages__/<major.minor>/bin 目录添加到 PATH 变量中。

VSCode#

.vscode/settings.json 的顶层字典中添加以下两个条目:

1
2
3
4
{
  "python.autoComplete.extraPaths": ["__pypackages__/<major.minor>/lib"],
  "python.analysis.extraPaths": ["__pypackages__/<major.minor>/lib"]
}

这个文件可以使用插件 pdm-vscode 自动生成。

启用全局 PEP582, 确保 VSCode 使用与您启用 PEP582 的用户和 shell 相同的方式运行。

无法全局启用 PEP582?

如果由于某种原因您无法全局启用 PEP582,则仍然可以在每个项目中的每个 "启动" 中配置: 在您的启动配置文件 .vscode/launch.json 中设置 PYTHONPATH 环境变量。 例如,要调试您的 pytest 运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "pytest",
            "type": "python",
            "request": "launch",
            "module": "pytest",
            "args": ["tests"],
            "justMyCode": false,
            "env": {"PYTHONPATH": "__pypackages__/<major.minor>/lib"}
        }
    ]
}

如果您的包位于 src 目录中,请将其添加到 PYTHONPATH 中:

1
"env": {"PYTHONPATH": "src:__pypackages__/<major.minor>/lib"}
使用 Pylance/Pyright?

如果您配置了 "python.analysis.diagnosticMode": "workspace", 并且由于此原因看到了大量错误/警告。 您可能需要在工作区目录中创建 pyrightconfig.json,并填写以下字段:

1
2
3
{
    "exclude": ["__pypackages__"]
}

然后重新启动语言服务器或 VS Code,您就可以开始了。 将来(microsoft/pylance-release#1150),也许问题会得到解决。

使用 Jupyter Notebook?

如果您希望使用 pdm 安装 jupyter 笔记本,并在 vscode 中与 python 扩展一起使用:

  1. 使用 pdm add notebook 或类似的命令安装 notebook
  2. 在项目目录内添加一个 .env 文件,内容如下:
1
PYTHONPATH=/your-workspace-path/__pypackages__/<major>.<minor>/lib

如果上述方法仍然不起作用,很可能是因为在笔记本启动时环境变量没有正确加载。有两种解决方法。

  1. 在终端中运行 code .。它将在当前目录中打开一个新的 VSCode 窗口,并在路径正确时使用 Jupyter Notebook。
  2. 如果您不想打开一个新窗口,请在您的 Jupyter Notebook 开头显式设置路径:
1
2
import sys
sys.path.append('/your-workspace-path/__pypackages__/<major>.<minor>/lib')

参考 Issue

PDM VSCode Task Provider

此外,还有一个 [VSCode Task Provider 扩展][VSCode Task Provider] 可供下载。

这使得 VSCode 可以自动检测到 pdm 脚本, 以便它们可以作为 VSCode 任务 在本机运行。

Neovim#

如果使用 neovim-lsppyright 并且希望将您的 __pypackages__ 目录添加到路径中, 则可以将以下内容添加到项目的 pyproject.toml 中。

1
2
[tool.pyright]
extraPaths = ["__pypackages__/<major.minor>/lib/"]

Emacs#

您有几种选择,但基本上您将希望告诉一个 LSP 客户端将 __pypackages__ 添加到它查看的路径中。下面是一些可用的选项:

使用 pyproject.toml 和 pyright#

将以下内容添加到您的项目的 pyproject.toml 中:

1
2
[tool.pyright]
extraPaths = ["__pypackages__/<major.minor>/lib/"]

eglot + pyright#

使用 pyrighteglot (包含在 Emacs 29 中),将以下内容添加到您的配置中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(defun get-pdm-packages-path ()
  "For the current PDM project, find the path to the packages."
  (let ((packages-path (string-trim (shell-command-to-string "pdm info --packages"))))
    (concat packages-path "/lib")))

(defun my/eglot-workspace-config (server)
  "For the current PDM project, dynamically generate a python lsp config."
  `(:python\.analysis (:extraPaths ,(vector (get-pdm-packages-path)))))

(setq-default eglot-workspace-configuration #'my/eglot-workspace-config)

您将希望全局安装 pyright,或者将其作为项目的依赖项(可能作为开发依赖项)安装。您可以使用以下命令之一将其添加:

1
pdm add --dev --group devel pyright

LSP-Mode + lsp-python-ms#

下面是一个示例代码片段,显示了如何使 PDM 与 Emacs 中的 lsp-python-ms 兼容。由 @linw1995.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  ;; TODO: Cache result
  (defun linw1995/pdm-get-python-executable (&optional dir)
    (let ((pdm-get-python-cmd "pdm info --python"))
      (string-trim
       (shell-command-to-string
        (if dir
            (concat "cd "
                    dir
                    " && "
                    pdm-get-python-cmd)
          pdm-get-python-cmd)))))

  (defun linw1995/pdm-get-packages-path (&optional dir)
    (let ((pdm-get-packages-cmd "pdm info --packages"))
      (concat (string-trim
               (shell-command-to-string
                (if dir
                    (concat "cd "
                            dir
                            " && "
                            pdm-get-packages-cmd)
                  pdm-get-packages-cmd)))
              "/lib")))

  (use-package lsp-python-ms
    :ensure t
    :init (setq lsp-python-ms-auto-install-server t)
    :hook (python-mode
           . (lambda ()
               (setq lsp-python-ms-python-executable (linw1995/pdm-get-python-executable))
               (setq lsp-python-ms-extra-paths (vector (linw1995/pdm-get-packages-path)))
               (require 'lsp-python-ms)
               (lsp))))  ; or lsp-deferred