#!/usr/bin/env python # 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)] lineno = 0 for path in py_files: with open(path) 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") # flake8 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 scape 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") main()