Merge genscripts revision 144, contains fixes for Bug 299260
[gentoolkit.git] / DEVELOPING
1 Python Code Guidelines
2 ----------------------
3 These are a few guidelines to stick to when modifying or adding code to
4 Gentoolkit. These guidelines do not apply to pym/gentoolkit/test/*.
5
6 First, read Python's PEP 8: http://www.python.org/dev/peps/pep-0008/
7 Next, read Portage's DEVELOPING file.
8 Next, read Portage's Docstring specs:
9       http://www.gentoo.org/proj/en/portage/doc/policies/docstring-spec.xml
10
11 (Portage DEVELOPING overrides PEP 8, so we use tabs instead of spaces.)
12
13 Here are a few clarifications and changes to the above:
14
15 Line-Wrapping
16 -------------
17 - Do NOT space out to an opening paren (etc). Different tab settings make this
18   difficult to read:
19
20   BAD:
21   foo = get_foo(keyword1=default,
22                 keyword2=default,
23                 ...)
24
25   OK:
26   foo = get_foo(keyword1=default, keyword2=default, keyword3=default,
27       keyword4=default, ...)
28
29   OK:
30   foo = get_foo(
31       keyword1=default,
32       keyword2=default,
33       ...
34   )
35
36 - Preferred line length is 80 characters with a tab length of 4 characters.
37 - "The preferred way of wrapping long lines is by using Python's implied line
38   continuation inside parentheses, brackets and braces" rather than using '\'
39   (PEP 8).
40   Even better than that is pre-defining a snippet of the long line in a
41   variable. So:
42
43   BAD: (too long)
44     self._descriptions = [e.text for e in self._xml_tree.findall("longdescription")]
45
46   OK:
47     self._descriptions = \
48         [e.text for e in self._xml_tree.findall("longdescription")]
49
50   OK:
51     self._descriptions = [
52         e.text for e in self._xml_tree.findall("longdescription")
53     ]
54
55   OK: (easiest to read and test)
56     long_descriptions = self._xml_tree.findall("longdescription")
57     self._descriptions = [e.text for e in long_descriptions]
58
59 Imports:
60 --------
61 Same as PEP 8 and Portage's DEVELOPING spec, but make sure to split python
62 default libraries, Gentoo system libraries (like portage) and lastly local
63 modules (gentoolkit), so:
64
65   GOOD:
66     import os
67     import re
68
69     import portage
70     from portage.versions import catpkgsplit, pkgcmp
71
72     from gentoolkit import CONFIG
73     from gentoolkit import pprinter as pp
74     ...
75
76 Exceptions:
77 -----------
78 Always raise exceptions and catch them at the last possible moment. This allows
79 API consumers to catch them and decide what to do with them. Do not print an
80 error to stderr from inside a consumable. Never "print" an error string, always
81 write it to stderr.
82
83 Lastly, if an error is fatal (should be raised all the way up), try to make
84 sure it is an errors.Gentoolkit* error. That allows API consumers to catch all
85 fatal errors with: except 'gentoolkit.errors.GentoolkitException'. See
86 bin/equery to see how we catch those exceptions before they hit the user.
87
88   BAD:
89     try:
90         result = tree.aux_get(str(self.cpv), [var])
91     except KeyError:
92         sys.stderr.write("aux_get returned unexpected results")
93
94   BETTER:
95     # This line raises KeyError if self.cpv not found:
96     result = tree.aux_get(str(self.cpv), [var])
97
98   BEST:
99     try:
100         result = tree.aux_get(str(self.cpv), [var])
101     except KeyError:
102         err = "aux_get returned unexpected results"
103         raise errors.GentoolkitFatalError(err)
104
105 Docstrings:
106 -----------
107 Follow this example for any complicated function or method. Don't worry about
108 docstring for private methods (like _foo) or methods like __this__. It's
109 definitely OK to write docstrings for these methods as well if there's
110 something tricky about them, though.
111
112   def graph_reverse_depends(...):
113       # A ONE-LINE sentence describing the function in simplest terms:
114       """Graph direct reverse dependencies for self.
115
116       # An optional second paragraph to give more detailed information.
117       # Sometimes an 'Example usage' here can be worth more than a lengthy
118       # description. Examples should be an ACTUAL interpreter session:
119       Example usage:
120           >>> from gentoolkit.dependencies import Dependencies
121           >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
122           >>> # I only care about installed packages that depend on me:
123           ... from gentoolkit.helpers import get_installed_cpvs
124           >>> # I want to pass in a sorted list. We can pass strings or
125           ... # Package or Atom types, so I'll use Package to sort:
126           ... from gentoolkit.package import Package
127           >>> installed = sorted(Package(x) for x in get_installed_cpvs())
128           >>> deptree = ffmpeg.graph_reverse_depends(
129           ...     only_direct=False,  # Include indirect revdeps
130           ...     pkgset=installed)   # from installed pkgset
131           >>> len(deptree)
132           44
133
134       # Lastly use epydoc's special fields to document further.
135       # See: http://epydoc.sourceforge.net/fields.html
136       @type pkgset: iterable
137       @keyword pkgset: sorted pkg cpv strings or anything sublassing
138           L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
139       @type max_depth: int
140       @keyword max_depth: Maximum depth to recurse if only_direct=False.
141           -1 means no maximum depth;
142            0 is the same as only_direct=True;
143           >0 means recurse only this many times;
144       @type only_direct: bool
145       @keyword only_direct: to recurse or not to recurse
146       @type printer_fn: callable
147       @keyword printer_fn: If None, no effect. If set, it will be applied to
148           each L{gentoolkit.atom.Atom} object as it is added to the results.
149       @rtype: list
150       @return: L{gentoolkit.dependencies.Dependencies} objects
151       """
152
153 Other concerns:
154 ---------------
155 - Choose names which are full, clear words (not necessary in small loops).
156 - It is NEVER necessary to prefix names with "my". It adds no useful
157   information.
158 - Comment and document in simple, unambiguous and non-repetitive English.
159 - When adding a TODO, FIXME or XXX comment, please date it and add your name so
160   that other devs know who to ask about the proposed change.
161 - Be careful of spelling.