相比 Pipenv,Poetry 是一个更好的选择

前情提要

Pipenv 描绘了一个美梦,让我们以为 Python 也有了其他语言那样完善的包管理器,不过这一切却在后来者 Poetry 这里得到了更好的实现。

这几年 Pipenv 收获了很多用户,但是也暴露了很多问题。虽然 Lock 太慢、Windows 支持不好和 bug 太多的问题都已经改进了很多,但对我来说,仍然不能接受随时更新锁定依赖的设定,在上一篇文章《不要用 Pipenv》里也吐槽了很多相关的问题。

在这篇文章里,我会介绍一个看起来和事实上都更靠谱的 Python 虚拟环境和依赖管理工具 Poetry,作者是 Sébastien Eustace。这是一个新的坑吗?我想并不是,尽管这是一个更年轻的工具,1.0 还没有发布,也存在各种各样的 bug,但至少基本使用流程没有问题,用法设计也符合直觉。

Poetry 是什么

Poetry 和 Pipenv 类似,是一个 Python 虚拟环境和依赖管理工具,另外它还提供了包管理功能,比如打包和发布。你可以把它看做是 Pipenv 和 Flit 这些工具的超集。它可以让你用 Poetry 来同时管理 Python 库和 Python 程序。

安装 Poetry

官方推荐的安装命令是使用自带的 get-poetry.py 脚本,使用 curl:

$ curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python

或者直接下载这个安装脚本 get-poetry.py,然后在本地执行。

因为这个命令在安装时会从 GitHub 下载一个 7M 的压缩包,如果不用代理某些地区可能会很慢。实际测试使用代理安装耗时约 30 秒,不用代理等了 5 分钟,然后连接被重置。

如果没有用代理,可以用 pip 安装(不过 Poetry 官方文档不建议这么做,因为有可能会造成依赖冲突,可以考虑用 pipxpipsi):

$ pip install --user poetry

安装后可以使用下面的命令确认安装成功:

$ poetry --version
Poetry 0.12.17

如果报错,可以试试重新创建一个命令行会话。

附注 在 Mac 上安装报错 ~/.bash_profile 权限错误,临时没管,也能正常运行。后续可以考虑手动把 ~/.poetry/bin 加进去。

Poetry 的基本用法

Poetry 的用法很简单,大部分命令和 Pipenv 接近。我们需要先了解一些基本概念和 Tips:

  • 使用 PEP 518 引入的新标准 pyproject.toml 文件管理依赖列表和项目的各种 meta 信息,用来替代 Pipfile、requirements.txt、setup.py、setup.cfg、MANIFEST.in 等等各种配置文件。
  • 依赖分为两种,普通依赖(生产环境)和开发依赖。
  • 安装某个包,会在 pyproject.toml 文件中默认使用 upper bound(中文翻译?)版本限定,比如 Flask^1.1。这被叫做 Caret requirements(中文翻译?),比如某个依赖的版本限定是 ^2.9.0,当你执行 poetry update 的时候,它或许会更新到 2.14.0,但不会更新到 3.0.0;假如固定的版本是 ^0.1.11,它可能会更新到 0.1.19,但不会更新到 0.2.0。总之,在更新依赖的时候不会修改最左边非零的数字号版本(对于 SemVer 版本号而言),这样的默认设定可以确保你在更新依赖的时候不会更新到具有不兼容变动的版本。另外也支持更多依赖版本限定符号
  • 不会像 Pipenv 那样随时更新你的锁定依赖版本,锁定依赖存储在 poetry.lock 文件里(这个文件会自动生成)。所以,记得把你的 poetry.lock 文件纳入版本控制。
  • 执行 poetry 或 poetry list 命令查看所有可用的命令。

如果你想了解更多进阶的内容,比如设置命令行补全、打包和发布等等,请阅读 Poetry 文档

准备工作

如果你是在一个已有的项目里使用 Poetry,你只需要执行 poetry init 命令来创建一个 pyproject.toml 文件:

$ poetry init

根据它的提示输入你的项目信息,不确定的内容就按下 Enter 使用默认值,后续也可以手动更新。指定依赖的环节可以跳过,手动安装会更高效一点。

如果你想创建一个新的 Python 项目,使用 poetry new <文件夹名称> 命令可以创建一个项目模板:

$ poetry new foo

这会创建一个这样的项目结构:

foo
├── pyproject.toml
├── README.rst
├── foo
│   └── __init__.py
└── tests
    ├── __init__.py
    └── test_foo.py

如果你想使用 src 文件夹,可以添加 –src 选项,这会把程序包嵌套在 src 文件夹里。

创建虚拟环境

使用 poetry install 命令创建虚拟环境(确保当前目录有 pyproject.toml 文件):

$ poetry install

这个命令会读取 pyproject.toml 中的所有依赖(包括开发依赖)并安装,如果不想安装开发依赖,可以附加 –no-dev 选项。如果项目根目录有 poetry.lock 文件,会安装这个文件中列出的锁定版本的依赖。如果执行 add/remove 命令的时候没有检测到虚拟环境,也会为当前目录自动创建虚拟环境。

激活虚拟环境

执行 poetry 开头的命令并不需要激活虚拟环境,因为它会自动检测到当前虚拟环境。如果你想快速在当前目录对应的虚拟环境中执行命令,可以使用 poetry run <你的命令> 命令,比如:

$ poetry run python app.py

如果你想显式的激活虚拟环境,使用 poetry shell 命令:

$ poetry shell

安装包

使用 poetry add 命令来安装一个包:

$ poetry add flask

添加 –dev 参数可以指定为开发依赖:

$ poetry add pytest --dev

追踪 & 更新包

使用 poetry show 命令可以查看所有安装的依赖(可以传递包名称作为参数查看具体某个包的信息):

$ poetry show

添加 –tree 选项可以查看依赖关系:

$ poetry show --tree

添加 –outdated 可以查看可以更新的依赖:

$ poetry show --outdated

执行 poetry update 命令可以更新所有锁定版本的依赖:

$ poetry update

如果你想更新某个指定的依赖,传递包名作为参数:

$ poetry update foo

卸载包

使用 poetry remove <包名称> 卸载一个包:

$ poetry remove foo

常用配置

Poetry 的配置存储在单独的文件中,比 Pipenv 设置环境变量的方式要方便一点。配置通过 poetry config 命令设置,比如下面的命令可以写入 PyPI 的账号密码信息:

$ poetry config http-basic.pypi username password

下面的命令设置在项目内创建虚拟环境文件夹:

$ poetry config settings.virtualenvs.in-project true

另一个常用的配置是设置 PyPI 镜像源,以使用豆瓣提供的 PyPI 镜像源为例,你需要在 pyproject.toml 文件里加入这部分内容:

[[tool.poetry.source]]
name = "douban"
url = "https://pypi.doubanio.com/simple/"

不过经过测试 Poetry 会使用 pip.ini 设置的 PyPI 镜像,而且豆瓣的源好像很久没更新了(创建虚拟环境安装的默认依赖里 importlib-metadata==0.20 找不到),这篇文章列出了一些其他国内的 PyPI 源。

总结

总的来说,我愿意深入尝试和使用 Poetry。当然,经过使用 Pipenv 的痛苦经历,我对推荐工具这种事情变得更保守了。所以我不推荐 Python 初学者使用,不推荐直接在生产环境使用,不推荐没法正常访问国际互联网的人使用。

列一些我了解到的优缺点:

优点

  • 使用标准的 pyproject.toml 标准,不用写多个配置文件
  • 同时支持管理 Python 程序和 Python 库
  • 更符合直觉的默认设计,比如不会随便更新锁定版本的依赖
  • 干净简洁的命令行输出,没有星星和蛋糕
  • 安装包的时候,使用 upper bound 版本限定,而不是 Pipenv 默认的通配符
  • 卸载包的时候,直接卸载孤立的子依赖,不需要像 Pipenv 那样需要再执行 pipenv clean

缺点

  • 「poetry」这个单词有一点难打……
  • 引入新的 pyproject.toml 标准,旧项目需要一点迁移成本和学习成本
  • 会有一些潜在的 bug
  • 解析依赖的过程有时候会久一点
  • 对虚拟环境的管理控制有些弱,没有 Pipenv 那样的删除虚拟环境和清空依赖的操作
  • 缺少一个稳定的维护团队,有大量 issue 和 PR 等待处理,但情况在逐渐好转

当然,你还是可以选择继续使用 virtualenv 和 pip 这些基础工具,直到有一个完美的解决方案出现。或者,也可以选择试试新东西,然后尝试改进它,让完美的解决方案早一点出现。

(2)

回应《也谈「不要用 Pipenv」》

看了董伟明老师(@董伟明)的《也谈「不要用 Pipenv」》,这篇文章对其中的一些观点做出一些回应和解释。

也看了 Frost Ming 老师(@岂不美哉)的《Pipenv 有什么问题》,很感谢他做出的努力,祝 Pipenv 早日脱离 Kenneth Retiz 的影响,越来越好。

(Kenneth Retiz 下文简称 KR)

KR 有没有借 PyPA 之名来做背书?

所以作者并没有想着用来背书。

我仍然认为 KR 有利用 PyPA 做背书,甚至在误导别人 Pipenv 是 Python 官方(混淆 PyPA 和 Python 官方的概念)推荐的工具。

证据 1

2017 年 8 月 29 号,在 Pipenv 还不够成熟的时候,PyPA 成员 Thea Flowers 创建了一个 PR 要把 Pipenv 添加到 PyPA 的打包教程里,介绍使用 Pipenv 安装和管理依赖(注意这时候 Pipenv 根据教程的内容在 Windows 上是没法正常使用的,具体见 9 月 2 号的这个 issue)。

2017 年 8 月 31 号,Thea Flowers 自己合并了 PR。注意这个教程页面是临时的单独页面,还没有正式放到打包教程的页面里。

2017 年 9 月 1 号,KR 在 Pipenv 的 README 里加了这样一行介绍语:「Pipenv — the officially recommended Python packaging tool from Python.org, free (as in freedom).」(commit

其中的关键内容翻译过来大概是「官方推荐的 Python 打包工具,来自 Python.org」。

仅仅因为在 PyPA 的打包文档里加入了一个短教程(其中介绍了使用 Pipenv 安装和管理依赖),然后 KR 就在 Pipenv 的介绍里宣传这是「官方推荐」,而注明的官方来源则是「Python.org」,这两个关键词背后的超链接都是 PyPA 打包文档的 Pipenv 介绍。packaging.python.org(PyPA) 和 python.org(Python 官方) 的区别很大,很明显,他清楚两者的区别,但又故意没有表达清楚。
退一步讲,不管他的意图是什么,这样的措辞都会让人以为是 Python 官方推荐的打包工具,尤其是对 PyPA 这个组织不了解的人,看到 Python.org 都会认为是 Python 官方。

证据 2

在 PyCon 2018(五月),KR 在演讲《Pipenv: The Future of Python Dependency Management》里介绍 Pipenv 的卖点的时候,列在第一条的仍然是上面那一句「Officially recommended tool from python.org」:

和在 README 里不同的是,这次在演讲上别人没法去点那个 python.org 的链接去甄别究竟是 python.org(Python 官方) 还是 packaging.python.org(PyPA)。而 KR 在介绍这里的时候没有任何说明,直接说是「来自 Python.org 的官方推荐」。

至于 KR 和 PyPA 或者说和 Thea Flowers 有什么关系?把 Pipenv 的介绍加到打包文档是 Thea Flowers 的个人意愿?还是 PyPA 的 35 个成员全部同意的结果?是什么促使 PyPA 在 Pipenv 还不成熟、甚至教程里的内容没法在 Windows 上正常操作的情况下添加到打包教程里?这些问题我暂时找不到答案。

(注:PyPA 指的是 Python Packaging Authority,一个负责维护 Python 打包相关的库(比如 pip、virtualenv 等)和文档的组织。)

从 0 升到 18 的措辞问题

而所谓的18.X.X是 calver versioning(基于日历的版本)

在上一篇文章里,我引用了一段 HN 上的评论来概括 Pipenv 在推广方式上的问题:

Kenneth Retiz 滥用他在 PyPA 的位置(而且快速把一个实际上是 beta 状态的产品的版本号从 0 升到 18)来暗示 Pipenv 已经非常稳定,受到大力支持并且非常官方,但事实却并不是这样。

这句话的英文原文是:

However, Kenneth abused his position with PyPA (and quickly bumped a what is a beta product to version 18) to imply Pipenv was more stable, more supported and more official than it really was.

其中关于版本的部分是「and quickly bumped a what is a beta product to version 18」,可能是我乱翻译造成了误解……我认为原文里的 18 就是一个夸张的表达方式,把这里的数字换成 100 也可以表达同样的意思(也可能是指 0.3.0 跳到 3.0.1 那次)。换用日期版本号(CalVer)那次看起来没什么问题(11.10.4 -> 2018.05.12),所以我认为这里的 18 和日期版本号没关系。

Lockfile 只要过期就重新生成是合理的吗?

Kenneth Reitz 先是说 lockfile 只要是过期了就总是会被重新生成

这是对的,Pipfile和Pipfile.lock是对应的,当执行pipenv install后改了Pipfile,对应的Pipfile.lock就定会改。错误的是,不应该改那些不相关包的版本: 既然已经是==的了,就表明确定了具体版本呀。

这些问题,其实源于 Pipfile对应依赖在一开始没指定具体版本,也就是Pipfile对标requirements.txt,而Pipfile.lock只是当前环境的一个「快照」,如果Pipfile没有明确版本就用Pipfile.lock里面指定的。

我的主要想法是这样的功能实现是不合理的。Pipenv 在安装一个包的时候默认就使用通配符(*)版本写到 Pipfile 是不合理的设计。这样的设计不符合正常的开发流程和使用习惯。如果我在安装一个包的时候就要明确自己要安装哪个版本,以便在 Pipfile 里固定版本,这样会很不方便,而且让 Pipfile.lock 的存在意义变得很弱。

按照 KR 自己的解释,Pipfile 对标的是 requirements.in,Pipfile.lock 对标的是 requirements.txt:

blank
按照大部分人的理解,Pipfile 是所有不固定版本的高层依赖的列表(unpinned),而 Pipfile.lock 是固定安装时采用版本的详细依赖列表(pinned),用来复现程序具体的依赖环境;除非我主动执行 update 命令更新某个依赖,否则 Pipfile.lock 不应该被改动。但实际的 Pipenv 并不是这样,更新 Pipfile.lock 变成了频繁发生(install/uninstall/update)的默认行为。

Poetry 分析依赖慢

Resolving dependencies… (422.9s)

安装个包7分钟,这… 谁能忍?你们试试把bluelog项目的依赖用poetry add加一遍需要多久?我反正体验不下去了

实际测试安装 Flask-SQLAlchemy,解析依赖花了 42 秒(Windows+代理),没有 7 分钟那么夸张。因为解析依赖的结果会被缓存,我就在另一台 Mac(代理)上也试了一遍,结果只花了 8.3 秒。可能是网络状况的问题?

另外我试了把 Bluelog 的所有依赖一次性安装(Windows+代理),其中解析依赖只花了 16.8 秒(因为解析结果缓存的原因,实际也许会稍久一点):

$ poetry add flask flask-ckeditor flask-mail flask-sqlalchemy flask-wtf flask-moment python-dotenv bootstrap-flask flask-login flask-debu gtoolbar gunicorn psycopg2 flask-migrate
Using version ^1.1 for flask
Using version ^0.4.3 for flask-ckeditor
Using version ^0.9.1 for flask-mail
Using version ^2.4 for flask-sqlalchemy
Using version ^0.14.2 for flask-wtf
Using version ^0.9.0 for flask-moment
Using version ^0.10.3 for python-dotenv
Using version ^1.0 for bootstrap-flask
Using version ^0.4.1 for flask-login
Using version ^0.10.1 for flask-debugtoolbar
Using version ^19.9 for gunicorn
Using version ^2.8 for psycopg2
Using version ^2.5 for flask-migrate

Updating dependencies
Resolving dependencies... (16.8s)

所以 Poetry 或许没那么糟糕(当然我还没深入使用过)。

星星

虽然不是决定性的,但是对于这Star不到6K的项目来说我是不敢用的

Pipenv 的 Star 的确很多(而且 README、文档甚至代码里到处都是星星✨),KR 可是个营销专家,但项目质量却并没有那么好。反正我现在一看见星星和蛋糕就有点头疼。

✨🍰✨

另外,顺便说一句,pip(5627)、virtualenv(3189)和 setuptools(883) 的 Star 数量都没到六千……

好吧,我承认最后这两段是在抬杠 :D

不要用 Pipenv

注意:本文写于 2019 年 8 月,其中描述的内容在新版本的 Pipenv 中或已得到修复或改进,请谨慎参考。

如果你是《Flask Web 开发实战》的读者,请访问《Flask Web 开发实战》虚拟环境/依赖/Pipenv 等问题解决方法


Pipenv 让我用的很痛苦,有一种被欺骗的感觉,而且很后悔在《Flask Web 开发实战》里采用它。

大部分情况下,它很好用,但却存在太多问题,有一些问题让人简直没法接受。我知道有人会说「这是开源程序,有 bug 就自己去修」、「爱用不用,没人强迫你」,但问题是,一个进行大肆推广,甚至借 PyPA 做背书来宣传(经常让人误以为是 Python 官方推荐)的工具却连基本的使用流程都没做好,这不是合理和正常的行为。 引用这个 HN 评论的话说就是:

Kenneth Retiz 滥用他在 PyPA 的位置(而且快速把一个实际上是 beta 状态的产品的版本号从 0 升到 18)来暗示 Pipenv 已经非常稳定,受到大力支持并且非常官方,但事实却并不是这样。

在这篇(劝退)文章里,我会分别从包的安装、更新、卸载来测试并指出 Pipenv 的一些问题。

测试准备

  • 项目:Bluelog(一个 Flask 博客)
  • Pipenv 版本:2018.11.26(最新版本)
  • 操作系统:Windows 10,Cmder
  • 测试流程:每一次测试命令前都会删除已经创建的虚拟环境(pipenv –rm),重置 Pipfile 和 Pipfile.lock 文件变动,然后重新创建虚拟环境(pipenv install)。

安装包

假设想给这个旧项目 Bluelog 添加新功能,拿到旧项目的代码,打算安装一个 Flask-Avatars 包。查了文档,发现安装包要使用 pipenv install 命令,所以执行了下面的命令:

$ pipenv install flask-avatars

结果发现其他所有的不相干依赖都被更新了……

WTF,这不是反人类吗(说好的「Python Development Workflow for Humans.」呢)?我安装一个包,默认行为竟然是更新其他所有不相干且已经锁定版本的依赖!

翻了文档才发现,要加一个 –keep-outdated 选项才能避免更新其他锁定的依赖:

$ pipenv install --help
...
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED]

好吧,那先忍着,多打一个命令行选项就是了:

$ pipenv install --keep-outdated flask-avatars

WTF,为什么所有依赖还是被更新了?

好吧,有 bug 很正常,我来提个 issue 吧,哎,好像有很多 issue 了?

重点评论:

Kenneth Reitz 先是说 lockfile 只要是过期了就总是会被重新生成(这是什么逻辑?),接着又说用 pipenv update depname,但其他人都回复不起作用(我下面会进行单独测试)。

接着,看到其他评论提到用 –selective-upgrade 选项:

$ pipenv install --help
...
--selective-upgrade Update specified packages.

我又继续使用 –selective-upgrade 选项:

$ pipenv install --selective-upgrade flask-avatars

仍然会更新所有依赖……

对了,顺便还测试了这个命令,依然没用:

$ pipenv install --keep-outdated --selective-upgrade flask-avatars

除了安装某个包会导致所有依赖版本被更新,Pipenv 在解决依赖的冲突上面也有一些不足,比如执行下面的安装命令(具体见 Poetry README):

$ pipenv install oslo.utils==1.4.0

会提示无法安装成功:

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

更新包

假设我想更新 Bluelog 这个项目用的 Flask 版本(从 1.0.2 更新到最新的 1.1.1)。查了文档,找到了 update 命令(https://docs.pipenv.org/en/latest/basics/#example-pipenv-upgrade-workflow),于是我执行下面的命令:

$ pipenv update flask
Locking [dev-packages] dependencies…
Success!
Locking [packages] dependencies…
Success!
Updated Pipfile.lock (fd55e3)!
Installing dependencies from Pipfile.lock (fd55e3)…
================================ 26/26 - 00:00:12
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
All dependencies are now up-to-date!

突然看到最后一行赫然写着「All dependencies are now up-to-date!」,我以为是搞错了,赶紧看了下 Pipfile.lock,WTF,为什么我所有的依赖(包括和 Flask 完全不相关的)又都被更新了?
依然,已经有很多相关 issue:

重点评论:

如果这个 issue 没有被锁定,这一句「I have no idea.」下面的图标不知道还会被点多少次。我猜 Kenneth Reitz 对这个 issue 让多少人头疼也没有 idea。

继续搜索,查文档,发现 update 命令也有 –keep-outdated 和 –selective-upgrade 两个选项:

$ pipenv update --help 
... 
--selective-upgrade Update specified packages.
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED]

先来试下 –keep-outdated:

$ pipenv update --keep-outdated flask

no luck,还是更新了所有依赖。继续试一下 –selective-upgrade:

$ pipenv update --selective-upgrade flask

依然没用,仍然会更新所有依赖……

继续查 issue,发现下面这些:

在 #3461 里发现了下面这个评论:

(因为 Frost Ming 是国内的同学,也是核心维护者,说明一下,这里无意冒犯,引用这个评论只是想说明 Pipenv 现在的开发状态。)

这段评论的重点是「In fact, the package name passed as argument is not used at all.」。

也就是说,pipenv update 实际上是不接受包名称参数的。这在下面这个评论也得到了印证:

Here is the important caveat:pipenv updatealwaystargets every package in your lockfile, without exception. It does not accept arguments.

一个还没实现的功能就写到文档里了?这真的不是开玩笑吗?不仅是写到了文档里,还写到了命令行帮助文档里:

$ pipenv update --help
Usage: pipenv update [OPTIONS] [PACKAGES]...

类似下面的场景:

  • 用户:怎么运行程序呢?好,查下文档,文档里说「执行 run 命令就可以运行程序」。哎?怎么没用?
  • 开发者:哦,这个功能还没实现,先写出来让你练练手。

最终的结果就是,如果你想更新一个包,那就只能手动把更新版本的包版本和 hash 编辑到 Pipfile.lock 里。这么做实在是太蠢了。

卸载包

假设我决定不再使用 Gunicorn,需要卸载它,在文档里查到 pipenv uninstall 命令https://docs.pipenv.org/en/latest/basics/#pipenv-uninstall),于是执行下面的命令:

$ pipenv uninstall gunicorn

结果呢?为什么我所有的依赖又都被更新了!?好,我已经习惯了。看命令行帮助文档,同样有 –keep-outdated 命令:

$ pipenv uninstall --help
...
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED] 

再试一下,虽然我已经不抱期待了:

$ pipenv uninstall --keep-outdated gunicorn

顺便说一句,卸载的另一个问题是,当你卸载一个包的时候,只会卸载这个包本身,而这个包引入的相关依赖都会被保留,需要手动使用 clean 或 sync 命令修正(参考 Poetry README)。

不要使用 Pipenv(至少是现在)

当然,Pipenv 一直在改进。比如 Windows 支持,Lock 很慢的问题,都有过很多的优化(暂且不提没优化之前的痛苦经历)。

但是,种种证据都在表明,这其实是一个半成品。承诺了很多,兑现的却很少。或许过一段时间等它真正成熟了,能够保证基本使用流程,并且可以修改哪些反人类的设定以后再考虑用它(我怀疑这一条是否能实现,除非完全「去 Kenneth Reitz 化」,并且有一个核心维护者能够来推动执行)。

现在,请不要使用它。

我很抱歉在《Flask Web 开发实战》以及文章《Pipenv:新一代Python项目环境与依赖管理工具》中,推动更多人用它,给大家带来潜在的麻烦。我计划了一些补救措施,会逐一执行:

  • 写这篇文章【DONE】
  • 给《Flask Web 开发实战》的五个实例项目追加 requirements.txt 文件,并在 README 中添加说明。
  • 如果《Flask Web 开发实战》能出第二版,修改所有 Python 包安装命令,去掉所有 Pipenv 相关介绍。
  • 写文章介绍替代的几种工具和用法,包括原有的 virtualenv+pip、virtualenvwrapper、Poetry 等。
  • 在 PyCon China 2019 的闪电演讲《Python 虚拟环境和依赖管理工具大乱斗》里提及这个信息。
  • 以后写推荐文章时对这种带着强烈个人风格的项目保持警惕,并对 Kenneth Reitz 这个名字相关的东西保持警惕。

你可以选择用回 virtualenv+pip(+virtualenvwrapper),或是尝试新工具,我会在下一篇文章介绍主要替代品 Poetry 的基本用法。

相关文章

(1)

PyCon China 2019 Tutorial:Python Web 开发第一课

这是在 PyCon China 2019 上海场 9 月 22 号上午九点开始的 Tutorial(实践课程)《Python Web 开发第一课》 的介绍和相关信息。

购票链接:https://www.bagevent.com/event/5886131(Tutorial T3,优惠码 hellogrey)

标题

Python Web 开发第一课

介绍

这是一个面向 Python 程序员的 Web 开发课程,目标听众需要对 Python 基本语法有一定的了解,但对 Web 开发的了解程度没有要求。

在这个课程里,我会将 Python Web 开发所涉及的相关概念进行一个系统的梳理和介绍,包括 HTTP 协议、前端基础知识、常用的 Python Web 框架以及其他各种工具。

这个课程还会包含一个动手编程的环节。我会从最让人头疼的开发环境搭建开始,一步一步教你如何使用 Flask 开发一个简单的 Web 程序。

在课程过后,参与者会对整个 Python Web 开发技术栈有一个全局认识,并掌握基本的 Web 开发知识,而且会对接下来的学习路径有一个清晰的了解。

流程

时长:三小时

一、基本概念

  • Python Web 开发技术栈地图
  • HTTP 协议基础知识(请求与响应、URL 等)
  • 前端基础知识(HTML、CSS、JavaScript、AJAX 等)
  • Python 后端框架的特点和选择(Flask、Django 等)
  • 传统 Web 程序和 Web API 的对比
  • 测试、部署、持续集成等相关概念快速扫盲

二、动手编程

  • 开发环境搭建
  • 运行和调试程序
  • 编写 HTML 模板
  • 添加表单支持
  • 添加数据库支持

三、Q&A

  • 介绍常见的学习误区和建议的学习方向
  • 关于代码或其他任何相关内容的提问

内容难度

初级

目标听众

  • 想了解 Web 开发的前端、运维、测试或其他工程师
  • 想自己做网站的编程爱好者
  • Web 开发或 Python 初学者

听众要求

  • 了解 Python 基本语法
  • 有一台安装了 Python 和浏览器的电脑,并且了解命令行基本操作

讲者介绍

李辉,Flask 等相关项目的维护者,《Flask 入门教程》和《Flask Web 开发实战》的作者,HelloFlask 社区创建者。他撰写过大量技术文章,回答过大量技术问题,在这个过程中积累了一些编程教学的技巧,擅长用简单的语言解释复杂的编程概念。你可以在他的个人网站 greyli.com 了解到更多相关信息。

PyCon China 2019:基于 Flask 的 Web API 开发指南(北)

Meta

成都场 2.0 版本(推荐)

上海场 1.0 版本


这是在 PyCon China 2019 上海场 9 月 21 号分会场 B 下午 1:30 开始的演讲《基于 Flask 的 REST API 开发指南》 的介绍和相关信息。

这场演讲也会参加 PyCon China 2019 成都场(10 月 26 号)。

标题

基于 Flask 的 Web API 开发指南

介绍

作为一个微框架,轻量灵活的 Flask 很适合用来开发 Web API。相对于 Django REST Framework 和 APIStar,Flask 有什么优势和缺点?为了减少工作量,我们通常会使用一些工具来辅助编写,面对 Flask-RESTful、Flask-RESTPlus、Flask-API、Webargs、Marshmallow 等扩展和工具库,我们应该如何选择?虽然我们经常使用 REST API 这个名称,但是大部分的 API 都不够 RESTful,那么什么样的 API 才能算是 REST API?在这个议题中,我们将对这几个问题逐一进行探讨,并了解如何使用 Flask 编写出功能完善的 Web API。

总结

尽管完成了两版,但是比预先计划的内容少了很多,没能完成 Flask 扩展和其他 Web API 框架的深入对比。这些估计要放到新书里了。

  • 上海站第一版花费时间:23h 31m
  • 成都站第二版花费时间:31h 51m

PyCon China 2019:Python 虚拟环境和依赖管理工具大乱斗

这是在 PyCon China 2019 上海场 9 月 21 号分会场 B 下午 4:40 开始的闪电演讲《Python 虚拟环境和依赖管理工具大乱斗》(时长限定 5 分钟以内) 的介绍和相关信息。

PyCon China 2019 上海场购票链接:https://www.bagevent.com/event/5293611(优惠码 helloflask)

Meta

标题

Python 虚拟环境和依赖管理工具大乱斗

介绍

大多数人都会在 Python 虚拟环境和依赖管理的路上经历重重困难。一开始,你使用 pip + virtualenv + requirements.txt 的工具组合,或者再加上 virtualenvwrapper;接着,更高级的新东西出现了。Pipenv 号称新一代 Python 项目环境和依赖管理工具,打算替代上面的复杂组合,但是发展并不顺利;接着,竞争者出现了,其中实力最强的 Poetry 除了可以替代 Pipenv 来管理依赖之外,甚至还可以让你不用写 setup.py。故事就这样结束了吗?当然没有,尚在草案阶段的 PEP 582 在一边虎视眈眈,试图终结这一切混乱……

总结

加上写相关研究文章的时间,一共花了 36 小时来准备这个演讲(5 分钟),整体效果还不错,后半部分时间太紧说的有点快。

另外最后几天太忙(懒),有几处幻灯片页面样式都使用了行内 CSS,暂时没时间改,就这样吧 :/

欢迎来 PyCon China 2019 听我的演讲和 Tutorial

有没有人要参加今年的 PyCon China 上海场(9/21)?欢迎一起来组团(加我微信,备注「PyCon 组团」)!我们到时可以一起见面交流,也可以做一些 Flask 相关的活动。Flask 作者 Armin Ronacher 和 Flask 维护者之一 Hsiaoming Yang(lepture)已经确认出席,到时我可以把他们俩拉来和大家一起聊天(英语不好没关系,会说 how do you do 就行)~

其实参加技术大会并不一定是要去学东西,也可以去交朋友,换工作(微软、AWS、Elastic、JetBrains 这些公司不考虑下么),找男/女朋友(没错,我们会有相亲墙,而且 PyCon 是有女生报名的,有多少暂时保密),感受技术氛围(假装会让自己变得更厉害)……

购票和优惠码

我从 PyCon 会务那里申请到三个优惠码,helloflask 可以让大会票减掉 100 块,hellopython 可以让所有 Tutorial(三小时一个的课程)打七折,hellogrey 可以让我的那个 Tutorial(T3,Python Web 开发第一课)打六折。每个优惠码各有 20 个名额。

大会购票和详细日程见 bagevent.com/event/5293

大会官网在 https://cn.pycon.org

主题演讲

在所有主题演讲中,我比较期待(和能听懂)的大概有下面这几个(主要都在分会场 B):

  • Python 的永恒之美 – Luciano Ramalho – 《流畅的 Python》作者
  • 调试是一种新的发布:慢语言的意外优势 – Armin Ronacher – Flask 作者
  • 危险的 Flask – Hsiaoming Yang(lepture) – Flask 维护者
  • 基于 Flask 的 REST API 开发指南 – 李辉 – 我
  • Django 中的 GraphQL – 李齐雨 – LeetCode
  • Django Migration Under the Hood – 赖信涛 – 蚂蚁金服

除此之外,还有好几个分会场大概 30 个演讲,包括语言特性、人工智能、微软专场、基础架构、创新领域。另外 AWS 还提供了一个 Workshop,具体日程和演讲介绍见这里

闪电演讲

闪电演讲(5 分钟一场)是今年我参与组织 PyCon China 后推动添加的新环节,我也会负责闪电演讲的主持(第一次做主持人……紧张 ><)。闪电演讲会在分会场 B(Web 专场)进行,下午 4:40 开始,是独占环节。如果你参加今年的 PyCon,一定不要忘记来这里。

  • 【Python 虚拟环境和依赖管理工具大乱斗】 – 李辉 – 我
  • 【500 行 Python 写一个渲染器】- 谭啸 – 蚂蚁金服开发工程师
  • 【基于 OwlReady2 的人机交互】- 宋从威 – 浙江工业大学之江学院讲师
  • 【Byte Code 的革命】- 赵俊德 – 西安德新软件创始人
  • 【使用 Sphinx 制作 Web 文档】- 陈照强 – 中科院上海药物所高级研究员
  • 【一键将 C/C++ 代码转换为 Python 能调用的代码】- 韦泽华 – 上海韦纳科技有限公司
  • 【闭幕:今天什么也没听懂,PyCon 就要结束了】- 李辉 – 主持人

Tutorial:Python Web 开发第一课

今年 Tutorial 的设置主要参考了 PyCon US,形式是三小时的收费实践课程(单独收费,在 9/22),由《流畅的 Python》作者的两个 Tutorial 打头阵,而微软贡献了一个免费的 Tutorial。

我的 Tutorial 是一个面向 Python 初学者的 Web 开发入门课程(T3:Python Web 开发第一课),动手实践的部分会使用 Flask,欢迎参加。另外,Flask 维护者之一 Hsiaoming Yang 也有一场面向初学者的 Tutorial(T6:从零开始打造一个 Python 开源项目),详情见下面的链接。

Tutorial 详细日程和购票:bagevent.com/event/5886

别忘了用优惠码,我的那场(T3)可以用 hellogrey 优惠码,其他的 Tutorial 可以用 hellopython 优惠码。下面是我的 Tutorial 介绍:

介绍

这是一个面向 Python 程序员的 Web 开发课程,目标听众需要对 Python 基本语法有一定的了解,但对 Web 开发的了解程度没有要求。

在这个课程里,我会将 Python Web 开发所涉及的相关概念进行一个系统的梳理和介绍,包括 HTTP 协议、前端基础知识、常用的 Python Web 框架以及其他各种工具。

这个课程还会包含一个动手编程的环节。我会从最让人头疼的开发环境搭建开始,一步一步教你如何使用 Flask 开发一个简单的 Web 程序。

在课程过后,参与者会对整个 Python Web 开发技术栈有一个全局认识,并掌握基本的 Web 开发知识,而且会对接下来的学习路径有一个清晰的了解。

流程

一、基本概念

  • Python Web 开发技术栈地图
  • HTTP 协议基础知识(请求与响应、URL 等)
  • 前端基础知识(HTML、CSS、JavaScript、AJAX 等)
  • Python 后端框架的特点和选择(Flask、Django 等)
  • 传统 Web 程序和 Web API 的对比
  • 测试、部署、持续集成等相关概念快速扫盲

二、动手编程

  • 开发环境搭建
  • 运行和调试程序
  • 编写 HTML 模板
  • 添加表单支持
  • 添加数据库支持

三、Q&A

  • 介绍常见的学习误区和建议的学习方向
  • 关于代码或其他任何相关内容的提问

内容难度:初级

目标听众

  • 想了解 Web 开发的前端、运维、测试或其他工程师
  • 想自己做网站的编程爱好者
  • Web 开发或 Python 初学者

听众要求

  • 了解 Python 基本语法
  • 有一台安装了 Python 和浏览器的电脑,并且了解命令行基本操作
  • 其他的要求我们会具体沟通

我不喜欢这个哪吒

前段时间去看了《哪吒之魔童降世》,但是过后总有一些不舒服的感觉。整部影片的观感还不错,尤其是剧情和对白以外的地方,总体上要比《流浪地球》那种烂片强得多。

让人讨厌的哪吒角色

哪吒的角色让我想起前段时间的《流浪地球》里刘启的角色,让人很反感。有时候未成年人的叛逆是值得鼓励和赞许的,比如你反抗不公平不合理的制度,和穿新衣的皇帝叫板,这些都是可贵的反叛。但是你喜欢说脏话、不懂礼貌、做事情不负责任,这就是另一种让人讨厌的幼稚叛逆。这时候如果你还声称要做自己,要打破别人的偏见,还要扯什么「我命由我不由天」,那我实在没法理解这其中的逻辑。

意思就是我欺负百姓,放火烧屋,不尊重长辈,但我其实是个好孩子?怪就怪你们当初否定我——你们说我坏,所以我就坏给你们看?为了证明自己并不坏,所以去抓妖怪,毁房烧屋也没关系,被误会和欺负就想杀人(几次差点杀死人)?

在我来看,这里的哪吒完全就是一个性情暴躁、品行恶劣、行为怪异的熊孩子。一个动不动就打打杀杀的暴力狂,大部分时间都是在发泄自己暴躁的情绪。很像《告白》里面那个发疯的杀人凶手少年 B。

从父母教育的层面看,如果我的儿子只有三年生命,我也会用正确的方式来教育他,而不是溺爱他,放任他胡作非为,不讲礼貌。即使白天再多妖怪要杀,晚上没时间陪孩子吗?怎么也不至于让他变成一个这么讨厌的野孩子……

空洞的反叛

电影的一些情节、对白和角色都让人感觉「很周星驰」,但是周星驰来拍的话,绝对不会加入那些煽情、说教和空洞的反叛口号。我认为这个电影想表达的反叛很空洞,完全是流于形式。

这里的哪吒满口「我命由我不由天」,给我的感觉只有虚伪和做作。明明就是混沌状态的熊孩子,一边做着各种不成熟的事情,一边还喊着这样的反叛口号,我真的没看出来这个哪吒在反叛什么。

按照我的理解,好的电影应该是一件雕塑,而不是一个箱子。既然没法合理的表达出反叛(在大部分值得反叛的东西没法通过审查的情况下),那就不要勉强。如果不能自然的搞笑,那就不要搞笑。不知道为什么国内的电影总是拼命塞进更多的笑料,不管是什么题材的电影,不管有些笑料多么低俗和无聊,就像是拼命塞满过时流行语的领导发言一样让人尴尬。

这种把电影当做箱子的另一个结果就是风格混乱,一会儿像周星驰(无厘头),一会儿像日漫(热血战斗)。如果不弄这些空洞的反叛,风格统一,就做一个完整的爆米花喜剧故事,应该会更好。

还有一些情节值得吐槽,但过去太久已经记不太清了,大概都是没有用心推敲的结果。比如最后老百姓围成一圈跪下来。为什么要跪?膝盖灌铅啦,中国影视剧里的古代普通民众总是动不动就要跪,皇帝和大官来了要跪,神仙和假装是神仙的妖怪来了要跪,不管是不是英雄,只要别人说是英雄来了也要跪。好像只要你走在街上,不管发生了什么事情,只要看到前面有人跪了,后面的人就要一个接一个的跪。虽然不知道古代人是不是真的这么喜欢跪,但在这个电影里,灾难本来就是哪吒带来的,他们都不知道吗?为什么要向自己害怕、厌恶和给自己带来灾难的人下跪?

最后,电影里哪吒一直想反抗的人类偏见消除了吗?我想并没有。所以这个电影拍到最后到底在表达什么?

哪吒不该是这样

我之所以想写这篇文章,是因为我在心里是珍视哪吒这个角色的。虽然他们有改编的自由,但这部电影在很多人心里毁掉了那个反抗权威和父权的哪吒形象(反而这部电影里敖丙才更哪吒)。

哪吒不像孙悟空那样无牵无挂,可以彻底的反叛和藐视一切。哪吒有父母,像是拴在身上的温柔锁链,这其实是现实里大多数人的状态。说起来也许有点幼稚,高中的时候我给自己起的英文名就是 Nalakuvara Li(李哪吒),Twitter 上的头像很长一段时间都是哪吒自刎的图片。相应的,那时候也很喜欢哪吒乐队和痛仰,不过前者早早的夭折,而在后者那里,哪吒最终还是双手合十,不再愤怒了。

blank

是呀,当你有家和孩子要养,有各种贷款要还的时候,谁还有时间、精力和勇气愤怒和反叛呢?


发现写影评好难,为了严谨你可能要重新看一遍甚至很多遍,要不然只能删掉不确定的内容。而且有的观点很难确认是自己一厢情愿的理解,还是电影本身想表达的东西。

再见小巴

小巴是我家的狗,大概两岁了。我从五月份离开家来南京就没再见过它,它现在失踪了。

这段时间,每天早上爸都会带它去散步,但现在早晨的田边却再也没有它的身影了。瑶瑶上周寄回家的一袋狗粮,大概还只吃了一点点。

blank

小巴小时候非常可爱

小巴在来我们家之前,有过一段挨饿的生活,所以养成了贪吃的习惯,而且在吃这件事情上非常自私。假如你给它一根鸡腿,但是它刚好不太饿,没有胃口,它就会坐在地上看着鸡腿。一旦你开始伸手去拿鸡腿,说着「不吃我要拿走咯」,它就会立刻开始吃起来。有时候,它还会把鸡腿藏到土里埋起来,等到哪天饿了却没东西吃,就把变了颜色沾满土的僵尸鸡腿拿出来吃,好像味道和新的没差别一样。

小巴还喜欢叼东西,大概是因为它可以把嘴长得很大,天分使然吧。它总是把任何可以当做玩具的东西叼到它的窝里面,网球(它的专属玩具)、水瓶(它的窝里通常都会堆着好几个水瓶)、鞋子(通常是我弟的,大概它比较喜欢有味道的东西……)等等各种能被叼起来的东西。

我们偶尔会把这个当做游戏逗它:把它的玩具偷偷从它的家里拿出来,然后丢到更远的地方,它一旦抢到就会飞奔带回去。次数多了以后,它一旦发现了喜欢的东西,就会立刻叼起来往家跑,所以有时它会从卧室偷偷叼起来拖鞋或袜子,然后爸妈就跟在后面追(不过小巴肯定以为在玩游戏)。偶尔它也会叼棍子回去,但是很多时候因为棍子比它的房门要宽,所以经常会卡在门口进不去……真是笨拙的可爱呀!

我们家对于养宠物这件事有一些共识,我们都不愿意把狗栓起来当做可怜的看门狗,所以我们家的狗都是自由的,而且伙食水平和其他邻居的狗相比简直就是天上地下的区别,所以每次要吃饭的时候总有很多邻居的狗围过来。在我们家的这段时间,小巴每天都可以和其他朋友到处疯跑,而家里永远提供水、食物、住宿和按摩服务。我想至少它快乐自由的生活了两年。

blank

和小巴在田里散步

在农村养狗真是一个风险很高的事情,除了做好疾病预防的准备,你还要避免让他吃太多,要不然长得太胖就会成为狗贩子的目标……我们都决定暂时不再养狗了,大家都不想再承受这种失去心爱宠物的痛苦。以前也有很多可爱的狗狗,我们都互相陪伴了一段时间,但总是它们先离开。写这篇文章,是想让这个世界多一些小巴的影子。

我不在家的这几个月,因为爸妈没时间好好照顾它,小巴瘦了很多,但是每天依然都玩的很开心,虽然没有我和瑶瑶陪它,但它还是有很多同类的朋友。不过不知道它会不会偶尔想起我来,会不会想,那个经常和我赛跑,抢网球,拿东西给我吃的人去哪里了?

blank

刚淋完雨回来的小巴