用正确的姿势开源Python项目

做个备忘,也希望可以帮到别人。

目录结构(初始化)

一般我们都会选择在项目的顶层包含较基础的文件,比如setup.pyrequirementsREADME等文件。
一般情况下,一个预发布的Python项目中应该包含以下几类文件:

  • projects (项目的主体文件)
  • setup.py
  • requirements
  • Readme (项目说明)
  • docs (项目文档)
  • test

其中,projects文件夹要以项目命名,存放实际的Python Package.
这里放一个我的项目的目录作为例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
➜ httpmultipart git:(master) tree -L 2
.
├── build
│   ├── bdist.linux-x86_64
│   └── lib.linux-x86_64-2.7
├── dist
│   ├── httpmultipart-0.1.0-py2.py3-none-any.whl
│   └── httpmultipart-0.1.0.tar.gz
├── docs
│   ├── _build
│   ├── conf.py
│   ├── index.rst
│   ├── Makefile
│   ├── userguide
│   └── userguide.rst
├── env
│   ├── bin
│   ├── include
│   ├── lib
│   └── local
├── httpmultipart
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── main.py
│   └── main.pyc
├── httpmultipart.egg-info
│   ├── dependency_links.txt
│   ├── not-zip-safe
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   └── top_level.txt
├── LICENSE
├── MANIFEST.in
├── README.rst
├── setup.cfg
├── setup.py
└── test_httpmultipart.py
14 directories, 21 files

这个目录中包含了我的virtual enviroment 和打包构建时候生成的文件夹。
这些东西在下面会提到。

文档

文档的话,并没有严格要求必须使用什么样的工具进行构建,也没有很严格的格式要求。不过我推荐使用SPHINX,它是用Python写的工具,使用了一种叫做reStructuredText的语法编辑,可以对多个文本文件重编,可以输出成HTML或者PDF等格式。

这里稍微介绍下使用:

  • 安装

    1
    $ pip install sphinx
  • 初始化

    1
    2
    3
    $ cd docs
    $ sphinx-quickstart
    ...#(这里有一系列的提问,进行初始化)
  • 编译

    1
    $ make html #(如果按照默认配置生成的Makefile的话,这样就可以编译出html了)

Sphinx的使用还有很多值得说的地方,推荐两个资料:

官方文档写的非常详细,那本书偏向于实战,是很不错的书。
还有很关键的一点是Sphinx有autodocautomodule的扩展,
可以从代码中提取出文档,与代码直接进行关联。提供一个例子可以方便的在文档和源码中进行跳转。

Read the Docs 持续文档集成

说完写文档就不得不提到Read the Dosc了,这是一个第三方的文档托管平台,使用Django开发,它可以很轻松的和Github上的项目进行集成,在每次代码提交的时候会自动进行文档构建,我们可以看看生成文档的效果。现在有很多开源项目的文档都是托管在这里的,具体的配置可以参考官方文档

TravisCI 持续集成

Travisci可以与Github非常好的结合,可以手动导入repo,并设置Webhooks & services,在每次提交的时候都会测试运行来发现是否存在异常。在使用之前,我们需要先做一些初始化的操作:

  • 创建.travis.yml文件

    我们需要告诉它一些基本的信息:

    • 我们项目使用的语言
    • 项目使用的语言版本
    • 环境的依赖
    • 是否需要sudo权限运行
    • 使用什么命令来安装
    • 使用什么命令运行测试

      下面提供一个例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      language: python
      python:
      - 2.7
      env:
      - DEPS=true
      - DEPS=false
      sudo: false
      install:
      - travis_retry python setup.py install
      - travis_retry pip install coveralls
      - coverage run test_httpmultipart.py
      - coverage report
      script:
      - python test_httpmultipart.py
      after_success:
      coveralls

PS: 在install这一项配置中要注意,如果项目存在依赖,一定要进行安装,还有Travisci提供了多项配置,包括出错重试等,具体配置可以参考官方文档,要特别注意travis_retry等命令的使用,非常容易出现坑。

测试

Python有内置的unittest测试库,支持断言;当然还有其他的一些测试框架,看个人喜好和项目需求。在项目中应该包含测试文件或者测试目录,测试文件一般以test_projects_name.py命名。我选择使用coverage作为测试覆盖率的检查,下面是一个运行示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜ httpmultipart git:(master) coverage run test_httpmultipart.py
....
----------------------------------------------------------------------
Ran 4 tests in 36.125s
OK
➜ httpmultipart git:(master) coverage report
Name Stmts Miss Branch BrPart Cover
-------------------------------------------------------------
httpmultipart/__init__.py 10 0 0 0 100%
httpmultipart/main.py 37 0 4 0 100%
-------------------------------------------------------------
TOTAL 47 0 4 0 100%

Coveralls 测试覆盖率检查

Coveralls可以很友好的支持Github和Bitbucket仓库的导入,同时也可以很好的和Travisci集成,在上面我们的.travis.yml文件中,在最后一项after_success中,我们就配置了对Coveralls的支持。

发布包到PyPI

PyPI - the Python Package Index
是Python的包仓库,它允许我们将自己写的包上传上去,这样就可以使用pipeasy_install进行安装了。这里说一个很重要的事情:
请在发布自己项目之前做好测试,确认自己上传的是一个可用,无害的包
当你已经完成上面的步骤之后,那么你只需要以下几步就可以完成了。以我的项目为例子:

  • 配置setup.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env python
import os
import re
import sys
from codecs import open
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
if sys.argv[-1] == 'publish':
os.system('python setup.py sdist upload')
sys.exit()
packages = [
'httpmultipart',
]
requires = []
version = ''
with open('httpmultipart/__init__.py', 'r') as fd:
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
fd.read(), re.MULTILINE).group(1)
if not version:
raise RuntimeError('Cannot find version information')
with open('README.rst', 'r', 'utf-8') as f:
readme = f.read()
setup(
name='httpmultipart',
version=version,
description='A httpmultipart post handler',
long_description=readme + '\n\n',
author='TaoBeier',
author_email='zhangjintao9020@gmail.com',
url='https://github.com/tao12345666333/httpmultipart',
packages=packages,
package_data={'': ['LICENSE']},
package_dir={'httpmultipart': 'httpmultipart'},
include_package_data=True,
install_requires=requires,
license='MIT',
zip_safe=False,
platforms='any',
classifiers=(
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Natural Language :: English',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
),
)

当然大部分时候也不需要这么麻烦,简化的配置可以参考官方的文档。注意最下面那部分classifiers是按照PyPI官方的列表填写的。还有,如果有些文件想要额外打包进去,那么需要在根目录添加MANIFEST.in文件。

  • 打包代码
1
2
$ python setup.py sdist # 生成pip支持的安装包
$ python setup.py bdist_wheel # 生成支持easy_install的安装包
  • 发布到PyPI上

    首先在PyPI注册一个帐号,然后进行下面两步:

    • 注册package
      1
      2
      3
      4
      5
      6
      $ python setup.py register
      $ # 在这一步会有一些提示,按照提示选择,输入自己帐号密码相关的信息即可
      $ # 在提示你要不要保存帐号信息的时候,
      $ # 你选择了‘y’,
      $ # 则会在你的用户目录下生成一个 .pypirc 的文件,
      $ # 其中明文保存着你的帐号和密码。

这里稍微再提一点,如果在提示是否保存账户信息的时候,选择了‘n’,想反悔了也没关系,我们可以手动创建一个~/.pypirc文件。格式如下:

1
2
3
4
5
6
7
[distutils]
index-servers=pypi
[pypi]
repository = https://pypi.python.org/pypi
username = your username
password = your password

  • 上传package
    1
    $ python setup.py sdist bdist_wheel upload

在上传完成提示200响应的时候,你就上传成功了,去PyPI上看看有没有上传成功的包,并且试着用pip进行安装吧!

这里我省略了一步,就是发布前的测试,你可以使用PyPI的测试服务器进行测试发布,发布到这里的包并不会对你正式发布有任何影响。

项目托管在Github上

开源到Github上后,就是继续维护和开发,这点就不多说了。

开发相关

建议使用virtualenv之类的工具构建纯净的环境,重复的动作交给Make之类的工具。

这篇算是一个小小的总结加备忘吧,经验尚缺,还望指正。