使用PyEnv等配置Emacs Python LSP
有许多文档描述了如何在Emacs中设置用于自动不全的Python语言服务器。然而,就我所看到的这些文档都缺少以下内容:
- 如何在多个Python版本之间进行切换
- 如何处理Python虚拟环境
我有一个简单但足够使用的工作流程,想与大家分享!
设置环境
用到的工具:
pyenv
: 管理多个python版本direnv
: 处理项目相关的环境配置venv
: 在Python虚拟环境之间进行切换
设置 pyenv
:
官方提供了一个自动安装脚本。我不太喜欢使用它来安装,因为存在安全风险,但我也没有要检查这个安装脚本的意思:
curl https://pyenv.run | bash
对于那些关注安全的人,请遵循其他安装说明。
Now, I'm using zsh
as my shell. Let's set up the pyenv
configuration in the ~/.zshenv
:
目前,我使用 zsh
作为日常使用shell. 让我们在 ~/.zshenv
中设置 pyenv
配置:
export PATH="$HOME/.pyenv/bin:$PATH" eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)"
注意,我在 ~/.zshenv
而不是在 ~/.zshrc
中执行该操作。这是因为 ~/.zshrc
仅被交互式shell加载。你也可以把这段内容放到 ~/.zprofile
中。
你可以在这个 很棒的stackoverflow帖子 中了解到 zsh
各个启动文件的不同作用。
现在,如果我们打开一个新的终端,输入 whereis pyenv
你应该得到类似下面的内容:
kota@kota-ThinkPad-P1> whereis pyenv ~ pyenv: /home/kota/.pyenv/bin/pyenv
:)
设置项目
我创建了一个非常简单的项目结构如下所示:
├── .envrc ├── foo │ ├── app.py │ ├── __init__.py │ └── __main__.py └── setup.py
这个应用程序要做的就是找出向量 (1,2,3)
的长度:
import numpy as np def run(): arr = np.array([1, 2, 3]) print(np.linalg.norm(arr))
这里的关键是它使用了一个外部依赖项。
让我们再来看看 setup.py
:
from setuptools import setup, find_packages from foo import __version__ setup( name="foo", version=__version__, packages=find_packages(exclude=["tests"]), author="Kota Weaver", install_requires=[ 'numpy' ], extras_require={ 'dev': [ 'python-language-server[all]' ], 'test': [ 'pytest', 'pyflakes' ] } )
注意,我在 dev
依赖中列出了Python LSP服务器。
现在让我们建立Python开发环境!首先是我 .envrc
:
export SIMENV_PYTHON=3.8.1 use pyenv $SIMENV_PYTHON
现在,如果我们进入项目目录,并执行 direnv allow
,应该就能够通过下面命令安装正确的Python版本:
pyenv install $SIMENV_PYTHON
(注意: 您可能需要安装以下依赖项: libffi-dev libssl-dev libreadline-dev libsqlite3-dev libbz2-dev
)
然后,离开并再次进入该目录,然后使用以下命令安装依赖项:
pip install -e .['dev','test']
(根据您的shell,您也许需要使用 ==
来转义 [
和 ]
)
确保 which python
显示的位置是正确的!
这允许我们使用下面命令运行程序使用:
kota@kota-ThinkPad-P1> python -m foo ~/foo 3.7416573867739413
耶!
设置Emacs
现在,我们进入最重要的部分……让我们配置Emacs来使用它做一些聪明的事情!
我使用 use-package
, 我设置成如果没有安装则自动进行安装。我将这个设置在一个非常简单的 ~/.emacs.d/init.el
中,你可以随意引用:
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("marmalade" . "https://marmalade-repo.org/packages/") ("melpa" . "https://melpa.org/packages/"))) (package-initialize) (when (not (package-installed-p 'use-package)) (package-refresh-contents) (package-install 'use-package)) (require 'use-package) ; direnv mode allows automatic loading of direnv variables (use-package direnv :ensure t :config (direnv-mode)) ; setup Emacs path from our ~/.zshenv (use-package exec-path-from-shell :ensure t :config (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize))) ; we also should make sure we have flycheck installed (use-package flycheck :ensure t) ; Let's set up company! perhaps not necessary but this is what i like to use (use-package company :ensure t :config (setq company-idle-delay 0) (setq company-minimum-prefix-length 1)) ; install lsp mode (use-package lsp-mode :ensure t :hook (python-mode . lsp-deferred) :commands (lsp lsp-deferred)) ; let's add the lsp company backend (use-package company-lsp :ensure t :config (push 'company-lsp company-backends)) ; also installs lsp as a dependency (use-package lsp-ui :ensure t :hook (lsp-mode . lsp-ui-mode))
下面是我机器上的一些截图(这是我的正常设置的截图,而不是上面精简版的截图,但应该有相同的功能):