用正确的姿势开源Python项目

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

目录结构(初始化)

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

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

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

➜  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等格式。

这里稍微介绍下使用:

  • 安装

    $ pip install sphinx
    
  • 初始化

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

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

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

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

Read the Docs 持续文档集成

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

TravisCI 持续集成

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

  • 创建.travis.yml文件

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

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

    下面提供一个例子:

      ```YML
      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作为测试覆盖率的检查,下面是一个运行示例:

➜  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:
#!/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='Jintao Zhang',
    author_email='[email protected]',
    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文件。

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

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

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

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

    [pypi]
    repository = https://pypi.python.org/pypi
    username = your username
    password = your password
    ```
  • 上传package bash $ python setup.py sdist bdist_wheel upload 在上传完成提示200响应的时候,你就上传成功了,去PyPI上看看有没有上传成功的包,并且试着用pip进行安装吧!

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

项目托管在Github上

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

开发相关

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

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

加载评论