|
| 1 | +# Coding Style |
| 2 | + |
| 3 | +Our Python coding style is [PEP8](http://www.python.org/dev/peps/pep-0008/). |
| 4 | + |
| 5 | +We have an additional standard for import order: |
| 6 | + |
| 7 | + # Standard libs first in alpha order. |
| 8 | + import abc |
| 9 | + import urlparse |
| 10 | + |
| 11 | + # Third party modules. |
| 12 | + import requests |
| 13 | + |
| 14 | + # Project modules (using relative paths where necessary) |
| 15 | + from . import mymodule |
| 16 | + |
| 17 | +It's easy to check your code for PEP8 compliance: |
| 18 | + |
| 19 | +**Checking from the command line** |
| 20 | + |
| 21 | +Install the `pep8` package and run it on one or more directories or files: |
| 22 | + |
| 23 | + $ pip install pep8 |
| 24 | + $ # check an individual file |
| 25 | + $ pep8 portal/core/admin.py |
| 26 | + $ # check a whole directory |
| 27 | + $ pep8 portal/mailers |
| 28 | + |
| 29 | +**Checking in IDEs/editors** |
| 30 | + |
| 31 | +1. In PyCharm, enable the `PEP8 Coding Style violation` and `PEP8 Naming |
| 32 | +Convention violation` inspection rules to have all PEP8 problems marked. |
| 33 | + |
| 34 | +2. In Sublime Text, install one of the PEP8 plugins, e.g. [PEP8 Autoformat] |
| 35 | +(https://packagecontrol.io/packages/Python%20PEP8%20Autoformat) or [Sublime |
| 36 | +Linter PEP8](https://packagecontrol.io/packages/SublimeLinter-pep8). |
| 37 | + |
| 38 | +**Automatically fixing PEP8 issues** |
| 39 | + |
| 40 | +Probably the best solution is *autopep8*: |
| 41 | + |
| 42 | + $ pip install autopep8 |
| 43 | + $ # Spot PEP8 issues and generate a diff |
| 44 | + $ autopep8 --diff portal/core/admin.py |
| 45 | + $ # Fix PEP8 issues directly in a file |
| 46 | + $ autopep8 --in-place portal/core/admin.py |
| 47 | + $ # Fix all PEP8 issues in files in a directory |
| 48 | + $ autopep8 -i portal/core/* |
| 49 | + $ # Fix all PEP8 issues in all files in a directory tree, recursively |
| 50 | + $ find portal/core -name '*.py' -print -exec autopep8 --in-place {} \; |
| 51 | + |
| 52 | +However, if fixing all the PEP8 issues in a file is scary, and all you did is |
| 53 | +touch a few lines, you can use `pep8radius`. This will apply `autopep8` to |
| 54 | +just the areas of any files that you have changed (it uses `git` to detect |
| 55 | +the changes). |
| 56 | + |
| 57 | + $ # Install pep8radius |
| 58 | + $ # Make your edits.... get all tests running... |
| 59 | + $ # Use pep8radius to fix issues in the code changed. The -vv |
| 60 | + $ # sets verbose mode so that the output shows progress. |
| 61 | + $ pep8radius -vv --in-place |
| 62 | + $ # Now rerun tests, then commit |
| 63 | + |
| 64 | +Note that `pep8radius` will only look at files modified but not yet committed, |
| 65 | +so your workflow should include it *before* commit. |
| 66 | + |
| 67 | + |
| 68 | +# Code Inspection |
| 69 | + |
| 70 | +There are a number of tools that can be used to check Python code for |
| 71 | +potential errors. You should run these on code that's already been checked |
| 72 | +for PEP8 compliance, so that PEP8 issues don't get flagged. |
| 73 | + |
| 74 | +## PyCharm |
| 75 | + |
| 76 | +The PyCharm IDE has built-in support for Python checking using the |
| 77 | +*Inspect Code...* tool. The default set of inspections (all of them) |
| 78 | +is suitable for use on our code. |
| 79 | + |
| 80 | +## PyLint |
| 81 | + |
| 82 | +Like `pep8`, `pylint` is a pip-installable package providing a command |
| 83 | +line tool that can be run on one or more files to check them. |
| 84 | + |
| 85 | + $ pip install pylint |
| 86 | + $ # Run on a single file |
| 87 | + $ pylint portal/core/admin.py |
| 88 | + $ # Run on a whole directory |
| 89 | + $ pylint portal/core |
| 90 | + |
| 91 | +By default, pylint will dump output to stdout showing: |
| 92 | + |
| 93 | +* Errors found, with the numbers of the offending lines |
| 94 | +* Statistics about the code |
| 95 | +* A tree of external Python module dependencies |
| 96 | +* A measure of duplications found (i.e., where the code could |
| 97 | +be DRY'd up) |
| 98 | +* A rating out of 10 for the code |
| 99 | + |
| 100 | +You may find the HTML output more useful: |
| 101 | + |
| 102 | + $ pylint -f html portal/core/admin.py > pylint.html |
| 103 | + |
| 104 | +It gives more detailed output, including function or class+method names |
| 105 | +to make it easier to locate the code that flagged an error. It's most useful |
| 106 | +when running pylint over a whole set of files (or a whole directory). |
| 107 | + |
| 108 | +PyLint is very configurable, and a reference config file is included in |
| 109 | +this repo (`pylint.rc`). You can save this in your project root, and pylint |
| 110 | +will use it by default (when you run `pylint` from that directory). |
| 111 | + |
| 112 | + $ pylint --rcfile=pylint.rc mystuff/myapp/myfile.py |
| 113 | + |
| 114 | +...or save it to `~/.py |
| 115 | + |
| 116 | +### Fixing PyLint issues |
| 117 | + |
| 118 | +There are two ways to fix an issue that pylint flags (three if you include *ignore |
| 119 | +it and hope someone else fixes it*). |
| 120 | + |
| 121 | +1. Modify the code to fix the issue. |
| 122 | +2. Include a comment to flag to PyLint that it should suppress the check. These |
| 123 | +comments take the form `# pylint: disable=<message-type-or-number>` and apply |
| 124 | +to the block in which the comment occurs. For example: |
| 125 | + |
| 126 | + class Foo(object): |
| 127 | + def unused_arg1(self, arg): |
| 128 | + """This method is flagged because it has an unused argument""" |
| 129 | + print self |
| 130 | + |
| 131 | + def unused_arg2(self, arg): |
| 132 | + """ |
| 133 | + This method is not flagged because we suppress the |
| 134 | + check for unused argument. |
| 135 | + """ |
| 136 | + # pylint: disable=unused-argument |
| 137 | + print self |
| 138 | + |
| 139 | +Checking this, we get: |
| 140 | + |
| 141 | + ************* Module a |
| 142 | + a.py:line 1: : C0111:missing-docstring:Missing module docstring |
| 143 | + a.py:line 1: Foo: C0111:missing-docstring:Missing class docstring |
| 144 | + a.py:line 2: Foo.unused_arg1: W0613:unused-argument:Unused argument 'arg' |
| 145 | + |
| 146 | +...i.e., the unused-argument message is flagged only for method `unused_arg1` |
| 147 | + |
| 148 | +There's a full list of all the pylint message codes and names here: http://pylint-messages.wikidot.com/all-codes |
| 149 | +and detailed documentation of what they mean here: http://docs.pylint.org/features.html |
| 150 | + |
| 151 | +Because the pylint disabling comments work for the whole block in which they |
| 152 | +occur, you can include them at module level to suppress a warning across the |
| 153 | +whole module. |
| 154 | + |
| 155 | +## Pyflakes |
| 156 | + |
| 157 | +Pyflakes is another pip-installable code analysis tool that focuses on |
| 158 | +identifying coding issues (and less on code layout and formatting). _"Pyflakes |
| 159 | +makes a simple promise: it will never complain about style, and it will try |
| 160 | +very, very hard to never emit false positives."_ |
| 161 | + |
| 162 | + $ pip install pyflakes |
| 163 | + $ # Check one file |
| 164 | + $ pyflakes portal/core/admin.py |
| 165 | + $ # Check all the files in a tree |
| 166 | + $ pyflakes portal/core |
| 167 | + |
| 168 | +Pyflakes has no configuration, and there is no way to suppress a warning |
| 169 | +by adding comments. However, it spots a smaller set of issues than PyLint |
| 170 | +(for example, it doesn't spot unused arguments). This does mean that anything |
| 171 | +it spots is worth checking. |
0 commit comments