Skip to content

Coding Conventions

Steve Wardle edited this page Jul 16, 2020 · 4 revisions

The styling of our source is derived from PEP8 and the flake8 tool. There are, however, issues relating to patterns of usage which are not covered by that. Eventually this page will become our settled guidelines on these issues but for the moment it is a place to record discussions.

How to import a module

There are two basic ways to use a module, they can be imported: whole or in parts. Which you choose effects how you refer to the included objects.

If the whole module is imported then any part thereof must be specified by a full path.

import some.old.toot
...
if some.old.toot.gubbins():
    variable = some.old.toot.Thing()

On the other hand importing only parts of a module allow them to be referred to directly.

from some.old.toot import gubbins, Thing
...
if gubbins():
    variable = Thing()

The great advantage of the second approach is that it shortens the references which helps keep a lid on line length. The major down side is that it is not uncommon for modules to provide convenience functions such as open which share names with built-in objects. These references then become ambiguous. It is also slightly less clear where a thing comes from at first glance.

Conversesly the first approach is very clear on the origin of things and there is no chance of collisions with built-in objects. However it can lead to the source being messy and lines get long very fast.

Matthew says:

I tend to use the full import for module level functions but like the partial import for classes. The problem with this is when you want to do both.

Steve says:

I like the suggestion of using the partial import for class names and the full import for functions; I guess in the case of needing both there's nothing stopping us from having 2 import lines? Perhaps with the added convention that where we have imports from the same module they must be adjacent to each other:

 import some.module
 from some.module import AClass

How to span argument lists across lines

When an argument list, either in function definition or call, gets too long for a line it must be broken across lines. There are many different ways to achieve this but it would probably be best to agree on just one.

There are so many alternative approaches that instead of trying to list all or even some of them it's probably best just to present the ones we like and then have a discussion about those.

Matthew says:

I like to include the first argument on the first line and line up remaining arguments below it:

def foo(first,
        second,
        third):

I find the aligned argument start to be easy to track down whithout adding any more lines than necessary. It does lead to diff churn when adding arguments at the beginning and end but I don't consider that a problem as code is read far more often than it is written or reviewed.

Steve says:

I do like the above approach (of keeping first argument inlined and subsequent arguments lined up with the first) but it gets awkward when you have this situation:

def quite_long_function_name(and_first_argument_is_also_quite_long, # maybe over 80
                             or_first_arg_is_short_but_later_arg_is_long,

It's quite possible (probably, even) that the above situation arises from a design failure and the arguments shouldn't ever be statement or appear long enough for this to happen... but when it does I tend to switch to the argument style of having the first argument on the next line (indented to 4 characters):

def quite_long_function_name(
    and_first_argument_is_also_quite_long,
    or_first_arg_is_short_but_later_arg_is_long,

General line continuation

Python is happy to continue to the next line with no newline escape provided it is enclosed by a structure of some sort:

setting_a_variable = [
    this_is_fine,
]

But where there isn't an enclosing structure one has to use either an escaped newline or an enclosing tuple, our most common case of doing this would be:

from fab.some.module import \
    thingA, \
    thingB

versus:

from fab.some.module import (
    thingA,
    thingB,
)

Steve says:

I think the latter (i.e. not using escapes) is more common

Clone this wiki locally