Python 历时这么久以来至今还未有一个事实上标准的项目管理及构建工具,以至于造成 Python 项目的结构与构建方式五花八门。这或许是体现了 Python 的自由意志。
不像 Java 在经历了最初的手工构建,到半自动化的 Ant, 再到 Maven 基本就是事实上的标准了。其间 Maven 还接受了其他的 Gradle(Android 项目主推), SBT(主要是 Scala 项目), Ant+Ivy, Buildr 等的挑战,但都很难撼动 Maven 的江湖地位,而且其他的差不多遵循了 Maven 的目录布局。
回到 Python,产生过 pip, pipenv, conda 那样的包管理工具,但对项目的目录布局没有任何约定。
关于构建很多还是延续了传统的 Makefile 的方式,再就是加上 setup.py 和 build.py 用程序代码来进行安装与构建。关于项目目录布局,有做成项目模板的,然后做成工具来应用项目模板。
下面大概浏览一下四个工具的使用
$?pip?install?cookiecutter
$?cookiecutter?gh:audreyr/cookiecutter-pypackage???
#?以?github?上的?audreyr/cookiecutter-pypackage?为模板,再回答一堆的问题生成一个?Python?项目
......
project_name?[Python?Boilerplate]:?sample
......
最后由 cookiecutter 生成的项目模板是下面的样子:
$?tree?sample
sample
├──?AUTHORS.rst
├──?CONTRIBUTING.rst
├──?HISTORY.rst
├──?LICENSE
├──?MANIFEST.in
├──?Makefile
├──?README.rst
├──?docs
│???├──?Makefile
│???├──?authors.rst
│???├──?conf.py
│???├──?contributing.rst
│???├──?history.rst
│???├──?index.rst
│???├──?installation.rst
│???├──?make.bat
│???├──?readme.rst
│???└──?usage.rst
├──?requirements_dev.txt
├──?sample
│???├──?__init__.py
│???├──?cli.py
│???└──?sample.py
├──?setup.cfg
├──?setup.py
├──?tests
│???├──?__init__.py
│???└──?test_sample.py
└──?tox.ini
3?directories,?26?files
这大概是当前比较流行的目录结构的主体框架,主要元素是:
$?tree?sample
sample
├──?Makefile
├──?README.rst
├──?docs
│???└──?index.rst
├──?requirements.txt
├──?sample
│???├──?__init__.py
│???└──?sample.py
├──?setup.cfg
├──?setup.py
└──?tests
????├──?__init__.py
????└──?test_sample.py
项目 sample 目录中重复 sample 目录中放置 Python 源文件,tests
?目录中是测试文件,再加一个?docs
?目录放文档,README.rst, 其他的用于构建的 setup, setup.cfg 和 Makefile 文件。
这其实是一个很经典的 Python 项目结构,接下来的构建就用?make
?命令了,输入?make
?会看到定义在 Makefile 文件中的指令
$?make
clean????????????????remove?all?build,?test,?coverage?and?Python?artifacts
clean-build??????????remove?build?artifacts
clean-pyc????????????remove?Python?file?artifacts
clean-test???????????remove?test?and?coverage?artifacts
lint?????????????????check?style
test?????????????????run?tests?quickly?with?the?default?Python
test-all?????????????run?tests?on?every?Python?version?with?tox
coverage?????????????check?code?coverage?quickly?with?the?default?Python
docs?????????????????generate?Sphinx?HTML?documentation,?including?API?docs
servedocs????????????compile?the?docs?watching?for?changes
release??????????????package?and?upload?a?release
dist?????????????????builds?source?and?wheel?package
install??????????????install?the?package?to?the?active?Python's?site-packages
为使用上面的构建过程,需要安装相应的包,如?tox
,?wheel
,?coverage
,?sphinx
,?flake8
, 它们都可以通过 ?pip
?来安装。之后就可以?make test
,?make coverage
,?make docs
,make dist
?等。其中?make docs
?可以生成一个很漂亮的 Web 文档。
PyScaffold 顾名思义,它是一个用来创建 Python 项目脚手架的工具,安装和使用:
$?pip?install?pyscaffold
$?putup?sample
这样创建了一个 Python 项目,目录结构与前面 ?cookiecutter 所选的模板差不多,只不过它把源文件放在了?src
?目录,而非?sample
?目录。
$?tree?sample
sample
├──?AUTHORS.rst
├──?CHANGELOG.rst
├──?CONTRIBUTING.rst
├──?LICENSE.txt
├──?README.rst
├──?docs
│???├──?Makefile
│???├──?_static
│???├──?authors.rst
│???├──?changelog.rst
│???├──?conf.py
│???├──?contributing.rst
│???├──?index.rst
│???├──?license.rst
│???├──?readme.rst
│???└──?requirements.txt
├──?pyproject.toml
├──?setup.cfg
├──?setup.py
├──?src
│???└──?sample
│???????├──?__init__.py
│???????└──?skeleton.py
├──?tests
│???├──?conftest.py
│???└──?test_skeleton.py
└──?tox.ini
整个项目的构建就要用?tox
?这个工具了。tox
?是一个自动化测试和构建工具,它在构建过程中可创建 Python 虚拟环境,这让测试和构建能有一个干净的环境。
tox -av
?能显示出定义在?tox.ini
?中所有的任务:
$?tox?-av
default?environments:
default???->?Invoke?pytest?to?run?automated?tests
additional?environments:
build?????->?Build?the?package?in?isolation?according?to?PEP517,?see?https://github.com/pypa/build
clean?????->?Remove?old?distribution?files?and?temporary?build?artifacts?(./build?and?./dist)
docs??????->?Invoke?sphinx-build?to?build?the?docs
doctests??->?Invoke?sphinx-build?to?run?doctests
linkcheck?->?Check?for?broken?links?in?the?documentation
publish???->?Publish?the?package?you?have?been?developing?to?a?package?index?server.?By?default,?it?uses?testpypi.?If?you?really?want?to?publish?your?package?to?be?publicly?accessible?in?PyPI,?use?the?`--?--repository?pypi`?option.
要执行哪个命令便用?tox -e build
,?tox -e docs
?等
在我体验 tox 命令过程中,每一步好像都比较慢,应该是创建虚拟机要花些时间。
最好再看另一个构建工具 PyBuilder, 它所创建出的目录结构很接近于 Maven, 下面来瞧瞧
$?pip?install?pybuilder
$?mkdir?sample?&&?cd?sample????#?项目目录需手工创建
$?pyb?--start-project??????????#?回答一些问题后创建所需的目录和文件
完后看下它的目录结构:
$?tree?sample
.
├──?build.py
├──?docs
├──?pyproject.toml
├──?setup.py
└──?src
????├──?main
????│???├──?python
????│???└──?scripts
????└──?unittest
????????└──?python
构建过程仍然是用?pyb
?命令,可用?pyb -h
?查看帮助,pyb -t
?列出所有的任务, PyBuilder 的任务是以插件的方式加入的,插件配置在 ?build.py
?文件中。
$?pyb?-t?sample
Tasks?found?for?project?"sample":
??????????????????analyze?-??Execute?analysis?plugins.
????????????????????????????depends?on?tasks:?prepare?run_unit_tests
????????????????????clean?-?Cleans?the?generated?output.
??????????compile_sources?-?Compiles?source?files?that?need?compilation.
????????????????????????????depends?on?tasks:?prepare
?????????????????coverage?-?<no?description?available>
????????????????????????????depends?on?tasks:?verify
??????????????????install?-?Installs?the?published?project.
????????????????????????????depends?on?tasks:?package?publish(optional)
??????????????????package?-?Packages?the?application.?Package?a?python?application.
????????????????????????????depends?on?tasks:?compile_sources?run_unit_tests(optional)
??????????????????prepare?-?Prepares?the?project?for?building.?Creates?target?VEnvs
????????print_module_path?-?Print?the?module?path.
???????print_scripts_path?-?Print?the?script?path.
??????????????????publish?-?Publishes?the?project.
????????????????????????????depends?on?tasks:?package?verify(optional)?coverage(optional)
????run_integration_tests?-?Runs?integration?tests?on?the?packaged?application.
????????????????????????????depends?on?tasks:?package
???????????run_unit_tests?-?Runs?all?unit?tests.?Runs?unit?tests?based?on?Python's?unittest?module
????????????????????????????depends?on?tasks:?compile_sources
???????????????????upload?-?Upload?a?project?to?PyPi.
???????????????????verify?-?Verifies?the?project?and?possibly?integration?tests.
????????????????????????????depends?on?tasks:?run_integration_tests(optional)
$?pyb?run_unit_tests?sample
PyBuilder 也是在构建或测试之前创建虚拟环境, 从 0.12.9 版开始可通过参数?--no-venvs
?跳过创建虚拟环境这一步。使用了?--no-venvs
?的话 Python 代码将会在运行 ?pyb
?的当前 Python 环境中执行,所需的依赖将要手工安装。
项目的依赖也要定义在?build.py
?文件中
@init
def?set_properties(project):
????project.depends_on('boto3',?'>=1.18.52')
????project.build_depends_on('mock')
随后在执行?pyb
?创建虚拟环境时就会安装上面的依赖,并在其中运行测试与构建。
最后一个 Poetry, 感觉这是一个更为成熟,项目活跃度也更高的 Python 构建,它有着更强大的信赖管理功能,用?poetry add boto3
?就能添加依赖,poetry show --tree
?显示出依赖树。看下如何安装及创建一个项目
$?pip?install?poetry
$?poetry?new?sample
它创建的项目比上面都简单
$?tree?sample
sample
├──?README.rst
├──?pyproject.toml
├──?sample
│???└──?__init__.py
└──?tests
????├──?__init__.py
????└──?test_sample.py
如果给?poetry new
?带上?--src
?参数,那么源文件目录?sample
?会放在?src
?目录下,即?sample/src/sample
.
poetry init
?会在当前目录中生成?pyproject.toml
?文件,目录等的生成需手动完成。
它不关注文档的生成,代码规范的检查,代码覆盖率都没有。它的项目配置更集中,全部在?pyproject.toml
?文件中,toml
?是什么呢?它是一种配置文件的格式 Tom's Obvious, Minimal Language (https://github.com/toml-lang/toml).
pyproject.toml
?有些类似 NodeJS 的?package.json
?文件,比如 poetry add, poetry install 命令的行
#?往?pyproject.toml?中添加对??boto3?的依赖并安装(add?还能从本地或?git?来安装依赖?),
poetry?add?boto3????
?#?将依照?pyproject.toml?文件中定义安装相应的依赖到当前的?Python?虚拟环境中
?#?比如在?<test-venv>/lib/python3.9/site-packages?目录中,安装好模块后也可让测试用例使用
poetry?install???????
其他主要的
1.??poetry?build????#?构建可安装的?*.whl?和?tar.gz?文件
2.??poetry?shell????#?会根据定义在?pyproject.toml?文件中的依赖创建并使用虚拟环境
3.??poetry?run?pytest????#?运行使用?pytest?的测试用例,如?tests/test_sample.py
4.??poetry?run?python?-m?unittest?tests/sample_tests.py??#?运行?unittest?测试用例
5.??poetry?export?--without-hashes?--output?requirements.txt??#?导出?requirements.txt?文件,?--dev??导出含?dev?的依赖,或者用?poetry?export?--without-hashes?>?requirements.txt
poetry run
?能执行任何系统命令,只是它会在它要的虚拟环境中执行。所以可以想见,poetry
?的项目要生成文档或覆盖率都必须用?poetry run ...
?命令来支持?sphinx
,?coverage
?或?flake8
。
在 sample 目录(与 pyproject.toml 文件平级)中创建文件?my_module.py
, 内容为
def?main():
????print('hello?poetry')
然后在?pyproject.toml
?中写上
[tool.poetry.scripts]
my-script="sample.my_module:main"
再执行
$?poetry?run?my-script
就会输出 "hello poetry"。
通过对以上四个工具的认识,项目结构的复杂度由 cookiecutter-pyproject -> PyScaffold -> PyBuilder -> Poetry 依次降低,使用的难度大略也是相同的顺序
--- EOF ---
我们爬虫第二期来了,加入我们,学更实用,更值钱的 Python 技术!
扫码发送「爬虫」咨询