Add DEVELOPING file, based upon the file from portage
authorfuzzyray <fuzzyray@gentoo.org>
Wed, 9 Dec 2009 16:26:43 +0000 (16:26 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Wed, 9 Dec 2009 16:26:43 +0000 (16:26 -0000)
svn path=/trunk/gentoolkit/; revision=705

DEVELOPING [new file with mode: 0644]

diff --git a/DEVELOPING b/DEVELOPING
new file mode 100644 (file)
index 0000000..ea5f457
--- /dev/null
@@ -0,0 +1,166 @@
+Code Guidelines
+---------------
+These are a few guidelines to stick to when modifying or adding code to
+Gentoolkit.
+
+First, read Python's PEP 8: http://www.python.org/dev/peps/pep-0008/
+Next, read Portage's DEVELOPING file.
+Next, read Portage's Docstring specs:
+      http://www.gentoo.org/proj/en/portage/doc/policies/docstring-spec.xml
+
+(Portage DEVELOPING overrides PEP 8, so we use tabs instead of spaces.)
+
+Here are a few clarifications and changes to the above:
+
+Line-Wrapping
+-------------
+- Do NOT space out to an opening paren (etc). Different tab settings make this
+  difficult to read:
+
+  BAD:
+  foo = get_foo(keyword1=default,
+                keyword2=default,
+                ...)
+
+  OK:
+  foo = get_foo(keyword1=default, keyword2=default, keyword3=default,
+      keyword4=default, ...)
+
+  OK:
+  foo = get_foo(
+      keyword1=default,
+      keyword2=default,
+      ...
+  )
+
+- Max. line length is strictly 80 characters with a tab length of 4 characters.
+- "The preferred way of wrapping long lines is by using Python's implied line
+  continuation inside parentheses, brackets and braces" rather than using '\'
+  (PEP 8).
+  Even better than that is pre-defining a snippet of the long line in a
+  variable. So:
+
+  BAD: (too long)
+    self._descriptions = [e.text for e in self._xml_tree.findall("longdescription")]
+
+  OK:
+    self._descriptions = \
+        [e.text for e in self._xml_tree.findall("longdescription")]
+
+  BETTER:
+    self._descriptions = [
+        e.text for e in self._xml_tree.findall("longdescription")
+    ]
+
+  BEST: (easiest to read and test)
+    long_descriptions = self._xml_tree.findall("longdescription")
+    self._descriptions = [e.text for e in long_descriptions]
+
+Imports:
+--------
+Same as PEP 8 and Portage's DEVELOPING spec, but make sure to split python
+default libraries, Gentoo system libraries (like portage) and lastly local
+modules (gentoolkit), so:
+
+  GOOD:
+    import os
+    import re
+
+    import portage
+    from portage.versions import catpkgsplit, pkgcmp
+
+    from gentoolkit import CONFIG
+    from gentoolkit import pprinter as pp
+    ...
+
+Exceptions:
+-----------
+Always raise exceptions and catch them at the last possible moment. This allows
+API consumers to catch them and decide what to do with them. Do not print an
+error to stderr from inside a consumable. Never "print" an error string, always
+write it to stderr.
+
+Lastly, if an error is fatal (should be raised all the way up), try to make
+sure it is an errors.Gentoolkit* error. That allows API consumers to catch all
+fatal errors with: except 'gentoolkit.errors.GentoolkitException'. See
+bin/equery to see how we catch those exceptions before they hit the user.
+
+  BAD:
+    try:
+        result = tree.aux_get(str(self.cpv), [var])
+    except KeyError:
+        sys.stderr.write("aux_get returned unexpected results")
+
+  BETTER:
+    # This line raises KeyError if self.cpv not found:
+    result = tree.aux_get(str(self.cpv), [var])
+
+  BEST:
+    try:
+        result = tree.aux_get(str(self.cpv), [var])
+    except KeyError:
+        err = "aux_get returned unexpected results"
+        raise errors.GentoolkitFatalError(err)
+
+Docstrings:
+-----------
+Follow this example for any complicated function or method. Don't worry about
+docstring for private methods (like _foo) or methods like __this__. It's
+definitely OK to write docstrings for these methods as well if there's
+something tricky about them, though.
+
+  def graph_reverse_depends(...):
+      # A ONE-LINE sentence describing the function in simplest terms:
+      """Graph direct reverse dependencies for self.
+
+      # An optional second paragraph to give more detailed information.
+      # Sometimes an 'Example usage' here can be worth more than a lengthy
+      # description. Examples should be an ACTUAL interpreter session:
+      Example usage:
+          >>> from gentoolkit.dependencies import Dependencies
+          >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
+          >>> # I only care about installed packages that depend on me:
+          ... from gentoolkit.helpers import get_installed_cpvs
+          >>> # I want to pass in a sorted list. We can pass strings or
+          ... # Package or Atom types, so I'll use Package to sort:
+          ... from gentoolkit.package import Package
+          >>> installed = sorted(Package(x) for x in get_installed_cpvs())
+          >>> deptree = ffmpeg.graph_reverse_depends(
+          ...     only_direct=False,  # Include indirect revdeps
+          ...     pkgset=installed)   # from installed pkgset
+          >>> len(deptree)
+          44
+
+      # Lastly use epydoc's special fields to document further.
+      # See: http://epydoc.sourceforge.net/fields.html
+      @type pkgset: iterable
+      @keyword pkgset: sorted pkg cpv strings or anything sublassing
+          L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
+      @type max_depth: int
+      @keyword max_depth: Maximum depth to recurse if only_direct=False.
+          -1 means no maximum depth;
+           0 is the same as only_direct=True;
+          >0 means recurse only this many times;
+      @type only_direct: bool
+      @keyword only_direct: to recurse or not to recurse
+      @type printer_fn: callable
+      @keyword printer_fn: If None, no effect. If set, it will be applied to
+          each L{gentoolkit.atom.Atom} object as it is added to the results.
+      @rtype: list
+      @return: L{gentoolkit.dependencies.Dependencies} objects
+      """
+
+Other concerns:
+---------------
+- Choose names which are full, clear words (not necessary in small loops).
+- It is NEVER necessary to prefix names with "my". Consider:
+    class FooThinger(object):
+        def __init__(self, foo):
+            self.myfoo = foo
+
+  Just use FooThinger.foo or FooThinger.orig_foo; "my"foo tells us nothing.
+- Comment and document in simple, unambiguous and non-repetitive English.
+- When adding a TODO, FIXME or XXX comment, please date it and add your name so
+  that other devs know who to ask about the proposed change.
+- Be careful of spelling. DO spell check your source code and be professional
+  when writing comments.