1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
#!/usr/bin/env python3
# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
This gets executed on 'git commit' and rejects the commit in case the
submitted code does not pass validation. Validation is run only against
the *.py files which were modified in the commit. Checks:
- assert no space at EOLs
- assert not pdb.set_trace in code
- assert no bare except clause ("except:") in code
- assert "flake8" returns no warnings
Install this with "make install-git-hooks".
"""
from __future__ import print_function
import os
import subprocess
import sys
def term_supports_colors():
try:
import curses
assert sys.stderr.isatty()
curses.setupterm()
assert curses.tigetnum("colors") > 0
except Exception:
return False
else:
return True
def hilite(s, ok=True, bold=False):
"""Return an highlighted version of 'string'."""
if not term_supports_colors():
return s
attr = []
if ok is None: # no color
pass
elif ok: # green
attr.append('32')
else: # red
attr.append('31')
if bold:
attr.append('1')
return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
def exit(msg):
msg = hilite(msg, ok=False)
print(msg, file=sys.stderr)
sys.exit(1)
def sh(cmd):
"""run cmd in a subprocess and return its output.
raises RuntimeError on error.
"""
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise RuntimeError(stderr)
if stderr:
print(stderr, file=sys.stderr)
if stdout.endswith('\n'):
stdout = stdout[:-1]
return stdout
def main():
out = sh("git diff --cached --name-only")
py_files = [x for x in out.split('\n') if x.endswith('.py') and
os.path.exists(x)]
c_files = [x for x in out.split('\n') if x.endswith(('.c', '.h')) and
os.path.exists(x)]
lineno = 0
kw = {'encoding': 'utf8'} if sys.version_info[0] == 3 else {}
for path in py_files:
with open(path, 'rt', **kw) as f:
for line in f:
lineno += 1
# space at end of line
if line.endswith(' '):
print("%s:%s %r" % (path, lineno, line))
return exit(
"commit aborted: space at end of line")
line = line.rstrip()
# pdb
if "pdb.set_trace" in line:
print("%s:%s %s" % (path, lineno, line))
return exit(
"commit aborted: you forgot a pdb in your python code")
# bare except clause
if "except:" in line and not line.endswith("# NOQA"):
print("%s:%s %s" % (path, lineno, line))
return exit("commit aborted: bare except clause")
# Python linter
if py_files:
try:
import flake8 # NOQA
except ImportError:
return exit("commit aborted: flake8 is not installed; "
"run 'make setup-dev-env'")
# XXX: we should escape spaces and possibly other amenities here
ret = subprocess.call(
"%s -m flake8 %s" % (sys.executable, " ".join(py_files)),
shell=True)
if ret != 0:
return exit("commit aborted: python code is not flake8 compliant")
# C linter
if c_files:
# XXX: we should escape spaces and possibly other amenities here
cmd = "%s scripts/internal/clinter.py %s" % (
sys.executable, " ".join(c_files))
print(cmd)
ret = subprocess.call(cmd, shell=True)
if ret != 0:
return exit("commit aborted: C code didn't pass style check")
main()
|