| = Python Coding Guidelines = |
| |
| //// |
| We prefer [[ch-NAME]], but older versions of asciidoc don't deal well |
| with that construct for chapter headings |
| //// |
| anchor:ch-python-coding[Chapter 3, Python Coding Guidelines] |
| |
| [[s-python-boilerplate]] |
| == Python Boilerplate == |
| |
| indexterm:[Python,boilerplate] |
| indexterm:[licensing,Python boilerplate] |
| |
| Every Python file should start like this: |
| |
| ==== |
| [source,Python] |
| ---- |
| [<SHEBANG>] |
| """ <BRIEF-DESCRIPTION> |
| """ |
| |
| # Pacemaker targets compatibility with Python 2.6+ and 3.2+ |
| from __future__ import print_function, unicode_literals, absolute_import, division |
| |
| __copyright__ = "Copyright (C) <YYYY[-YYYY]> Andrew Beekhof <andrew@beekhof.net>" |
| __license__ = "<LICENSE> WITHOUT ANY WARRANTY" |
| ---- |
| ==== |
| |
| If the file is meant to be directly executed, the first line (+<SHEBANG>+) |
| should be +#!/usr/bin/python+. If it is meant to be imported, omit this line. |
| |
| +<BRIEF-DESCRIPTION>+ is obviously a brief description of the file's |
| purpose. The string may contain any other information typically used in |
| a Python file https://www.python.org/dev/peps/pep-0257/[docstring]. |
| |
| The +import+ statement is discussed further in <<s-python-future-imports>>. |
| |
| +<YYYY>+ is the year the code was 'originally' created (it is the most |
| important date for copyright purposes, as it establishes priority and |
| the point from which expiration is calculated). If the code is modified |
| in later years, add +-YYYY+ with the most recent year of modification. |
| |
| +<LICENSE>+ should follow the policy set forth in the |
| https://github.com/ClusterLabs/pacemaker/blob/master/COPYING[+COPYING+] file, |
| generally one of "GNU General Public License version 2 or later (GPLv2+)" |
| or "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)". |
| |
| |
| == Python Compatibility == |
| |
| indexterm:[Python,2] |
| indexterm:[Python,3] |
| indexterm:[Python,versions] |
| |
| Pacemaker targets compatibility with Python 2.6 and later, and Python 3.2 and |
| later. These versions have added features to be more compatible with each |
| other, allowing us to support both the 2 and 3 series with the same code. It is |
| a good idea to test any changes with both Python 2 and 3. |
| |
| [[s-python-future-imports]] |
| === Python Future Imports === |
| |
| The future imports used in <<s-python-boilerplate>> mean: |
| |
| * All print statements must use parentheses, and printing without a newline |
| is accomplished with the +end=' '+ parameter rather than a trailing comma. |
| * All string literals will be treated as Unicode (the +u+ prefix is |
| unnecessary, and must not be used, because it is not available in Python 3.2). |
| * Local modules must be imported using +from . import+ (rather than just |
| +import+). To import one item from a local module, use |
| +from .modulename import+ (rather than +from modulename import+). |
| * Division using +/+ will always return a floating-point result (use +//+ if |
| you want the integer floor instead). |
| |
| === Other Python Compatibility Requirements === |
| |
| * When specifying an exception variable, always use +as+ instead of a comma |
| (e.g. +except Exception as e+ or +except (TypeError, IOError) as e+). |
| Use +e.args+ to access the error arguments (instead of iterating over or |
| subscripting +e+). |
| * Use +in+ (not +has_key()+) to determine if a dictionary has a particular key. |
| * Always use the I/O functions from the +io+ module rather than the native |
| I/O functions (e.g. +io.open()+ rather than +open()+). |
| * When opening a file, always use the +t+ (text) or +b+ (binary) mode flag. |
| * When creating classes, always specify a parent class to ensure that it is a |
| "new-style" class (e.g. +class Foo(object):+ rather than +class Foo:+) |
| * Be aware of the bytes type added in Python 3. Many places where strings are |
| used in Python 2 use bytes or bytearrays in Python 3 (for example, the pipes |
| used with +subprocess.Popen()+). Code should handle both possibilities. |
| * Be aware that the +items()+, +keys()+, and +values()+ methods of dictionaries |
| return lists in Python 2 and views in Python 3. In many case, no special |
| handling is required, but if the code needs to use list methods on the |
| result, cast the result to list first. |
| * Do not name variables +with+ or +as+. |
| * Do not raise or catch strings as exceptions (e.g. +raise "Bad thing"+). |
| * Do not use the +cmp+ parameter of sorting functions (use +key+ instead, if |
| needed) or the +$$__cmp__()$$+ method of classes (implement rich comparison |
| methods such as +$$__lt__()$$+ instead, if needed). |
| * Do not use the +buffer+ type. |
| * Do not use features not available in all targeted Python versions. Common |
| examples include: |
| ** The +argparse+, +html+, +ipaddress+, +sysconfig+, and +UserDict+ modules |
| ** The +collections.OrderedDict+ class |
| ** The +subprocess.run()+ function |
| ** The +subprocess.DEVNULL+ constant |
| ** +subprocess+ module-specific exceptions |
| ** Set literals (+{1, 2, 3}+) |
| |
| === Python Usages to Avoid === |
| |
| Avoid the following if possible, otherwise research the compatibility issues |
| involved (hacky workarounds are often available): |
| |
| * long integers |
| * octal integer literals |
| * mixed binary and string data in one data file or variable |
| * metaclasses |
| * +locale.strcoll+ and +locale.strxfrm+ |
| * the +configparser+ and +ConfigParser+ modules |
| * importing compatibility modules such as +six+ (so we don't have |
| to add them to Pacemaker's dependencies) |
| |
| == Formatting Python Code == |
| |
| indexterm:[Python,formatting] |
| |
| * Indentation must be 4 spaces, no tabs. |
| * Do not leave trailing whitespace. |
| * Lines should be no longer than 80 characters unless limiting line length |
| significantly impacts readability. For Python, this limitation is |
| flexible since breaking a line often impacts readability, but |
| definitely keep it under 120 characters. |
| * Where not conflicting with this style guide, it is recommended (but not |
| required) to follow https://www.python.org/dev/peps/pep-0008/:[PEP 8]. |
| * It is recommended (but not required) to format Python code such that |
| `pylint --disable=line-too-long,too-many-lines,too-many-instance-attributes,too-many-arguments,too-many-statements` |
| produces minimal complaints (even better if you don't need to disable all |
| those checks). |