-
Notifications
You must be signed in to change notification settings - Fork 103
Coding Style Guide
ReFrame follows the PEP8 coding style with some exceptions. These exceptions are mostly based on our perception of some aesthetic aspects of the code.
In short, we ignore the following PEP8 error codes:
- E129: visually indented line with same indent as next logical line
- E221: multiple spaces before operator
- E226: missing whitespace around arithmetic operator
- E241: whitespace after
:
- E272: multiple spaces before keyword
- E741: do not use variables named 'l', 'O', or 'I
- E742: do not define classes named ‘l’, ‘O’, or ‘I’
- E743: do not define functions named ‘l’, ‘O’, or ‘I’
- W504: line break after binary operator
A rationale for these exceptions follows along with some other formatting conventions that are not prescribed by PEP8 explicitly, but should be followed by the ReFrame code.
Variable names must be descriptive but not excessively long. We prefer abbreviations. We do allow length one variables of any character in certain contexts, e.g., as automatic variables in unit tests:
# OK
l = [1, 2]
# not OK
alist = [1, 2]
We believe that the appropriate variable naming is a matter of proper code review.
For this reason, we choose to ignore the errors E741
, E742
and E743
.
We prefer the Pythonic implicit line continuation inside parentheses or brackets.
This happens naturally for lists, tuples, sets and dictionaries, but for expressions or lengthy assingments you must use parentheses explicitly instead of the \
line continuation character:
# OK
if (a_long_var > 0 and
b_long_var == 3):
do_sth()
# not OK
if a_long_var > 0 and \
b_long_var == 3:
do_sth()
However, the above if
statement might give the E129 formatting error, but we choose to ignore it, because we prefer to be consistent with our line continuation policy.
After all, PEP8 leaves it free.
NOTE on commenting
if
blocks: A comment that explains a specific branch should go inside the branch, not before theif
. If the comment explains generally what we check, it should go outside.
Apart from the PEP8 requirements of the number of blank lines between functions, methods and classes, ReFrame code should follow the following rules:
- Leave a blank line after each indented block of code, e.g., after
if
,for
,while
,with
andtry/except
statements. - Leave a blank line before a line comment that explains the code following it
- Other blank lines are discouraged.
Here are some examples:
if msg == 'hello':
do_sth()
else:
do_sth_else()
do_more_stuff_now()
# not OK
if msg == 'hello':
do_sth()
else:
do_sth_else()
do_more_stuff_now()
# not OK
if msg == 'hello':
do_sth()
else:
do_sth_else()
do_more_stuff_now()
# not OK
if msg == 'hello':
do_sth()
else:
do_sth_else()
do_more_stuff_now()
# OK
try:
x = mydict[key]
except KeyError:
handle_error()
do_other_stuff_now()
# not OK
try:
x = mydict[key]
except KeyError:
handle_error()
do_other_stuff_now()
# not OK
try:
x = mydict[key]
except KeyError:
handle_error()
do_other_stuff_now()
Some more examples follow on the use of single comments and blank lines.
# OK
# Create foos and bars
foo1 = Foo()
foo2 = Foo()
bar1 = Bar()
bar2 = Bar()
# Connect foos with bars
foo1.connect(bar1)
foo2.connect(bar2)
# not OK
# Create foos and bars
foo1 = Foo()
foo2 = Foo()
bar1 = Bar()
bar2 = Bar()
# Connect foos with bars
foo1.connect(bar1)
foo2.connect(bar2)
# maybe OK
# Create foos and bars
foo1 = Foo()
foo2 = Foo()
bar1 = Bar()
bar2 = Bar()
foo1.connect(bar1)
foo2.connect(bar2)
When formatting dictionaries, we follow the PEP8 convention in general, with the exception that we allow multiple spaces after :
in order to align the values. This forces us to ignore E241.
# OK -- PEP8
multiline_dict = {
'foo': 1,
'foobar': 2
}
# OK
multiline_dict = {
'foo': 1,
'foobar': 2
}
# not OK
multiline_dict = {
'foo' : 1,
'foobar' : 2
}
# not OK
multiline_dict = {
'foo' : 1,
'foobar': 2
}
In multiline conditionals we always split the line after the operator. For aesthetics reasons we allow alignment of the binary operators as follows:
# OK
if (a_long_var > 0 and
b_long_var == 3 and
smaller != 5):
do_sth()
However, if the two lines have very different lengths, it's better not to align. Similar rules apply to assignments in successive lines:
# OK
a_long = 1
a_longer = 2
# OK, but maybe ugly
a_long = 1
a_very_long = 2
For these reasons, we ignore E221 and W504.
Finally, we allow the following type of aligning in successive assignemnts:
cflags = my_cflags or ''
cxxflags = my_cppflags or ''
The alignment of or
here forces us to ignore E272 as well.
We are also a bit relaxed regarding the whitespace around arithmetic operators. We generally prefer the whitespace around every arithmetic operator, however, we accept removal of the whitespace in cases it makes the reading of an expression clearer (e.g., by creating visual groupings of the operands of operators with higher precedence):
# OK
n = 2*x + 1 # (1)
# OK, but not so readable as the previous one
n = 2 * x + 1 # (2)
# not OK
n = 2*x+1 # (3)
In order to accept syntax (1), we ignore also E226.
We use single quotes for strings, except if a single quote is part of the string, in which case we are using double quotes:
# OK
s = 'hello, world'
# OK
s = "could not find file `foo.txt'"
# not OK
s = "hello, world"
For consistency, we use three single quotes for docstrings:
def foo():
'''Returns the FOO number.'''
PEP8 leaves some grey area regarding the imports.
Apart from their order (standard library, third-party, application), it does not go into detail on how to arrange the import ...
vs. from ... import ...
statements.
To promote consistency, our coding style builds upon PEP8 and defines the following rules:
- The imports must be grouped into three major groups as required by PEP8.
- The different groups must be separated by a single blank line.
- Inside each group,
import ...
statements must precede thefrom ... import ...
statements. - All the
import ...
and thefrom ... import ...
statements must be sorted alphabetically. - Names imported with single line import statements must be sorted alphabetically
An example of an import sequence follows:
import abc
import os
from datetime import datetime
from subprocess import Popen
import thirdparty
import thirdparty.core
from thirdparty.abc import sth
from thirdparty.module import sth_else
import reframe.utility.sanity as sn
from reframe.core.pipeline import RegressionTest
from reframe.core.exceptions import ConfigError, JobError
-
pycodestyle
- Usage:
pycodestyle --ignore=E129,E221,E226,E241,E272,E741,E742,E743,W504 [FILE|DIR]
- Produces a list non-compliances.
- Usage:
-
autopep8
- Based on
pycodestyle
, auto-corrects the source file. - Usage:
autopep8 -i --ignore=E129,E221,E226,E241,E272,E741,E742,E743,W504 [FILE]
- Based on
NOTE: Although auto-formatting tools do a great job in homogenizing the code and correcting formatting mistakes, they cannot fully replace the developer's best judgement, especially when splitting long lines. Often they will split in not so meaningful places just to meet the formatting requirements. As a rule of thumb, try to conform to the style as you type.
- Emacs:
- Install the
py-autopep8
package - Add the following lines in your
.emacs
file (format file on save):(add-hook 'python-mode-hook 'py-autopep8-enable-on-save) (setq py-autopep8-options '("--ignore=E129,E221,E226,E241,E272,E741,E742,E743,W504"))
- Install the
- Vim:
ReFrame is released under the BSD-3 license. The following text should be inserted in the beginning of each file of the project:
# Copyright 2016-<current-year> Swiss National Supercomputing Centre (CSCS/ETH Zurich)
# ReFrame Project Developers. See the top-level LICENSE file for details.
#
# SPDX-License-Identifier: BSD-3-Clause