blob: 15dda011530787a8a232677e38d6fb28a6eb6a3b [file] [log] [blame] [view]
# Python Coding Style Guide
## Basics
### Summary
Python3 is the main language used for simple tools.
As tools grow in complexity, or the requirements on them develop, they serve as valuable prototypes to re-implement tools or their behaviors more maintainably.
Python can be written in vastly different styles, which can lead to code conflicts and code review latency.
This style guide aims to promote Python readability across groups.
To quote the C++ style guide: "Creating common, required idioms and patterns makes code much easier to understand."
This guide defines the lowRISC style for Python version 3.
The goals are to:
* promote consistency across hardware development projects
* promote best practices
* increase code sharing and re-use
### Terminology Conventions
Unless otherwise noted, the following terminology conventions apply to this style guide:
* The word ***must*** indicates a mandatory requirement.
Similarly, ***do not*** indicates a prohibition.
Imperative and declarative statements correspond to ***must***.
* The word ***recommended*** indicates that a certain course of action is preferred or is most suitable.
Similarly, ***not recommended*** indicates that a course of action is unsuitable, but not prohibited.
There may be reasons to use other options, but the implications and reasons for doing so must be fully understood.
* The word ***may*** indicates a course of action is permitted and optional.
* The word ***can*** indicates a course of action is possible given material, physical, or causal constraints.
### Style Guide Exceptions
***Justify exceptions with a comment.***
No style guide is perfect.
There are times when the best path to a working design, or for working around a tool issue, is to simply cut the Gordian Knot and create code that is at variance with this style guide.
It is always okay to deviate from the style guide by necessity, as long as that necessity is clearly justified by a brief comment, as well as a lint waiver pragma where appropriate.
A common case where you may wish to disable tool-enforced reformatting is for large manually formatted data literals.
In this case, no explanatory comment is required and yapf can be disabled for that literal [with a single pragma](https://github.com/google/yapf#why-does-yapf-destroy-my-awesome-formatting).
## Python Conventions
### Summary
The lowRISC style matches [PEP8](https://www.python.org/dev/peps/pep-0008/) with the following options:
* Bitwise operators should be placed before a line split
* Logical operators should be placed before a line split
To avoid doubt, the interpretation of PEP8 is done by [yapf](https://github.com/google/yapf) and the style guide is set using a `.style.yapf` file in the top level directory of the repository.
This just sets the base style to pep8 and overrides with the exceptions given above.
In addition to the basic style, imports must be ordered alphabetically within sections:
* Future
* Python Standard Library
* Third Party
* Current Python Project
The import ordering matches that enforced by [isort](https://github.com/timothycrosley/isort).
Currently the `isort` defaults are used.
If this changes a `.isort.cfg` file will be placed in the top level directory of the repository.
### Lint tool
The `lintpy.py` utility in `util` can be used to check Python code.
It checks all Python (`.py`) files that are modified in the local repository and will report problems.
Both `yapf` and `isort` checks are run.
Basic lintpy usage is just to run from the util directory.
If everything is fine the command produces no output, otherwise it will report the problems.
Additional information will be printed if the `--verbose` or `-v` flag is given.
```console
$ cd $REPO_TOP/util
$ ./lintpy.py
$ ./lintpy.py -v
```
Checking can be done on an explicit list of files using the `--file` or `-f` flag.
In this case the tool will not derive the list from git, so any file can be checked even if it has not been modified.
```console
$ cd $REPO_TOP/util
$ ./lintpy.py -f a.py subdir/*.py
```
Errors may be fixed using the same tool to edit the problem file(s) in-place (you may need to refresh the file(s) in your editor after doing this).
This uses the same set of files as are being checked, so unless the`--file` or `-f` flag is used this will only affect files that have already been modified (or staged for commit if `-c`is used) and will not fix errors in Python files that have not been touched.
```console
$ cd $REPO_TOP/util
$ ./lintpy.py --fix
```
lintpy.py can be installed as a git pre-commit hook which will prevent commits if there are any lint errors.
This will normally be a symlink to the tool in util so changes are automatically used (it also works if `lintpy.py` is copied to `.git/hooks/pre-commit` but in that case the hook must be reinstalled each time the tool changes).
Since git hooks are not automatically installed the symlink hook can be installed if required using the tool:
```console
$ cd $REPO_TOP/util
$ ./lintpy.py --hook
```
Fixing style errors for a single file can also be done with `yapf` directly:
```console
$ yapf -i file.py
```
Fixing import ordering errors for a single file can be done with `isort`:
```console
$ isort file.py
```
Yapf and isort are Python packages and should be installed with pip:
```console
$ pip3 install --user yapt
$ pip3 install --user isort
```
### File Extensions
***Use the `.py` extension for Python files***
### General File Appearance
#### Characters
***Use only UTF-8 characters with UNIX-style line endings(`"\n"`).***
Follows PEP8.
#### POSIX File Endings
***All lines on non-empty files must end with a newline (`"\n"`).***
#### Line Length
***Wrap the code at 79 characters per line.***
The maximum line length follows PEP8.
Exceptions:
- Any place where line wraps are impossible (for example, an include path might extend past 79 characters).
#### No Tabs
***Do not use tabs anywhere.***
Use spaces to indent or align text.
To convert tabs to spaces on any file, you can use the [UNIX `expand`](http://linux.die.net/man/1/expand) utility.
#### No Trailing Spaces
***Delete trailing whitespace at the end of lines.***
### Indentation
***Indentation is four spaces per level.***
Follows PEP8.
Use spaces for indentation.
Do not use tabs.
You should set your editor to emit spaces when you hit the tab key.
### Executable Python tools
Tools that can be executed should use `env` to avoid making assumptions about the location of the Python interpreter.
Thus they should begin with the line:
```console
#!/usr/bin/env python3
```
This should be followed by a comment with the license information and the doc string describing the command.
#### Argument Parsing
***Use argparse to parse command line arguments.***
In command line tools use the [argparse library](https://docs.python.org/3/library/argparse.html) to parse arguments.
This will provide support for `--help` and `-h` to get usage information.
Every command line program should provide `--version` to provide standard version information.
This lists the git repository information for the tool and the version numbers of any Python packages that are used.
The `show_and_exit` routine in `reggen/version.py` can be used to do this.
Options that consume an arbitrary number of command line arguments with `nargs="*"` or `nargs="+"` should be avoided wherever possible.
Comma separated lists can be passed to form single arguments and can be split after arguments are parsed.
Args that allow for lists should mention that capability and the separator using the help keyword.
To display proper delimiting of lists, args that allow for lists may demonstrate the separator with the metavar keyword.