1 # Copyright (C) 2012 W. Trevor King
3 # This file is part of update-copyright.
5 # update-copyright is free software: you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
10 # update-copyright is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with update-copyright. If not, see
17 # <http://www.gnu.org/licenses/>.
19 """Useful utilities for backend classes."""
21 import email.utils as _email_utils
22 import os.path as _os_path
23 import subprocess as _subprocess
27 _MSWINDOWS = _sys.platform == 'win32'
28 _POSIX = not _MSWINDOWS
31 def invoke(args, stdin=None, stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
34 expect should be a tuple of allowed exit codes.
38 q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
39 stdout=stdout, stderr=stderr)
41 assert _MSWINDOWS == True, 'invalid platform'
42 # win32 don't have os.execvp() so run the command in a shell
43 q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
44 stdout=stdout, stderr=stderr, shell=True)
46 raise ValueError([args, e])
47 stdout,stderr = q.communicate(input=stdin)
49 if status not in expect:
50 raise ValueError([args, status, stdout, stderr])
51 return status, stdout, stderr
54 """Recursively split a path into elements.
60 >>> splitpath(os.path.join('a', 'b', 'c'))
62 >>> splitpath(os.path.join('.', 'a', 'b', 'c'))
65 path = _os_path.normpath(path)
68 dirname,basename = _os_path.split(path)
69 elements.insert(0,basename)
70 if dirname in ['', '.']:
73 return tuple(elements)
75 def strip_email(*args):
76 """Remove email addresses from a series of names.
81 >>> strip_email('J Doe')
83 >>> strip_email('J Doe <jdoe@a.com>')
85 >>> strip_email('J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>')
86 ['J Doe', 'JJJ Smith']
89 for i,arg in enumerate(args):
92 author,addr = _email_utils.parseaddr(arg)
98 def reverse_aliases(aliases):
99 """Reverse an `aliases` dict.
101 Input: key: canonical name, value: list of aliases
102 Output: key: alias, value: canonical name
108 ... 'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>', 'J'],
109 ... 'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
110 ... None:['Anonymous <a@a.com>'],
112 >>> r = reverse_aliases(aliases)
113 >>> for item in sorted(r.items()):
115 ('Anonymous <a@a.com>', None)
116 ('J', 'J Doe <jdoe@a.com>')
117 ('Jingly <jjjs@b.edu>', 'JJJ Smith <jjjs@a.com>')
118 ('Johnny <jdoe@b.edu>', 'J Doe <jdoe@a.com>')
121 for canonical_name,_aliases in aliases.items():
122 for alias in _aliases:
123 output[alias] = canonical_name
126 def replace_aliases(authors, with_email=True, aliases=None):
127 """Consolidate and sort `authors`.
129 Make the replacements listed in the `aliases` dict (key: canonical
130 name, value: list of aliases). If `aliases` is ``None``, default
134 ... 'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>'],
135 ... 'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
136 ... None:['Anonymous <a@a.com>'],
139 ... 'JJJ Smith <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
140 ... 'Jingly <jjjs@b.edu>', 'J Doe <jdoe@a.com>', 'Anonymous <a@a.com>']
141 >>> replace_aliases(authors, with_email=True, aliases=aliases)
142 ['J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>']
143 >>> replace_aliases(authors, with_email=False, aliases=aliases)
144 ['J Doe', 'JJJ Smith']
148 rev_aliases = reverse_aliases(aliases)
149 for i,author in enumerate(authors):
150 if author in rev_aliases:
151 authors[i] = rev_aliases[author]
152 authors = sorted(list(set(authors)))
155 if with_email == False:
156 authors = strip_email(*authors)