大多数Python开发者至少都写过一个像工具、脚本、库或框架等对其他人也有用的工具。我写这篇文章的目的是让现有Python代码的开源过程尽可能清晰和无痛。我不是简单的指——“创建一个GitHub库,提交,在Reddit上发布,每天调用它”。在本文的结尾,你可以把现有的代码转换成一个能够鼓励他人使用和贡献的开源项目。
然而每一个项目都是不同的,但其中将现有代码开源的流程对所有的Python项目都是类似的。在另一个受欢迎的文章系列里我写了“以正确方式开始一个Django项目”,我将概述在开源Python项目我发现的有必要的步骤。
更新 (8月17号): 感谢@pydann提醒我Cookiecutter的存在,@audreyr的一个不起的项目。我在文章结尾添加了其中的一段。看一下Audrey的项目吧!
更新 2 (8月18号):感谢@ChristianHeimes(和其他人)关于ontox这一段。Christian也让我想起了PEP 440和其他一些都已实现很棒的改进建议。
特别是,我发现一些工具和概念十分有用或者说是必要的。下面我就会谈及这方面主题,包括需要运行的精确的命令和需要设置的配置值。其终极目标就是让整个流程简单明了。
当准备一个项目时,正确合理的布局(目录结构)是十分重要的。一个合理的布局意味着想参与开发者不必花时间来寻找某些代码的位置; 凭直觉就可以找到文件的位置。因为我们在处理一个项目,就意味着可能需要到处移动一些东西。
让我们从顶层开始。大多数项目都有很多顶层文件(如setup.py, README.md, requirements等等)。每个项目至少应该有下面三个目录:
为了更好理解文件该如何组织,这里是一个我的简单项目:sandman 布局快照。
$ pwd ~/code/sandman $ tree . |- LICENSE |- README.md |- TODO.md |- docs | |-- conf.py | |-- generated | |-- index.rst | |-- installation.rst | |-- modules.rst | |-- quickstart.rst | |-- sandman.rst |- requirements.txt |- sandman | |-- __init__.py | |-- exception.py | |-- model.py | |-- sandman.py | |-- test | |-- models.py | |-- test_sandman.py |- setup.py
如你所看到那样,这里有一些顶层文件,一个docs目录(建立一个空目录,因为sphinx会将生成的文档放到这里),一个sandman目录,以及一个在sandman目录下的test目录。
setup.py文件,你可能已经在其它包中看到过,被distuils包用来安装Python包的。对于任何一个项目,它都是一个很重要的文件,因为它包含了版本,包依赖信息,PyPi需要的项目描述,你的名字和联系信息,以及其它一些信息。它允许以编程的方式搜索安装包,提供元数据和指令说明让工具如何做。
setuptools包(实际上就是对distutils的增强)简单化了建立发布python包。使用setuptools给python包打包,和distutils打包没什么区别。这实在是没有任何理由不使用它。
setup.py应该放在你的项目的根目录。setup.py中最重要的一部分就是调用setuptools.setup,这里面包含了此包所需的所有元信息。这里就是sandman的setup.py的所有内容
from __future__ import print_function from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand import io import codecs import os import sys import sandman here = os.path.abspath(os.path.dirname(__file__)) def read(*filenames, **kwargs): encoding = kwargs.get('encoding', 'utf-8') sep = kwargs.get('sep', '\n') buf = [] for filename in filenames: with io.open(filename, encoding=encoding) as f: buf.append(f.read()) return sep.join(buf) long_description = read('README.txt', 'CHANGES.txt') class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): import pytest errcode = pytest.main(self.test_args) sys.exit(errcode) setup( name='sandman', version=sandman.__version__, url='http://github.com/jeffknupp/sandman/', license='Apache Software License', author='Jeff Knupp', tests_require=['pytest'], install_requires=['Flask>=0.10.1', 'Flask-SQLAlchemy>=1.0', 'SQLAlchemy==0.8.2', ], cmdclass={'test': PyTest}, author_email='jeff@jeffknupp.com', description='Automated REST APIs for existing database-driven systems', long_description=long_description, packages=['sandman'], include_package_data=True, platforms='any', test_suite='sandman.test.test_sandman', classifiers = [ 'Programming Language :: Python', 'Development Status :: 4 - Beta', 'Natural Language :: English', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', ], extras_require={ 'testing': ['pytest'], } )
(感谢Christian Heimes的建议让setup.py更符合人们的语言习惯。反过来,也让我借用其它的项目一目了然了。)
大多数内容浅显易懂,可以从setuptools文档查看到,所以我只会触及"有趣"的部分。使用sandman.__version__和gettinglong_description方法(尽管我也记不住是哪一个,但是却可以从其它项目的setup.py中获得)来减少我们需要写的引用代码。相反,维护项目的版本有三个地方(setup.py, 包自身的__version__, 以及文档),我们也可以使用包的version来填充setup里面的version参数
有一些sandman 用不到的启动参数,在你的包里可能会用到。举个例子,你可能正在分派一些脚本并希望你的用户能够从命令行执行。在这个例子中,脚本会和你其他的代码一起安装在正常的site-packages位置。用户安装完后,没有其他的简单方法运行它。基于这一点,setup可以带有一个的脚本参数来指明Python脚本应该如何安装。在包中安装一个调用go_foo.py的脚本,这个用来启动的调用包括下面这行:
scripts = ['go_foo.py'],确保在脚本中填入相对路径,并不仅仅是一个名称 (如scripts = ['scripts/foo_scripts/go_foo.py']).同样,你的脚本应该以"shebang"行和"python"开始,如下:
#! /usr/bin/env python
distutils将会在安装过程中自动用当前解释器位置取代这一行。
如果你的包比我们这里讨论的要复杂,你可在官方文档中参看启动工具文档和分布python模块。
在这两者中,你可以解决一些你可能会遇到的问题。
在“以正确的方式开始一个Django项目”中,我建议版本控制使用git 或者 mercurial。如果对于以共享与贡献的项目来说,只有一个选择:git。事实上,从长远来说,如果你想人们能使用和参与贡献,那么不仅使用git很有必要,而且,你也能够使用GitHub来管理维护你的项目。
这并不是夸大其词(尽管很多人会以它为嚼头)。然而,管它好与差,git和GitHub事实上已经成为了开源项目的实际标准了。GitHub是很多潜在的贡献者最想注册的和最熟悉的。所以,我深信,这并不是掉以轻心,而是深思熟虑的产物。
新建一个README.md文件
在GitHub的代码仓库中,项目的描述是从项目的根目录中的:README.md文件获取的。这个文件应该包含下面几点:
它(README)读起来很傻的感觉,但是确是一个很重要的文件。它可能是你未来的用户或者贡献者首先从它了解你的项目的。花些时间来写一个清楚明白的说明和使用GFM(GitHubFlavoredMarkdown)来使它更好看。实际上,如果使用原生的Markdown来写文档不爽,那么可以在Github上使用立即预览来创建或者修改这个文件
我们还没触及列表中的第二和第三项(ReadTheDocs和TravisCI),你会在接下来看到。
跟生活中的很多事情一样,你投入GitHub越多,你收获的越多。因为用户会使用GitHub的“Issues”页面反馈bug,使用该页面跟踪特性要求和改进是很有意义的。
更重要的是,它允许贡献者以一种优雅的方式看到:一个可能实现特性的列表以及自动化的管理合并请求流程(pull request)。GitHub的issues可以与评论、你项目里的其他issues及其他项目里的issues等交织,这使得“issues”页面成为一个有关所有bug修复、改进和新特性要求信息汇总的地方。
确保“Issues”及时更新,至少及时回应新的问题。作为一个贡献者,没有什么比修复bug后看着它呈现在issues页面并等待着被合并更有吸引力的了。
评论删除后,数据将无法恢复
评论(8)
支持多种测试风格:
- 单元测试
- 文档测试
- nose测试
原文是
Support for multiple testing styles
- unittest
- doctest
- nose tests
在这里建议unittest, doctest和nose tests无需翻译,应为这是几个不一样的测试框架名称,硬是翻译了反而让人无法理解。
引用来自“iamxi”的评论
以正确方式开始一个Django项目 ..有翻译吗,求