根据一些开发人员的说法,Python是2019年排名前五的编程语言之一[1]。基于其开源社区的实力以及在新兴领域(例如大数据分析和机器学习)的高采用率,当人们谈论到其在未来几年的潜在发展趋势时,应该不会有人对此感到惊讶。由于Python软件包数量的逐年增长,这也使得越来越多的人开始关注Python包的开发中来。
本文的主要目的就是描述一个清晰的程序包结构,使开发人员更容易测试,构建和发布它,并在遵守相关默认约定的同时,尽可能编写少的配置。
一个清晰的工程结构
在开发一个Python包时,建议的文件夹和文件(包括测试内容)如下所示:
project-root
├──src/
└──package_name/
└──__init__.py
├──tests/
└──package_name/
├──.coveragerc
├──.gitignore
├──LICENSE
├──README.md
├──setup.cfg
└──setup.py
除了.gitignore
,LICENSE
和README.md
这三个文件外,上面的每一个文件在下面我们都会进行详细的介绍。如果对于上述三个文件有任何疑问,请自行上网查阅。
让我们首先开始从setup.py
说起。setup.py
是一个Python包的描述文件,它由一个Python脚本组成,并且可以在其中声明多个属性,如下所示:
from setuptools import find_packages, setup
setup(
name='python-package-cheat-sheet',
version='1.0.0',
author='Your Name',
author_email='you@yourdomain.com',
description='Python package developer\'s cheat sheet',
platforms='Posix; MacOS X; Windows',
packages=find_packages(where='./src'),
package_dir={
'': 'src'
},
include_package_data=True,
install_requires=(
'stringcase',
),
classifiers=[
'Development Status :: 1 - Alpha',
'Natural Language :: English',
'Programming Language :: Python',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
)
在这个文件中声明的各类属性都将会被Python管理器(如pip
)或者是IDE(如PyCharm)进行组织起来,这就意味着所有的Python包都必须包含这么一个文件。
从上面的代码可以看到,一些属性的含义非常简单:名称(name
),版本(version
),作者(author
)等。但是其他属性则需要一些解释:
packages,package_dir
:用于设置包源文件的保存位置,及其声明的命名空间。在上面的示例中,scr
是Python包源代码的根目录,并且在本示例中src
下面还有一个子目录(package_name
)。同时,只有当package_name
中存在一个__init__.py
文件时,该模块才会被加载到相应的索引路径中。install_requires
:一个包含整个包正常工作时所需要的依赖关系元组。甚至你还可以认为它是requirements.txt
的替代品,如果你更熟悉后者的话。
这些属性使我们可以仅捆绑用户在使用我们提供的软件包时所需的文件,从而可以分发较小的Python分发文件。而且,他们将仅会下载每个Python程序包运行时所需要的依赖项,从而避免不必要的网络和存储开销。
这里有一个实际的练习可以看到它的效果, GitHub上也提供了相应的本文和示例代码:
https://github.com/ricardolsmendes/python-package-cheat-sheet
当然,我们也可以通过pip
来安装这一项目,然后查重其相关依赖关系:
pip install git+https://github.com/ricardolsmendes/python-package-cheat-sheet
pip freeze
此时,你将会看到有两个Python包被安装:
python-package-cheat-sheet==1.0.0
stringcase==1.2.0
其中第一个包就是上面我们声明在setup.py
中的Python包,而第二个就是安装第一个包所要依赖的包。
下面,让我们在Python的交互界面中调用方法package_cheat_sheet.StringFormatter.format_to_snakecase
:
python
>>> from package_cheat_sheet import StringFormatter
>>> print(StringFormatter.format_to_snakecase('FooBar'))
foo_bar
>>> exit()
正如你所见,foo_bar
将作为StringFormatter.format_to_snakecase('FooBar')
的结果进行输出,这就意味着上面整个安装过程是成功的。以上内容就快速演示了一个Python包的简单开发流程,并且介绍了如何通过几行代码就能将自己开发的Python包分享给其他用户。
必不可少的测试环节
现代软件的开发过程依赖于自动化的测试过程,如果没有它们我们甚至无法考虑开始开发Python软件包。为了达到这一目的,通常情况下Pytest
是一个不二的选择,下面就让我们来看看如何进行使用。
首先,我们需要先卸载上面所安装的包,然后将其对应的代码克隆到本地,以源代码的形式进行安装:
pip uninstall python-package-cheat-sheet
git clone https://github.com/ricardolsmendes/python-package-cheat-sheet.git
cd python-package-cheat-sheet
pip install --editable .
基于安装文件触发测试套件的命令是python setup.py test
,并且默认情况下它不会使用pytest
。但是,这里有另外一个方法来进行代替:即在这个Python包的根目录中创建一个setup.cfg
文件,然后再为test
命令设置一个别名。并且还可以在同一个文件中pytest
:
[aliases]
test = pytest
[tool:pytest]
addopts = --cov --cov-report html --cov-report term-missing
从现在开始,我们就需要为
pytest
来添加对应的依赖项,否则别名创建将会失败。下面,我们将依赖关系的不同属性添加到setup.py
中:setup_requires=(
'pytest-runner',
),
tests_require=(
'pytest-cov',
)其中
pytest-runner
负责为安装任务提供pytest
支持;pytest-cov
则是用来帮助我们生成代码的测试覆盖统计信息(将会在下面进行展示)。
同时,Python软件包的根文件夹中必须再包含一个配置文件:
.coveragerc
:用于控制coverage
脚本的范围。当项目中有不需要进行监视的测试文件夹时,此功能非常有用。在上述所示的工程项目中,仅src
文件夹必须覆盖:[run]
source = src
现在,我们准备运行python setup.py test
进行测试。默认情况下,pytest
将会在tests
文件夹中查找对应的测试文件。对于刚刚克隆的GitHub仓库,其测试对应的预期输出为:
plugins: cov-2.8.1
collected 10 items
tests/package_cheat_sheet/string_formatter_test.py .......... [100%]
---------- coverage: platform darwin, python 3.7.4-final-0 ---------
Name Stmts Miss Cover
--------------------------------------------------------------------
src/package_cheat_sheet/__init__.py 2 0 100%
src/package_cheat_sheet/string_formatter.py 17 0 100%
--------------------------------------------------------------------
TOTAL 19 0 100%
Coverage HTML written to dir htmlcov
在运行结束pytest
后,请同时也检查一下生成的htmlcov/index.html
文件。因为当你需要对整个测试覆盖有一个更深入的理解时,查看这个文件是非常有必要的。
![625bff16f0b2318302635978101f2225.png](https://i-blog.csdnimg.cn/blog_migrate/70d1c47847605cf8080cb540162c7c46.png)
总结
本文介绍了一个清晰的Python包结构,涵盖了常规设置和测试工具。它通过使用Python标准和一些默认的约定在项目源码和测试文件之间建立了一个明显的界限。这使得我们在实际的操作过程中能够尽可能的减少代码的编写工作。
- END -编程代码、技术探索、前沿追踪,你想要的这里都有!
转载请联系本公众号获得授权