Coding Guidelines
Warning
This documentation is currently under construction and may not be up to date.
Python Code
Style Guide
- Use the Style Guide for Python Code
- Exception - Limit all lines to a maximum of 120 characters for docstrings and comments. (79 in the the PEP 8 guidelines (reference)).
- All
CDDS
packages should contain apackage_name/package_name/tests/test_coding_standards.py
module that uses the pycodestyle package to check PEP 8 conformance. - In addition to this, it is recommended to use a tool that checks for errors in Python code, coding standard (e.g., PEP 8) conformance and general code quality e.g., pylint.
- However, some PEP 8 guidelines are not checked by these tools; please also:
- Be consistent within a module or function (reference).
- In Python, single-quoted strings and double-quoted strings are the same; pick a rule and stick to it (reference).
- Use Python's implied line continuation inside parentheses, brackets and braces to wrap long lines, rather than using a backslash
- Add a new line before a binary operator, rather than after (reference).
- Use blank lines in functions, sparingly, to indicate logical sections (reference).
- Always use double quote characters for triple-quoted strings """ to be consistent with the docstring conventions in PEP 257 and PEP 8.
- Write comments using complete sentences (reference).
- Use the
str.format()
method to perform a string formatting operation, e.g.,'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
, i.e. avoid using the%
operator on strings. - Include the line
(c) British Crown Copyright [year of creation in the form <YYYY>]-[year of last modification in the form <YYYY>], Met Office.
as a comment at the top of every module.
Naming Conventions
- Abstract Base Classes should have a name that contains Abstract for clarity.
- Nouns should be used when naming classes.
- Use descriptive names that clearly convey a meaning; refrain from using overly general / ambiguous names e.g., data.
- Use the
.cfg
extension for configuration files, e.g. those read by configparser.
Imports
- Use absolute imports as they are recommended by PEP 8. Also, being able to tell immediately where the function comes from greatly improves code readability and comprehension (Readability Counts). Example:
import my_package.my_subpackage.my_module
and use themy_module.my_function syntax
, e.g.,import os; os.walk
.from my_package.my_subpackage.my_module import my_function
and usemy_function
directly.- group imports, with a blank line between each group, in the following order: standard library imports, related third party imports, local application/library specific imports (reference needed).
- Within each import group (see above), order the imports alphabetically.
- place module level "dunders" after the module docstring but before any import statements except
from __future__ imports
(reference needed). - Use the pattern
import numpy as np
.
Typing
- Use typing for adding type hints and annotations.
- Use mypy for running static code analysis using aforementioned type hints.
Docstrings
Warning
Historically CDDS used several different doc string formats so there is a mix of styles. If in doubt use the same format as the module
as of 2022 the PEP-257 format was adopted.
This means there is currently a mix of docstring formats used
throughout the CDDS
code.
- Docstrings should now be written using reStructuredText as recommended by pep 287.
- A detailed example of the reStructuredText style docstrings can be found here
- Use double backticks `` around argument names
- Use the appropriate substitutions for glossary terms.
- Make use of the docstring conventions.
- The docstring is a phrase ending in a period and prescribes the function or method's effect as a command, not as a description reference needed.
- It is not necessary to write docstrings for non-public classes, methods and functions, see
cdds/pylintrc
andmip_convert/pylintrc
. (The maintenance overhead is reduced when refactoring non-public classes, methods and functions).
Below is an example reStructuredText Docstring Example docstring incorporating all of the guidelines above.
def my_function(my_param1: float, my_param2: str) -> int:
"""
Return something.
Here's a longer description about the something that is returned.
It's so long, it goes over one line!
:param my_param1: Description of the first parameter ``my_param1``.
:type my_param1: float
:param my_param2: Description of the second parameter ``my_param2``.
:type my_param2: string
:raises ValueError If ``my_param1`` is less than 0.
:return: Description of anonymous integer return value.
:rtype: int
"""
Doctest
- Doctests have been included historically in MIP Convert and CDDS, but we recommend adding code to test modules instead
Entry Point Scripts
- Script names should not have an extension, should be lowercase, and with words separated by underscores as necessary to improve readability, e.g., just_do_it.
- It is recommended that scripts call a main() function located in an importable module so that it is possible to run the code in the script from the Python interpreter / a test module e.g.,
import my_module; my_module.main()
. - Argparse should be used to parse command line options.
def main(args):
# Parse the arguments.
args = parse_args(args)
# Create the configured logger.
configure_logger(args.log_name, args.log_level, args.append_log)
# Retrieve the logger.
logger = logging.getLogger(__name__)
try:
exit_code = my_func(args)
except BaseException as exc:
exit_code = 1
logger.exception(exc)
return exit_code
Bash Scripts
- Use the Google Styleguide for bash scripts as recommended by the Cylc documentation
- Run scripts through Shellcheck for catching possible bad practice. (The latest version can be installed using conda for easier access and improvements over the centrally installed version)