如何快速发布一个python软件包

如何快速发布一个python软件包

分类: programming

如何快速发布一个python软件包

本文简要的介绍一下发布一个python软件包的大致流程。目录如下:

使用__all__

https://stackoverflow.com/questions/44834/can-someone-explain-all-in-python

定义了可被外部调用的对象:It’s a list of public objects of that module.

编辑.gitignore

简介:https://www.jianshu.com/p/a49124700abc
常用语法:https://blog.csdn.net/Mr_JavaScript/article/details/91788035

.gitignore可以忽略你不想上传的文件。

例如,你在开发的时候可能会使用一些内部测试数据,在开发过程中,你并不想一并上传到仓库中,可以把这些数据添加到.gitignore文件中。

另外一些常见的非上传文件包括:

打包好的软件压缩包,xxx.zip,如果使用setuptools进行打包,通常会出现在dist目录下。

编辑__Init__.py

用于从包中Import模块,并定义一些在import的同时就顺便完成的动作。

下面分别演示。

1.从包中Import模块

例如,下面是有一个包名为your_package,该文件夹下有2个模块:

your_package
 |-- __init__.py
 |-- module1.py
 |-- module2.py

为了能够完成from your_package import module1,你必须在your_package文件夹下创建一个__init__.py

2.定义一些预置动作

假设在module1.py下有一个非常重要的函数,名为:imp_func,按照常规操作,你必须使用from your_package.module1 import imp_func才能导入该函数。

如果你想希望用户能更轻松的导入该函数,比如:from your_package import imp_func,你可以在__init__.py中加入这句话:

from .module1 import imp_func

这样,用户在import your_package的同时就会完成__init__.py中预置好的语句。

__init__.py使用非常频繁,因为很可能你的软件包名和你的主模块名是相同的,例如:假设我有一个软件包名为Circos(packages),我的主模块名为circos.py,主模块内有一个Class Circos

|--circos(目录)
    |--circos.py(包含Class Circos类)
    |--others.py
    |--__init__.py
|--README
|-- ...

这个时候,建议在circos目录下的__init__.py插入:

from .circos import Circos

这样用户使用circos类的时候就可以直接通过:

from circos import Circos

而不是:

from circos.circos import Circos

这么别扭。

撰写Docstrings

https://www.datacamp.com/community/tutorials/docstrings-python

docstring通常用于注释class, module, function,method,attribute。其用途在于帮助用户快速理解被注释对象的功能以及用法。

docstring可以通过python内置属性__doc__或者内置方法help来获取。

两者的区别在于__doc__输出更简洁。

例如,假设我们想查看sklearn模块LinearRegression类的属性与方法:

from sklearn.linear_model import LinearRegression
print(LinearRegression.__doc__)
help(LinearRegression)

输出内容如下:

Ordinary least squares Linear Regression.

Parameters
----------
fit_intercept : boolean, optional, default True
whether to calculate the intercept for this model. If set to False, no intercept will be used in calculations(e.g. data is expected to be already centered).
...


可以看到,开发者对该类的描述,参数、方法、属性的说明都被打印了出来。

那么,怎么撰写docstring呢?

最简单的是,利用三个连续的引号(单双均可)将注释内容括起来,例如:

def my_sum(x, y):
 """calculates the sum of x and y."""
	return x+y

值得注意的是:

docstring与一般#号开头的注释并不同,#开头的注释通常用于注释某个具体的语句,方便他人阅读。#不会被内置属性__doc__或者内置方法help识别。

下面列举一些常用的注释风格:

PyDoc风格

Google风格

Numpy/Scipy风格

Sphinx风格

各种风格的注释方式各有特点,比如Sphinx非常简洁,使用reStructured Text(.rst)语法,一种类似于markdown的语法;Google风格非常直观;本人最喜欢,也是各大软件包最常见的风格是Numpy风格,它提供了注释对象详细的描述,参数以及返回信息。例如:

def my_sum(x, y):
 """calculates the sum of x and y.
 Parameters:
 ---------
 x (int): a number
 y (int): another number
 Returns:
 ----------
 int: sum of x and y 
 """
 return x+y

单元测试

https://www.liaoxuefeng.com/wiki/1016959663602400/1017604210683936

在完成了程序开发之后,首先需要对程序进行测试。

测试的类型分成很多种,如:单元测试,集成测试,系统测试。其中对于开发人员最首要的是完成单元测试。

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

下面是编写单元测试模块的基本流程。

1.继承TestCase类

在python中,编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。

2.编写测试方法

如果你希望测试原模块中某一个类,可以通过在测试类中编写test开头的方法,它会被TestCase的main方法识别并运行。不以test开头的方法不被认为是测试方法,测试的时候不会被执行。

测试的内容要求尽可能全面覆盖该类下的所有内容:构造函数/属性/方法/异常等。

测试方法的编写通常需要调用TestCase中预定义好的断言方法,如:self.assertEqual/`self.assertTrue

`等。

3.编写setUp/tearDown方法

在单元测试中还可以编写两个特殊的setUp()tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。如果在每一个测试函数的开头都需要用到某一个功能,你可以把该功能放到setUp()方法内。

4.运行单元测试模块

最后,只需要通过unittest.main()即可运行单元测试模块。

下面是一个简单的单元测试模板:

from your_package import module1

class TestDict(unittest.TestCase):

    def setUp(self):
        pass

    def test_init(self):
        pass

    def test_attr(self):
        pass      

    def test_your_function(self):
        pass

    def test_your_error(self):
        pass

    def tearDown(self):
        pass
if __name__ == '__main__':
    unittest.main()

具体的单元测试编写案例请移步:

https://www.liaoxuefeng.com/wiki/1016959663602400/1017604210683936

命名版本号

https://en.wikipedia.org/wiki/Software_versioning

https://blog.csdn.net/qq_42184799/article/details/83016073

https://www.jianshu.com/p/c675121a8bfd

有多种版本编号方案可以用于命名软件的不同版本。其中最流行的是语义版本控制。

语义版本控制(Semantic versioning,也叫 SemVer),是目前在该类别中最知名和使用最广泛的版本方案,它使用三位数的序列(Major.Minor.Patch),编号从左至右的改变程度递减。可以朴素的理解为:

Major:重大变化,可能会影响API的调用,例如整个算法实现思路变了。

当Major以0打头时,表明软件尚处在未正式发布。

Minor:重大功能变化,但不影响API调用。

Patch,小的功能变化/补丁/BUG修复。

README

Readme文件是对包内容的总结。当放在包的顶部目录时,GitHub使用它作为包的“登陆页”,作为访问者看到的第一件东西显示给他们。

一个好的readme文件将包含包的概述、安装信息(如需求)和一些快速启动示例,描述包的基本用法。

readme文件通常以一种名为MarkDown的格式编写,您可以在这里了解更多信息。

License

当代码即将公开时,你应该为其附加许可证,向他人解释其他人应如何使用代码。 常见的许可证如:MIT许可证或Apache2.0许可证。

GitHub将通过单击许可选项来帮助选择一个,它将为软件包添加一个名为LICENSE的新文件。 如果您坚持不使用GitHub,则可以手动添加文件。

提交pypi

1.setup.py

为了提交pypi,你还需要往你的包主目录下添加一个setup.py文件:

your_package 

|-- setup.py 

|-- README.md 

|-- LICENSE

 |-- your_package

 |-- __init__.py

 |-- module1.py

 |-- module2.py

该文件规定了安装器(通常是pip)在安装包时需要的所有内容。

一个简单的模板可以如下所示:

import pathlib
from setuptools import setup, find_packages

HERE = pathlib.Path(__file__).parent

VERSION = '0.1.0'

PACKAGE_NAME = 'your_package'

AUTHOR = 'You'

AUTHOR_EMAIL = 'you@email.com'

URL = 'https://github.com/you/your_package'

LICENSE = 'Apache License 2.0'

DESCRIPTION = 'Describe your package in one sentence'

LONG_DESCRIPTION = (HERE / "README.md").read_text()

LONG_DESC_TYPE = "text/markdown"

CLASSIFIERS = [


    'Programming Language :: Python :: 3.6',


    'Programming Language :: Python :: 3.7',


    'Programming Language :: Python :: 3.8',


    'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)',


    'Topic :: Scientific/Engineering :: Visualization',


    'Topic :: Multimedia :: Graphics',


    'Operating System :: POSIX',


    'Operating System :: Unix',


    'Operating System :: MacOS'

]
INSTALL_REQUIRES = [
      'numpy',
      'pandas'
]

setup(name=PACKAGE_NAME,
      version=VERSION,
      description=DESCRIPTION,
      long_description=LONG_DESCRIPTION,
      long_description_content_type=LONG_DESC_TYPE,
      author=AUTHOR,
      license=LICENSE,
      author_email=AUTHOR_EMAIL,
      url=URL,
      install_requires=INSTALL_REQUIRES,
      include_package_data=True,
      packages=find_packages(),
      classifiers=CLASSIFIERS
      )


除了编辑个人信息外,有几件事你需要注意:

1.VERSION:每次将一个新版本发布到PyPI(稍后我们将讨论这个问题)时,都必须修改版本号

2.classifiers:https://pypi.org/classifiers/

目的是为了在发布到pypi后便于归类。具体查看官网。所填选项可以直接在官网复制粘贴得到。

3.Install requires:这是您的包的所有外部依赖项的列表。请务必在这里列出所有内容,以便Python将它们与您的包一起安装。

你可以将这些依赖的包写在一个文本文件中:requirements.txt,并在setup.py中读取该文件,并传入setup函数中。这样用户就能快速注意到软件所以来的包了。

如下图所示:

your_package 
|-- setup.py 
|-- requirements.txt
|-- README.md 
|-- LICENSE
 |-- your_package
 |-- __init__.py
 |-- module1.py
 |-- module2.py

以下是一个最简单的requirements.txt,只依赖4个包:

numpy
scipy
matplotlib
pandas

4.include_package_data=True

建议在setup.py中加入include_package_data=True,这样方便后续用户在使用pip install安装时能把一些包内必要的数据也安装上。

2.MANIFEST.in

打包的时候有时候会忽略一些文件,为了将这些被忽略的一并打包,可以添加到MANIFEST.in(放在主目录中),使得在打包时能一并识别。

例子:

include README.md
include CONTRIBUTING.md
include LICENSE
recursive-include licences *

3.打包/本地安装

完成前面的文件配置后,可以进行打包了。我们借助setuptools进行打包。打包后,可以进行本地安装测试,安装的形式有很多种,例如以源码安装,二进制安装等。

我们以源码安装为例:

1.首先,进入项目的主目录下,将软件打包成源码压缩包格式:

python setup.py sdist --formats=zip

假设我们得到了一个名字为xxx.zip的软件源码压缩包。下一步就是进行安装测试。

2.解压缩并安装(确保处在软件解压缩后的主目录下)

unzip xxx.zip

## 利用setup.py

python setup.py install

也可以利用pip直接对压缩包进行本地安装:

## 利用pip(可以直接对压缩包格式进行安装)
pip install xxx.zip

附:其他安装格式

除了源码打包,还有一些其他常用的打包格式:

## 预编译版本(包含pyc文件),会严格记录所用的操作系统,python版本等信息。

## 用户如果不符合条件可能是需要重新编译。
python setup.py -bdist --formats=zip

## 打包成egg格式,用户以egg格式安装软件包
python setup.py -bdist_egg --formats=zip

## 打包成whl格式,用户以wheel格式安装软件包
python setup.py -bdist_wheel --formats=zip

## 打包成windows下的exe格式,以可执行文件安装软件包
python setup.py -bdist_wininst --formats=zip


4.部署到pypi

完成了本地安装测试后,就可以上传到pypi了。

流程如下:

1.到pypi官网注册账号。

2.利用setup.py对包进行打包

python setup.py -sdist --formats=zip

3.将打包好的包利用twine进行上传(会提示输入pypi账户 密码)

pip install twine
twine upload xxx.zip

4.安装测试(从pypi安装)

pip install xxx.zip

5.版本迭代

如果发布的软件包有bug修复或者版本升级,需要将新版本上传到pypi的话,只需要将setup.py中的version进行修改,然后重新上传即可。

## github release 在github主页上发布release版本,具体参考:

https://blog.csdn.net/weixin_41287260/article/details/97617620


上一篇: 负二项分布及其在基因组学中的应用
下一篇: 聊一聊Conditional independence