Changes between Version 26 and Version 27 of CodingStandards
- Timestamp:
- 2013-12-19T01:32:50Z (11 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
CodingStandards
v26 v27 1 = coding standards =1 = Coding standards = 2 2 3 3 Here are some Python code style guidelines. We also include official Python guidelines by reference (see the section on "official Python standards" below). This document overrides the official standards whenever there is a conflict. 4 4 5 == basic standards ==6 === compatibility ===5 == Basic standards == 6 === Compatibility === 7 7 8 8 Tahoe requires Python v2.6.6 or greater (although the current code only refuses to run on Python < 2.6). No effort should be made to offer compatibility with versions of Python older than 2.6.6. Effort should be made to work with every Python release from v2.6.6 to the most recent 2.x, inclusive. 9 9 10 === naming and layout ===10 === Naming and Layout === 11 11 12 12 * Use {{{underscore_separated_names}}} for functions, {{{CamelCapNames}}} for classes, {{{alllowercasenames}}} for modules, and {{{ALL_CAPS_NAMES}}} for constants. Use all lower-case variable names (e.g. {{{variable_name}}} or {{{variablename}}}). Prepend a leading underscore to private names. 13 13 * Put parentheses around tuples if it helps make the code more readable, leave them off if not. 14 14 15 === comments, idioms, miscellany, license, imports, docstrings, line widths ===15 === Comments, Idioms, Miscellany, License, Imports, Docstrings, Line widths === 16 16 17 17 Here is a useful header for starting new Python files: … … 19 19 {{{ 20 20 """ 21 optional doc string describing the module here 21 Optional doc string describing the module here. 22 22 """ 23 23 … … 39 39 * PEP 8 says: "If operators with different priorities are used, consider adding whitespace around the operators with the lowest priority(ies)." This is a good rule; note that it also applies to some non-obvious low-priority operators, like '{{{:}}}' for list slicing. (Example: {{{a[b-c : d]}}} good, {{{a[b - c:d]}}} bad. If a slice is from the start or to the end of the array, put the '{{{:}}}' immediately next to the bracket on that side.) 40 40 41 === truths and falsehoods ===41 === Truths and Falsehoods === 42 42 43 43 * Don't use the literals {{{True}}} or {{{False}}} in conditional expressions -- instead just write the expression which will evaluate to true or false. For example, write {{{if expr:}}} instead of {{{if expr == True:}}} and {{{if not expr:}}} instead of {{{if expr == False:}}}. 44 44 * Avoid relying on the fact that empty sequences, empty strings, empty dicts, {{{0}}}, and {{{None}}} are treated as false. Write {{{if len(items) == 0:}}}, {{{if thing is None:}}}, etc. 45 45 46 == advanced idioms ==46 == Idioms == 47 47 48 === Preconditions and Assertions === 48 49 49 50 === preconditions and assertions === 51 52 ==== basic preconditions and assertions ==== 50 ==== Basic preconditions and assertions ==== 53 51 54 52 Make sure you have {{{from allmydata.util.assertutil import _assert, precondition, postcondition}}} in your imports (as shown in the template above). Now design preconditions for your methods and functions, and assert them like this: … … 72 70 }}} 73 71 74 The "error message" that will accompany a failed expression should be a statement of what is required for correct operation. Don't write something like "Spam isn't firm.", because that is ambiguous: the error could be that the spam is supposed to be firm and it isn't, or the error could be that spam isn't supposed to be firm and it is! The same ambiguity can apply to the sentence "Spam must be firm.".It helps to use the words "required to" in your message, for example "Spam is required to be firm.".72 The "error message" that will accompany a failed expression should be a statement of what is required for correct operation. Don't write something like "Spam isn't firm.", because that is ambiguous: the error could be that the spam is supposed to be firm and it isn't, or the error could be that spam isn't supposed to be firm and it is! It helps to use the words "required to" in your message, for example "Spam is required to be firm.". 75 73 76 74 Assertions are not a substitute for proper error handling! An assertion, precondition or postcondition should only be used for cases that "cannot happen" unless the code is incorrect. They should not be used to check for invalid inputs; in that case, raise an exception instead. 77 75 78 ==== avoid "bare assert" ====76 ==== Avoid "bare assert" ==== 79 77 80 78 Python's built-in {{{assert}}} statement, unlike {{{allmydata.util.assertutil._assert}}}, can be switched off by the {{{-O}}} option to {{{python}}} or the {{{PYTHONOPTIMIZE}}} environment variable. Although this might sound useful to reduce the overhead of assertions, in practice that overhead is negligable, and conditional assertions are more trouble than they're worth (partly because they create a configuration that is mostly untested). We are in the process of removing all bare asserts from the codebase (#1968). 81 79 82 ==== class invariants ====80 ==== Class invariants ==== 83 81 84 82 If your class has internal state which is complicated enough that a bug in the class's implementation could lead to garbled internal state, then you should have a class invariant. A class invariant is a method like this (an actual example from !BlockWrangler, but truncated for space): … … 100 98 Now you can put {{{assert self._assert_invariants()}}} everywhere in your class where the class ought to be in an internally consistent state. For example, at the beginning of every externally-callable method. This technique can be very valuable in developing a complex class -- it catches bugs early, it isolates bugs into specific code paths, and it clarifies the internal structure of the class so that other developers can hack on it without subtle misunderstandings. 101 99 102 * we actually appear to only have one instance of this pattern in Tahoe at time of writing, in {{{allmydata.util.dictutil}}}. It has the disadvantage of cluttering up the logic with calls to {{{_assert_invariants}}}, and should probably be used sparingly. -- Daira100 * We actually appear to only have one instance of this pattern in Tahoe at time of writing, in {{{allmydata.util.dictutil}}}. It has the disadvantage of cluttering up the logic with calls to {{{_assert_invariants}}}, and should probably be used sparingly. -- Daira 103 101 104 ==== assertion policy ====102 ==== Assertion policy ==== 105 103 106 104 One axis of interest is how time-consuming the checks are. Many precondition … … 134 132 135 133 136 === configuration ===134 === Configuration === 137 135 138 ==== minimizing configuration ====136 ==== Minimizing configuration ==== 139 137 140 138 * Do not implement configuration files for modules or libraries -- code that is going to be used by other code. Only applications -- code that is going to be used by humans -- have configuration files. Modules and libraries get "configured" by the code that calls them, for example by passing arguments to their constructors. … … 142 140 * Design algorithms so that they have as few "voodoo constants" and "tweakable parameters" as possible. 143 141 144 ==== how to implement configuration ====142 ==== How to implement configuration ==== 145 143 146 144 Whether in application code or in library code, never pass configuration values via a configuration object. Instead use Python parameters. For example -- here's another real-life example -- do not write … … 170 168 171 169 172 == official Python standards ==170 == Official Python Standards == 173 171 174 172 These are listed in decreasing order of priority, so if a point in one of the latter guidelines contradicts a point in one of the earlier ones, then go with the earlier. The Tahoe-LAFS-specific guidelines above override all else, of course.