Coding Style Guide

Follow PEP8 (the official style guide for Python). Most PEP8 formatting conventions are enforced in the build by pylint, flake8, and a combination of black, autopep8, and isort. Therefore, if you do not follow them, the build may not pass.

However, for Ion, we limit the lengths of lines to 150 characters, not 80 characters.

Note: CSS/JS/template formatting is enforced by scripts/static_templates_format.sh. Currently, this just strips trailing whitespace.

Main points

  • Indent using 4 spaces.

  • Use underscores in favor of camel case for all names except the names of classes.

  • Limit the line length of docstrings or comments to 72 characters.

  • Separate top-level functions and class definitions with two blank lines.

  • Separate method definitions inside a class with a single blank line.

  • Use two spaces before inline comments and one space between the pound sign and comment.

  • Use a plugin for your text editor to check for/remind you of PEP8 conventions.

  • When in doubt, running ./scripts/format.sh will fix a lot of things.

  • Capitalize and punctuate comments and Git commit messages properly.

What is enforced in the build

At the time of this writing, the GitHub Actions build runs the following commands:

flake8 --max-line-length 150 --exclude=*/migrations/* .
pylint --jobs=0 --disable=fixme,broad-except,global-statement,attribute-defined-outside-init intranet/
isort --check --recursive intranet
./scripts.format.sh
./scripts/static_templates_format.sh  # Static/template files

Note: When the ./scripts/format.sh and ./scripts/static_templates_format.sh checks are run, the build will fail if they have to make any changes.

flake8 is a PEP8 style checker, pylint is a linter (but it also enforces some PEP8 conventions), and isort, when called with these options, checks that all imports are sorted alphabetically.

./scripts/format.sh runs black intranet && autopep8 --in-place --recursive intranet && isort --recursive intranet. The reason for the multiple commands is that black introduces certain formatting changes which flake8/pylint do not agree with (and offers no options to change them), so we have autopep8 fix it.

It is recommended that you run all of these locally before opening a pull request (though the Ion developers sometimes skip running the pylint check locally because it takes a long time to run). All of them are intended to be run from the root directory of the Git repository.

If flake8 or pylint throw errors, the error messages are usually human-readable. if isort gives any errors, you can have it automatically correct the order of all imports by running isort --recursive intranet. If the build fails because running scripts/format.sh resulted in changes, you can simply run ./scripts/format.sh to fix your formatting.

Imports

  • Group imports in the following order:
    1. Standard library imports

    2. Third-party imports

    3. Imports from Django

    4. Local imports

  • Within these groups, place from ... import ... imports after import ... imports, and order imports alphabetically within those groups.

  • Avoid using from ... import *.

  • Explicitly import each module used.

  • Use relative imports to avoid hardcoding a module’s package name. This greatly improves portability and is useful when importing from another module in the current app.

Examples

Standard library imports:

from math import sqrt
from os.path import abspath

Core Django imports:

from django.db import models

Third-party app imports:

from django_extensions.db.models import TimeStampedModel

Good:

from .models import SomeModel  # explicit relative import
from otherdjangoapp.models import OtherModel  # absolute import

Bad:

# intranet/apps/users/views.py
from intranet.apps.users.models import MyModel  # absolute import within same package