开发完美的pypi包并支持不同版本的python

这是一本关于如何为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中并不是一个很聪明的主意...


如何管理依赖关系?


只能使用virtualenvrequirements.txt 。 你可以进步,可以运用诗歌 。 也许我会使用tox-一种简化自动化和测试的工具,这也将允许我管理依赖项。


我创建一个简单的tox.ini配置并安装pytest


 [tox] envlist = py37 ;     ,   python3.7 [testenv] ;     deps =  `deps`  ,   . -r requirements.txt ;     -r requirements-test.txt ; commands = pytest ;   

最初,我明确指出了依赖关系,但与第三方服务集成的实践表明,最好的方法仍然是将依赖关系存储在requirements.txt文件中。


一个非常微妙的时刻出现了。 在开发时修复当前版本,还是始终放置最新版本?


如果提交,则在安装过程中,由于所使用依赖项的版本不同,软件包之间可能会发生冲突。 如果您不提交,则程序包可能突然停止工作。 对于最终产品来说,后一种情况非常不愉快,因为由于隐式依赖项的较小更新,所有构建可能在一夜之内“变成红色”。 而且根据墨菲的法律,这将在发布当天发生。


因此,我为自己制定了一条规则:


  1. 始终为最终产品修复版本,因为使用哪个版本是他们的责任。
  2. 不要修复用于已安装软件包的版本。 如果软件包功能需要,则将其限制在一个范围内。

接下来是什么?


我在写测试!


我填写了函数正文,添加了注释并使测试正确运行。
在这一点上,大多数开发人员通常会停下来(我仍然相信每个人都编写测试=),发布程序包并破解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): # Python 3 code in this block else: # Python 2 code in this block 

添加新环境以创建覆盖率报告。


 [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.7tox也将使用此版本)。
照常,徽章的链接已添加到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 

接下来,您需要执行最少的步骤来配置:


  1. github默认建议以Markdown格式创建README.md文件,而默认情况下作为sphinx建议使用ReStructuredText

因此,我不得不以.rst格式重写它。 如果我至少读了一遍入门手册,我意识到sphinx可以在Markdown中完成
README.rst文件README.rstindex.rst


 .. include:: ../../README.rst 

  1. 为了从源代码中的注释自动生成文档,我添加了扩展名sphinx.ext.autodoc
  2. conf.py包文件夹conf.pyconf.py 这将允许sphinx导入我们的代码进行分析。
     import os import sys sys.path.insert(0, os.path.abspath('./../../src')) import numeral_system 
  3. 我添加了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 。 我没有提交sphinxsphinx_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/ 

发牌


值得考虑选择软件包的许可证。
这是一个非常广泛的主题,因此我阅读了这篇文章 。 基本上,选择是在MITApache 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文件,您可以在其中指定所有细微差别。 找到了有关如何填充此文件的小模板 。 结果,我同时使用了该文件和该文件-更加方便。


这个文件也可以用来配置pylintflake8和其他设置,但是我没有。


如何组装包装?


我再次写了一个环境,可以帮助我整理必要的软件包:


 [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软件包中)。 有两种选择:


  1. 提交与3.4兼容的colorama版本;
  2. 掉落支持3.4 :)

为了支持第二种选择,最后一个论点是pip在版本19.1中放弃了支持3.4。


还存在分析器,格式化程序和其他服务形式的固定依赖项。 这些依赖项可以同时更新。 如果幸运的话,您只能使用升级版本;否则,您将不得不更正代码,甚至添加设置。


TravisCI


对于MacOS和Windows不支持python。 在同一工作中为所有版本的python运行tox会有困难。


套餐版本


必须遵守语义版本控制 ,即格式:


 MAJOR.MINOR.PATCH 

元信息重复


必须指定软件包的版本和一些其他参数以安装软件包(在setup.cfgsetup.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/ . :


  • ;
  • , , ;

:


  • PyCharm ( ?) — 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 .


谢谢你

Source: https://habr.com/ru/post/zh-CN483512/


All Articles