-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathStyleGuidelines.phtml
More file actions
362 lines (244 loc) · 21.7 KB
/
Copy pathStyleGuidelines.phtml
File metadata and controls
362 lines (244 loc) · 21.7 KB
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
<% header('Webware Style Guidelines', None,
'''.what {
font-weight: bold;
color: #002352;
margin-top: 2ex;
margin-left: 2em;
}
.why {
margin-top: 0.3ex;
margin-left: 2em;
}
.examples, .negatives, .exceptions {
padding-top: 0.5ex;
margin-left: 2em;
}''')
%>
<!-- contents -->
<a id="Introduction"></a><h2>Introduction</h2>
<p>Style refers to various aspects of coding including indentation practices, naming conventions, use of design patterns, treatment of constants, etc. One of the goals of Webware is to maintain fairly consistent coding style with respect to certain basics as described in this document.</p>
<p>This document is therefore very important for those who routinely develop Webware or who wish to contribute code, although ordinary users may still find it interesting and useful in understanding the Webware APIs.</p>
<p>Consistent source code style conventions are important for several reasons:</p>
<ul>
<li> Consistent conventions reduce the learning curve because users know what to expect from the API.</li>
<li> Consistent conventions can actually enable certain code or features. For example, the <a href="#SynNaming_MethodCategories">method categories convention</a> allows for better file summaries to be generated by Webware's installer.</li>
<li> Webware is open source, resulting in many people reading the code in an attempt to understand it, extend it and debug it.</li>
<li> Webware consists of tens of thousands of lines of source code and will undoubtedly continue to grow.</li>
</ul>
<p>This document divides the conventions into three major categories:</p>
<ol style="padding-left: 2em">
<li> <a href="#Syntax">Syntax and Naming</a></li>
<li> <a href="#Structure">Structure and Patterns</a></li>
<li> <a href="#MiscNotes">Miscellaneous</a></li>
</ol>
<a id="Syntax"></a><h2>Syntax and Naming</h2>
<a id="SynNaming_MethodsAndAttrs"></a><h3>Methods and Attributes</h3>
<p>Methods and attributes are an important topic because they are used so frequently and therefore have an impact on using the classes, learning them, remembering them, etc.</p>
<p>The first thing that is important to understand is that Webware is constructed in an object-oriented fashion, including full encapsulation of object attributes. In other words, you communicate and use objects completely via their methods (except that classes and subclasses access their own attributes – somebody's got to).</p>
<p>The primary advantages of using methods are:</p>
<ul>
<li> Method implementations can be changed with minimal impact on the users of the class.</li>
<li> Subclasses can customize methods to suit their needs and still be used in the usual fashion (as defined by the superclass).</li>
</ul>
<p>In the case of a method that returns a value, that value may manifest in several ways:</p>
<ol style="padding-left: 2.5em">
<li> Stored as attribute.</li>
<li> Stored as an attribute, only on demand (e.g., lazy and cached).</li>
<li> Computed upon every request.</li>
<li> Delegated to another object.</li>
</ol>
<p>By requiring that object access is done solely through methods, the implementer of the class is free to switch between the techniques above.</p>
<p>Keeping that in mind, it is apparent that naming conventions are needed for attributes, the methods that return them and the methods that set them. Let's suppose the basic "thing" is <code>status</code>. The convention then is:</p>
<ul>
<li> <code>_status</code> - the attribute</li>
<li> <code>status()</code> - the retrieval method</li>
<li> <code>setStatus()</code> - the setter method</li>
</ul>
<p>The underscore that precedes the attribute denotes that it is semi-private and allows for a cleanly named retrieval method. The <code>status()</code> and <code>setStatus()</code> convention originated many years ago with Smalltalk and proved successful with that language as well as others, including Objective-C.</p>
<p>Some styles prefix the retrieval method with <code>get</code>, but Webware does not for the sake of brevity and because there are methods for which it is not always clear if a <code>get</code> prefix would make sense.</p>
<p>Methods that return a Boolean are prefixed with <code>is</code> or <code>has</code> to make their semantics more obvious. Examples include <code>request.isSecure()</code>, <code>user.isActive()</code> and <code>response.hasHeader()</code>.</p>
<a id="SynNaming_MethodCategories"></a><h3>Method Categories</h3>
<p>Webware classes divide their methods into logical groups called categories purely for organizational purposes. This often helps in understanding the interface of a class, especially when looking at its summary.</p>
<p>Upon installation, Webware generates HTML summaries for each Python source file. See the summary of <a href="../WebKit/Docs/Source/Summaries/HTTPResponse.html">WebKit.HTTPResponse</a> for an example.</p>
<p>By convention, a category is named with a comment beginning and ending with two pound signs, <code>##</code>, with two blanks lines before and one blank line after.</p>
<a id="SynNaming_Abbreviations"></a><h3>Abbreviations</h3>
<p>Using abbreviations is a common coding practice to reduce typing and make lines shorter. However, removing random characters to abbreviate is a poor way to go about this. For example, <code>transaction</code> could be abbreviated as <code>trnsact</code> or <code>trnsactn</code>, but which letters are left out is not obvious or easy to remember.</p>
<p>The typical technique in Webware is to use the first whole syllable of the word. For example, <code>trans</code> is easy to remember, pronounce and type.</p>
<p>Attributes and methods that return the number of some kind of object are quite common. Suppose that the objects in questions are requests. Possible styles include <code>numRequests</code>, <code>numberOfRequests</code>, <code>requestCount</code> and so on. Webware uses the first style in all cases, for consistency:</p>
<pre class="py">
numRequests
</pre>
<p>If there is a corresponding attribute, it should have the same name (prefixed by an underscore).</p>
<a id="SynNaming_CompoundNames"></a><h3>Compound Names</h3>
<p>Identifiers often consist of multiple names. There are three major styles for handling compound names:</p>
<ol>
<li> <code>serverSidePath</code> - the Webware convention</li>
<li> <code>serversidepath</code></li>
<li> <code>server_side_path</code></li>
</ol>
<p>Python itself uses all three styles (<code>isSet</code>, <code>getattr</code>, <code>has_key</code>), but Webware uses only the first which is more readable than the second and easier to type that the third.</p>
<p>This rule also applies to class names such as <code>HTTPRequest</code> and <code>ServletFactory</code>.</p>
<a id="SynNaming_ComponentNames"></a><h3>Component Names</h3>
<p>Names of object-oriented Webware components are often suffixed with <b>Kit</b>, as in <i>WebKit</i> and <i>MiddleKit</i>.</p>
<p>The occasional component that serves as a collective library (rather than an OO framework) is suffixed with <b>Utils</b>, as in <i>MiscUtils</i> and <i>WebUtils</i>.</p>
<a id="SynNaming_Rules"></a><h3>Rules</h3>
<p>What follows is a list of additional syntax and naming rules that are actually policed by the <a href="#Misc_checksrc">checksrc</a> utility, described later in this document.</p>
<% def convention(dict):
results = []
for key in ['what', 'why', 'examples', 'negatives', 'exceptions']:
if key not in dict:
continue
value = dict[key]
if key in ('examples', 'negatives', 'exceptions'):
if key != 'exceptions':
value = ('\n' in value and '<pre class="py">%s</pre>'
or '<code>%s</code>') % value
value = '%s: %s' % (key.capitalize(), value)
results.append('<div class="%s">%s</div>' % (key, value))
return '\n'.join(results) %>
<%= convention({'what': 'Filenames are capitalized.',
'why': 'For consistency and to match the capitalization convention used for classes.',
'examples': 'Servlet.py WebKit.cgi HTTPServlet.py',
'negatives': 'myClass.py session.py',
'exceptions': 'Files located in <code>Webware/bin/</code> that are intended solely for command line use are left in all lower case similar to other command line programs such as grep and find.'})%>
<%= convention({'what': 'Files do not contain <code>\\r</code> (on POSIX systems).',
'why': "To avoid problems with POSIX tools that don't like <code>\\r</code> and using the same Git repository on different platforms.<br>This problem typically happens when a Windows file is FTPed in binary mode to a Unix machine from which it is then checked in."})%>
<%= convention({'what': 'Indentation is done with 4 spaces per level.',
'why': "In the past, Webware for Python used tabs for indentation, but nowadays, 4 spaces per indentation level is the most popular and therefore recommended style.<br>So you will not need to switch your editor any more when working on different projects."}) %>
<%= convention({'what': 'Tabs are not used at all.',
'why': "To avoid alignment problems between people who have tabs set to different widths.<br>A common use of tabs is to align the values of a dictionary or the right hand sides of several consecutive assignment statements. There is no problem when using tabs for indentation, but when they are used past non-tab characters, the alignment will be skewed when the tab width is changed (e.g., when a different developer with a different tab width views the code)."}) %>
<%= convention({'what': 'Class names start with a capital letter.',
'why': 'For consistency and to make class names prominent in source code. This convention is used in almost every object-oriented language.',
'examples': 'Servlet HTTPServlet ThreadedAppServer',
'negatives': 'servlet myClass'}) %>
<%= convention({'what': 'Method names start with a lower case letter.',
'why': 'For consistency and ease of typing. This convention is also used in almost every object-oriented language.',
'examples': 'respond() isSecure() setTimeout() htForDict()',
'negatives': 'Respond() IsSecure() SetTimeout() HtForDict()'}) %>
<%= convention({'what': 'Method names do not start with <b>get</b>.',
'why': 'See <a href="#SynNaming_MethodsAndAttrs">Methods and Attributes</a> above.',
'examples': 'status() numRequests()',
'negatives': 'getStatus() getNumRequests()'}) %>
<%= convention({'what': 'Data attributes start with an underscore (_).',
'why': 'The non-underscored version of the name is reserved for the accessor method. See <a href="#SynNaming_MethodsAndAttrs">Methods and Attributes</a> above.',
'examples': 'self._status self._numRequests',
'negatives': 'self.status self.numRequests'}) %>
<%= convention({'what': 'Method and attribute names have no underscores after the first character.',
'why': 'Capitalization is used instead.',
'examples': 'self.numRequests() factory.servletForTransaction()',
'negatives': 'self.num_requests() factory.servlet_for_transaction()'})%>
<%= convention({'what': 'Expressions following if, while and return are not enclosed in parentheses.',
'why': "Because this isn't C or Perl. <kbd>:-)</kbd>",
'examples': 'if serialNum>0:',
'negatives': 'if (serialNum>0):'}) %>
<%= convention({'what': 'Class definitions and category comments are preceded by 2 blank lines and followed by one.',
'why': 'To help with spacing and readability. Also, see <a href="#SynNaming_MethodCategories">Method Categories</a>.',
'examples': '''## Transactions ##
def transaction(self):
return self._transaction
## Values ##
def hasValue(self, name):
return name in self._fields or name in self._cookies''',
'exceptions': 'No category comment is added when the class implementation is <code>pass</code>.'}) %>
<%= convention({'what': 'Use docstrings and the <a href="https://www.python.org/dev/peps/pep-0257/">PEP 257</a> conventions.',
'why': 'This allows for creating appropriate code documentation automatically. Please note that in the past, this has not been heeded very well. But as the project matures and grows, the docstrings should be improved.',
'examples': '''def function(a, b):
"""Do X and return a list."""''',
'negatives': '''def function(a, b):
"""function(a, b) -> list"""'''}) %>
<%= convention({'what': '"To do" marks should be marked as <code>@@ yyyy-mm-dd initials: comment</code>.',
'why': 'These things are easier to track if they are dated and marked in a consistent way.',
'examples': '# @@ 2000-05-03 ce: This is bogus. Disregard for now.',
'negatives': '# I think this is bogus !!'}) %>
<%= convention({'what': 'Limit all lines to a maximum of 100 characters if possible.',
'why': 'This has been settled as a compromise between the people wanting to utilize their huge monitors with line sizes of 130 characters and those wanting to follow the stricter Python style guide recommendation of only 80 characters.'}) %>
<%= convention({'what': 'If not specified otherwise, follow the <a href="https://www.python.org/dev/peps/pep-0008/">PEP 8</a> conventions.',
'why': "For historical and opinionated reasons of Webware's BDFL, Webware follows its own style guidelines which overrule the standard guidelines for Python. :-)"}) %>
<a id="Structure"></a><h2>Structure and Patterns</h2>
<a id="Structure_Classes"></a><h3>Classes</h3>
<p>Webware overwhelmingly uses classes rather than collections of functions for several reasons:</p>
<ul>
<li> Classes allow for subclassing and therefore, a proven method of software extension and customization.</li>
<li> Classes allow for creating multiple instances (or objects) to be used in various ways. Examples include caching created objects for increased performance and creating multi-threaded servers.</li>
<li> Classes can provide for and encourage encapsulation of data, which gives implementers more freedom to improve their software without breaking what depends on it.</li>
</ul>
<p>By using classes, all three options above are available to the developer on an on-going basis. By using collections of functions, none of the above are readily available.</p>
<p>Note that making classes in Python is extraordinarily easy. Doing so requires one line:</p>
<pre class="py">
class Foo(SuperFoo):
</pre>
<p>and the use of <code>self</code> when accessing attributes and methods. The difference in time between creating a class with several methods vs. a set of several functions is essentially zero.</p>
<a id="Structure_ConfigFiles"></a><h3>Configuration Files</h3>
<p>Specific numbers and strings often appear in source code for determining things such as the size of a cache, the duration before timeout, the name of the server and so on. Rather than place these values directly in source, Webware provides configuration files that are easily discerned and edited by users, without requiring a walk through the source.</p>
<p>Webware uses ordinary Python dictionaries for configuration files for several reasons:</p>
<ul>
<li> Those who know Python will already understand the syntax.</li>
<li> Python dictionaries can be quickly and easily read (via Python's <code>eval()</code> function).</li>
<li> Dictionaries can contain nested structures such as lists and other dictionaries, providing a richer configuration language.</li>
</ul>
<p>By convention, these configuration files:</p>
<ul>
<li> Contain a Python dictionary.</li>
<li> Use a <code>.config</code> extension.</li>
<li> Capitalize their keys.</li>
<li> Are named after the class that reads them.</li>
<li> Are located in a <code>Configs/</code> subdirectory or in the same directory as the program.</li>
</ul>
<p>WebKit provides a <code>Configurable</code> mix-in class that is used to read configuration files. It allows subclasses to say <code>self.setting('Something')</code> so that the use of configuration information is easy and recognizable throughout the code.</p>
<a id="Structure_NamedObjs"></a><h3>Accessing Named Objects</h3>
<p>Several classes in Webware store dictionaries of objects keyed by their name. <code>HTTPRequest</code> is one such class which stores a dictionary of form fields. The convention for providing an interface to this information is as follows:</p>
<pre class="py">
## Fields ##
def field(self, name, default=_NoDefault):
def hasField(self, name):
def fields(self):
</pre>
<p>A typical use would be:</p>
<pre class="py">
req.field('city')
</pre>
<p>which returns the field value of the given name or raises an exception if there is none. Like the <code>get()</code> method of Python's dictionary type, a second parameter can be specified which will be returned if the value is not found:</p>
<pre class="py">
req.field('city', None)
</pre>
<p><code>req.hasField('city')</code> is a convenience method that returns <code>True</code> if the value exists, <code>False</code> otherwise.</p>
<p><code>req.fields()</code> returns the entire dictionary so that users have complete access to the full suite of dictionary methods such as <code>keys()</code>, <code>values()</code>, <code>items()</code>, etc. Users of this method are trusted not to modify the dictionary in any way. A paranoid class may choose to return a copy of the dictionary to help reduce abuse (although Webware classes normally do not for performance reasons).</p>
<p>In cases where the user of the class should be able to modify the named objects, the following methods are provided:</p>
<pre class="py">
def setValue(self, name, value):
def delValue(self, name):
</pre>
<p>Often Python programmers will provide dictionary-style access to their objects by implementing <code>__getitem__</code> so that users may say:</p>
<pre class="py">
req['city']
</pre>
<p>Webware generally avoids this approach for two reasons. The first and most important is that Webware classes often have more than one set of named objects. For example, HTTPRequest has both fields and cookies. HTTPResponse has both cookies and headers. These objects have their own namespaces, making the semantics of <code>obj['name']</code> ambiguous. Also, the occasional class that has only one dictionary could potentially have more in the future.</p>
<p>The second reason is the readability provided by expressions such as <code>response.cookie(name)</code> which states clearly what is being asked for.</p>
<p>In those cases where objects provide dictionary-like access, the class is typically a lightweight container that is naturally thought of in terms of its dictionary components. Usually these classes inherit from <code>dict</code>.</p>
<a id="Structure_Components"></a><h3>Components</h3>
<p>Webware consists of multiple components that follow particular conventions, not only for the sake of consistency, but also to enable scripts to manipulate them (such as generating documentation upon installation).</p>
<p>Example components include WebKit, PSP and MiscUtils.</p>
<p>These conventions are not yet formally documented, however if you quickly browse through a couple components, some conventions about directory structure and source code become apparent.</p>
<p>Also, if a component serves as a WebKit plug-in, then there are additional conventions for them to follow in order to work correctly. See <a href="../WebKit/Docs/UsersGuide.html#plug-ins">Plug-ins</a> in the WebKit <a href="../WebKit/Docs/UsersGuide.html">User's Guide</a>.</p>
<a id="MiscNotes"></a><h2>Miscellaneous Notes</h2>
<a id="Misc_Limitations"></a><h3>Limitations</h3>
<p>Some features that have been introduced in newer Python versions, like properties or decorators, could be used to create more readable code. However, since we want Webware to be backward compatible, these newer features should currently not be used. In future versions of Webware we may certainly lift these restrictions and adapt the style guidelines accordingly.</p>
<a id="Misc_Permissions"></a><h3>Permissions</h3>
<p>When adding standalone Python scripts, like those found in Webware/bin, you should mark them as executable in to the Git repository with <code>git update-index --chmod=+x</code>. Otherwise Unix-based developers will not have execute permissions after checking the scripts out.</p>
<a id="Misc_SendingUpdates"></a><h3>Sending Updates</h3>
<p>Those who actually develop Webware should send messages to <a href="mailto:webware-devel@lists.sourceforge.net">webware-devel@lists.sourceforge.net</a> when they update the repository.</p>
<p>The subject should contain the word "update" so that release notes can be easily compiled.</p>
<p>Note that automatic messages from the Git repository are not a good substitute for these types of messages which are more informative and more easily compiled into interesting release notes.</p>
<a id="Misc_BreakingRules"></a><h3>Breaking the Rules</h3>
<p>Of course, there are times when the rules are broken for good reason. To quote a cliché: "Part of being an expert is knowing when to break the rules."</p>
<p>But regarding style, Webware developers do this very infrequently for the reasons stated in the introduction.</p>
<a id="Misc_checksrc"></a><h3>checksrc.py</h3>
<p><code>checksrc.py</code> is a program located in <code>Webware/bin</code> used to check the syntax and naming conventions in the actual Webware source code. You can invoke the program with <code>-h</code> or <code>--help</code> to get information on how to use it and there is a good doc string at the top of file if you're interested in more details. <code>checksrc.py</code> should be run periodically and especially before each release of Webware.</p>
<p>You can also use <code>pylint</code> (available from <a href="http://www.logilab.org">Logilab.org</a>) for this purpose, which can be easily integrated in most IDEs. A suitable rcfile <code>.pylintrc</code> has been placed in the Webware root directory.</p>
<a id="Misc_Future"></a><h3>Future Work</h3>
<p>A list of future work for this document:</p>
<ul>
<li>Document the conventions for Webware components.</li>
<li>Adapt the document regarding modern Python features.</li>
</ul>
<% footer() %>