41da98f3409809814b131d6f07072dfe68219345
[update-copyright.git] / update_copyright / utils.py
1 # Copyright (C) 2012 W. Trevor King
2 #
3 # This file is part of update-copyright.
4 #
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.
9 #
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.
14 #
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/>.
18
19 import difflib as _difflib
20 import os as _os
21 import os.path as _os_path
22 import textwrap as _textwrap
23 import time as _time
24
25 from . import LOG as _LOG
26
27
28 def long_author_formatter(copyright_year_string, authors):
29     """
30     >>> print '\\n'.join(long_author_formatter(
31     ...     copyright_year_string='Copyright (C) 1990-2010',
32     ...     authors=['Jack', 'Jill', 'John']))
33     Copyright (C) 1990-2010 Jack
34                             Jill
35                             John
36     """
37     lines = ['%s %s' % (copyright_year_string, authors[0])]
38     for author in authors[1:]:
39         lines.append(' '*(len(copyright_year_string)+1) + author)
40     return lines
41
42 def short_author_formatter(copyright_year_string, authors):
43     """
44     >>> print '\\n'.join(short_author_formatter(
45     ...     copyright_year_string='Copyright (C) 1990-2010',
46     ...     authors=['Jack', 'Jill', 'John']*5))
47     Copyright (C) 1990-2010 Jack, Jill, John, Jack, Jill, John, Jack, Jill, John, Jack, Jill, John, Jack, Jill, John
48     """
49     blurb = '%s %s' % (copyright_year_string, ', '.join(authors))
50     return [blurb]
51
52 def copyright_string(original_year, final_year, authors, text, info={},
53                      author_format_fn=long_author_formatter,
54                      formatter_kwargs={}, prefix='', wrap=True,
55                      **wrap_kwargs):
56     """
57     >>> print(copyright_string(original_year=2005, final_year=2005,
58     ...                        authors=['A <a@a.com>', 'B <b@b.edu>'],
59     ...                        text=['BLURB',], prefix='# '
60     ...                        )) # doctest: +REPORT_UDIFF
61     # Copyright (C) 2005 A <a@a.com>
62     #                    B <b@b.edu>
63     #
64     # BLURB
65     >>> print(copyright_string(original_year=2005, final_year=2009,
66     ...                        authors=['A <a@a.com>', 'B <b@b.edu>'],
67     ...                        text=['BLURB',]
68     ...                        )) # doctest: +REPORT_UDIFF
69     Copyright (C) 2005-2009 A <a@a.com>
70                             B <b@b.edu>
71     <BLANKLINE>
72     BLURB
73     >>> print(copyright_string(original_year=2005, final_year=2005,
74     ...                        authors=['A <a@a.com>', 'B <b@b.edu>'],
75     ...                        text=['This file is part of %(program)s.',],
76     ...                        author_format_fn=short_author_formatter,
77     ...                        info={'program':'update-copyright'},
78     ...                        width=25,
79     ...                        )) # doctest: +REPORT_UDIFF
80     Copyright (C) 2005 A <a@a.com>, B <b@b.edu>
81     <BLANKLINE>
82     This file is part of
83     update-copyright.
84     >>> print(copyright_string(original_year=2005, final_year=2005,
85     ...                        authors=['A <a@a.com>', 'B <b@b.edu>'],
86     ...                        text=[('This file is part of %(program)s.  '*3
87     ...                               ).strip(),],
88     ...                        info={'program':'update-copyright'},
89     ...                        author_format_fn=short_author_formatter,
90     ...                        wrap=False,
91     ...                        )) # doctest: +REPORT_UDIFF
92     Copyright (C) 2005 A <a@a.com>, B <b@b.edu>
93     <BLANKLINE>
94     This file is part of update-copyright.  This file is part of update-copyright.  This file is part of update-copyright.
95     """
96     for key in ['initial_indent', 'subsequent_indent']:
97         if key not in wrap_kwargs:
98             wrap_kwargs[key] = prefix
99
100     if original_year == final_year:
101         date_range = '%s' % original_year
102     else:
103         date_range = '%s-%s' % (original_year, final_year)
104     copyright_year_string = 'Copyright (C) %s' % date_range
105
106     lines = author_format_fn(copyright_year_string, authors,
107                              **formatter_kwargs)
108     for i,line in enumerate(lines):
109         lines[i] = prefix + line
110
111     for i,paragraph in enumerate(text):
112         try:
113             text[i] = paragraph % info
114         except ValueError, e:
115             _LOG.error(
116                 "{}: can't format {} with {}".format(e, paragraph, info))
117             raise
118         except TypeError, e:
119             _LOG.error(
120                 ('{}: copright text must be a list of paragraph strings, '
121                  'not {}').format(e, repr(text)))
122             raise
123
124     if wrap == True:
125         text = [_textwrap.fill(p, **wrap_kwargs) for p in text]
126     else:
127         assert wrap_kwargs['subsequent_indent'] == '', \
128             wrap_kwargs['subsequent_indent']
129     sep = '\n%s\n' % prefix.rstrip()
130     return sep.join(['\n'.join(lines)] + text)
131
132 def tag_copyright(contents, tag=None):
133     """
134     >>> contents = '''Some file
135     ... bla bla
136     ... # Copyright (copyright begins)
137     ... # (copyright continues)
138     ... # bla bla bla
139     ... (copyright ends)
140     ... bla bla bla
141     ... '''
142     >>> print tag_copyright(contents, tag='-xyz-CR-zyx-')
143     Some file
144     bla bla
145     -xyz-CR-zyx-
146     (copyright ends)
147     bla bla bla
148     <BLANKLINE>
149     """
150     lines = []
151     incopy = False
152     for line in contents.splitlines():
153         if incopy == False and line.startswith('# Copyright'):
154             incopy = True
155             lines.append(tag)
156         elif incopy == True and not line.startswith('#'):
157             incopy = False
158         if incopy == False:
159             lines.append(line.rstrip('\n'))
160     return '\n'.join(lines)+'\n'
161
162 def update_copyright(contents, tag=None, **kwargs):
163     """
164     >>> contents = '''Some file
165     ... bla bla
166     ... # Copyright (copyright begins)
167     ... # (copyright continues)
168     ... # bla bla bla
169     ... (copyright ends)
170     ... bla bla bla
171     ... '''
172     >>> print update_copyright(contents, original_year=2008,
173     ...                        authors=['Jack', 'Jill'],
174     ...                        text=['BLURB',], prefix='# ', tag='--tag--'
175     ...     ) # doctest: +ELLIPSIS, +REPORT_UDIFF
176     Some file
177     bla bla
178     # Copyright (C) 2008-... Jack
179     #                         Jill
180     #
181     # BLURB
182     (copyright ends)
183     bla bla bla
184     <BLANKLINE>
185     """
186     current_year = _time.gmtime()[0]
187     string = copyright_string(final_year=current_year, **kwargs)
188     contents = tag_copyright(contents=contents, tag=tag)
189     return contents.replace(tag, string)
190
191 def get_contents(filename):
192     if _os_path.isfile(filename):
193         f = open(filename, 'r')
194         contents = f.read()
195         f.close()
196         return contents
197     return None
198
199 def set_contents(filename, contents, original_contents=None, dry_run=False):
200     if original_contents is None:
201         original_contents = get_contents(filename=filename)
202     _LOG.debug('check contents of {}'.format(filename))
203     if contents != original_contents:
204         if original_contents is None:
205             _LOG.info('creating {}'.format(filename))
206         else:
207             _LOG.info('updating {}'.format(filename))
208             _LOG.debug('\n'.join(
209                     _difflib.unified_diff(
210                         original_contents.splitlines(), contents.splitlines(),
211                         fromfile=_os_path.normpath(
212                             _os_path.join('a', filename)),
213                         tofile=_os_path.normpath(_os_path.join('b', filename)),
214                         n=3, lineterm='')))
215         if dry_run == False:
216             f = file(filename, 'w')
217             f.write(contents)
218             f.close()
219     _LOG.debug('no change in {}'.format(filename))
220
221 def list_files(root='.'):
222     for dirpath,dirnames,filenames in _os.walk(root):
223         for filename in filenames:
224             yield _os_path.normpath(_os_path.join(root, dirpath, filename))