Coding style guide for the Ghostwriter code base
To maintain consistency as the project develops, all contributors to the project should keep the following requirements in mind before committing code or submitting a pull request. Using Visual Studio Code (VSCode) makes it easier to follow this style guide. If a developer uses a different editor, pre-commit hooks are provided at the bottom of the page.
The Ghostwriter project uses Python Black to enforce code style.
Microsoft's official Python extension for VSCode supports formatters like Black. Microsoft's documentation covers this topic:
Black is "opinionated" and automatically changes things to keep code consistent – like intelligently changing single quotes to double quotes.
- 1.Install the Python extension in VSCode (SHIFT+CMD+X and search for
- 2.Install Black for the Python interpreter or virtual environment selected for VSCode
- 3.Add the following settings to VSCode's settings.json:
VSCode will now use Black to format all Python files on save actions.
Black will not automatically delete trailing whitespace or whitespace on otherwise empty lines. Trailing whitespace will be identified by the linter (below). Add this line to VSCode's settings.json file to automatically delete trailing whitespace on save:
The Ghostwriter project also uses
Isortwhich is part of the Python extensions
Python Refactortoolkit (
Python Refactor: Sort Imports).
This tool sorts imports by library types (FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER) and then alphabetically. It is customizable to generate comments and sort by line length. Ghostwriter code repository includes an .isort.cfg file. Ghostwriter's current configuration contains:
import_heading_thirdparty=Django & Other 3rd Party Libraries
This configuration enforces the use of parentheses, adds newlines between sections, and keeps the line length <= 90 characters. It also adds custom comments before standard libraries, third-party libraries, and Ghostwriter's local first-party libraries.
Here is an example:
# Ghostwriter Libraries
from ghostwriter.modules import codenames
from .filters import ClientFilter, ProjectFilter
from .forms import (
from .models import (
Once the Python extension is installed, run
isortby pressing SHIFT+CMD+P and selecting
Python Refactor: Sort Imports.
The Ghostwriter project enforces a 90 to 119-character line length limit.
The PEP-8 style guide says to limit lines to 79-characters, but that leads to longer files that use half the horizontal space. The Django Project enforces 119-character lines because that's the maximum characters displayed (without scrolling) by GitHub's code viewer.
Black defaults to 88, but says "90-ish is a wise choice." See here:
You probably noticed the peculiar default line length. Black defaults to 88 characters per line, which happens to be 10% over 80. This number was found to produce significantly shorter files than sticking with 80 (the most popular), or even 79 (used by the standard library). In general, 90-ish seems like the wise choice.
The Ghostwriter project does not use 88 because it is registered as a numerical hate symbol by the Anti-Defamation League.
Isortdefaults to 79 to match PEP-8, so a line length must be configured to avoid style conflicts.
For these reasons, the Ghostwriter project requires maximum line length be between 90 and 119-characters to keep everything comfortable to read in code editors and on GitHub. Any lines shorter than 90 should not be split, and longer lines should not exceed 119-characters without reason.
The Ghostwriter project requires consistent docstrings for all views, functions, forms, models, and other classes and objects. Ghostwriter's docstrings deviate from PEP-8 in favor of Django's style. Django can read docstrings and generate documentation. That only works for database models and views, but the style should be applied to other parts of the project for consistency.
See Django's documentation:
A good view docstring looks like this:
class ClientDetailView(LoginRequiredMixin, generic.DetailView):
Display an individual :model:`rolodex.Client`.
List of :model:`shepherd.Domain` associated with :model:`rolodex.Client`.
List of :model:`shepherd.StaticServer` associated with :model:`rolodex.Client`.
List of :model:`shepherd.TransientServer` associated with :model:`rolodex.Client`.