在这里编辑"PyTwisted/WorkingOnTheTwistedCodeBase/CodingStandard".
【Twisted编码标准】
- 命名
- 测试
- 空白缩进
- 模块
- 包
- doc文档
- 脚本
- 标准库扩展模块
- 变更日志
- class
- method
- 回调参数
- 特殊方法
- function
- 属性
- 数据库
- c代码
- checkin信息
- 建议
【命名】
- 应该选择一个容易记又有具体含义的名字。模块的命名可以随意一些(例如 twisted.spread ...),但是类命名必须十分清晰明了。写代码的时候请保证手边就有字典或是辞典,方便随时查看。 应该避免用一个术语表示多种含义。要避免这个问题比想象中难的多,因为大多数常用的单词已经被许多其它软件用过了,所以这个规则经常被破坏。更重要的是,应该避免使用那些没有任何意义的单词。特别是像handler、processor、engine、manager和component这些词,这些词只能说明某个东西确实能做一些事情,但是并不能真的表述出这个东西的实际功能, 在命名和python的doc字串请使用美式拼写方法。例如,对于像filesystem这样的合成技术名词,在编码和doc字串中都应该使用没有连字符的拼写方式,以避免不必要的单词首字母大写问题。
【测试】
- 单元测试应该使用twisted.trial框架编写。twisted.test包中有许多例子可以参考。测试模块的名称应该以“test_”开头。每个源码文件中都应该有一个“test-case-name”标签来指定与之相关的测试代码。验收测试(acceptance test)现在全部由admin/accepttests中的脚本自动完成。(TODO: 真正的验收测试策略) 在你checkin任何修改之前,请运行单元测试来测试你的代码。 再次重申,在你checkin任何修改之前,请运行单元测试来测试你代码。由于验收测试通常是不可移植的,并且在某些情况下验收测试自身的运行也会出错,所以出现破坏功能约束的代码这种事儿也是不可避免的,我们也能理解,也可以原谅的。但如果不进行验收测试就提交代码,并造成了对原有功能的损害,就可能会导致从“大家对你的代码所造成的破坏的无情嘲弄”到“取消你在cvs上的commit权限”等各种后果。
强烈建议开发者学会使用Emacs,并在Emacs中把TwistedEmacs包中的twisted-dev.el文件与F9绑定在一起用来运行单元测试。其它编辑器中现在还没有做类似的支持,但是如果您确实有需要,我们很乐意提供给你。 如果你修改或是撰写了一个新的HOWTO文档,请参考Lore documentation来学习文档的编写格式。
【空白缩进】
- 每个缩进的大小为4个空格键,不允许使用Tab键。强烈建议为每个代码段另起一行,这使代码中的控制结构的缩进更清晰。
【模块】
- 模块的名称全部小写,并尽可短,最好是一个单词。如果模块的名称包含多个单词,可以使用下划线分隔或是直接将几个单词连起来写。 大多数情况下,模块应该包含多个类,函数或是方法。如果一个模块只包含一个对象,可以考虑通过代码重构将一些相关的功能包含的该模块中。 根据具体情况,下面的几种import方式都是可以的: from twisted.internet.defer import Deferred
- 或是:
- 也就是说,可以通过from ... import语句来导入module、class和function,但是不能通过from ... import语句导入一个package。
【包】
- 包的命名规则与模块大体相同。所有的模块都要封装在某包中。嵌套的包可以用来进一步组织相关的模块。
init.py文件中除了doc信息和一个可选的all属性之外,不能包含任何其它的东西。包和模块是不同的东西,应该区别对待。但是在代码重构中,如果一个模块被用来构造一个嵌套包中的一部分时,为了保证向后兼容,则不得不破坏这个规则。 如果你想把一个模块升级为一个包(例如:为了将一个很大的模块分隔为多个较小的文件),使用“导入模块”进行升级是一个较好的方法,下面是一个例子:
- # parent/
# --- init.py --- import child # --- child.py --- import parent class Foo:
- pass
- # parent/
- 每个包都应该放到setup.py的列表中。 多个包之间不可以有循环依赖。为了简化维护工作,两个不同的包之间不应该同时进行相互导入。在Twisted中,所有的包都遵循上述约定。应该特别注意一个名为twisted.python的包,要保证这个包不能与Twisted中任何一个其它的包产生依赖关系。
【doc文档】
只要条件允许,就应该使用doc文档来描述python中的方法、函数、类和模块的功能信息。如果某些时候需要完全避免文档描述信息(例如,一个还在开发阶段的接口),可以插入一个doc文档的占位符(例如:“UNDOCUMENT”就不错,_)。这样就不会错误地将其它无用注释作为该方法、函数、类或是模块的doc文档信息,包含到那些自动产生的API文档中。 doc文档从不用来提供一个对象语义信息。如果这些代码将用于一个确实有这样需求的系统中(例如:Zope),可能会违反这个规则。 doc文档应该与它所描述的代码具有相同的缩进级别。 doc文档应该用三个双引号开头。 doc文档用epytext格式书写,更多的信息请参考Epytext Markup Language document。 此外,建议Emacs的用户:
- doc文档中的单引号应该使用其转义形式书写,以防止Emacs将文件中的其它部分误认为是一个String
- doc文档中的代码示例应该用|开头,以防止IM-Python将doc文档中的示例代码当作真正的代码
- def foo2bar(f):
- """这个函数用来将foo转换为bar。 当你有一个foo,但是你想要一个bar的时候,可以使用这个函数; 注意这是一个没有破坏性的操作。如果无法(can\'t)将foo转换为bar,
就会产生一个FooException异常。 例如: | import wombat | def sample(something): | f = something.getFoo() | f.doFooThing() | b = wombat.foo2bar() | b.doBarThing() | return b """ # 实际的代码从这里开始
- """这个函数用来将foo转换为bar。 当你有一个foo,但是你想要一个bar的时候,可以使用这个函数; 注意这是一个没有破坏性的操作。如果无法(can\'t)将foo转换为bar,
【脚本】
- 对于你想让一个Twisted用户通过命令行来执行的每个脚本,都必须做下面的事儿:
- 在twisted.scripts中编写一个模块,其中包含一个名为run的可调用的全局函数。这个函数会通过不带参数的命令行方式被调用(通常使用sys.argv读取)。如果你认为对其他人有用处,可以在自己的模块中随意添加函数和类供别人使用。
- 在bin/目录下编写一个文件,其中包含Twisted的running-from-CVS的头部,并以下面的语句结尾。
- from twisted.scripts.yourmodule import run
- run()
- 在doc/man中编写对应的manpage。在debian系统中,你可以找到一个manpage的结构示例,它保存在/usr/share/doc/man-db/examples/manpage.example中。
- 把你的脚本添加到setup.py中的脚本列表中。
【标准库扩展模块】
- 当使用一个Python中已有模块的新的扩展版本时,请将import语句放到try/except块中,当import扩展模块失败时,可以import原有Python中的对应模块。这样你的代码就能在没有扩展库支持的平台上维持运行了。例如:
- try:
- import cPickle as pickle
except ImportError:
- import pickle
- try:
【变更日志】
- 所有能够影响最终用户使用Twisted方式的变更都要以一个恰当的方式记录在变更日志中,并记录所有的影响。 变更日志的正确格式是GNU的changelog格式。Emacs有个专门的模式用来编辑GNU changelog格式的文件,可以使用M-x来增加一个变更日志项。无论处于何种荒谬可笑的理由,如果你正在使用一个Emacs以外的编辑器编辑Twisted代码,你可以使用Moshe Zadka提供的admin/change脚本来添加一个符合格式要求的日志项。
【类】
- 类的命名使用一种混和的拼写方式,类名的首字母要大写,类名中的每个单词的首字母也要大写,用以区分多个单词。如果类名是一些单词的首字母缩写,则所有字母都要大写。类名不需要使用其所在模块的名称作为类名前缀。下面给出了几个符合上述规范的类名示例:
twisted.spread.pb.ViewPoint twisted.parser.patterns.Pattern 下面给出几个不符合上述规范的类名示例: event.EventHandler main.MainGadget 应该尽量避免不同模块中的类名之间相互冲突,以减少导入时刻的类名资格检查。例如,一个用于论坛的服务子类可能叫做twisted.forum.service.ForumService,而一个用于word的服务子类可能叫做twisted.words.service.wordsService。这样两个模块中的类名就可以直接导入到用户的命名空间中使用,而不会产生命名冲突,。
【方法】
- 方法的命名使用一种混和的拼写方式,方法名的首字母要小写,方法名中除方法名首字母外的每个单词的首字母要大写,用以区分多个单词。例如:someMethodName、method。 有时候,一个类中会给方法起一些专门的名称来做特殊用途,例如:twisted.reflect.Accessor。这种情况下,方法名会有一个小写的单词加上一个下划线作为前缀,于是方法名中就包含了一个下划线,例如:get_someAttribute。因此在Twisted中,带有下划线的方法名可能会具有某些特殊的语义含义。 还有一些方法,典型的就是addCallback及多个相关的函数族,都会返回函数本身,用以支持链式函数调用。这种情况下,请把整个函数调用链用括号括起来,并为每个单独的调用另起一行。例如:
- return (foo()
- addCallback(bar)
- addCallback(thud)
- addCallback(wozers))
- return (foo()
【回调参数】
- Twisted中有许多方法,它们的功能就是帮助用户建立回调函数,例如:Deferred.addCallback方法或是reactor的callLater方法。为了作到尽量透明的访问回调函数,大多数的这类方法都会使用**kwargs来扑捉传递给回调函数的所有参数。这使得“建立回调函数”的函数调用看上去非常像是最终要被真正调用的“目标回调函数”。
在这类方法中,要小心防止其它的参数名称覆盖了用户回调函数中的参数。为了保险起见,可以将内部参数的名称前面加上一个下划线作为前缀。例如:RemoteReference.callRemote会被这样调用:
- myref.callRemote("addUser", "bob", "555-1212")
- def addUser(name, phone)
- ..
- myref.callRemote("addUser", name="bob", phone="555-1212")
- def callRemote(self, _name, *args, **kwargs)
- ..
【特殊方法】
在iadd或是其它类似的具名方法中定义的扩展的赋值协议,允许对象做适当的(in place)修改,或是为immutable对象重新绑定名称。上述两种操作都使用相同的操作符。这会导致代码混乱,语义不清,进而导致错误代码的产生。由于这个原因,不应该在Twisted中使用扩展的赋值协议。
【函数】
- 函数的命名规则与方法的命名规则基本相同。 用于相应事件,并完成callback或是errback调用的函数或是方法应该命名为_cbMethodName或是_ebMethodName,用以和一般的函数或方法相区别。
【属性】
- 属性的命名规则与函数和方法类似。属性的名称应该是描述式的。通常来说,将属性命名为mode、type或是buf并不能明确说明问题。因此,最好使用displayMode、playerType或是inputBuffer这样的名称来命名属性。 不要使用Python中的私有属性语法,而应该在非共有属性前加上单下划线作为前缀。因为Twisted中有很多同名的类,因此只能通过他们所在的包来区分他们,所以在某些情况下Python的双下划线name-mangle机制并不能可靠。而且在单元测试和类的持久化操作时,通过name-mangle获得的私有变量也难于定位。 当下面的一个或多个条件成立时,一个属性(函数、方法或是类)应该被认为是私有的:
- 属性用来表示某种中间状态,但该属性并不会时刻保持最新状态
- 参考该属性的内容或是维护该属性的一个引用可能会导致资源泄漏
- 为该属性赋值可能会破坏某些内部假设
- 该属性是一个known-to-be-sub-optimal接口的一部分,并注定会从今后的版本中去掉。
【数据库】
- 数据库的表会使用名词的复数形式命名。 数据库的列会使用下划线分隔多个单词,全部列名小写,因为大多数的数据库对列名是不区分大小写的。 任何直接对应于数据库的一个列的属性、方法参数、或是方法名称都应该使用与列相同的命名方式。这种情况下,无需考虑其它的命名方式。 所有的SQL关键字都应该大写。
【C代码】
- 无论什么情况下,使用C代码来实现都是一种可选的方案,而Python的实现才是默认的,两者应该相互配合。