An adventure in automated static code analysis
A framework for managing and maintaining multi-language pre-commit* hooks.
bash$ git init
Initialized empty Git repository in .git/
bash$ ls .git/hooks/
applypatch-msg.sample pre-applypatch.sample pre-rebase.sample
commit-msg.sample pre-commit.sample pre-receive.sample
fsmonitor-watchman.sample pre-merge-commit.sample prepare-commit-msg.sample
post-update.sample pre-push.sample update.sample
*The pre-commit tool actually supports 10 hook types.
bash$ pip install pre-commit
bash$ cat .pre-commit-config.yaml
repos:
- repo: https://github.com/PyCQA/pylint
rev: master
hooks:
- id: pylint
bash$ pre-commit install
bash$ head -n 20 .git/hooks/pre-commit
#!/usr/bin/env python
"""File generated by pre-commit: https://pre-commit.com"""
from __future__ import print_function
import distutils.spawn
import os
import subprocess
import sys
# work around https://github.com/Homebrew/homebrew-core/issues/30445
os.environ.pop('__PYVENV_LAUNCHER__', None)
HERE = os.path.dirname(os.path.abspath(__file__))
Z40 = '0' * 40
ID_HASH = '138fd403232d2ddd5efb44317e38bf03'
# start templated
CONFIG = '.pre-commit-config.yaml'
HOOK_TYPE = 'pre-commit'
INSTALL_PYTHON = '/usr/local/opt/python/bin/python3.7'
SKIP_ON_MISSING_CONFIG = False
import sys
import os
name = input('What is your name?\n')
x = True
if x:
print ('Hi, %s.' % name)
print ("This is a really long line" + ", and that's okay because" + " lines are allowed to be long, right?")
bash$ git commit -m "example commit"
pylint...................................................................Failed
hookid: pylint
************* Module test
foo/test.py:8:0: W0311: Bad indentation. Found 1 spaces, expected 4 (bad-indentation)
foo/test.py:9:0: C0301: Line too long (109/80) (line-too-long)
foo/test.py:9:0: W0311: Bad indentation. Found 1 spaces, expected 4 (bad-indentation)
foo/test.py:11:0: C0305: Trailing newlines (trailing-newlines)
foo/test.py:6:0: C0103: Constant name "x" doesn't conform to '(([A-Z_][A-Z0-9_]*)|(__.*__))$' pattern (invalid-name)
foo/test.py:1:0: W0611: Unused import sys (unused-import)
foo/test.py:2:0: W0611: Unused import os (unused-import)
------------------------------------
Your code has been rated at 0.00/10
bash$ cat .pre-commit-config.yaml
repos:
# black: python code formatting
- repo: https://github.com/psf/black
rev: stable
hooks:
- id: black
# python import sorting, remove unused imports
- repo: https://github.com/sqlalchemyorg/zimports/
rev: 0.2.0
hooks:
- id: zimports
- repo: https://github.com/PyCQA/pylint
rev: master
hooks:
- id: pylint
bash$ git commit -m "example commit"
black....................................................................Failed
hookid: black
Files were modified by this hook. Additional output:
reformatted foo/test.py
All done! ✨ 🍰 ✨
1 file reformatted.
zimports.................................................................Failed
hookid: zimports
Files were modified by this hook. Additional output:
[Writing] foo/test.py ([20% of lines are imports] [source +0L/-2L] [2 imports removed in 0.0038 sec])
pylint...................................................................Failed
hookid: pylint
************* Module test
foo/test.py:4:0: C0103: Constant name "x" doesn't conform to '(([A-Z_][A-Z0-9_]*)|(__.*__))$' pattern (invalid-name)
------------------------------------------------------------------
Your code has been rated at 8.00/10 (previous run: 0.00/10, +8.00)
bash$ git diff foo/test.py
diff --git a/foo/test.py b/foo/test.py
index 1b50268..ab066ec 100644
--- a/foo/test.py
+++ b/foo/test.py
@@ -1,11 +1,11 @@
-import sys
-import os
-name = input('What is your name?\n')
+name = input("What is your name?\n")
x = True
if x:
- print ('Hi, %s.' % name)
- print ("This is a really long line" + ", and that's okay because" + " lines are allowed to be long, right?")
-
-
+ print("Hi, %s." % name)
+ print(
+ "This is a really long line"
+ + ", and that's okay because"
+ + " lines are allowed to be long, right?"
+ )
There are currently 348 hooks tracked by pre-commit, including hooks for:
*but it has the best community support
The pre-commit hook phase of the developer workflow is a great place to do static code analysis.