diff options
| author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-02-21 12:40:20 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-21 12:40:20 +0100 |
| commit | 567547fa3ba3f11ee4f2dc9e73d37a146fe49e1b (patch) | |
| tree | fdfa665b3dc017b974a762f1e2e697f267216751 /scripts | |
| parent | f2e0c98ec0348810afe0e12347991438123c86f1 (diff) | |
| download | psutil-567547fa3ba3f11ee4f2dc9e73d37a146fe49e1b.tar.gz | |
Git hook for renamed/added/deleted files + flake8 print() + tidelift (#1704)
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/internal/.git-pre-commit | 131 | ||||
| -rwxr-xr-x | scripts/internal/clinter.py | 11 | ||||
| -rwxr-xr-x | scripts/internal/git_pre_commit.py | 141 | ||||
| -rw-r--r-- | scripts/internal/tidelift.py | 40 | ||||
| -rwxr-xr-x | scripts/internal/winmake.py | 3 |
5 files changed, 190 insertions, 136 deletions
diff --git a/scripts/internal/.git-pre-commit b/scripts/internal/.git-pre-commit deleted file mode 100755 index e6009253..00000000 --- a/scripts/internal/.git-pre-commit +++ /dev/null @@ -1,131 +0,0 @@ -#!/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() diff --git a/scripts/internal/clinter.py b/scripts/internal/clinter.py index 1d4ba9b1..fde1a3f2 100755 --- a/scripts/internal/clinter.py +++ b/scripts/internal/clinter.py @@ -43,16 +43,19 @@ def check_line(path, line, idx, lines): sls = s.lstrip() if sls.startswith('//') and sls[2] != ' ' and line.strip() != '//': warn(path, line, lineno, "no space after // comment") - # e.g. "if(..." after keywords keywords = ("if", "else", "while", "do", "enum", "for") for kw in keywords: if sls.startswith(kw + '('): warn(path, line, lineno, "missing space between %r and '('" % kw) # eof - if eof: - if not line.endswith('\n'): - warn(path, line, lineno, "no blank line at EOF") + if eof and not line.endswith('\n'): + warn(path, line, lineno, "no blank line at EOF") + + ss = s.strip() + if ss.startswith(("printf(", "printf (", )): + if not ss.endswith(("// NOQA", "// NOQA")): + warn(path, line, lineno, "printf() statement") def process(path): diff --git a/scripts/internal/git_pre_commit.py b/scripts/internal/git_pre_commit.py new file mode 100755 index 00000000..2ec4303d --- /dev/null +++ b/scripts/internal/git_pre_commit.py @@ -0,0 +1,141 @@ +#!/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 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" checks pass +- assert C linter checks pass +- abort if files were added/renamed/removed and MANIFEST.in was not updated + +Install this with "make install-git-hooks". +""" + +from __future__ import print_function +import os +import subprocess +import sys + + +PYTHON = sys.executable +PY3 = sys.version_info[0] == 3 +THIS_SCRIPT = os.path.realpath(__file__) + + +def term_supports_colors(): + try: + import curses + assert sys.stderr.isatty() + curses.setupterm() + assert curses.tigetnum("colors") > 0 + except Exception: + return False + 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): + print(hilite("commit aborted: " + msg, ok=False), file=sys.stderr) + sys.exit(1) + + +def sh(cmd): + 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 open_text(path): + kw = {'encoding': 'utf8'} if PY3 else {} + return open(path, 'rt', **kw) + + +def git_commit_files(): + 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)] + new_rm_mv = sh("git diff --name-only --diff-filter=ADR --cached") + # XXX: we should escape spaces and possibly other amenities here + new_rm_mv = new_rm_mv.split() + return (py_files, c_files, new_rm_mv) + + +def main(): + py_files, c_files, new_rm_mv = git_commit_files() + # Check file content. + for path in py_files: + if os.path.realpath(path) == THIS_SCRIPT: + continue + with open_text(path) as f: + lines = f.readlines() + for lineno, line in enumerate(lines, 1): + # space at end of line + if line.endswith(' '): + print("%s:%s %r" % (path, lineno, line)) + return exit("space at end of line") + line = line.rstrip() + # pdb + if "pdb.set_trace" in line: + print("%s:%s %s" % (path, lineno, line)) + return exit("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("bare except clause") + + # Python linter + if py_files: + assert os.path.exists('.flake8') + # XXX: we should escape spaces and possibly other amenities here + cmd = "%s -m flake8 --config=.flake8 %s" % (PYTHON, " ".join(py_files)) + ret = subprocess.call(cmd, shell=True) + if ret != 0: + return exit("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" % (PYTHON, " ".join(c_files)) + ret = subprocess.call(cmd, shell=True) + if ret != 0: + return exit("C code didn't pass style check") + if new_rm_mv: + out = sh("%s scripts/internal/generate_manifest.py" % PYTHON) + with open_text('MANIFEST.in') as f: + if out.strip() != f.read().strip(): + exit("some files were added, deleted or renamed; " + "run 'make generate-manifest' and commit again") + + +main() diff --git a/scripts/internal/tidelift.py b/scripts/internal/tidelift.py new file mode 100644 index 00000000..fcba3e61 --- /dev/null +++ b/scripts/internal/tidelift.py @@ -0,0 +1,40 @@ +#!/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. + +""" +Update news entry of Tidelift with latest HISTORY.rst section. +Put your Tidelift API token in a file first: +~/.tidelift.token +""" + +from __future__ import print_function +import os +import requests +import psutil +from psutil.tests import import_module_by_path + + +def upload_relnotes(package, version, text, token): + url = "https://api.tidelift.com/external-api/" + \ + "lifting/pypi/%s/release-notes/%s" % (package, version) + res = requests.put( + url=url, + data=text.encode('utf8'), + headers={"Authorization": "Bearer: %s" % token}) + print(version, res.status_code, res.text) + res.raise_for_status() + + +def main(): + here = os.path.abspath(os.path.dirname(__file__)) + path = os.path.join(here, "print_announce.py") + get_changes = import_module_by_path(path).get_changes + with open(os.path.expanduser("~/.tidelift.token")) as f: + token = f.read().strip() + upload_relnotes('psutil', psutil.__version__, get_changes(), token) + + +if __name__ == "__main__": + main() diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 4d3fa318..c9aa2952 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -490,7 +490,8 @@ def test_memleaks(): def install_git_hooks(): """Install GIT pre-commit hook.""" if os.path.isdir('.git'): - src = os.path.join(ROOT_DIR, "scripts", "internal", ".git-pre-commit") + src = os.path.join( + ROOT_DIR, "scripts", "internal", "git_pre_commit.py") dst = os.path.realpath( os.path.join(ROOT_DIR, ".git", "hooks", "pre-commit")) with open(src, "rt") as s: |
