在python的项目中,如何管理所用的全部依赖库呢?最主流的做法是维护一份“requirement.txt”,记录下依赖库的名字及其版本号。
那么。如何来生成这份文件呢?在上篇文章《由浅入深:Python中如何实现自动导入缺失的库?》中,提到一种常规的方法:
pip freeze > requirements.txt
这种方法用起来方便,但有几点不足:
- 它搜索依赖库的范围是全局环境,因此会把项目之外的库加进来,造成冗余(一般是在虚拟环境中试用,但还是可能包含无关的依赖库)。
- 它只会记录以"pip install"方式安装的库。
- 它对依赖库之间的依赖关系不做区分。
- 它无法判断版本差异及循环依赖的情况
- …
可用于项目依赖管理的工具很多,本文主要围绕与requirements.txt相关的、比较相似却又各具特色的4个三方库,简要介绍他们的使用方法,罗列一些显著的功能点。至于哪个是最好的管理方案呢?
pipreqs
这是个很受欢迎的用于管理项目中依赖库的工具,可以用"pip install pipreqs"命令来安装。它主要特点有: - 搜索依赖库的范围是基于目录的方式,很有针对性。
- 搜索的依据是脚本中所import的内容。
- 可以在未安装依赖库的环境上生成依赖文件。
- 查找软件包信息时,可以指定查询方式(只在本地查询,在Pypi查询、或者在自定义的Pypi服务)。
基本的命令选项如下:
Usage:
pipreqs [options] <path>
Options:
--use-local Use ONLY local package info instead of querying PyPI
--pypi-server <url> Use custom PyPi server
--proxy <url> Use Proxy, parameter will be passed to requests library. You can also just set the
environments parameter in your terminal:
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="https://10.10.1.10:1080"
--debug Print debug information
--ignore <dirs>... Ignore extra directories
--encoding <charset> Use encoding parameter for file open
--savepath <file> Save the list of requirements in the given file
--print Output the list of requirements in the standard output
--force Overwrite existing requirements.txt
--diff <file> Compare modules in requirements.txt to project imports.
--clean <file> Clean up requirements.txt by removing modules that are not imported in project.
其中需注意,很可能遇到编码错误:UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0xae in。需要指定编码格式:
--encoding=utf-8
在以生成依赖文件”requirements.txt的情况下,它可以强行覆盖、对比差异以及清除不在使用的依赖项。
pipar
pipar同样可以根据项目路径来生成依赖文件,而且会列出依赖库在文件中那些位置使用到了,这个功能充分利用了requirement.txt文件中的注释,可以提供很丰富的信息。
pipar对于查询真实的导入源很有帮助,例如bs4模块来自beautifulsoup4 库,MySQLdb 则来自于MySQL_Python 库。可以通过“-s”参数,查找真实的依赖库。
$ pigar -s bs4 MySQLdb
它使用解析AST的方式,而非正则表达式,可以很方便的从exce/eval的参数、文档字符串的文档测试中提取出依赖库。另外,他对于不同的Python版本的差异可以很好的支持,例如,concurrent.futures是Python 3.2+的标准库,而在之前早期版本中,需要安装第三方库futures,才能使用它。pipar做到了有效地识别区分。
pip-tools
pip-tools包含一组管理项目依赖地工具:pip-compile与pip-sync,可以使用命令"pip install pip-tools"统一安装。他最大地优势是可以精确地控制项目地依赖库。
两个工具地用途及关系图如下:
pip-compile命令主要用于生成依赖文件和升级依赖库,另外它可以支持pip地“Hash-Checking Mode",并支持在一个依赖文件中嵌套其它地依赖文件。
它可以根据setup.py文件来生成requirement.txt,假如一个Flask项目地setup.py 文件中写了“install_requires=[‘Flask’]”,那么可以用命令来生成它的所有依赖:
$ pip-compile
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt setup.py
#
click==6.7 # via flask
flask==0.12.2
itsdangerous==0.24 # via flask
jinja2==2.9.6 # via flask
markupsafe==1.0 # via jinja2
werkzeug==0.12.2 # via flask
在不使用 setup.py 文件的情况下,可以创建“requirements.in”,在里面写入“Flask”,再执行“pip-compile requirements.in”,可以达到跟前面一样的效果。
pip-sync 命令可以根据 requirements.txt 文件,来对虚拟环境中进行安装、升级或卸载依赖库(注意:除了 setuptools、pip 和 pip-tools 之外)。这样可以有针对性且按需精简地管理虚拟环境中的依赖库。
另外,该命令可以将多个“*.txt”依赖文件归并成一个:
$ pip-sync dev-requirements.txt requirements.txt
pipdeptree
它的主要用途是展示 Python 项目的依赖树,通过有层次的缩进格式,显示它们的依赖关系,不像前面那些工具只会生成扁平的并列关系。
除此之外,它还可以:
- 生成普遍适用的 requirements.txt 文件
- 逆向查找某个依赖库是怎么引入进来的
- 提示出相互冲突的依赖库
- 可以发现循环依赖,进行告警
- 生成多种格式的依赖树文件(json、graph、pdf、png等等)
它也有缺点,比如无法穿透虚拟环境。如果要在虚拟环境中工作,必须在该虚拟环境中安装 pipdeptree。因为跨虚拟环境会出现重复或冲突等情况,因此需要限定虚拟环境。但是每个虚拟环境都安装一个 pipdeptree,还是挺让人难受的。
好啦,4 种库介绍完毕,它们的核心功能都是分析依赖库,生成 requirements.txt 文件,同时,它们又具有一些差异,补齐了传统的 pip 的某些不足。
本文不对它们作全面的测评,只是选取了一些主要特性进行介绍,好在它们安装方便(pip install xxx),使用也简单,感兴趣的同学不妨一试。