Coding standards¶
This document describes the coding standards used in this project.
Note
Just want to run all quality checks and tests? See Testing checklist below.
Linters¶
django-ca is linted and formatted with the following formatters:
To test all linters, simply run (pylint is separate for now, as it is very slow):
$ ./dev.py code-quality
$ pylint ca/django_ca/
Type hints¶
The source code also uses type hints and is checked using mypy. To check typehints, use mypy:
$ mypy ca/django_ca/
Overrides¶
isort, flake8, pylint and mypy support overriding warnings. If necessary, follow these general rules:
Use overrides as rarely as possible.
Exclude specific errors (so e.g. for flake8, use
# NOQA: E501
instead of# NOQA
).Add comments explaining the exclude. If possible, comment in the same line:
import unused # NOQA: F401 # Import this for some important reason
If your comment does not fit in the same line, add a comment above prefixed with
$SW NOTE:
:# PYLINT NOTE: A really long explanation why we have the bar argument that is not used. # TYPE NOTE: We don't type this, since it's only a demo. def func(foo, bar): # type: ignore # pylint: disable=unused-argument """Comment to make pylint happy.""" print(foo)
Documentation¶
Documentation is checked using doc8 and spell checked using sphinxcontrib.spelling.
$ doc8 docs/source/
$ make -C docs spelling
Warnings are always turned into errors, as this uncovers various mistakes such as broken references. To build the documentation, simply run:
$ make -C docs html
tox¶
To run all checkers with tox, simply run:
$ tox -e lint,pylint,mypy,docs,dist-test
Note that pylint (currently) runs for an extremely long time.
Test coverage¶
The test suite must ensure 100% test coverage. Completely excluding code from test coverage is only allowed
when absolutely necessary. To generate a coverage report in docs/build/coverage/
, simply run:
$ ./dev.py coverage
Conditional pragmas¶
In addition to the standard # pragma: no cover
and # pragma: no branch
, the test suite adds pragmas to
exclude code based on the Python version or library versions. For example:
if sys.version_info >= (3, 8): # pragma: only py>=3.8
from typing import Literal
else: # pragma: only py<3.8
from typing_extensions import Literal
If you have branches that are only relevant for some versions, there’s also pragmas for that:
if sys.version_info >= (3, 8): # pragma: py>=3.8 branch
print("Do something that's only useful in Python 3.8 or newer.")
if django.VERSION[:2] >= (3, 2): # pragma: django>=3.2 branch
print("Do something that's only useful in Django 3.2 or newer.")
You can use all operators (<
, <=
, ==
, !=
, >
, >=
), and we add pragma for the versions
of Python, Django, cryptography.
Please check ca/django_ca/tests/base/pragmas.py
for a tested file that includes all supported pragmas.
Correctly using the pragmas is mandatory, as they are also used for finding outdated code when older versions
are deprecated.a
Testing checklist¶
The following commands, assuming you have a virtualenv active, run all linters, test code coverage and check documentation (note that pylint currently takes a long time).
$ tox -e lint,pylint,mypy,docs,dist-test
$ ./dev.py coverage