在python开发中,开发或者运行环境很重要。

隆重推出Poetry,一款优秀的python开发环境管理工具。

就像某个大神说的那样:

井然有序的複雜

使用 Poetry 來管理專案的套件與虛擬環境,需要一定的學習成本,但帶來的效益還是相當可觀的,尤其在你希望能夠乾淨且安心地移除套件之際,可謂莫它莫屬。

本文力求通过一篇文章讲解清晰,在macos和linux环境下,如何正确的理解开发环境。

系统级环境

每个系统,每个版本的python我们只需要装一套,系统默认使用其中一套。拿mac为例,通常通过brew安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看brew库里的版本
% brew search python|grep python@  
python@3.10
python@3.11
python@3.12
python@3.7
python@3.8
python@3.9
# 查看本地安装过的版本
% brew list|grep python@
python@3.10
python@3.11
python@3.12
python@3.7
python@3.8
python@3.9
# 查看默认的python3版本
% python --version
Python 3.12.2
# 设置python 默认版本
brew link --overwrite python@3.9

虚拟环境

由于不同项目不同的模块之间的依赖关系,通常开发某个项目时需要启用单独的虚拟环境,实现python环境的隔离。

虚拟环境实现了每个项目拥有一个运行环境,避免了版本冲突

python有自己自带虚拟环境模块venv。

TL;DR 以下只是为了理解虚拟环境

通常,我们在项目根目录下安装虚拟环境

1
2
3
4
5
6
7
8
9
cd project_base
# 创建虚拟环境
python -m venv .venv
# 激活虚拟环境
source .venv/bin/activate
(.venv) calfen@Mac-mini demo % 
# 在此环境中做一切事情,比如pip install 
# 退出虚拟环境
deactivate

包依赖

起初,我使用reuirements管理第三方包

TL;DR 以下只是为了理解包依赖,还有更好的工具

1
2
3
4
5
6
7
# 将目前环境的包依赖导出
pip freeze > requirements.txt
# 在目前环境安装包
pip install -r requrements.txt
# 管理版本号
vi requirements.txt
package_name >=1.0, <=2.0

但有个最大的问题:

  • 无法知晓包之间的关系

  • 无法做包删除,使用pip uninstall的时候,也会因为没有包依赖关系而混乱。

Poetry

虚拟环境+包管理+发布的一体化解决方案. Poetry管理的是整个python项目

有不少第三方工具来做一揽子解决方案,比如Pipenv,Poetry。

个人认为Poetry是目前最优的解决方案,各种pycharm等IDE也都支持。

💡我觉得可以做以下简单类比

poetry = vevn + pip

系统环境+poetry=项目环境

安装poetry

1
2
3
4
5
6
7
8
brew install poetry
# 或者
curl -sSL https://install.python-poetry.org | python -
# 安装的路径为
$HOME/.local/bin/poetry
# 将其加入path
vi ~/.bashrc
export PATH=$PATH:$HOME/.local/bin

配置poetry

默认情况下,peotry将虚拟环境统一建在一个和项目无关的地方,我个人喜欢建立在项目的.venv下。

1
2
3
poetry config virtualenvs.in-project true
# 有兴趣了解更多的配置,请参考
poetry config --list

生成pyproject.toml

pyproject.toml 是Python从PEP 518开始引入的使用管理项目元数据的方案。

1
2
3
cd project_dir
#使用poetry初始化项目
poetry init

接下来是一连串交互式问答,做的相当人性化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
This command will guide you through creating your pyproject.toml config.
Package name [p]:  poetry_demo
Version [0.1.0]:  
Description []:  
Author [calfen <calfen@gmail.com>, n to skip]:  
License []:  
Compatible Python versions [^3.12]:  
# 是否进入交互式增加包,现在选否
Would you like to define your main dependencies interactively? (yes/no) [yes] no
Generated file

[tool.poetry]
name = "poetry-demo"
version = "0.1.0"
description = ""
authors = ["calfen <calfen@gmail.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Do you confirm generation? (yes/no) [yes] yes

现在目录里产生了pyproject.toml的文件。

建立虚拟环境

💡虚拟环境是运行poetry的前提,你可以建立任系统里已经安装的python版本的虚拟环境,如果不理解,请回到系统级环境

1
2
3
4
5
6
7
8
brew list|grep python@
python@3.11
python@3.12
python@3.8

poetry env use python3.12
Creating virtualenv poetry-demo in /Users/calfen/git_code/p/.venv
Using virtualenv: /Users/calfen/git_code/p/.venv

进入与退出虚拟环境

1
2
3
4
5
6
7
8
poetry shell
Spawning shell within /Users/calfen/git_code/p/.venv

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ . /Users/calfen/git_code/p/.venv/bin/activate
(poetry-demo-py3.12) bash-3.2$ exit

peotry流程

增加模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
poetry add fastapi
Using version ^0.110.0 for fastapi

Updating dependencies
Resolving dependencies... (0.9s)

Package operations: 9 installs, 0 updates, 0 removals

  - Installing idna (3.6)
  - Installing sniffio (1.3.1)
  - Installing typing-extensions (4.10.0)
  - Installing annotated-types (0.6.0)
  - Installing anyio (4.3.0)
  - Installing pydantic-core (2.16.3)
  - Installing pydantic (2.6.4)
  - Installing starlette (0.36.3)
  - Installing fastapi (0.110.0)

这时候pyproject.toml也改变了,记录了你要安装的模块,不含依赖模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[tool.poetry]                                                                                       
name = "poetry-demo"
version = "0.1.0"
description = ""
authors = ["calfen <calfen@gmail.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.110.0"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

其他所有的依赖都在peorty.lock里,这相当于以前的reuirements.txt

当执行 poetry add的时候,自动完成了三件事

  1. 更新pyproject.toml。
  2. 依照pyproject.toml的內容,更新poetry.lock。
  3. 依照poetry.lock的內容,更新虚拟环境

详解pyproject.toml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 可以安装与版本字符串最左边的非零数字匹配的任何版本 0.110.*
fastapi = "^0.110.0"
# 固定版本
django = 4.2.9
# 可以安装与版本字符串最左边ID二哥的非零数字匹配的任何版本。也就是4.2.*
django = ~4.2.9
# 通配
django = 4.2.*
# 区间
flask >= 1.2,< 1.5

常用命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 增加模块
poetry add module_name
# 更新poetry.lock
poetry lock
# 更新套件
poetry update
# 只在开发环境使用(有些套件,比如pytest、flake8等等,只會在開發環境中使用)
poetry add module_name --group dev
poetry add module_name -D dev

# 列出全部模块
poetry show tree
# 移除
poetry remove -D
# 导出 requirements
poetry export -f requirements.txt -o requirements.txt --without-hashes

[to be continued]