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 用程序代码来进行安装与构建。关于项目目录布局,有做成项目模板的,然后做成工具来应用项目模板。
下面大概浏览一下四个工具的使用
CookieCutter
PyScaffold
PyBuilder
Poetry
CookieCutter 一个经典的 Python 项目目录结构
$ 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
lintcheckstyle
testrun tests quicklywiththedefaultPython
test-all run testsonevery Pythonversionwithtox
coveragecheckcode coverage quicklywiththedefaultPython
docs generate Sphinx HTML documentation,includingAPI docs
servedocs compile the docs watchingforchanges
releasepackageandupload arelease
dist buildssourceandwheelpackage
installinstallthepackagetothe active Python's site-packages
为使用上面的构建过程,需要安装相应的包,如tox,wheel,coverage,sphinx,flake8, 它们都可以通过pip来安装。之后就可以make test,make coverage,make docs,make dist等。其中make docs可以生成一个很漂亮的 Web 文档。
PyScaffold 创建一个项目
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
defaultenvironments:
default-> Invoke pytesttorun automated tests
additional environments:
build -> Build the packageinisolation accordingtoPEP517, see https://github.com/pypa/build
clean -> Remove old distribution filesandtemporary build artifacts (./buildand./dist)
docs -> Invoke sphinx-buildtobuild the docs
doctests -> Invoke sphinx-buildtorun doctests
linkcheck -> Checkforbroken linksinthe documentation
publish -> Publish the package you have been developingtoa packageindexserver. Bydefault, itusestestpypi.Ifyou really wanttopublish your packagetobe publicly accessibleinPyPI, use the `-- --repository pypi` option.
要执行哪个命令便用tox -e build,tox -e docs等
在我体验 tox 命令过程中,每一步好像都比较慢,应该是创建虚拟机要花些时间。
PyBuilder
最好再看另一个构建工具 PyBuilder, 它所创建出的目录结构很接近于 Maven, 下面来瞧瞧
$ pip install pybuilder
$mkdir sample &&cdsample# 项目目录需手工创建
$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-Executeanalysis plugins.
dependsontasks:preparerun_unit_tests
clean - Cleans thegeneratedoutput.
compile_sources - Compilessourcefiles that need compilation.
dependsontasks:prepare
coverage -
dependsontasks:verify
install- Installs the published project.
dependsontasks:packagepublish(optional)
package- Packages the application.Packagea python application.
dependsontasks: compile_sources run_unit_tests(optional)
prepare- Prepares theprojectforbuilding. Creates target VEnvs
print_module_path - Print themodulepath.
print_scripts_path - Print the script path.
publish - Publishes the project.
dependsontasks:packageverify(optional) coverage(optional)
run_integration_tests - Runs integration testsonthe packaged application.
dependsontasks:package
run_unit_tests - Runs all unit tests. Runs unit tests basedonPython'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
defset_properties(project):
project.depends_on('boto3','>=1.18.52')
project.build_depends_on('mock')
随后在执行pyb创建虚拟环境时就会安装上面的依赖,并在其中运行测试与构建。
Poetry
最后一个 Poetry, 感觉这是一个更为成熟,项目活跃度也更高的 Python 构建,它有着更强大的信赖管理功能,用poetry add boto3就能添加依赖,poetry show --tree显示出依赖树。看下如何安装及创建一个项目
$ pipinstallpoetry
$ poetrynewsample
它创建的项目比上面都简单
$ 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 来安装依赖 ),
poetryaddboto3
# 将依照 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. poetryexport--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, 内容为
defmain():
print('hello poetry')
然后在pyproject.toml中写上
[tool.poetry.scripts]
my-script="sample.my_module:main"
再执行
$ poetry runmy-script
就会输出 "hello poetry"。
通过对以上四个工具的认识,项目结构的复杂度由 cookiecutter-pyproject -> PyScaffold -> PyBuilder -> Poetry 依次降低,使用的难度大略也是相同的顺序。
网友评论