Convert update-copyright.py to a more modular framework.
[update-copyright.git] / update_copyright / vcs / utils.py
1 # Copyright
2
3 """Useful utilities for backend classes."""
4
5 import email.utils as _email_utils
6 import os.path as _os_path
7 import subprocess as _subprocess
8 import sys as _sys
9
10
11 _MSWINDOWS = _sys.platform == 'win32'
12 _POSIX = not _MSWINDOWS
13
14
15 def invoke(args, stdin=None, stdout=_subprocess.PIPE, stderr=_subprocess.PIPE,
16            expect=(0,)):
17     """
18     expect should be a tuple of allowed exit codes.
19     """
20     try :
21         if _POSIX:
22             q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
23                                   stdout=stdout, stderr=stderr)
24         else:
25             assert _MSWINDOWS == True, 'invalid platform'
26             # win32 don't have os.execvp() so run the command in a shell
27             q = _subprocess.Popen(args, stdin=_subprocess.PIPE,
28                                   stdout=stdout, stderr=stderr, shell=True)
29     except OSError, e:
30         raise ValueError([args, e])
31     stdout,stderr = q.communicate(input=stdin)
32     status = q.wait()
33     if status not in expect:
34         raise ValueError([args, status, stdout, stderr])
35     return status, stdout, stderr
36
37 def splitpath(path):
38     """Recursively split a path into elements.
39
40     Examples
41     --------
42
43     >>> import os.path
44     >>> splitpath(os.path.join('a', 'b', 'c'))
45     ('a', 'b', 'c')
46     >>> splitpath(os.path.join('.', 'a', 'b', 'c'))
47     ('a', 'b', 'c')
48     """
49     path = _os_path.normpath(path)
50     elements = []
51     while True:
52         dirname,basename = _os_path.split(path)
53         elements.insert(0,basename)
54         if dirname in ['', '.']:
55             break
56         path = dirname
57     return tuple(elements)
58
59 def strip_email(*args):
60     """Remove email addresses from a series of names.
61
62     Examples
63     --------
64
65     >>> strip_email('J Doe')
66     ['J Doe']
67     >>> strip_email('J Doe <jdoe@a.com>')
68     ['J Doe']
69     >>> strip_email('J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>')
70     ['J Doe', 'JJJ Smith']
71     """
72     args = list(args)
73     for i,arg in enumerate(args):
74         if arg == None:
75             continue
76         author,addr = _email_utils.parseaddr(arg)
77         if author == '':
78             author = arg
79         args[i] = author
80     return args
81
82 def reverse_aliases(aliases):
83     """Reverse an `aliases` dict.
84
85     Input:   key: canonical name,  value: list of aliases
86     Output:  key: alias,           value: canonical name
87
88     Examples
89     --------
90
91     >>> aliases = {
92     ...     'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>', 'J'],
93     ...     'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
94     ...     None:['Anonymous <a@a.com>'],
95     ...     }
96     >>> r = reverse_aliases(aliases)
97     >>> for item in sorted(r.items()):
98     ...     print item
99     ('Anonymous <a@a.com>', None)
100     ('J', 'J Doe <jdoe@a.com>')
101     ('Jingly <jjjs@b.edu>', 'JJJ Smith <jjjs@a.com>')
102     ('Johnny <jdoe@b.edu>', 'J Doe <jdoe@a.com>')
103     """
104     output = {}
105     for canonical_name,_aliases in aliases.items():
106         for alias in _aliases:
107             output[alias] = canonical_name
108     return output
109
110 def replace_aliases(authors, with_email=True, aliases=None):
111     """Consolidate and sort `authors`.
112
113     Make the replacements listed in the `aliases` dict (key: canonical
114     name, value: list of aliases).  If `aliases` is ``None``, default
115     to ``ALIASES``.
116
117     >>> aliases = {
118     ...     'J Doe <jdoe@a.com>':['Johnny <jdoe@b.edu>'],
119     ...     'JJJ Smith <jjjs@a.com>':['Jingly <jjjs@b.edu>'],
120     ...     None:['Anonymous <a@a.com>'],
121     ...     }
122     >>> authors = [
123     ...     'JJJ Smith <jjjs@a.com>', 'Johnny <jdoe@b.edu>',
124     ...     'Jingly <jjjs@b.edu>', 'J Doe <jdoe@a.com>', 'Anonymous <a@a.com>']
125     >>> replace_aliases(authors, with_email=True, aliases=aliases)
126     ['J Doe <jdoe@a.com>', 'JJJ Smith <jjjs@a.com>']
127     >>> replace_aliases(authors, with_email=False, aliases=aliases)
128     ['J Doe', 'JJJ Smith']
129     """
130     if aliases == None:
131         aliases = ALIASES
132     rev_aliases = reverse_aliases(aliases)
133     for i,author in enumerate(authors):
134         if author in rev_aliases:
135             authors[i] = rev_aliases[author]
136     authors = sorted(list(set(authors)))
137     if None in authors:
138         authors.remove(None)
139     if with_email == False:
140         authors = strip_email(*authors)
141     return authors