Link

Style Guide

Pre-commit

Scikit-HEP uses pre-commit to check code style. It can be installed through brew (macOS) or pip (anywhere). There are two modes to use it locally; you can check manually with pre-commit run (changes only) or pre-commit run --all-files (all). You can also run pre-commit install to add checks as a git precommit hook (which is where it gets its name). It’s worth trying, even if you’ve tried and failed to set up a custom precommit hook before; it’s quite elegant and does not add or commit the changes, it just makes the changes and allows you to check and add them. You can always override the hook with -n.

Here is a minimal .pre-commit-config.yaml file with some handy options:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
  rev: v3.1.0
  hooks:
  - id: check-added-large-files
  - id: check-case-conflict
  - id: check-merge-conflict
  - id: check-symlinks
  - id: check-yaml
  - id: debug-statements
  - id: end-of-file-fixer
  - id: mixed-line-ending
  - id: requirements-txt-fixer
  - id: trailing-whitespace

For a Python 2+3 codebase, the following is also useful:

  - id: fix-encoding-pragma

Black

Black is a popular auto-formatter from the Python Software Foundation. One of the main features of Black is that it is “opinionated”; that is, it is almost completely unconfigurable. Instead of allowing you to come up with your own format, it enforces one on you. While I am quite sure you can come up with a better format, having a single standard makes it possible to learn to read code very fast - you can immediately see nested lists, matching brackets, etc. There also is a faction of developers that dislikes all auto-formatting tools, but inside a system like pre-commit, auto-formatters are ideal. They also speed up the writing of code because you can ignore formatting your code when you write it. By imposing a standard, all Scikit-HEP developers can quickly read any package’s code.

Also, properly formatted code has other benefits, such as if two developers make the same change, they get the same formatting, and merge requests are easier. The style choices in Black were explicitly made to optimize git diffs!

There are a few options, mostly to enable/disable certain files and to change the line length, and those go in your pyproject.toml file.

Here is the snippet to add Black to your .pre-commit-config.yml:

- repo: https://github.com/psf/black
  rev: 19.10b0
  hooks:
  - id: black

You can also add language_version: python3.6 or similar if you want to target a specific version. And you can add a Black badge to your repo as well if you want.

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :target: https://github.com/psf/black

Check-Manifest

Check-manifest is a fantastic, highly recommended tool that verifies you have working SDists. You can install it from PyPI. Run it on your repository and see what it says. If you want to ignore files (like test folders, example folders, docs, etc) you can add these into your config files, either pyproject.toml or setup.cfg:

# pyproject.toml
[tool.check-manifest]
ignore = [".travis.yml"]

# setup.cfg or tox.ini
[check-manifest]
ignore =
    .travis.yml    

Add the following to your pre-commit config:

- repo: https://github.com/mgedmin/check-manifest
  rev: "0.42"
  hooks:
  - id: check-manifest

Type checking (new)

One of the most exciting advancements in Python in the last 10 years has been static type hints. Scikit-HEP is just beginning to make sure packages are type-hint ready. One of the challenges for providing static type hints is that it was developed in the Python 3 era and it really shines in a Python 3.7+ codebase (due to from __future__ import annotations, which turns annotations into strings and allows you to use future Python features in Python 3.7+ annotations as long as your type checker supports them). For now, it is recommended that you make an attempt to support type checking through your public API in the best way that you can (based on your supported Python versions). Stub files or type comments allow Python 2 or Python 3.5 to be supported. MyPy is suggested for type checking, though there are several other good options to try, as well. If you have built-in support for type checking, you need to add empty py.typed files to all packages/subpackages to indicate that you support it.

The MyPy addition for pre-commit:

- repo: https://github.com/pre-commit/mirrors-mypy
  rev: v0.770
  hooks:
  - id: mypy
    files: src

You can also add items to the virtual environment setup for mypy by pre-commit, for example:

    additional_dependencies: [attrs==19.3.0]

Warnings

Python hides important warnings by default, mostly because it’s trying to be nice to users. You are a developer, you don’t want it to be “nice”. You want to find and fix warnings before they cause user errors! Always either run Python/PyTest with -Wd, or set export PYTHONWARNINGS=d in your environment. You can also add the following to your setup.cfg file:

[tool:pytest]
addopts = -Wd

Clang-format (C++ only)

If you have C++ code, you should have a .clang-format file and use the following pre-commit config:

- repo: local
  hooks:
  - id: docker-clang-format
    name: Docker Clang Format
    language: docker_image
    types:
    - c++
    entry: unibeautify/clang-format:latest
    args:
    - -style=file
    - -i

You can make a similar non-docker file, but that should sit beside the docker one for use on CI. You can install this non-docker file locally if you want it and have clang already installed. Note that formatting changes between versions, so only the above recipe is guaranteed to work identically to CI.