这是一本关于如何为python创建“完美” pypi软件包的小手册/故事,任何人都可以使用cherished命令安装该软件包:
pip install my-perfect-package
它是针对初学者的,但是我敦促专业人士就如何改进“完美”软件包发表意见。 因此,我要猫。
“理想”的包装是什么意思?
我将从以下要求出发:
- 在github上开源;
每个人都应该能够为发展做出贡献,并感谢作者。 - 支持所有当前/流行的python版本(2.7、3.5、3.6、3.7、3.8);
Python是不同的,并且仍然在2.7上积极编写代码。 - 100%的单元测试覆盖率;
单元测试可改进体系结构并自动执行回归检查。
带有珍贵数字的徽章会引发FAQ,并为其他人设置标准 。 - 使用CI:
自动检查非常方便! 还有一堆很酷的徽章
- 在所有平台和所有版本的python上运行单元测试;
不要相信那些声称python和已安装软件包是跨平台的人 ,因为您总是会遇到bug 。 - 样式代码检查;
统一的样式可以提高可读性,并减少评论中空白讨论的数量。 - 静态代码分析器;
自动搜索代码中的错误? 给我两个!
- 实际文件;
使用软件包的示例,方法/类的描述以及典型错误的分析-记录的经验将降低初学者的入门门槛。 - 跨平台开发;
不幸的是,仅仅因为开发人员改进了Unix的工具就很难对项目做出个人贡献。 例如,我使用bash脚本进行汇编。 - 该软件包非常有用,可以使世界变得更美好。
一个困难的要求,因为从pypi中的软件包数量(〜210k)来看,开发人员是野性利他主义者,并且已经编写了很多内容。
从哪里开始?
没有什么好主意,所以我选择了一个非常老套且非常受欢迎的主题-使用数字系统。 第一个版本应该能够将数字转换为罗马数字,反之亦然。 对于本手册来说比较复杂,没有必要。 哦,是的,最重要的是名称: numsys-作为数字系统的解密。 数字系统py 。
怎么测试?
我使用python3.7 ,首先使用标准的unittests模块使用功能存根(我们都是TDD )编写了测试。
我进行以下项目结构:
src/ numeral-system/ __init__.py roman.py tests __init__.py test_roman.py
我不会将测试放在包装中,因此我将其分开 谷壳 。 最初, src/
没有创建src/
文件夹,但是进一步的开发表明它对我来说更方便。 因此,这是没有必要的。
我决定使用pytest进行启动-它知道如何与标准模块中的测试完美配合。 看起来有点不合逻辑,但是对我来说测试的标准模块 好像 似乎有点舒服。 现在,我建议使用pytest写作风格。
但是,他们说将pytest
(像其他依赖项一样)放在系统python中并不是一个很聪明的主意...
如何管理依赖关系?
只能使用virtualenv和requirements.txt
。 你可以进步,可以运用诗歌 。 也许我会使用tox-一种简化自动化和测试的工具,这也将允许我管理依赖项。
我创建一个简单的tox.ini配置并安装pytest
:
[tox] envlist = py37 ; , python3.7 [testenv] ; deps = `deps` , . -r requirements.txt ; -r requirements-test.txt ; commands = pytest ;
最初,我明确指出了依赖关系,但与第三方服务集成的实践表明,最好的方法仍然是将依赖关系存储在requirements.txt
文件中。
一个非常微妙的时刻出现了。 在开发时修复当前版本,还是始终放置最新版本?
如果提交,则在安装过程中,由于所使用依赖项的版本不同,软件包之间可能会发生冲突。 如果您不提交,则程序包可能突然停止工作。 对于最终产品来说,后一种情况非常不愉快,因为由于隐式依赖项的较小更新,所有构建可能在一夜之内“变成红色”。 而且根据墨菲的法律,这将在发布当天发生。
因此,我为自己制定了一条规则:
- 始终为最终产品修复版本,因为使用哪个版本是他们的责任。
- 不要修复用于已安装软件包的版本。 如果软件包功能需要,则将其限制在一个范围内。
接下来是什么?
我在写测试!
我填写了函数正文,添加了注释并使测试正确运行。
在这一点上,大多数开发人员通常会停下来(我仍然相信每个人都编写测试=),发布程序包并破解bug。 但是我继续 跳进兔子洞 。
如何使用不同版本的python?
在tox
配置中, tox
指定在所有有趣的python版本上启动测试:
[tox] envlist = py{27,35,36,37,38}
使用pyenv,我在本地向我提供了必要的版本,以便tox
可以找到它们并创建测试环境。
珍爱的100%在哪里?
我将对代码的覆盖率进行衡量-为此,它提供了出色的覆盖率软件包,并且与pytest- pytest-cov的集成度同样出色。
requirements-test.txt
现在看起来像这样:
six=1.13.0 pytest=4.6.7 pytest-cov=2.8.1 parameterized=0.7.1
根据上述规则,我修复了用于运行测试的软件包的版本。
我更改了测试运行命令:
deps = -r requirements.txt -r requirements-test.txt commands = pytest \ --cov=src/ \ --cov-config="{toxinidir}/tox.ini" \ --cov-append
我正在从src/
文件夹中收集所有代码的覆盖率统计信息-包本身( numerical_system / )和测试代码所必需的( tests / )-我不希望测试本身包含不可执行的部分吗?
--cov-append
不同版本的python下每个调用的所有收集到的统计信息--cov-append
为一个,因为第二个和第三个python的覆盖范围可能不同(嗨,取决于版本的代码和模块6 !),但总计为100 % 一个简单的例子:
if sys.version_info > (3, 0):
添加新环境以创建覆盖率报告。
[testenv:coverage_report] deps = coverage commands = coverage html ; , coverage report --include="src/*" --fail-under=100 -m ; 100%
在所有版本的python上运行测试后,我将环境添加到列表中。
[tox] envlist = py{27,35,36,37,38} coverage_report
运行tox
命令后,包含index.html
文件和漂亮报告的tox
文件夹应出现在项目根目录中。
对于令人垂涎的徽章,我将100%与codecov服务集成在一起,该服务本身已经与github
集成,并允许您查看代码覆盖率更改的历史记录。 为此,当然,您必须在此处创建一个帐户。
最终的启动环境如下:
[testenv:coverage_report] deps = coverage==5.0.2 codecov==2.0.15 commands = coverage html coverage report --include="src/*" --fail-under=100 -m coverage xml codecov -f coverage.xml --token=2455dcfa-f9fc-4b3a-b94d-9765afe87f0f ; codecov,
现在仅保留指向README.rst
的徽章的链接:
|Code Coverage| .. |Code Coverage| image:: https://codecov.io/gh/zifter/numeral-system-py/branch/master/graph/badge.svg :target: https://codecov.io/gh/zifter/numeral-system-py
不存在许多分析器,因为它们在很大程度上是相互补充的。 因此,我将集成流行的静态分析器,以检查是否符合PEP8 ,发现潜在问题并 修复所有错误 统一格式化代码。
您应该立即考虑在哪里指定用于微调分析仪的参数。 为此,您可以使用tox.ini
文件, setup.cfg
,单个自定义文件或分析仪的特定文件。 我决定直接使用tox.ini
,因为您可以将tox.ini
复制到以后的项目中。
isort
isort是用于格式化导入的实用程序。
我创建以下环境以代码格式模式运行isort
。
[testenv:isort] changedir = {toxinidir}/src deps = isort==4.3.21 commands = isort -y -sp={toxinidir}/tox.ini
不幸的是, isort
无法指定用于格式化的文件夹。 因此,您必须通过changedir
更改启动目录,并使用-sp={toxinidir}/tox.ini
设置文件的路径。 需要使用-y
开关来禁用交互方式。
要在测试中运行,您需要一种验证模式-为此,有一个--check-only
标志:
[testenv:isort-check] changedir = {toxinidir}/src deps = isort==4.3.21 commands = isort --check-only -sp={toxinidir}/tox.ini
黑色的
接下来,我与黑色代码格式化程序集成。 我用isort
类推地isort
:
[testenv:black] deps = black==19.10b0 commands = black src/ [testenv:black-check] deps = black==19.10b0 commands = black --check src/
一切正常,但与isort
冲突- 导入的格式有所不同。
在其中的一条评论中,我找到了一个最小兼容的isort
设置,该设置用于:
[isort] multi_line_output=3 include_trailing_comma=True force_grid_wrap=0 use_parentheses=True line_length=88
薄片8
接下来,我与flake8静态分析器集成。
[testenv:flake8-check] deps = flake8==3.7.9 commands = flake8 --config=tox.ini src/
再次与black
融合存在问题 。 我们必须添加一个微调,实际上, black
本身推荐这样做 :
[flake8] max-line-length=88 ignore=E203
不幸的是,这第一次无效。 出现错误E231 missing whitespace after ','
,我不得不添加此错误以忽略:
[flake8] max-line-length=88 ignore=E203,E231
皮林特
与pylint静态代码分析器集成
[testenv:pylint-check] deps = {[testenv]deps} # pylint , pylint==2.4.4 commands = pylint --rcfile=tox.ini src/
我立即遇到奇怪的限制-函数名称为30个字符(是的,我写了很长的测试方法名称),并在代码中警告了TODO
的存在。
我必须添加一些例外:
[MESSAGES CONTROL] disable=fixme,invalid-name
同样,令人不快的时刻是pylint
开发pylint
已经掩埋了python2.7
,并且不再为其开发软件包。 因此,应在当前软件包python3.7
上运行检查。
将适当的行添加到配置中:
[tox] envlist = isort-check black-check flake8-check pylint-check py{27,35,36,37,38} coverage_report basepython = python3.7
这对于在不同平台上运行测试也很重要,因为CI系统中python的默认版本不同。
CI怎么了?
传送带
与appveyor -Windows CI集成。 初始设置很简单-可以在界面中完成所有操作,然后下载yaml文件并将其提交到存储库。
version: 0.0.{build} install: - cmd: >- C:\\Python37\\python -m pip install --upgrade pip C:\\Python37\\pip install tox build: off test_script: - cmd: C:\\Python37\\tox
在这里,我明确指定python3.7
的版本,因为默认情况下将使用python2.7
(尽管我明确指定了python3.7
, tox
也将使用此版本)。
照常,徽章的链接已添加到README.rst
|Build status Appveyor| .. |Build status Appveyor| image:: https://ci.appveyor.com/api/projects/status/github/zifter/numeral-system-py?branch=master&svg=true :target: https://ci.appveyor.com/project/zifter/numeral-system-py
特拉维斯
之后,我在Linux(以及在Windows的MacOS和Windows)下与Travis CI -CI集成,但是Python builds are not available on the macOS and Windows environments
。设置有些复杂,因为配置文件将直接从存储库中使用。配置已经准备好了,我挤进了一个漂亮的提交,合并请求也准备好了。
language: python python: 3.8 # dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069) addons: apt: sources: - deadsnakes packages: - python3.5 - python3.6 - python3.7 - pypy install: - pip install tox script: - tox
( 反问:为什么CI项目会像yaml格式那么多? )
我指出了python3.8
的版本,因为它无法通过addon
正常工作,并且Travis CI
从指定的版本成功创建了virtualenv
。
熟悉Travis CI
可能会问,为什么不以这种方式明确指定python版本? 毕竟, Travis CI
自动创建virtualenv
并在其中执行必要的命令。
原因是我们需要收集所有版本的代码覆盖率数据。 但是测试将同时在不同的作业中运行,这就是为什么它无法收集总体覆盖率报告的原因。
当然,我敢肯定,可以多一些理解,并且可以解决此问题。
按照传统,指向徽章的链接也会添加到README.rst
|Build Status Travis CI| .. |Build Status Travis CI| image:: https://travis-ci.org/zifter/numeral-system-py.svg?branch=master :target: https://travis-ci.org/zifter/numeral-system-py
该文件
我认为每个python开发人员都至少使用过一次该服务-readthedocs.org 。 在我看来,这是托管其文档的最佳服务。
我将使用标准工具生成Sphinx文档。 我遵循入门手册中的步骤并获得以下结构:
src/ docs/ build/ # html source/ # _static/ # , , _templates/ # conf.py # index.rst # make.bat Makefile # make make
接下来,您需要执行最少的步骤来配置:
github
默认建议以Markdown格式创建README.md
文件,而默认情况下作为sphinx
建议使用ReStructuredText 。
因此,我不得不以.rst
格式重写它。 如果我至少读了一遍入门手册,我意识到sphinx
可以在Markdown中完成 。
我README.rst
文件README.rst
在index.rst
.. include:: ../../README.rst
- 为了从源代码中的注释自动生成文档,我添加了扩展名sphinx.ext.autodoc 。
- 我
conf.py
包文件夹conf.py
到conf.py
这将允许sphinx
导入我们的代码进行分析。
import os import sys sys.path.insert(0, os.path.abspath('./../../src')) import numeral_system
- 我添加了
docs/source/api-docs
文件夹,并在其中放置每个模块的描述文件。 文档应通过注释自动生成:
Roman numeral system ========================= .. automodule:: numeral_system.roman :members:
之后,该项目准备向世界展示其描述。 您需要创建一个帐户(最好通过github
上的帐户)并导入您的项目,详细步骤在说明中进行了描述。
按照传统,我创建了一个tox
的环境:
[testenv:gen_docs] deps = -r docs/requirements.txt commands = sphinx-build -b html docs/source/ docs/build/
我明确使用了sphinx-build
,而不是make
,因为Windows下不存在该sphinx-build
。 而且我不想违反跨平台开发的原则。
一旦更改被冻结, readthedocs.org
将自动收集文档并将其发布。
但是... Build failed
。 我没有提交sphinx
和sphinx_rtd_theme
,我希望readthedocs.org
使用当前版本。 但是事实并非如此。 我修复:
sphinx==2.3.1 sphinx_rtd_theme==0.4.3
我为readthedocs.org
创建了一个特殊的配置文件.readthedocs.yml
,其中描述了启动构建的环境:
python: version: 3.7 install: - requirements: docs/requirements.txt - requirements: requirements.txt
这就是依赖项位于requirements.txt
文件中这一事实派上用场的地方。 我正在等待构建,并且文档可用 。
再次添加徽章:
|Docs| .. |Docs| image:: https://readthedocs.org/projects/numeral-system-py/badge/?version=latest&style=flat :target: https://numeral-system-py.readthedocs.io/en/latest/
发牌
值得考虑选择软件包的许可证。
这是一个非常广泛的主题,因此我阅读了这篇文章 。 基本上,选择是在MIT和Apache 2.0之间。 我喜欢从上下文中成功删除的短语:
MIT
我完全同意,我会这样做,如果计划有所更改,您可以轻松更改许可证(尽管以前的版本将在旧版本下)。
再次添加徽章 徽章神 :
|License| .. |License| image:: https://img.shields.io/badge/License-MIT-yellow.svg :target: https://opensource.org/licenses/MIT
在这里,您可以找到所有许可证的徽章。
如何上传到pypi?
首先,您需要在pypi.org上创建一个帐户。 然后继续准备包装。
我创建setup.cfg
必须正确描述用于安装/构建软件包的配置。 我按照指示进行 。 可以通过setup.py
设置数据,但是没有设置某些参数的选项。 因此,请使用setup.cfg
文件,您可以在其中指定所有细微差别。 找到了有关如何填充此文件的小模板 。 结果,我同时使用了该文件和该文件-更加方便。
这个文件也可以用来配置pylint
, flake8
和其他设置,但是我没有。
如何组装包装?
我再次写了一个环境,可以帮助我整理必要的软件包:
[testenv:build_wheel] skip_install = True deps = ; wheel docutils pygments commands = python -c 'import shutil; (shutil.rmtree(p, ignore_errors=True) for p in ["build", "dist"]);' python setup.py sdist bdist_wheel
为什么要使用python删除文件夹? 我想遵守跨平台开发的要求,在Windows和Unix下没有方便的方法可以做到这一点。
我启动测试环境:
tox -e build_wheel
结果,在dist
文件夹中,我得到:
dist/ numeral-system_py-0.1.0.tar.gz numeral_system-py-0.1.0-py2.py3-none-any.whl
我填写!
不完全是
首先,值得检查程序包是否正常工作。 我将其上传到测试包存储库。 因此,您需要创建另一个帐户,但是已经在test.pypi.org上 。
为此,我使用了twine软件包-一种在PyPi中填充工件的工具。
[testenv:test_upload] skip_install = True deps = twine ; commands = python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
最初,该项目名为numsys
,但是在尝试填充该项目时,我遇到了一个事实,即已经存在具有相同名称的软件包! 而且最烦人的是-他还知道如何转换为罗马数字:)他不是很沮丧,而是将其重命名为numeral-system-py
。
现在,您需要从测试环境中安装软件包。 验证也应自动进行:
[testenv:test_venv] skip_install = True ; deps = ; commands = pip install -i https://test.pypi.org/simple/ numeral-system-py
现在,您只需要运行:
tox -e test_venv ... test_venv: commands_succeeded congratulations :)
似乎有效:)
现在肯定倒了!
是的
我创建了一个用于上传到生产存储库的环境。
[testenv:pypi_upload] skip_install = True deps = twine commands = python -m twine upload dist/*
以及用于生产验证的环境。
[testenv:pypi_venv] skip_install = True deps = ; commands = pip install numeral-system-py
一切正常吗?
我用简单的命令检查:
> virtualenv venv > source venv/bin/activate (venv) > pip install numeral-system-py (venv) > python >>> import numeral_system >>> numeral_system.roman.encode(7) 'VII'
一切都很棒!
我在github上剪切了发行版 ,收集了软件包并将其填充到propy pypi中。
备注
依赖关系更新
在本文准备期间,发布了新版本的pytest,实际上其中已删除了对python 3.4的支持( 实际上在colorama软件包中)。 有两种选择:
- 提交与3.4兼容的colorama版本;
- 掉落支持3.4 :)
为了支持第二种选择,最后一个论点是pip
在版本19.1中放弃了支持3.4。
还存在分析器,格式化程序和其他服务形式的固定依赖项。 这些依赖项可以同时更新。 如果幸运的话,您只能使用升级版本;否则,您将不得不更正代码,甚至添加设置。
TravisCI
对于MacOS和Windows不支持python。 在同一工作中为所有版本的python运行tox
会有困难。
套餐版本
必须遵守语义版本控制 ,即格式:
MAJOR.MINOR.PATCH
必须指定软件包的版本和一些其他参数以安装软件包(在setup.cfg
或setup.py
)和文档中。 为了避免重复,他只在软件包numeral_system/__init__.py
做了说明:
__version__ = '0.2.0'
然后在setup.py
明确使用此变量
setup(version=numeral_system.__version__)
docs/source/conf.py
也是如此
release = numeral_system.__version__
上面的内容适用于任何元信息REAMDE.rst
,项目描述,许可证,作者姓名等。
的确,这导致包装在组装时被导入的事实,这可能是不希望的。
依赖复制
, requirements.txt
setup.cfg
.
, — setup.cfg
.
. , requirements-dev.txt
.
, src/
. :
:
— .
, :
Add badge with supported version Support for py38
:
Try fix py38 env creating Try fix py38 env creating Try fix py38 env creating Fix check
, 3 ! Travis CI.
, . — , .
, . CHANGELOG
.
, Markdown
. , rst
.
徽章
— , , , . , .
coverage .
-
, , , . six
, , type hint
, asyncio
— .
python2.7
, . , python3.5+, . , , CI . .
?
, :
结论
open source , .
, python github
, .
stackoverflow.com
issues github
.
. . ?..
, , .
, .
github .
谢谢你