Statistics
| Branch: | Tag: | Revision:

root / doc / dev-codestyle.rst @ eea5e916

History | View | Annotate | Download (16.9 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
White space
355
~~~~~~~~~~~
356
Like in Python, surround binary operators with one space on either side. Do no
357
insert a space after a lamda::
358

    
359
  -- bad
360
  map (\ n -> ...) lst
361
  -- good
362
  foldl (\x y -> ...) ...
363

    
364
Use a blank line between top-level definitions, but no blank lines between
365
either the comment and the type signature or between the type signature and
366
the actual function definition.
367

    
368
.. note::
369
  Ideally it would be two blank lines between top-level definitions, but the
370
  code only has one now.
371

    
372
As always, no trailing spaces. Ever.
373

    
374
Spaces after comma
375
******************
376

    
377
Instead of::
378

    
379
  ("a","b")
380

    
381
write::
382

    
383
  ("a", "b")
384

    
385
Naming
386
~~~~~~
387
Functions should be named in mixedCase style, and types in CamelCase. Function
388
arguments and local variables should be mixedCase.
389

    
390
When using acronyms, ones longer than 2 characters should be typed capitalised,
391
not fully upper-cased (e.g. ``Http``, not ``HTTP``).
392

    
393
For variable names, use descriptive names; it is only allowed to use very
394
short names (e.g. ``a``, ``b``, ``i``, ``j``, etc.) when:
395

    
396
* the function is trivial, e.g.::
397

    
398
    sum x y = x + y
399

    
400
* we talk about some very specific cases, e.g.
401
  iterators or accumulators in folds::
402

    
403
    map (\v -> v + 1) lst
404

    
405
* using ``x:xs`` for list elements and lists, etc.
406

    
407
In general, short/one-letter names are allowed when we deal with polymorphic
408
values; for example the standard map definition from Prelude::
409

    
410
  map :: (a -> b) -> [a] -> [b]
411
  map _ []     = []
412
  map f (x:xs) = f x : map f xs
413

    
414
In this example, neither the ``a`` nor ``b`` types are known to the map
415
function, so we cannot give them more explicit names. Since the body of the
416
function is trivial, the variables used are longer.
417

    
418
However, if we deal with explicit types or values, their names should be
419
descriptive.
420

    
421
.. todo: add a nice example here.
422

    
423
Finally, the naming should look familiar to people who just read the
424
Prelude/standard libraries.
425

    
426
Naming for updated values
427
*************************
428

    
429
.. highlight:: python
430

    
431
Since one cannot update a value in Haskell, this presents a particular problem
432
on the naming of new versions of the same value. For example, the following
433
code in Python::
434

    
435
  def failover(pri, sec, inst):
436
    pri.removePrimary(inst)
437
    pri.addSecondary(inst)
438
    sec.removeSecondary(inst)
439
    sec.addPrimary(inst)
440

    
441
.. highlight:: haskell
442

    
443
becomes in Haskell something like the following::
444

    
445
  failover pri sec inst =
446
    let pri'  = removePrimary pri inst
447
        pri'' = addSecondary pri' inst
448
        sec'  = removeSecondary sec inst
449
        sec'' = addPrimary sec' inst
450
    in (pri'', sec'')
451

    
452
When updating values, one should add single quotes to the name for up to three
453
new names (e.g. ``inst``, ``inst'``, ``inst''``, ``inst'''``) and otherwise
454
use numeric suffixes (``inst1``, ``inst2``, ``inst3``, ..., ``inst8``), but
455
that many updates is already bad style and thus should be avoided.
456

    
457
Type signatures
458
~~~~~~~~~~~~~~~
459

    
460
Always declare types for functions (and any other top-level bindings).
461

    
462
If in doubt, feel free to declare the type of the variables/bindings in a
463
complex expression; this usually means the expression is too complex, however.
464

    
465
Similarly, provide Haddock-style comments for top-level definitions.
466

    
467
Parentheses, point free style
468
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
469

    
470
Prefer the so-called `point-free`_ style style when declaring functions, if
471
applicable::
472

    
473
  -- bad
474
  let a x = f (g (h x))
475
  -- good
476
  let a = f . g . h
477

    
478
Also use function composition in a similar manner in expressions to avoid extra
479
parentheses::
480

    
481
  -- bad
482
  f (g (h x))
483
  -- better
484
  f $ g $ h x
485
  -- best
486
  f . g . h $ x
487

    
488
.. _`point-free`: http://www.haskell.org/haskellwiki/Pointfree
489

    
490
Language features
491
~~~~~~~~~~~~~~~~~
492

    
493
Extensions
494
**********
495

    
496
It is recommended to keep the use of extensions to a minimum, so that the code
497
can be understood even if one is familiar with just Haskel98/Haskell2010. That
498
said, some extensions are very common and useful, so they are recommended:
499

    
500
* `Bang patterns`_: useful when you want to enforce strict evaluation (and better
501
  than repeated use of ``seq``)
502
* CPP: a few modules need this in order to account for configure-time options;
503
  don't overuse it, since it breaks multi-line strings
504
* `Template Haskell`_: we use this for automatically deriving JSON instances and
505
  other similar boiler-plate
506

    
507
.. _Bang patterns: http://www.haskell.org/ghc/docs/latest/html/users_guide/bang-patterns.html
508
.. _Template Haskell: http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html
509

    
510
Such extensions should be declared using the ``Language`` pragma::
511

    
512
  {-# Language BangPatterns #-}
513

    
514
  {-| This is a small module... -}
515

    
516
Comments
517
********
518

    
519
Always use proper sentences; start with a capital letter and use punctuation
520
in top level comments::
521

    
522
  -- | A function that does something.
523
  f :: ...
524

    
525
For inline comments, start with a capital letter but no ending punctuation.
526
Furthermore, align the comments together with a 2-space width from the end of
527
the item being commented::
528

    
529
  data Maybe a = Nothing  -- ^ Represents empty container
530
               | Just a   -- ^ Represents a single value
531

    
532
The comments should be clear enough so that one doesn't need to look at the
533
code to understand what the item does/is.
534

    
535
Use ``-- |`` to write doc strings rather than bare comment with ``--``.
536

    
537
Tools
538
*****
539

    
540
We generate the API documentation via Haddock, and as such the comments should
541
be correct (syntax-wise) for it. Use markup, but sparingly.
542

    
543
We use hlint_ as a lint checker; the code is currently lint-clean, so you must
544
not add any warnings/errors.
545

    
546
.. _hlint: http://community.haskell.org/~ndm/darcs/hlint/hlint.htm
547

    
548
Use these two commands during development::
549

    
550
  make hs-apidoc
551
  make hlint
552

    
553
QuickCheck best practices
554
*************************
555

    
556
If you have big type that takes time to generate and several properties to
557
test on that, by default 500 of those big instances are generated for each
558
property. In many cases, it would be sufficient to only generate those 500
559
instances once and test all properties on those. To do this, create a property
560
that uses ``conjoin`` to combine several properties into one. Use
561
``printTestCase`` to add expressive error messages. For example::
562

    
563
  prop_myMegaProp :: myBigType -> Property
564
  prop_myMegaProp b =
565
    conjoin
566
      [ printTestCase
567
          ("Something failed horribly here: " ++ show b) (subProperty1 b)
568
      , printTestCase
569
          ("Something else failed horribly here: " ++ show b)
570
          (subProperty2 b)
571
      , -- more properties here ...
572
      ]
573

    
574
  subProperty1 :: myBigType -> Bool
575
  subProperty1 b = ...
576

    
577
  subProperty2 :: myBigType -> Property
578
  subProperty2 b = ...
579

    
580
  ...
581

    
582
Maybe Generation
583
''''''''''''''''
584

    
585
Use ``genMaybe genSomething`` to create ``Maybe`` instances of something
586
including some ``Nothing`` instances.
587

    
588
Use ``Just <$> genSomething`` to generate only ``Just`` instances of
589
something.
590

    
591
String Generation
592
'''''''''''''''''
593

    
594
To generate strings, consider using ``genName`` instead of ``arbitrary``.
595
``arbitrary`` has the tendency to generate strings that are too long.