Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update authors.py #210

Merged
merged 1 commit into from
Jun 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 78 additions & 32 deletions util/authors.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#!/usr/bin/env python
# -*- encoding:utf-8 -*-
"""
git-authors [OPTIONS] REV1..REV2
List the authors who contributed within a given revision interval::

List the authors who contributed within a given revision interval.
python tools/authors.py REV1..REV2

`REVx` being a commit hash.

To change the name mapping, edit .mailmap on the top-level of the
repository.

"""
# Author: Pauli Virtanen <pav@iki.fi>. This script is in the public domain.
Expand All @@ -11,25 +17,20 @@
import re
import sys
import os
import io
import subprocess
import collections


from scipy._lib.six import u, PY3
if PY3:
stdout_b = sys.stdout.buffer
else:
stdout_b = sys.stdout


NAME_MAP = {
u('Helder'): u('Helder Oliveira'),
}
stdout_b = sys.stdout.buffer
MAILMAP_FILE = os.path.join(os.path.dirname(__file__), "..", ".mailmap")


def main():
p = optparse.OptionParser(__doc__.strip())
p.add_option("-d", "--debug", action="store_true",
help="print debug output")
p.add_option("-n", "--new", action="store_true",
help="print debug output")
options, args = p.parse_args()

if len(args) != 1:
Expand All @@ -40,36 +41,38 @@ def main():
except ValueError:
p.error("argument is not a revision range")

NAME_MAP = load_name_map(MAILMAP_FILE)

# Analyze log data
all_authors = set()
authors = set()
authors = collections.Counter()

def analyze_line(line, names, disp=False):
line = line.strip().decode('utf-8')

# Check the commit author name
m = re.match(u('^@@@([^@]*)@@@'), line)
m = re.match(u'^@@@([^@]*)@@@', line)
if m:
name = m.group(1)
line = line[m.end():]
name = NAME_MAP.get(name, name)
if disp:
if name not in names:
stdout_b.write((" - Author: %s\n" % name).encode('utf-8'))
names.add(name)
names.update((name,))

# Look for "thanks to" messages in the commit log
m = re.search(u(r'([Tt]hanks to|[Cc]ourtesy of) ([A-Z][A-Za-z]*? [A-Z][A-Za-z]*? [A-Z][A-Za-z]*|[A-Z][A-Za-z]*? [A-Z]\. [A-Z][A-Za-z]*|[A-Z][A-Za-z ]*? [A-Z][A-Za-z]*|[a-z0-9]+)($|\.| )'), line)
m = re.search(r'([Tt]hanks to|[Cc]ourtesy of|Co-authored-by:) ([A-Z][A-Za-z]*? [A-Z][A-Za-z]*? [A-Z][A-Za-z]*|[A-Z][A-Za-z]*? [A-Z]\. [A-Z][A-Za-z]*|[A-Z][A-Za-z ]*? [A-Z][A-Za-z]*|[a-z0-9]+)($|\.| )', line)
if m:
name = m.group(2)
if name not in (u('this'),):
if name not in (u'this',):
if disp:
stdout_b.write(" - Log : %s\n" % line.strip().encode('utf-8'))
name = NAME_MAP.get(name, name)
names.add(name)
names.update((name,))

line = line[m.end():].strip()
line = re.sub(u(r'^(and|, and|, ) '), u('Thanks to '), line)
line = re.sub(r'^(and|, and|, ) ', u'Thanks to ', line)
analyze_line(line.encode('utf-8'), names)

# Find all authors before the named range
Expand All @@ -84,24 +87,39 @@ def analyze_line(line, names, disp=False):

# Sort
def name_key(fullname):
m = re.search(u(' [a-z ]*[A-Za-z-\']+$'), fullname)
m = re.search(u' [a-z ]*[A-Za-z-]+$', fullname)
if m:
forename = fullname[:m.start()].strip()
surname = fullname[m.start():].strip()
else:
forename = ""
surname = fullname.strip()
surname = surname.replace('\'', '')
if surname.startswith(u('van der ')):
if surname.startswith(u'van der '):
surname = surname[8:]
if surname.startswith(u('de ')):
if surname.startswith(u'de '):
surname = surname[3:]
if surname.startswith(u('von ')):
if surname.startswith(u'von '):
surname = surname[4:]
return (surname.lower(), forename.lower())

authors = list(authors)
authors.sort(key=name_key)
# generate set of all new authors
if vars(options)['new']:
new_authors = set(authors.keys()).difference(all_authors)
n_authors = list(new_authors)
n_authors.sort(key=name_key)
# Print some empty lines to separate
stdout_b.write(("\n\n").encode('utf-8'))
for author in n_authors:
stdout_b.write(("- %s\n" % author).encode('utf-8'))
# return for early exit so we only print new authors
return

try:
authors.pop('GitHub')
except KeyError:
pass
# Order by name. Could order by count with authors.most_common()
authors = sorted(authors.items(), key=lambda i: name_key(i[0]))

# Print
stdout_b.write(b"""
Expand All @@ -110,11 +128,14 @@ def name_key(fullname):

""")

for author in authors:
for author, count in authors:
# remove @ if only GH handle is available
author_clean = author.strip('@')

if author in all_authors:
stdout_b.write(("* %s\n" % author).encode('utf-8'))
stdout_b.write((f"* {author_clean} ({count})\n").encode('utf-8'))
else:
stdout_b.write(("* %s +\n" % author).encode('utf-8'))
stdout_b.write((f"* {author_clean} ({count}) +\n").encode('utf-8'))

stdout_b.write(("""
A total of %(count)d people contributed to this release.
Expand All @@ -123,8 +144,32 @@ def name_key(fullname):

""" % dict(count=len(authors))).encode('utf-8'))

stdout_b.write("\nNOTE: Check this list manually! It is automatically generated "
"and some names\n may be missing.\n")
stdout_b.write(("\nNOTE: Check this list manually! It is automatically generated "
"and some names\n may be missing.\n").encode('utf-8'))


def load_name_map(filename):
name_map = {}

with io.open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line.startswith(u"#") or not line:
continue

m = re.match(r'^(.*?)\s*<(.*?)>(.*?)\s*<(.*?)>\s*$', line)
if not m:
print("Invalid line in .mailmap: '{!r}'".format(line), file=sys.stderr)
sys.exit(1)

new_name = m.group(1).strip()
old_name = m.group(3).strip()

if old_name and new_name:
name_map[old_name] = new_name

return name_map


#------------------------------------------------------------------------------
# Communicating with Git
Expand Down Expand Up @@ -182,6 +227,7 @@ def test(self, command, *a, **kw):
call=True, **kw)
return (ret == 0)


git = Cmd("git")

#------------------------------------------------------------------------------
Expand Down