Skip to content

Python

  1. Version
    1. The version of Python used SHOULD be 3.11 or above
    2. The version of Python used MUST be 3.8 or above
    3. Python projects SHOULD use one of the TNA base Docker images
  2. Style/linting
    1. Python code MUST be styled with Black, Flake8 and isort
    2. The maximum cyclomatic complexity of the code MUST be no larger than 20
    3. The maximum cyclomatic complexity of the code SHOULD be no larger than 12
    4. Line lengths SHOULD NOT exceed 80 characters
    5. Absolute imports SHOULD be used
    6. Relative imports COULD be used for importing files within the same directory
  3. Dependencies
    1. Python dependencies SHOULD be managed using Poetry
  4. Frameworks, tools and libraries
    1. Python applications MUST use one of the approved frameworks
  5. Packages
    1. Python packages SHOULD be made using pip
    2. Python packages SHOULD be deployed to PyPI
    3. Python packages COULD be hosted in AWS CodeArtifact
  6. Security
    1. A CSP SHOULD be set up

Frameworks

Use either Flask, Django or FastAPI for your Python applications.

Framework Best choice for making
Flask Applications with a UI
Django Applications that need to work with data and databases
FastAPI RESTful JSON APIs

Application templates have been made for new projects to enable you to get started much quicker.

Tools and libraries

Some suggested tools and libraries for Python applications are:

Tool/library Use case
Wagtail Services that require a CMS
WTForms Validating form inputs from Flask applications
flask-talisman Adding a CSP and other security measures to Flask applications
django-csp Adding a CSP and other security measures to Django applications
WhiteNoise Serving static files in production from django.contrib.staticfiles

Formatters and linters

Black

Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting.

To ensure compatibility with Flake8 (sometimes the two disagree) the following config can be used in a pyproject.toml file:

[tool.black]
line-length = 80
include = '\.pyi?$'

Flake8

Flake8 is a Python linting tool that checks your Python codebase for errors, styling issues and complexity and follows the PEP 8 style guide for Python code.

The following configuration can be set in a .flake8 file to ensure all projects remain consistent and compliant with Black:

[flake8]
ignore = E203, E266, E501, W503, F403, F401
exclude = venv*,__pycache__,node_modules,migrations
max-line-length = 80
max-complexity = 12
select = B,C,E,F,W,T4,B9

max-complexity will put a limit on the cyclomatic complexity of the code.

Note: you can also ignore rules on particular lines of code or files by adding a # noqa comment - see flake8's noqa syntax.

If using the tna-python-dev Docker image, this Flake8 configuration is included.

isort

The order of the imports can be standardised with isort.

Add the following configuration to your pyproject.toml file:

[tool.isort]
profile = "black"

Dev Docker image

The Dev Docker image comes preinstalled with Black, Flake8 and isort. It also includes all the relevant configurations.

You can use it to lint your code by mounting the container image with your project code in your docker-compose.yml:

services:
  dev:
    image: ghcr.io/nationalarchives/tna-python-dev:latest
    volumes:
      - ./:/app

Now you can lint your code by running:

docker compose exec dev format

Alternatively, you can simply run format inside the container.

Poetry

Poetry is a tool for dependency management and packaging in Python.

PEP 20 – The Zen of Python

If in doubt, consult The Zen of Python:

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!