| # 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. |