move "assertion policy" section to a more relevant place

[Imported from Trac: page CodingStandards, version 17]
davidsarah 2011-09-26 22:44:01 +00:00
parent 83ef120c62
commit ab1a5dd059

@ -96,45 +96,9 @@ def _assert_invariants(self):
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. 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.
* 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. -- DavidSarah * 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. -- David-Sarah
=== configuration === ==== assertion policy ====
==== minimizing configuration ====
* 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.
* If there are constant values which end-users do not need to modify, then do not make them configurable, but put them in all-caps variables at the beginning of the Python file in which they are used.
* Design algorithms so that they have as few "voodoo constants" and "tweakable parameters" as possible.
==== how to implement configuration ====
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
```
class BlockStore:
def __init__(self, confdict={}, recoverdb=True, name='*unnamed*'):
if confdict.has_key('MAX_MEGABYTES'):
self.maxspace = (2**20) * int(confdict.get('MAX_MEGABYTES'))
else:
self.maxspace = None
self.basepath = os.path.abspath(confdict.get("PATH", ""))
self.maintainertype = confdict.get("MAINTAINER", "rnd").lower()
self.backendtype = confdict.get("BACKEND", "flat").lower()
```
, but instead write
```
class BlockStore:
def __init__(self, maxspace=None, path="", maintainertype="rnd", backendtype="flat", recoverdb=True, name='*unnamed*'):
self.basepath = os.path.abspath(path)
self.maintainertype = maintainertype
self.backendtype = backendtype
```
.
#### assertion policy
One axis of interest is how time-consuming the checks are. Many precondition One axis of interest is how time-consuming the checks are. Many precondition
checks can cause typical runtime to explode to O(n^2^) or O(n^3^), for example checks can cause typical runtime to explode to O(n^2^) or O(n^3^), for example
@ -184,6 +148,42 @@ callers.
altogether) before committing. altogether) before committing.
### configuration
#### minimizing configuration
* 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.
* If there are constant values which end-users do not need to modify, then do not make them configurable, but put them in all-caps variables at the beginning of the Python file in which they are used.
* Design algorithms so that they have as few "voodoo constants" and "tweakable parameters" as possible.
#### how to implement configuration
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
```
class BlockStore:
def __init__(self, confdict={}, recoverdb=True, name='*unnamed*'):
if confdict.has_key('MAX_MEGABYTES'):
self.maxspace = (2**20) * int(confdict.get('MAX_MEGABYTES'))
else:
self.maxspace = None
self.basepath = os.path.abspath(confdict.get("PATH", ""))
self.maintainertype = confdict.get("MAINTAINER", "rnd").lower()
self.backendtype = confdict.get("BACKEND", "flat").lower()
```
, but instead write
```
class BlockStore:
def __init__(self, maxspace=None, path="", maintainertype="rnd", backendtype="flat", recoverdb=True, name='*unnamed*'):
self.basepath = os.path.abspath(path)
self.maintainertype = maintainertype
self.backendtype = backendtype
```
.
## official Python standards ## official Python standards
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. 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.