Ran update-copyright.py
[update-copyright.git] / update_copyright / vcs / utils.py
1 # Copyright (C) 2012-2013 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of update-copyright.
4 #
5 # update-copyright is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
8 # later version.
9 #
10 # update-copyright is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 # more details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # update-copyright.  If not, see <http://www.gnu.org/licenses/>.
17
18 """Useful utilities for backend classes."""
19
20 import email.utils as _email_utils
21 import os.path as _os_path
22 import subprocess as _subprocess
23 import sys as _sys
24
25 from .. import LOG as LOG
26 from ..utils import ENCODING as _ENCODING
27
28
29 _MSWINDOWS = _sys.platform == 'win32'
30 _POSIX = not _MSWINDOWS
31
32
33 def invoke(args, stdin=None, stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
34            cwd=None, expect=(0,), unicode_output=False, encoding=None):
35     """Invoke an external program and return the results
36
37     ``expect`` should be a tuple of allowed exit codes.
38
39     When ``unicode_output`` is ``True``, convert stdout and stdin
40     strings to unicode before returing them.
41     """
42     LOG.debug('{}$ {}'.format(cwd, args))
43     try :
44         if _POSIX:
45             q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
46                                   stdout=stdout, stderr=stderr,
47                                   close_fds=True, cwd=cwd)
48         else:
49             assert _MSWINDOWS == True, 'invalid platform'
50             # win32 don't have os.execvp() so run the command in a shell
51             q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
52                                   stdout=stdout, stderr=stderr, shell=True,
53                                   cwd=cwd)
54     except OSError as e:
55         raise ValueError([args, e])
56     stdout,stderr = q.communicate(input=stdin)
57     status = q.wait()
58     if unicode_output == True:
59         if encoding is None:
60             encoding = _ENCODING
61         if stdout is not None:
62             stdout = unicode(stdout, encoding)
63         if stderr is not None:
64             stderr = unicode(stderr, encoding)
65     if status not in expect:
66         raise ValueError([args, status, stdout, stderr])
67     return status, stdout, stderr
68
69 def splitpath(path):
70     """Recursively split a path into elements.
71
72     Examples
73     --------
74
75     >>> import os.path
76     >>> splitpath(os.path.join('a', 'b', 'c'))
77     ('a', 'b', 'c')
78     >>> splitpath(os.path.join('.', 'a', 'b', 'c'))
79     ('a', 'b', 'c')
80     """
81     path = _os_path.normpath(path)
82     elements = []
83     while True:
84         dirname,basename = _os_path.split(path)
85         elements.insert(0,basename)
86         if dirname in ['/', '', '.']:
87             break
88         path = dirname
89     return tuple(elements)
90
91 def strip_email(*args):
92     """Remove email addresses from a series of names.
93
94     Examples
95     --------
96
97     >>> strip_email('J Doe')
98     ['J Doe']
99     >>> strip_email('J Doe <jdoe@a.com>')
100     ['J Doe']
101     >>> strip_email('J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>')
102     ['J Doe', 'JJJ Smith']
103     """
104     args = list(args)
105     for i,arg in enumerate(args):
106         if arg == None:
107             continue
108         author,addr = _email_utils.parseaddr(arg)
109         if author == '':
110             author = arg
111         args[i] = author
112     return args
113
114 def reverse_aliases(aliases):
115     """Reverse an `aliases` dict.
116
117     Input:   key: canonical name,  value: list of aliases
118     Output:  key: alias,           value: canonical name
119
120     Examples
121     --------
122
123     >>> aliases = {
124     ...     'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>', 'J'],
125     ...     'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
126     ...     None:['Anonymous <a@a.com>'],
127     ...     }
128     >>> r = reverse_aliases(aliases)
129     >>> for item in sorted(r.items()):
130     ...     print item
131     ('Anonymous <a@a.com>', None)
132     ('J', 'J Doe <jdoe@a.com>')
133     ('Jingly <jjjs@b.edu>', 'JJJ Smith <jjjs@a.com>')
134     ('Johnny <jdoe@b.edu>', 'J Doe <jdoe@a.com>')
135     """
136     output = {}
137     for canonical_name,_aliases in aliases.items():
138         for alias in _aliases:
139             output[alias] = canonical_name
140     return output
141
142 def replace_aliases(authors, with_email=True, aliases=None):
143     """Consolidate and sort `authors`.
144
145     Make the replacements listed in the `aliases` dict (key: canonical
146     name, value: list of aliases).  If `aliases` is ``None``, default
147     to ``ALIASES``.
148
149     >>> aliases = {
150     ...     'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>'],
151     ...     'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
152     ...     None:['Anonymous <a@a.com>'],
153     ...     }
154     >>> authors = [
155     ...     'JJJ Smith <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
156     ...     'Jingly <jjjs@b.edu>', 'J Doe <jdoe@a.com>', 'Anonymous <a@a.com>']
157     >>> replace_aliases(authors, with_email=True, aliases=aliases)
158     ['J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>']
159     >>> replace_aliases(authors, with_email=False, aliases=aliases)
160     ['J Doe', 'JJJ Smith']
161     """
162     if aliases == None:
163         aliases = ALIASES
164     rev_aliases = reverse_aliases(aliases)
165     authors = list(authors)
166     for i,author in enumerate(authors):
167         if author in rev_aliases:
168             authors[i] = rev_aliases[author]
169     authors = sorted(list(set(authors)))
170     if None in authors:
171         authors.remove(None)
172     if with_email == False:
173         authors = strip_email(*authors)
174     return authors