行则将至

人生在勤,不索何获

0%

Python 项目管理最佳实践 Poetry

依赖管理

依赖管理一直都在编程语言中占据着至关重要的地位,无论是 Python、JavaScript 还是 Java、Go。

虽然不同编程语言的依赖管理工具不尽相同,但是它们追求的目标都是大同小异的。能够对项目以来进行更加轻松统一的管理,能够更加便捷的进行项目迁移和部署。

因此,就出现了 maven、npm、pip 这些依赖包管理工具,但是,对比于 Java 和 JS,Python 在依赖管理方面更加特殊。所以,先后出现了多款不同的管理工具,每一次升级都是为了更好的做好 Python 的依赖包管理工具。

Python 的特殊性

Python 在依赖包管理方面主要的特殊性就来自于,不仅需要考虑项目以来的第三方包,还要着重考虑虚拟环境。这一点,是由于 Python 和 Java、JS 这些编程语言本质上存在差异导致的。

Java、JS 是以项目进行隔离,每当开发一个项目,依赖包都是安装在项目工程路径下。每个工程之间是隔离的,这样,即便是不同工程用到同一个包的不同版本,它们之间也不会产生冲突。

但是,Python 则不同,在 Python 中无法通过工程对依赖包进行隔离。

当我们之间使用pip安装第三方包时,所有的依赖都会安装到安装目录下的site-packages

试想一下,如果我们开发 A 工程,用到了 TensorFlowv0.1。但是,开发 B 工程,需要用到 TensorFlowv0.2。这时候,site-packages 下的依赖包就会产生冲突,后面安装的依赖包就会把前面已经安装的版本卸载掉然后再安装新版本依赖包。

显然,这样是非常不友好的。

现有虚拟环境的局限性

为了解决这个问题,虚拟环境就起到了至关重要的作用。

在开发不同的工程时,可以创建并激活不同的虚拟环境。这样,不同的工程就会用到不同环境下的解析器,我们也可以把依赖包安装到不同虚拟环境的 site-packages 路径下。

因此,在 Python 中依赖管理一般指代依赖管理 + 虚拟环境

virtualenv 的局限性

以往,针对 Python 依赖管理和虚拟环境经常会用到 2 个工具:pipvirtualenv

其中 pip 用于依赖包的管理,virtualenv 用于虚拟环境的管理。

这样虽然解决了不同工程之间环境隔离的问题,但是也存在着明显的不足:

  • 需要同时依赖 2 款管理工具

  • 不能动态更新 requirements.txt

其中比较突出的问题就是第 2 点。

在工程开发过程中,需要一个配置文件来记录依赖包和环境参数,例如,maven 的 pom.xml、npm 的 package.json。

而在 Python 中常用的就是 requirements.txt。

这种纯文本的格式只能用于记录依赖包的名称,而不能像 yaml、json、xml 这些文本存储格式一样记录更多环境参数信息。

另外,更致命的一点就是,每次导出依赖包都需要手动执行pip freeze > requirements.txt命令。很明显,这样是非常不合理的。试想一下,如果由于忙碌忘记手动执行这项操作,那么费九牛二虎之力部署到生产环境之后发现报错,这样会造成很大的损失。

因此,pipenv就诞生了。

pipenv 的局限性

pipenv 是由 requests、flask 等知名工具包的作者 Kenneth Reitz 于 2017 年发布的一款 Python 依赖包管理工具。

pipenv 可以看成是 pip+virtualenv 两款工具的合体,它集合了 pip 的依赖包管理和 virtualenv 虚拟环境管理于一身。另外,在依赖包记录方面使用 Pipfile 替代原来的 requirements.txt。而且,它能够自动记录并更新记录文件,这样就不再需要手动执行命令来更新 requirements.txt。

pipenv 的出现的确大大降低了 Python 依赖包管理的复杂度,提升了项目开发、工程迁移的效率。

因此,近几年关于 pipenv 的评价一直都非常好,各大内容平台对于 pipenv 称颂的文章更是多如牛毛

但是,我至今没有成为 pipenv 的忠实用户,而且,也不会选择 pipenv 来代替原来 pip+virtualenv 的方案。

我相信,那些满篇称颂 pipenv 的作者也未必是它的忠实用户。

不可否认,pipenv 在 Python 依赖包管理方面提供了一个很好的解决方案。但是,它依然有一些致命的缺陷,比原来 pip+virtualenv 方案的繁琐、手动操作更为棘手。

  • Lock 速度缓慢

  • 强行更新不相干依赖

  • 依赖处理效果较差

这里举个例子来解释一下第 3 点。

使用 pipenv 安装一个包,

1
$ pipenv install oslo.utils==1.4.0

这时候就会发生报错,

1
Could not find a version that matches pbr!=0.7,!=2.1.0,<1.0,>=0.6,>=2.0.0

这个报错的含义是无法找到合适版本的 pbr,即便是有合适的版本,pipenv 也会简单粗暴的抛出错误,无法完成依赖包的处理和安装。

这时,更为优秀的工具包出现了,它就是 poetry

依赖管理的最佳实践 poetry

什么是 poetry

poetry 是一款可以管理 Python 依赖、环境,同时可以用于 Python 工程打包和发布的一款第三方工具包。

poetry 通过配置文件pyproject.toml来完成依赖管理、环境配置、基本信息配置等功能。相当于把 Python 项目中的Pipfilesetup.pysetup.cfgrequirements.txtMANIFEST.in融合到一起。

通过pyproject.toml文件,不仅可以配置依赖包,还可以用于区分开发、测试、生产环境、配置源路径。

为什么选择 poetry

poetry 相比于 pipenv 具有很多明显优势的功能:

  • 更强大的依赖处理功能
  • 易于打包和构建 Python 工程
  • 易于发布工具包
  • 结构化展示依赖关系

1. 更强大的依赖处理功能

以前面介绍 pipenv 的例子来介绍 poetry 在依赖处理方面的强大之处。

pipenv 在安装工具包遇到直观的问题时会直接报错,并终止。

而使用 poetry 安装则不会,

1
2
3
$ poetry add oslo.utils=1.4.0  
- Installing pytz (2018.3)  
- Installing netifaces (0.10.6)  - Installing netaddr (0.7.19)  - Installing oslo.i18n (2.1.0)  - Installing iso8601 (0.1.12)  - Installing six (1.11.0)  - Installing babel (2.5.3)  - Installing pbr (0.11.1)  - Installing oslo.utils (1.4.0)

poetry 在遇到pbr (>=0.6,!=0.7,<1.0)这个限定条件时,它会尝试去安装最新的 pbr(0.11.1 版),同时会选择oslo.i18n==3.20.0,但是,这时候发现oslo.i18n的版本和 pbr 最新版本冲突。如果在 pipenv 中则会报错。但是 poetry 会尝试找出解决方案,最后发现oslo.i18n==2.1.0时可以满足所有相互依赖关系的要求。最终,成功安装依赖包。

2. 易于打包和构建 Python 工程

在工程开发过程中,项目迁移和工程化部署是无法绕开的问题。

如果需要把我们的工程部署到生产环境服务器上,这时候就需要用到 Python 的打包和安装功能。

在以往,会写繁琐的setup.pysetup.cfg。而在 poetry 中,一行命令就可以解决,

1
$ poetry buildBuilding poetry (1.0.0)- Building sdist- Built poetry-1.0.0.tar.gz- Building wheel- Built poetry-1.0.0-py2.py3-none-any.whl

3. 易于发布工具包

如果想要把工程发布到 PyPI 仓库怎么办?

在 poetry 中,只需要简单配置一下pyproject.toml,就可以实现一行命令发布工具包,

1
$ poetry publishPublishing poetry (1.0.0) to PyPI  - Uploading poetry-1.0.0.tar.gz 100%  - Uploading poetry-1.0.0-py2.py3-none-any.whl 58%

结构化展示依赖关系

Python 是一款对第三方工具包依赖很强的一种编程语言,一个项目中会用到很多款不同的工具包,而这些包的依赖关系是什么样的,很多开发者对其了解却很少。

poetry 可以实现结构化展示每个工具包的依赖关系,让工程的依赖一目了然,

1
$ poetry show --treerequests-toolbelt 0.8.0 A utility belt for advanced users...└── requests <3.0.0,>=2.0.1    ├── certifi >=2017.4.17    ├── chardet >=3.0.2,<3.1.0    ├── idna >=2.5,<2.7    └── urllib3 <1.23,>=1.21.1$ poetry show --latestpendulum 2.0.4   1.4.5 Python datetimes made easy.django   1.11.11 2.0.3 A high-level Python Web framework ...requests 2.18.4  2.18.4 Python HTTP for Humans.

poetry 安装和初始化

poetry 安装

poetry 提供多种安装方式,个人推荐从以下 2 种方式中选择:

方式一:curl(推荐)

Linux, macOS, Windows (WSL)

1
curl -sSL https://install.python-poetry.org | python3 -

Note: On some systems, python may still refer to Python 2 instead of Python 3. We always suggest the python3 binary to avoid ambiguity.

Windows (Powershell)

1
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

If you have installed Python through the Microsoft Store, replace py with python in the command above.

方式二:pip (有可能会造成依赖冲突)

1
$ pip install --user poetry

poetry 配置环境变量

The installer creates a poetry wrapper in a well-known, platform-specific directory:

  • $HOME/.local/bin on Unix.
  • %APPDATA%\Python\Scripts on Windows.
  • $POETRY_HOME/bin if $POETRY_HOME is set.

If this directory is not present in your $PATH, you can add it in order to invoke Poetry as poetry. Alternatively, the full path to the poetry binary can always be used:

  • ~/Library/Application Support/pypoetry/venv/bin/poetry on MacOS.
  • ~/.local/share/pypoetry/venv/bin/poetry on Linux/Unix.
  • %APPDATA%\pypoetry\venv\Scripts\poetry on Windows.
  • $POETRY_HOME/venv/bin/poetry if $POETRY_HOME is set.

poetry 工程初始化

如果当前还没有创建工程,可以使用 poetry 新建工程:

1
$ poetry new poetry-demo

这时候,会创建一个包含如下内容的工程,

1
2
3
4
5
6
7
8
poetry-demo
├── pyproject.toml
├── README.rst
├── poetry_demo
│   └── __init__.py
└── tests
    ├── __init__.py
    └── test_poetry_demo.py

除了新建工程,还可以在已有工程的基础上进行创建,

1
$ poetry init

这时候,它会让你输入包名称、版本号等信息,你可以选择输入,也可以选择按下ENTER键使用默认值。

poetry 更换国内镜像源

在 pyproject.toml 配置文件中最下方添加国内镜像源地址配置, 提高下载速度。

1
2
3
4
[[tool.poetry.source]]  
name = "aliyun"
url = "http://mirrors.aliyun.com/pypi/simple"
default = true

poetry 运行 Python 脚本

切换到项目中 pyproject.toml 同级目录下, 执行 peotry run python 脚本文件即可。(代码import路径要从项目根目录开始)

Pycharm 中使用 poetry

找到虚拟环境文件夹所在位置,并选中 bin 目录下的 Python 后,就可以用 Pycharm 引入虚拟环境了

poetry 依赖包管理

  • 安装依赖包

可以使用 add 命令来安装一款 Python 工具包,

1
$ poetry add numpy

还可以,通过添加配置参数--dev来区分不同环境下的依赖包。

可以使用 install 命令直接解析并安装pyproject.toml的依赖包,

1
$ poetry install
  • 更新依赖包

更新所有锁定版本的依赖包,

1
$ poetry update

更新指定依赖包,

1
$ poetry update numpy
  • 卸载依赖包
1
$ poetry remove numpy

poetry 虚拟环境管理

  • 创建虚拟环境

创建虚拟环境有 2 种方式:

方式 1:

如果在配置文件中配置了virtualenvs.create=true,执行poetry install时会检查是否有虚拟环境,否则会自动创建。

方式 2:

利用poetry env use命令,

1
$ poetry env use python3.7
  • 激活虚拟环境
1
$ poetry shell
  • 查看虚拟环境信息
1
$ poetry env info
  • 显示虚拟环境列表
1
$ poetry env list
  • 删除虚拟环境
1
$ poetry env remove python3.7

Poetry 常用命令行界面 (CLI) 命令