summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-02-21 12:40:20 +0100
committerGitHub <noreply@github.com>2020-02-21 12:40:20 +0100
commit567547fa3ba3f11ee4f2dc9e73d37a146fe49e1b (patch)
treefdfa665b3dc017b974a762f1e2e697f267216751 /scripts
parentf2e0c98ec0348810afe0e12347991438123c86f1 (diff)
downloadpsutil-567547fa3ba3f11ee4f2dc9e73d37a146fe49e1b.tar.gz
Git hook for renamed/added/deleted files + flake8 print() + tidelift (#1704)
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/internal/.git-pre-commit131
-rwxr-xr-xscripts/internal/clinter.py11
-rwxr-xr-xscripts/internal/git_pre_commit.py141
-rw-r--r--scripts/internal/tidelift.py40
-rwxr-xr-xscripts/internal/winmake.py3
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: