python打包发布pypi踩坑记(最新贴心版本,认真对待自己的库)

在之前这个实现json比较的库的帖子里,我把库上传pypi的时候,相当坑。而且pypi有个特点,就是你一旦弄错了,就不能删除原来的名字(但可以删库,也可以更新版本,就是不能替换)。所以上传时应该尽量谨慎。

1. 注册pypi的账号

访问pypi.org并点击register(有部分网络pypi访问速度较差,可能发生上传或下载失败的情况,请换个网络环境,或者自备代理)。可以使用QQ邮箱,它会要求你的邮箱验证。
在这里插入图片描述
如果你想使用pypitest来测试发布,也可以同时注册pypitest。

2. 处理路径。你的项目应该呈现这样的结构:

在这里插入图片描述
其中里层的是你要上传的包。而外层来写setup.py和readme。这两个将在后面介绍。dist是打包发布后自动生成的文件夹,里面有打成的包(python编译exe的同学应该对此不陌生,因为编译exe的输出路径一般也在dist里面),而MANIFEST也是自动生成。所以我们着重要做的就是__init__.py,还有reademe和setup.py这三个文件的编写。

3. 代码规范化。

在打包前应该注意一些事情。

  • 整个代码中,除了if __name__ == "__main__"可以包含打印输出、调试的部分之外,其他地方都应该只有函数、类、全局变量等的定义。导入时不应该出现print等副作用。除非你确实希望如此例外。
  • 在例子中,json_compare是我实现功能的库,而test_json_compare是测试用例。其中__init__.py文件则是python中将一个目录识别为包必不可少的文件,是访问包的入口。在内应导入所有需要暴露的类或函数。为兼容,最好如下图使用相对路径导入。(如果不是一个包,普通目录内这样写IDE会报错)
    在这里插入图片描述
  • 版本兼容。考虑你的代码究竟适合于哪些版本,并小心测试通过。在我的例子中,我支持了python2.x和3.x,所以特意使用six模块进行适配,有些地方需要去针对不同版本做不同的操作。另外,要考虑平台的兼容性。所以每个文件的头两行应该使用标准的方法来写,避免放到Linux的机器上不识别。
#!/usr/bin/env python
# coding: utf-8

在这里插入图片描述
在这里插入图片描述

  • 编写注释和类型提示。尤其是针对对外暴露的方法编写注释。
    使用一对三双引号,是python中函数和类注释文档的规范。函数写完后输入三个",pycharm会自动帮你形成文档骨架。:param表示参数,随后写参数的类型,最后是参数名,比如参数a的类型就是str/unicode/list/tuple/dict中的一种(实际上这显然是针对python2,3里面应该对应bytes,但这样写无妨),而参数ignore_list_seq类型则是bool。return下面也要写明返回值的类型。
    这些类型不是必需的,但是有诸多好处。诸如Pycharm这样的IDE会自动针对返回值和参数提供类型提示和方法联想,比如若你使用了这个函数的返回值变量,那只有在返回值被注释为str类型或者IDE能自己判断出返回类型时,才会给你提供.join等方法的联想。同时,当用户传错了参数类型时,也会被IDE提示警告,从而引起用户警觉。这类动态语言应该非常注意类型的校验,否则容易发生幽灵bug,且代码的可读性,可调试性都会变差。
    在这里插入图片描述

4. 编写readme.md文档

一个项目能做什么,很大程度取决于文档。要想类似下图这样,给你的项目在pypi页面上写个好的介绍,你需要准备项目的介绍文档。 强烈推荐给自己的项目准备文档。 我们这里使用我们熟悉的markdown。但需要注意的是,要么你安装twine且升级pip,配置新的选项,否则pypi不支持markdown,它支持的是rst文件(这个非常坑,如果搞错了格式,它也不会报错,不会有任何警告,就只是文档无法展示而已)。好在markdown和rst可以转换。我们稍后会用代码将其转换。
在这里插入图片描述
例如这上面的文档,上传后会产生如下效果
在这里插入图片描述

5. 准备转换文档的工具

需要安装一个转换文档格式的模块

pip install pypandoc

安装好后,我在其他项目源码中看到过类似的代码,但是总是发现文档无法展示。后来
在这里插入图片描述
去掉了上面的except,查看报错信息,结果发现pypandoc是需要依赖pandoc这个程序的,它只是pandoc的python接口。于是又去安装pandoc。在官网的这个链接可以下载安装程序。https://www.pandoc.org/installing.html安装比较简单。不过装好以后要配置环境变量。将下面图上的路径加入到path里。
在这里插入图片描述
在这里插入图片描述
保存后,重新打开cmd,验证能够通过where找到就算成功
在这里插入图片描述

6. 编写setup.py文件

仍然记得之前的规范(编码、python解释器路径等),示例如下:

#!/usr/bin/env python
# coding: utf-8
from distutils.core import setup
from os import path
this_directory = path.abspath(path.dirname(__file__))

try:
    import pypandoc
    long_description = pypandoc.convert('README.md', 'rst')
except:
    long_description = ""

setup(
    name='jsoncomparedeep',
    version='1.12',
    description='A recursive Json comparing library that handles list orders and fuzzy types',
    author='Rainy Chan',
    author_email='[email protected]',
    url='https://rainydew.blog.csdn.net/article/details/93904318',
    packages=['json_compare'],
    install_requires=['six>=1.12.0'],
    keywords='json compare comparison order unicode fuzzy',
    long_description=long_description,
    python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
)

一些注意点解释如下

  • 使用path.abspath来活化路径,否则用户在pip下载安装你的包时,会被红色字体报错警告(大部分时候仍然可以安装),影响用户体验。
  • long_description这里必须使用try处理。否则用户在pip安装时,也会执行此setup.py。若用户机没有pypandoc,或者虽然有但环境变量里找不到pandoc.exe,则都会报错导致安装失败。这一句话的用途是将markdown文档转换为rst格式文档并上传。
  • name请在https://pypi.org先做搜索。不能和pypi上已有的项目重名,否则会上传失败。不要使用中文名。同时取名请慎重,因为一旦取好将不能再修改,只能取新的名字。这里有个小技巧,就是比如我做的是jsoncompare,但pypi上已经有了同样名字的项目,那么我如果要被搜索到,我应该取jsoncomparedeep这样的后缀,而非看起来更自然的deepjsoncompare。因为pypi会从开头来匹配,输入json compare,可以搜到jsoncomparedeep,却搜不到后者。
  • version可以从0.1开始,也可以是三段的版本号比如0.1.0。当你更新升级了你的库以后,你只能修改version而不能修改名字来上传,否则pip install -U将不起作用。而且version只能越来越大。比如1.12之后的下一个版本只能从1.13开始。要小心的是,一个version一旦传上去了,就不能再修改。你可以删除,但即便删除,这个版本号也不能再使用,只能使用下一个版本号。pypi以此来保障包的一致性,即用户使用pip下载同样名字和同样的版本,不会下载到实际上不相同的包。如果你的包发现了严重bug,则应该尽快删除它并上传新版本。
  • url是你的项目主页路径
  • packages里应该取图上目录中,你的包的文件夹名(和setup.py同级的文件夹)。大的项目有多个文件夹,则列表写多个目录。
  • install_requires这个非常重要,注意了,不是有些文章或者demo里的requires!!里面写你需要依赖的库(和其版本)的情况。只写非官方库。我的这个库引用了json re sys traceback和six,只有用于python2/3兼容的库six是三方库,所以我这里只写了six。最好要求指定版本号。如果你不清楚版本,可以用pip --list命令去查看你当前使用的版本。这一项的目的是能让pip自动处理依赖。比如用户安装我的pip install jsoncomparedeep,且系统发现这个用户还没有安装six库,就会自动一并安装six库,这样用户安装好我的库以后,就不会在import的时候报错。这个地方十分坑,我开始写的是文档里的requires,后来看了flask的源码的setup.py,才发现要这么写才能认得。
  • long_description就是展示在首页的文档。而description会在搜索库的时候就展示,所以应该简练清晰的表明库的功能。例如在这里插入图片描述
  • python_requires可以防止用户使用不兼容的python版本来安装你的库。例如我上面的公式,则是要求用户的python版本为2.6~2.7,或3.5以上。

7. 建立 .pypirc 文件

对于Linux用户,直接在家目录下建立;对于windows用户,在运行-cmd里默认指向的路径下建立。windows系统会弱智到不允许你建立一个以英文.开头的文件。可以运行cmd,并使用这样的命令
在这里插入图片描述
回车后即可建立这个文件,请用notepad++之类的打开(不允许BOM,所以千万不要用记事本去修改)。编辑配置如下并保存。其中username和password写你在pypi和pypitest的用户名和密码。如果你不上传pypitest,下面的可以不要。最好以unix换行符(LF)作为文件的换行标志。
在这里插入图片描述

8. 上传到pypi

确保你的python.exe在环境变量内,否则需要提供python.exe的完整路径。
在setup.py的目录下打开cmd(可以pycharm里setup.py右键-在终端内打开)。然后输入

python setup.py sdist upload -r pypi

并回车,命令行提示200,即上传成功(如果是上传pypitest则命令改为pypitest)。随后请在pypi内查看自己的包,观察首页展示,并测试下载和安装。尤其是推荐在标准版python(没有三方库)的虚拟机等处测试安装。
在我的ubuntu虚拟机上测试安装
在这里插入图片描述
可以看到能够自动安装six模块,意味着能自动处理依赖。
然后在python中尝试导入并执行成功。
在这里插入图片描述
enjoy~

发布了25 篇原创文章 · 获赞 22 · 访问量 9328

猜你喜欢

转载自blog.csdn.net/qq_27884799/article/details/96664812