Zope3宝典/Zope3开发过程
Chapter 5:The Zope 3 Development Process(第 5 章:Zope3 开发过程)
原文出处:The Zope 3 Developers Book - An Introduction for Python Programmers
原文作者:StephanRichter, zope.org
授权许可:创作共用协议
翻译人员:
- --
校对人员:Leal, FireHare
适用版本:Zope 3
文章状态:校正阶段
Difficulty(难度)
Newcomer(新手)
Skills(技能)
- Be familiar with Python’s general coding style.
Problem/Task(问题/任务)
The simple question this chapter tries to answer is: “How can I participate in the Zope 3 development?” However, the Zope 3 developers strongly encourage 3rd party package developers to adopt the same high standard of code quality and stability that was used for Zope 3 itself.
这一章节尝试解答这样一个简单的问题:“我怎样才能参与到Zope3的开发中呢?”无论如何,Zope3的开发者们强烈鼓励第三方软件包的开发者采用与Zope3本身相同的具有高质量和高稳定性的代码进行开发。
Solution(解决方案)
Since Zope 3 was developed from scratch, there was much opportunity; not only in the sense of the software, but also in terms of processes, organization and everything else around a software project.
Very early on, it was decided that the Zope 3 project would provide a great opportunity to implement some of the methods suggested by the eXtreme Programming development paradigm. The concept of “sprints” was introduced in the Zope development community, which are designed to boost the development and introduce the new framework to community members. Other changes include the establishment of a style guide and a unique development process. It is the latter that I want to discuss in this chapter, since it is most important to the actual developer.
5.1 From an Idea to the Implementation
When you have a big software project, it is necessary to have some sort of (formal) process that controls the development. Such a process stops developers from hacking and checking in half-baked code, which requires a lot of discipline among free software developers, since there is often no compensation. With the start of the Zope 3 development, we tried to implement a flexible process that can be adjusted to the task at hand.
Once a developer has an idea about a desired feature, he usually presents the idea on the Zope 3 developers mailing list or on IRC. Here the developer can figure out whether his feature already exists or whether the task the feature seeks to complete can be accomplished otherwise. If the idea is a good one, then one of the core developers usually suggests to write a formal proposal that will be available in the Zope 3 Proposals wiki. Once the developer has written a proposal, s/he announces it to the mailing list for discussion. Comments to the proposals are usually given via E-mail or directly as comments on the wiki page. The discussion often requires changes to be made to the proposal by adjusting the design or thinking of further use-cases. Once a draft is approved by the other developers - silence is consent, though Jim Fulton normally likes to have the last say - it can be implemented. While we tried hard not to make the proposal writer the implementor, it works out that the proposal writer almost always has to implement the proposal.
But how can you get the developer “status” for Zope 3? Besides the community acceptance, there are a couple formal steps one must take to receive checkin rights. The first step is to sign the “Zope Contributor Agreement” , which can be found at http://dev.zope.org/CVS/Contributor.pdf. Once you have signed and sent this document to Zope Corporation, you can deposit your SSH key online for secure access. All this is described in very much detail on the http://dev.zope.org/CVS/ wiki pages.
As developer you usually check in new features (components) or bug fixes. Both processes are slightly different, since a bug fix is much less formal.
5.1.1 Implementing new Components
Let’s say you want to implement a new component and integrate it into Zope 3. For the development of new software eXtreme Programming provides a nice method, which we adopted for Zope 3 and is outlined in the following paragraphs.
Starting with your proposal, you develop the interface(s) for your component. Often you will have written the interfaces already in the proposal, since it helps explaining the functionality. If not, use the text of the proposal for documenting your interfaces and its methods. For a detailed introduction to interfaces see the next chapter, which covers the formal aspects of writing interfaces.
Next, one should write the tests that check the implementation and the interfaces themselves. When testing against interfaces, one can use so-called stub-implementations of the interfaces. See chapter “??”. If you are not certain about implementation, use prototype code which is thrown away afterwards. Note: I found that this step usually requires the most understanding of Zope 3, so that most of the learning process happens at this point. However, it is also the point were most new developers get frustrated. See the “??” part in general for an extended discussion about tests.
Now, you are ready to write the implementation of the interfaces. There is a nice little tool called pyskel.py (found in the ZOPE3/utilities folder) that will help you to get a skeleton for the class implementing a specified interface. You can get hands-on experience in the “??” part.
The final step is to run the tests. Start out by running the new tests only and, after everything passes, all of the Zope 3 tests as confirmation. Once all of Zope 3’s tests pass, you can check-in the code. It is a good idea to ask the other developers to update their sandboxes and review your code and functionality.
5.1.2 Fixing a bug
In order to effectively fix bugs, you need to have supporter status for the Zope 3 Bug and Feature Collector, so that you can change the status of the various issues you are trying to solve. You can also ask an existing supporter of course, though this is much more cumbersome. Contact the Zope 3 mailing list ( [email protected]) to get this status and they will help you.
Once you have access, accept an issue by assigning it to you in the Collector. At this point noone else will claim the issue anymore. The first step is to create tests that clearly point out the bug and fail due to the bug. Now try to fix the bug. While fixing you might discover other untested functionality and side-effects, so it is common to write more tests during the “fixing” process.
Finally, similar to the development of new components, you should run the new/local tests first, see whether they pass and then run the global tests. It sometimes happens that you will not be able to solve a bug, since tests of other packages will fail that you do not understand. At that stage, you should create a branch and ask other developers for help. Once you are done with the code and all tests pass, check in the changes and ask people to have a look. Once you are more experienced, a code review will not be necessary anymore.
5.2 Zope 3 Naming Rules
Zope 2 was a big mess when it comes to the naming of classes, methods and files. It was almost impossible to find a method in the API. I was always very impressed by the well-organized and consistent Java API, where you often just “guess” a method name without looking it up. Therefore the Zope 3 developers introduced a strict naming style guide that is maintained as part of the Zope 3 development wiki. In retrospect the guide brought us a lot of cleanness to the code base and made it much easier to remember Zope 3’s APIs.
In the following sub-sections I will give you an overview of these conventions. See http://dev.zope.org/Zope3/CodingStyle for the detailed and up-to-date guide.
5.2.1 Directory Hierarchy Conventions
First of all, package and module names are all lowercase. They should be also kept short, so that two consecutive words should be rare. If they appear, just put them together; use no underscore.
The top-level directories are considered packages, like zodb or zope.i18n for example. There is a special package called zope.app that contains the Zope 3 application server. It is special because it contains application server specific sub-packages, which can be distributed separately. Each distribution package contains an interfaces module (depending on your needs it can be implemented as a file-based module or as package). This module should capture all externally available interfaces. Local interfaces that will be only implemented once can live in the same module with its implementation.
Usually, presentation-specific code lives in a separate module in the package; therefore you will often see a browser directory or browser.py file in the package. If the package only defines a few small views a file is preferred, as it is usually the case for XML-RPC views, since you usually have only a couple of classes for the XML-RPC support.
There are no rules on where a third party package must be placed as long as it is found in the Python path. Some developers prefer to place additional packages into ZOPE3/src and make them top-level packages. Others place their packages in ZOPE3/src/zope/app to signalize the package’s dependency on the Zope application server.
5.2.2 Python Naming and Formatting Conventions
While the generic Python style guide allows you to either use spaces or tabs as indentation, we only use four spaces in Zope for indentation, since otherwise the code base might be corrupted, which can cause a lot of grief.
For method and variable naming we use exclusively Camel case naming (same as Java) throughout the public API. Private attribute names can contain underscores. For other usages of the underscore character ( _), see PEP 8, the Python style guide. The underscore character is mainly used in method and variable names to signify private and protected attributes and methods (including built-in ones, like init). Classes always start with upper case with exception of ZCML directives, which is a special case. Attribute and method names always begin with a lower letter, whereby a method should always start with a verb. Here is an example of these rules:
#!python class SimpleExample: def <u>init</u>(self, text): self.text = text def getText(self): return self.text
Additionally, for legal reasons and to protect the developers’ rights, the Zope 3 community requires a file header in every Python file. Excluded are empty init.py files. Here is the standard ZPL file header:
1 ############################################################################## 2 # 3 # Copyright (c) 2004 Zope Corporation and Contributors. 4 # All Rights Reserved. 5 # 6 # This software is subject to the provisions of the Zope Public License, 7 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. 8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED 9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 11 # FOR A PARTICULAR PURPOSE. 12 # 13 ############################################################################## 14 """A one line description of the content of the module. 15 16 If necessary, one can write a longer description here. This is also a 17 good place to include usage notes and requirements. 18 19 $Id$ 20 """
- Line 3: The copyright notice should always contain all years this file has been worked on. At the beginning of each year we use a script to add the new year to the file header.
- Line 1-13: Only use this header, if you are the only author of the code or you have the permission of the original author to use this code and publish it under the ZPL 2.1. Just to be on the safe side, always ask Jim Fulton about checking in code that was not developed by yourself. Of course, you must be yourself willing to publish your code under the ZPL. Note that you do *not* need to sign a contributor agreement to add this header, unless you want to add the code in the zope.org source repository. Also, the ZPL 2.1 does not automatically make Zope Corporation a copyright owner of the code as well, as it was the code for ZPL 2.0.
- Line 19: This is a place holder for the source code repository to insert file revision information, which can be extremely useful.
- Line 14-20: The module documentation string should always be in a file. The first line of the doc string is a short description of the module. Next, an empty line is inserted after which you can give more detailed documentation of the module. For example, in executable files, you usually store all of the help in this doc string.
In general, you should document your code well, so that others can quickly understand what it does. Feel free to refer to the interface documentation.
Interfaces have a couple more naming constraints. The name of an interface should always start with a capital “I”, so that they can be easily distinguished from classes. Also, interface declarations are used for documenting the behavior and everything around the component, so that you should have almost all your documentation here. Zope 3’s API documentation tool mainly uses the interface doc strings for providing information to the reader.
5.2.3 ZCML Naming and Formatting Conventions
ZCML is a dialect of XML, so that the usual good practices of XML apply. ZCML sub-elements are indented by two spaces; again, let’s not use tab characters, as their width is ambiguous. All attributes of an element should be each on a separate line with an indentation of four spaces.
1 <configure 2 namespace="http://namespaces.org/my-namespace"> 3 4 <namespace:directive 5 attr="value"> 6 7 <namespace:sub-directive 8 attr1="value" 9 attr2="This is a long value that 10 spans over two lines." 11 /> 12 13 </namespace:directive> 14 15 </configure>
While it is possible to specify any amount of ZCML namespaces inside a configure element, you should only declare the namespaces you actually use, like you only import objects in Python that the code below works with. As for all source code in Zope, we try to avoid long lines greater than 80 characters. However, sometimes you will notice that this is not possible, since the dotted Python identifiers and ids can become very long. In these rare cases it is okay to have lines longer than 80 characters.
5.2.4 Page Template Naming and Formatting Conventions
Page Templates were designed, like the name says, to provide presentation templates. Unfortunately, it is possible to supply Python-based logic via TALES expressions as well. In Zope 3 templates we avoid Python code completely, since the necessary logic can be provided by an HTML Presentation component as seen in the “??” chapter. In general, even if you have complex non-Python logic in a template, try to think about a way to move your logic into the Python-based presentation class.
Finally, HTML is a subset of XML, which means that all contents of double tags should be indented by two spaces (as for ZCML). For tag attributes we use four spaces as indentation. Remember that it is very hard to read and debug incorrectly indented HTML code!
5.2.5 Test Writing Conventions
While not necessary for the standard Python unittest package, the Zope 3 test runner expects unit tests to be in a module called tests. If tests is a directory (package), then you should not forget to have an empty init.py file in the directory. Similarly, you should have an ftests module for functional tests .
Usually you have several testing modules in a tests directory. All of these modules should begin with the prefix test_. Note that this is one of the few violations to not use the underscore character in a module name. The prefix is very helpful in detecting the actual tests, since sometimes supporting files are also located in the tests directory.
The testing methods inside a TestCase do not have doc strings, since the test runner would use the doc strings for reporting instead of the method names, which makes it much harder to find failing tests. Instead, choose an instructive method name - do not worry if it is long - and write detailed comments if necessary. Finally, if a test case tests a particular component, then you should always include an interface verification test:
#!python def test_interface(self): self.assert_(IExample.providedBy(SimpleExample()))
This is probably the most trivial test you write, but also a very important one, since it tells you whether you fully implemented the interface.
5.2.6 Why is Having and Following the Conventions Important?
While I addressed some of the motivations for strict naming conventions at the beginning of the chapter, here are some more reasons. First off, there are many people working on the project. Everybody has an idea of an ideal style for his/her code, but in a project like this, only one can be adopted. If none would be created, people would constantly correct others’ code and at the end we would have a most colorful mix of styles and the code would be literally unmaintainable.
Naming conventions also make the the API more predictable and are consequently more “usable” in a sense. For example, if I only remember the name of a class, like “simple example”, I will always now how the spelling of the class will look like, i.e. SimpleExample. There is no ambiguity in the name at all.
A lot of time was spent on developing the directory structure. The goal is to have a well thought-out and flat tree so that the imports of the corresponding modules would stay manageable. Eventually we adopted the Java design for this reason. The development team also desired to be able to separate contract, implementation and presentation in order to address the different development roles (developer, scriptor, graphic designer, information architect and translator) of the individuals contributing to the project.
Ultimately we hope that these conventions are followed by all ZPL code in the zope.org source code repository.
For more information see the Zope 3 development wiki page at: http://dev.zope.org/Zope3/CodingStyle