root / doc / dev-codestyle.rst @ 7618eba2
History | View | Annotate | Download (17.8 kB)
1 |
Code style guide |
---|---|
2 |
================ |
3 |
|
4 |
Python |
5 |
------ |
6 |
|
7 |
.. highlight:: python |
8 |
|
9 |
These are a few guidelines for Ganeti code and documentation. |
10 |
|
11 |
In simple terms: try to stay consistent with the existing code. `PEP 8`_ says: |
12 |
|
13 |
.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ |
14 |
|
15 |
A style guide is about consistency. Consistency with this style guide is |
16 |
important. Consistency within a project is more important. Consistency |
17 |
within one module or function is most important. |
18 |
|
19 |
.. note:: |
20 |
|
21 |
You might also want to take a look at the `Google style guide`_, since we |
22 |
have some things in common with it. |
23 |
|
24 |
.. _Google style guide: http://google-styleguide.googlecode.com/svn/trunk/pyguide.html |
25 |
|
26 |
Indentation |
27 |
~~~~~~~~~~~ |
28 |
In general, always indent using two (2) spaces and don't use tabs. |
29 |
|
30 |
The two spaces should always be relative to the previous level of indentation, |
31 |
even if this means that the final number of spaces is not a multiple of 2. |
32 |
|
33 |
When going on a new line inside an open parenthesis, align with the content of |
34 |
the parenthesis on the previous line. |
35 |
|
36 |
Valid example:: |
37 |
|
38 |
v = (somevalue, |
39 |
a_function([ |
40 |
list_elem, # 7 spaces, but 2 from the previous indentation level |
41 |
another_elem, |
42 |
])) |
43 |
|
44 |
Formatting strings |
45 |
~~~~~~~~~~~~~~~~~~ |
46 |
Always use double quotes (``""``), never single quotes (``''``), except for |
47 |
existing code. Examples for formatting strings:: |
48 |
|
49 |
var = "value" |
50 |
|
51 |
# Note: The space character is always on the second line |
52 |
var = ("The quick brown fox jumps over the lazy dog. The quick brown fox" |
53 |
" jumps over the lazy dog. The quick brown fox jumps over the lazy" |
54 |
" dog.") |
55 |
|
56 |
fn("The quick brown fox jumps over the lazy dog. The quick brown fox jumps" |
57 |
" over the lazy dog.") |
58 |
|
59 |
fn(constants.CONFIG_VERSION, |
60 |
("The quick brown fox jumps over the lazy dog. The quick brown fox" |
61 |
" jumps over the lazy dog. The quick brown fox jumps over the lazy" |
62 |
" dog.")) |
63 |
|
64 |
Don't format strings like this:: |
65 |
|
66 |
# Don't use single quotes |
67 |
var = 'value' |
68 |
|
69 |
# Don't use backslash for line continuation |
70 |
var = "The quick brown fox jumps over the lazy dog. The quick brown fox"\ |
71 |
" jumps over the lazy dog." |
72 |
|
73 |
# Space character goes to the beginning of a new line |
74 |
var = ("The quick brown fox jumps over the lazy dog. The quick brown fox " |
75 |
"jumps over the lazy dog. The quick brown fox jumps over the lazy " |
76 |
"dog.") |
77 |
|
78 |
Formatting sequences |
79 |
~~~~~~~~~~~~~~~~~~~~ |
80 |
Built-in sequence types are list (``[]``), tuple (``()``) and dict (``{}``). |
81 |
When splitting to multiple lines, each item should be on its own line and a |
82 |
comma must be added on the last line. Don't write multiline dictionaries in |
83 |
function calls, except when it's the only parameter. Always indent items by |
84 |
two spaces. |
85 |
|
86 |
:: |
87 |
|
88 |
# Short lists |
89 |
var = ["foo", "bar"] |
90 |
var = ("foo", "bar") |
91 |
|
92 |
# Longer sequences and dictionary |
93 |
var = [ |
94 |
constants.XYZ_FILENAME_EXTENSION, |
95 |
constants.FOO_BAR_BAZ, |
96 |
] |
97 |
var = { |
98 |
"key": func(), |
99 |
"otherkey": None, |
100 |
} |
101 |
|
102 |
# Multiline tuples as dictionary values |
103 |
var = { |
104 |
"key": |
105 |
("long value taking the whole line, requiring you to go to a new one", |
106 |
other_value), |
107 |
} |
108 |
|
109 |
# Function calls |
110 |
var = frozenset([1, 2, 3]) |
111 |
var = F({ |
112 |
"xyz": constants.XYZ, |
113 |
"abc": constants.ABC, |
114 |
}) |
115 |
|
116 |
# Wrong |
117 |
F(123, "Hello World", |
118 |
{ "xyz": constants.XYZ }) |
119 |
|
120 |
We consider tuples as data structures, not containers. So in general please |
121 |
use lists when dealing with a sequence of homogeneous items, and tuples when |
122 |
dealing with heterogeneous items. |
123 |
|
124 |
Passing arguments |
125 |
~~~~~~~~~~~~~~~~~ |
126 |
Positional arguments must be passed as positional arguments, keyword arguments |
127 |
must be passed as keyword arguments. Everything else will be difficult to |
128 |
maintain. |
129 |
|
130 |
:: |
131 |
|
132 |
# Function signature |
133 |
def F(data, key, salt=None, key_selector=None): |
134 |
pass |
135 |
|
136 |
# Yes |
137 |
F("The quick brown fox", "123456") |
138 |
F("The quick brown fox", "123456", salt="abc") |
139 |
F("The quick brown fox", "123456", key_selector="xyz") |
140 |
F("The quick brown fox", "123456", salt="foo", key_selector="xyz") |
141 |
|
142 |
# No: Passing keyword arguments as positional argument |
143 |
F("The quick brown fox", "123456", "xyz", "bar") |
144 |
|
145 |
# No: Passing positional arguments as keyword argument |
146 |
F(salt="xyz", data="The quick brown fox", key="123456", key_selector="xyz") |
147 |
|
148 |
Docstrings |
149 |
~~~~~~~~~~ |
150 |
|
151 |
.. note:: |
152 |
|
153 |
`PEP 257`_ is the canonical document, unless epydoc overrules it (e.g. in how |
154 |
to document the type of an argument). |
155 |
|
156 |
For docstrings, the recommended format is epytext_, to be processed via |
157 |
epydoc_. There is an ``apidoc`` target that builds the documentation and puts it |
158 |
into the doc/api subdir. Note that we currently use epydoc version 3.0. |
159 |
|
160 |
.. _PEP 257: http://www.python.org/dev/peps/pep-0257/ |
161 |
.. _epytext: http://epydoc.sourceforge.net/manual-epytext.html |
162 |
.. _epydoc: http://epydoc.sourceforge.net/ |
163 |
|
164 |
Note that one-line docstrings are only accepted in the unittests. |
165 |
|
166 |
Rules for writing the docstrings (mostly standard Python rules): |
167 |
|
168 |
* the docstring should start with a sentence, with punctuation at the end, |
169 |
summarizing the the aim of what is being described. This sentence cannot be |
170 |
longer than one line |
171 |
* the second line should be blank |
172 |
* afterwards the rest of the docstring |
173 |
* special epytext tags should come at the end |
174 |
* multi-line docstrings must finish with an empty line |
175 |
* do not try to make a table using lots of whitespace |
176 |
* use ``L{}`` and ``C{}`` where appropriate |
177 |
|
178 |
Here's an example:: |
179 |
|
180 |
def fn(foo, bar): |
181 |
"""Compute the sum of foo and bar. |
182 |
|
183 |
This functions builds the sum of foo and bar. It's a simple function. |
184 |
|
185 |
@type foo: int |
186 |
@param foo: First parameter. |
187 |
@type bar: float |
188 |
@param bar: The second parameter. This line is longer |
189 |
to show wrapping. |
190 |
@rtype: float |
191 |
@return: the sum of the two numbers |
192 |
|
193 |
""" |
194 |
return foo + bar |
195 |
|
196 |
Some rules of thumb which should be applied with good judgement on a case-to- |
197 |
case basis: |
198 |
|
199 |
* If the meaning of parameters is already obvious given its name and the |
200 |
methods description, don't document it again. Just add a ``@type`` tag. |
201 |
* Refer to the base methods documentation when overwriting methods. Only |
202 |
document more if it applies to the current subclass only, or if you want to |
203 |
clarify on the meaning of parameters for the special subclass. |
204 |
|
205 |
Rules for classes and modules |
206 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
207 |
As `PEP 257`_ says, the docstrings of classes should document their attributes |
208 |
and the docstrings of modules should shortly document the exported |
209 |
functions/variables/etc. |
210 |
|
211 |
See for example the pydoc output for the ``os`` or ``ConfigParser`` standard |
212 |
modules. |
213 |
|
214 |
Haskell |
215 |
------- |
216 |
|
217 |
.. highlight:: haskell |
218 |
|
219 |
The most important consideration is, as usual, to stay consistent with the |
220 |
existing code. |
221 |
|
222 |
As there's no "canonical" style guide for Haskell, this code style has been |
223 |
inspired from a few online resources, including the style guide for the |
224 |
`Snap framework`_, `this style guide`_ and `this other style guide`_. |
225 |
|
226 |
.. _Snap framework: http://snapframework.com/docs/style-guide |
227 |
.. _this style guide: https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md |
228 |
.. _this other style guide: http://www.cs.caltech.edu/courses/cs11/material/haskell/misc/haskell_style_guide.html |
229 |
|
230 |
Files |
231 |
~~~~~ |
232 |
Use ordinary, non-`literate`_ Haskell ``.hs`` files. |
233 |
|
234 |
.. _literate: http://www.haskell.org/haskellwiki/Literate_programming |
235 |
|
236 |
Use proper copyright headers, and proper Haddock style documentation headers:: |
237 |
|
238 |
{-| Short module summary. |
239 |
|
240 |
Longer module description. |
241 |
|
242 |
-} |
243 |
|
244 |
{- |
245 |
|
246 |
Copyright (C) ... |
247 |
|
248 |
This program is free software ... |
249 |
|
250 |
-} |
251 |
|
252 |
If there are module-level pragmas add them right at the top, before the short |
253 |
summary. |
254 |
|
255 |
Imports |
256 |
~~~~~~~ |
257 |
Imports should be grouped into the following groups and inside each group they |
258 |
should be sorted alphabetically: |
259 |
|
260 |
1. standard library imports |
261 |
2. third-party imports |
262 |
3. local imports |
263 |
|
264 |
It is allowed to use qualified imports with short names for: |
265 |
|
266 |
* standard library (e.g. ``import qualified Data.Map as M``) |
267 |
* local imports (e.g. ``import qualified Ganeti.Constants as C``), although |
268 |
this form should be kept to a minimum |
269 |
|
270 |
Indentation |
271 |
~~~~~~~~~~~ |
272 |
Use only spaces, never tabs. Indentation level is 2 characters. For Emacs, |
273 |
this means setting the variable ``haskell-indent-offset`` to 2. |
274 |
|
275 |
Line length should be at most 78 chars, and 72 chars inside comments. |
276 |
|
277 |
Use indentation-based structure, and not braces/semicolons. |
278 |
|
279 |
.. note:: |
280 |
|
281 |
Special indendation of if/then/else construct |
282 |
|
283 |
For the ``do`` notation, the ``if-then-else`` construct has a non-intuitive |
284 |
behaviour. As such, the indentation of ``if-then-else`` (both in ``do`` |
285 |
blocks and in normal blocks) should be as follows:: |
286 |
|
287 |
if condition |
288 |
then expr1 |
289 |
else expr2 |
290 |
|
291 |
i.e. indent the then/else lines with another level. This can be accomplished |
292 |
in Emacs by setting the variable ``haskell-indent-thenelse`` to 2 (from the |
293 |
default of zero). |
294 |
|
295 |
If you have more than one line of code please newline/indent after the "=". Do |
296 |
`not` do:: |
297 |
|
298 |
f x = let y = x + 1 |
299 |
in y |
300 |
|
301 |
Instead do:: |
302 |
|
303 |
f x = |
304 |
let y = x + 1 |
305 |
in y |
306 |
|
307 |
or if it is just one line:: |
308 |
|
309 |
f x = x + 1 |
310 |
|
311 |
Multiline strings |
312 |
~~~~~~~~~~~~~~~~~ |
313 |
Multiline strings are created by closing a line with a backslash and starting |
314 |
the following line with a backslash, keeping the indentation level constant. |
315 |
Whitespaces go on the new line, right after the backslash. |
316 |
|
317 |
:: |
318 |
|
319 |
longString :: String |
320 |
longString = "This is a very very very long string that\ |
321 |
\ needs to be split in two lines" |
322 |
|
323 |
Data declarations |
324 |
~~~~~~~~~~~~~~~~~ |
325 |
.. warning:: |
326 |
Note that this is different from the Python style! |
327 |
|
328 |
When declaring either data types, or using list literals, etc., the columns |
329 |
should be aligned, and for lists use a comma at the start of the line, not at |
330 |
the end. Examples:: |
331 |
|
332 |
data OpCode = OpStartupInstance ... |
333 |
| OpShutdownInstance ... |
334 |
| ... |
335 |
|
336 |
data Node = Node { name :: String |
337 |
, ip :: String |
338 |
, ... |
339 |
} |
340 |
|
341 |
myList = [ value1 |
342 |
, value2 |
343 |
, value3 |
344 |
] |
345 |
|
346 |
The choice of whether to wrap the first element or not is up to you; the |
347 |
following is also allowed:: |
348 |
|
349 |
myList = |
350 |
[ value1 |
351 |
, value2 |
352 |
] |
353 |
|
354 |
For records, always add spaces around the braces and the equality sign. |
355 |
:: |
356 |
|
357 |
foo = Foo { fBar = "bar", fBaz = 4711 } |
358 |
|
359 |
foo' = Foo { fBar = "bar 2" |
360 |
, fBaz = 4712 |
361 |
} |
362 |
|
363 |
node' = node { ip = "127.0.0.1" } |
364 |
|
365 |
|
366 |
White space |
367 |
~~~~~~~~~~~ |
368 |
Like in Python, surround binary operators with one space on either side. Do no |
369 |
insert a space after a lamda:: |
370 |
|
371 |
-- bad |
372 |
map (\ n -> ...) lst |
373 |
-- good |
374 |
foldl (\x y -> ...) ... |
375 |
|
376 |
Use a blank line between top-level definitions, but no blank lines between |
377 |
either the comment and the type signature or between the type signature and |
378 |
the actual function definition. |
379 |
|
380 |
.. note:: |
381 |
Ideally it would be two blank lines between top-level definitions, but the |
382 |
code only has one now. |
383 |
|
384 |
As always, no trailing spaces. Ever. |
385 |
|
386 |
Spaces after comma |
387 |
****************** |
388 |
|
389 |
Instead of:: |
390 |
|
391 |
("a","b") |
392 |
|
393 |
write:: |
394 |
|
395 |
("a", "b") |
396 |
|
397 |
Naming |
398 |
~~~~~~ |
399 |
Functions should be named in mixedCase style, and types in CamelCase. Function |
400 |
arguments and local variables should be mixedCase. |
401 |
|
402 |
When using acronyms, ones longer than 2 characters should be typed capitalised, |
403 |
not fully upper-cased (e.g. ``Http``, not ``HTTP``). |
404 |
|
405 |
For variable names, use descriptive names; it is only allowed to use very |
406 |
short names (e.g. ``a``, ``b``, ``i``, ``j``, etc.) when: |
407 |
|
408 |
* the function is trivial, e.g.:: |
409 |
|
410 |
sum x y = x + y |
411 |
|
412 |
* we talk about some very specific cases, e.g. |
413 |
iterators or accumulators in folds:: |
414 |
|
415 |
map (\v -> v + 1) lst |
416 |
|
417 |
* using ``x:xs`` for list elements and lists, etc. |
418 |
|
419 |
In general, short/one-letter names are allowed when we deal with polymorphic |
420 |
values; for example the standard map definition from Prelude:: |
421 |
|
422 |
map :: (a -> b) -> [a] -> [b] |
423 |
map _ [] = [] |
424 |
map f (x:xs) = f x : map f xs |
425 |
|
426 |
In this example, neither the ``a`` nor ``b`` types are known to the map |
427 |
function, so we cannot give them more explicit names. Since the body of the |
428 |
function is trivial, the variables used are longer. |
429 |
|
430 |
However, if we deal with explicit types or values, their names should be |
431 |
descriptive. |
432 |
|
433 |
.. todo: add a nice example here. |
434 |
|
435 |
Finally, the naming should look familiar to people who just read the |
436 |
Prelude/standard libraries. |
437 |
|
438 |
Naming for updated values |
439 |
************************* |
440 |
|
441 |
.. highlight:: python |
442 |
|
443 |
Since one cannot update a value in Haskell, this presents a particular problem |
444 |
on the naming of new versions of the same value. For example, the following |
445 |
code in Python:: |
446 |
|
447 |
def failover(pri, sec, inst): |
448 |
pri.removePrimary(inst) |
449 |
pri.addSecondary(inst) |
450 |
sec.removeSecondary(inst) |
451 |
sec.addPrimary(inst) |
452 |
|
453 |
.. highlight:: haskell |
454 |
|
455 |
becomes in Haskell something like the following:: |
456 |
|
457 |
failover pri sec inst = |
458 |
let pri' = removePrimary pri inst |
459 |
pri'' = addSecondary pri' inst |
460 |
sec' = removeSecondary sec inst |
461 |
sec'' = addPrimary sec' inst |
462 |
in (pri'', sec'') |
463 |
|
464 |
When updating values, one should add single quotes to the name for up to three |
465 |
new names (e.g. ``inst``, ``inst'``, ``inst''``, ``inst'''``) and otherwise |
466 |
use numeric suffixes (``inst1``, ``inst2``, ``inst3``, ..., ``inst8``), but |
467 |
that many updates is already bad style and thus should be avoided. |
468 |
|
469 |
Type signatures |
470 |
~~~~~~~~~~~~~~~ |
471 |
|
472 |
Always declare types for functions (and any other top-level bindings). |
473 |
|
474 |
If in doubt, feel free to declare the type of the variables/bindings in a |
475 |
complex expression; this usually means the expression is too complex, however. |
476 |
|
477 |
Similarly, provide Haddock-style comments for top-level definitions. |
478 |
|
479 |
Use sum types instead of exceptions |
480 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
481 |
|
482 |
Exceptions make it hard to write functional code, as alternative |
483 |
control flows need to be considered and compiler support is limited. |
484 |
Therefore, Ganeti functions should never allow exceptions to escape. |
485 |
Function that can fail should report failure by returning an appropriate |
486 |
sum type (``Either`` or one of its glorified variants like ``Maybe`` or |
487 |
``Result``); the preferred sum type for reporting errors is ``Result``. |
488 |
|
489 |
As other Ganeti functions also follow these guide lines, they can safely |
490 |
be composed. However, be careful when using functions from other libraries; |
491 |
if they can raise exceptions, catch them, preferably as close to their |
492 |
origin as reasonably possible. |
493 |
|
494 |
Parentheses, point free style |
495 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
496 |
|
497 |
Prefer the so-called `point-free`_ style style when declaring functions, if |
498 |
applicable:: |
499 |
|
500 |
-- bad |
501 |
let a x = f (g (h x)) |
502 |
-- good |
503 |
let a = f . g . h |
504 |
|
505 |
Also use function composition in a similar manner in expressions to avoid extra |
506 |
parentheses:: |
507 |
|
508 |
-- bad |
509 |
f (g (h x)) |
510 |
-- better |
511 |
f $ g $ h x |
512 |
-- best |
513 |
f . g . h $ x |
514 |
|
515 |
.. _`point-free`: http://www.haskell.org/haskellwiki/Pointfree |
516 |
|
517 |
Language features |
518 |
~~~~~~~~~~~~~~~~~ |
519 |
|
520 |
Extensions |
521 |
********** |
522 |
|
523 |
It is recommended to keep the use of extensions to a minimum, so that the code |
524 |
can be understood even if one is familiar with just Haskel98/Haskell2010. That |
525 |
said, some extensions are very common and useful, so they are recommended: |
526 |
|
527 |
* `Bang patterns`_: useful when you want to enforce strict evaluation (and better |
528 |
than repeated use of ``seq``) |
529 |
* CPP: a few modules need this in order to account for configure-time options; |
530 |
don't overuse it, since it breaks multi-line strings |
531 |
* `Template Haskell`_: we use this for automatically deriving JSON instances and |
532 |
other similar boiler-plate |
533 |
|
534 |
.. _Bang patterns: http://www.haskell.org/ghc/docs/latest/html/users_guide/bang-patterns.html |
535 |
.. _Template Haskell: http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html |
536 |
|
537 |
Such extensions should be declared using the ``Language`` pragma:: |
538 |
|
539 |
{-# Language BangPatterns #-} |
540 |
|
541 |
{-| This is a small module... -} |
542 |
|
543 |
Comments |
544 |
******** |
545 |
|
546 |
Always use proper sentences; start with a capital letter and use punctuation |
547 |
in top level comments:: |
548 |
|
549 |
-- | A function that does something. |
550 |
f :: ... |
551 |
|
552 |
For inline comments, start with a capital letter but no ending punctuation. |
553 |
Furthermore, align the comments together with a 2-space width from the end of |
554 |
the item being commented:: |
555 |
|
556 |
data Maybe a = Nothing -- ^ Represents empty container |
557 |
| Just a -- ^ Represents a single value |
558 |
|
559 |
The comments should be clear enough so that one doesn't need to look at the |
560 |
code to understand what the item does/is. |
561 |
|
562 |
Use ``-- |`` to write doc strings rather than bare comment with ``--``. |
563 |
|
564 |
Tools |
565 |
***** |
566 |
|
567 |
We generate the API documentation via Haddock, and as such the comments should |
568 |
be correct (syntax-wise) for it. Use markup, but sparingly. |
569 |
|
570 |
We use hlint_ as a lint checker; the code is currently lint-clean, so you must |
571 |
not add any warnings/errors. |
572 |
|
573 |
.. _hlint: http://community.haskell.org/~ndm/darcs/hlint/hlint.htm |
574 |
|
575 |
Use these two commands during development:: |
576 |
|
577 |
make hs-apidoc |
578 |
make hlint |
579 |
|
580 |
QuickCheck best practices |
581 |
************************* |
582 |
|
583 |
If you have big type that takes time to generate and several properties to |
584 |
test on that, by default 500 of those big instances are generated for each |
585 |
property. In many cases, it would be sufficient to only generate those 500 |
586 |
instances once and test all properties on those. To do this, create a property |
587 |
that uses ``conjoin`` to combine several properties into one. Use |
588 |
``printTestCase`` to add expressive error messages. For example:: |
589 |
|
590 |
prop_myMegaProp :: myBigType -> Property |
591 |
prop_myMegaProp b = |
592 |
conjoin |
593 |
[ printTestCase |
594 |
("Something failed horribly here: " ++ show b) (subProperty1 b) |
595 |
, printTestCase |
596 |
("Something else failed horribly here: " ++ show b) |
597 |
(subProperty2 b) |
598 |
, -- more properties here ... |
599 |
] |
600 |
|
601 |
subProperty1 :: myBigType -> Bool |
602 |
subProperty1 b = ... |
603 |
|
604 |
subProperty2 :: myBigType -> Property |
605 |
subProperty2 b = ... |
606 |
|
607 |
... |
608 |
|
609 |
Maybe Generation |
610 |
'''''''''''''''' |
611 |
|
612 |
Use ``genMaybe genSomething`` to create ``Maybe`` instances of something |
613 |
including some ``Nothing`` instances. |
614 |
|
615 |
Use ``Just <$> genSomething`` to generate only ``Just`` instances of |
616 |
something. |
617 |
|
618 |
String Generation |
619 |
''''''''''''''''' |
620 |
|
621 |
To generate strings, consider using ``genName`` instead of ``arbitrary``. |
622 |
``arbitrary`` has the tendency to generate strings that are too long. |