本篇文章来详细讲一下将自己的代码发布到pypi。发布到pypi的好处有:

  1. 其他人可以方便的通过pip install xxx来使用你的软件包;
  2. 在自己的项目中,也可以简化代码的组织,且不会造成路径问题的困扰,通过pip install xxx的软件包会放在site-packages文件夹中,可以在整个python的环境中直接导入,如果是单个的文件而没有发布到pypi上,这时候如果路径有问题的话,会报路径错误,最常见的错误就是我们在pycharm里面写程序的时候,能够完美的运行,但是在终端或者vscode里面执行的时候,就会报路径错误,这是因为pycharm帮我们添加了PYTHONPATH的管理。但是在终端和vscode中,需要我们自己去管理相对路径、绝对路径问题,所以会遇到很多的路径相关的处理。这无疑会给开发带来一定的困扰,那么我们就会想把编写、测试成功的代码发布到pypi上,直接安装到系统的python路径中,这样就不会因为相对路径、绝对路径问题带来开发上的困扰了。

在这里插入图片描述

将自己的代码发布到pypi,我们需要遵循一定的结构。
在这里插入图片描述
从上往下来说,
LICENSE是许可证文件,我们常用的Apache或者GNU都可以;
README.md是说明文件;
build文件夹是编译生成的,我们在编写代码的时候不需要;
dist文件夹是分发文件夹,里面的文件是被上传到pypi的文件;
easy_logx文件夹是主文件夹,里面是我们的代码主文件;
easy_logx.egg-info是便宜生成的额外文件;
setup.py是安装这个主文件的指导文件;

下面我们来演示整个过程:

  1. 为了我们代码方便管理和迭代,我们将源码放在GitHub上管理,所以我们去我们的GitHub新建一个仓库,名字随意,那么我就新建为 rlenvs 。(注:图片上显示的仓库为有一次推送后的截图)
    在这里插入图片描述
  2. 我们将仓库clone到本地,然后依次编写每个文件。
  3. 我们先按照pypi的要求,也就是上面说的目录结构新建一些目录和文件
  4. 新建一个drlenvs文件,用来存放主文件,注意这不是一个普通的文件夹,二是一个python package,因为文件夹中含有一个init.py 文件
  5. 在drlenvs文件夹里面,我们将主文件代码放进去,我的这个就是kuka_visual_reach.py文件,关于文件的具体内容,可访问 https://github.com/borninfreedom/rlenvs
    在这里插入图片描述
  6. 我们再在drlenvs文件夹中新建一个init.py文件,里面可以不用写任何内容,只是为了标识这是一个python package,而不是普通的文件夹,python package可以由python寻找到。
  7. 我们再编写setup.py文件,setup.py文件是一个setuptools的构建脚本,其中包含了项目和代码文件的信息,这是一个基础版的setup.py文件,是可以正常工作的,setup.py文件也可以写的很复杂,
import setuptools

setuptools.setup(
    name="drlenvs",
    version="0.0.1",
    author="bug404",
    author_email="z19040042@s.upc.edu.cn",
    description="Some environments for reinforcement learning.",
    long_description="Some environments for reinforcement learning.",
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
)

setup.py的参数信息如下:

  • name:项目的名称
  • version:项目的版本
  • author,author_email:作者和作者邮件
  • description:项目的简单介绍
  • long_description:项目的详细介绍
  • long_description_content_type:表示用哪种方式显示long_description,一般是 md 方式
  • url:项目的主页地址
  • packages:项目中包含的子包,find_packages() 是自动发现根目录中的所有的子包。
  • classifiers:其他信息,这里写了使用 Python3,MIT License许可证,不依赖操作系统。
  1. 文件都编写好了,下面我们就编译源码,将编译好的文件推送到pypi以及将源码推送到GitHub
  2. 在编译之前,我们需要setuptools和wheel模块,首先安装他们
    pip install setuptools wheel
    
  3. 然后执行编译和上传(在setup.py同级目录下)

    python setup.py bdist_wheel
    twine upload dist/*
    

    在上传的时候,需要我们设置pypi的token,按照步骤设置一下就行。

  4. 然后我们将源码上传到GitHub,使用git GUI、tertoiseGit或者命令行,自己喜欢的方式就可

上传完成之后,我们测试一下效果。

  1. 我们在终端执行 pip install drlenvs
  2. 然后编写如下代码测试(代码这样编写的原因是这份代码的目的就是创建一个强化学日系的环境,这也是标准的强化学习环境测试代码)
from drlenvs.kuka_visual_reach import KukaVisualReachEnv

if __name__ == '__main__':
    env=KukaVisualReachEnv(is_render=True,skip=1)
    obs=env.reset()
    for _ in range(100):
       action=env.action_space.sample()

发现是可以正常工作的。

OK,以上就是将代码发布到pypi的完整过程。


UPDATE 2021/11/1,对于项目中有资源文件,比如声音文件、图像文件等的怎么办呢?

在这里插入图片描述

对于这类项目,我们需要添加一个MANIFEST.in文件,这里面说明了我们要添加的资源文件,同时我们在setup.py文件中需要添加一些额外的说明。

import setuptools

setuptools.setup(
    name="xinqing",
    version="0.0.5",
    author="heihei",
    author_email="heihei@hh.com",
    description="A class describes our mood.",
    long_description="A class describes our mood.",
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(include=["xinqing","xinqing.*"]),
    install_requires=['python-vlc'],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>3',
    include_package_data=True,
    data_files=[('xinqing',['xinqing/typing_long.mp3'])],
)

这就是如果有资源文件的话,setup.py里面该怎么写。

  1. packages=setuptools.find_packages(include=["xinqing","xinqing.*"])这个指明,我不仅想要xinqing这个文件夹的内容,我还要文件夹下所有的内容,包括资源文件。
  2. install_requires=['python-vlc']指明我们需要的第三方包,这里我要做一个打字机的音效,所以得用到第三方包VLC来播放音效,当用户pip install xinqing的时候,python-vlc也会一同安装。
  3. include_package_data=True表明我们需要所有的数据文件,包括资源文件。
  4. data_files=[('xinqing',['xinqing/typing_long.mp3'])],这个也是最重要的一句,就是要我们添加额外的文件,比如图片、音频等,这个在setup的文档里有写,可以仔细阅读一下。https://docs.python.org/3/distutils/setupscript.html#installing-package-data

我们在MANIFEST.in里面,就可以写明需要的资源文件了。
从我的目录结构中可以看出,我需要里面的两个mp3文件,所以我就可以这么写

recursive-include xinqing/ *.mp3

同时还要修改的是主文件中资源的路径问题,需要使用项目中文件的绝对路径。
在这里插入图片描述
我在xinqing.py中需要使用typing_long.mp3文件,但是我不能直接写
vlc.MediaPlayer('typing_long.mp3'),这样发布到pypi上之后,会报路径错误,正确的做法是使用项目的绝对路径,也就是这样写

cur_file_path = os.path.abspath(__file__)
cur_file_folder = os.path.dirname(cur_file_path)
mp3_folder = cur_file_folder
self.sound = vlc.MediaPlayer(os.path.join(mp3_folder,'typing_long.mp3'))

然后我们还可以做一些优化。
如果按照现在的目录结构,如果我们想导入xinqing.py,而且使用xinqing.py里面的类的话,需要这样写代码:

from xinqing.xinqing import Xinqing

非常的冗余,那么优化方法就是使用xinqing文件夹下的__init__.py文件。
我们在__init__.py里面写

from .xinqing import Xinqing

这样就通过__init__.py文件将Xinqing class暴漏出来了,后面我们通过pip install xinqing之后,调用方法就简单多了

from xinqing import Xinqing

就可以了。
然后就可以按照上面的发布指令,发布到pypi了。注意每次发布,都要修改一下版本号。