4 # Copyright (C) 2017 CEA/DEN, EDF R&D
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License Version 3 as
8 # published by the Free Software Foundation.
10 # This program is distributed in the hope that it will be useful, but
11 # 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 this program; if not, you may download a copy of license
17 # from https://www.gnu.org/licenses/gpl-3.0.
20 Command line tool to insert copyright notice to a file.
21 Usage: type "insert_copyright --help" to learn how to use tool.
30 # pragma pylint: disable=redefined-builtin
32 # -----------------------------------------------------------------------------
47 # -----------------------------------------------------------------------------
50 Print error message to stderr and exit.
53 msg (str): Error message.
55 sys.stderr.write("ERROR: {}\n".format(msg))
59 # -----------------------------------------------------------------------------
62 Print wating message to stderr.
65 msg (str): Warning message.
67 sys.stderr.write("WARNING: {}\n".format(msg))
70 # -----------------------------------------------------------------------------
73 Get supported formats of comments.
76 list[str]: List of formats.
78 return list(_COMMENTS)
81 # -----------------------------------------------------------------------------
82 def search_line(lines, rex, depth=1):
84 Search regexp in given lines.
87 lines (list[str]): List of strings.
88 regex (str): Regular expression.
89 depth (Optional[int]): Depth of search. Defaults to 1 line.
92 int: Index of first matched line.
94 for i in range(depth if depth >= 0 else len(lines)):
96 if re.search(rex, lines[i]):
101 # -----------------------------------------------------------------------------
102 def get_owner(owner):
107 owner (str): Owner's name of alias or list of owners separated
113 if owner.lower() in 'all':
114 return get_owner('cea,edf,occ')
116 owners = [i.strip() for i in owner.split(',')]
119 i = _OWNERS.get(i.lower(), i)
122 return ', '.join(result)
125 # -----------------------------------------------------------------------------
126 def get_comment(file_format):
128 Get comment for given format.
131 format (str): Format of comments.
134 str: Comment signature for given format; *None* for unsupported
137 return _COMMENTS.get(file_format) if file_format else None
140 # -----------------------------------------------------------------------------
141 def get_copyright(comment, owner, year):
143 Generate copyright from template.
146 comment (str): Comment signature.
147 owner (str): Copyright owner.
148 year (str): Copyright year(s).
151 list[str]: List of strings with copyright data.
153 template = os.path.join(os.path.dirname(sys.argv[0]), 'copyright.template')
156 with open(template) as fid:
157 the_copyright = fid.readlines()
159 error_exit("cannot find copyright template")
160 the_copyright = [i.replace('@year@', year) for i in the_copyright]
161 the_copyright = [i.replace('@owner@', owner) for i in the_copyright]
162 the_copyright = [comment + ' ' + i if i.strip() else comment + '\n'
163 for i in the_copyright]
167 # -----------------------------------------------------------------------------
168 def get_module_owner(module):
170 Get owner of given module.
173 module (str): Module name.
178 modules_info = os.path.join(os.path.dirname(sys.argv[0]), 'modules.info')
181 with open(modules_info) as fid:
182 lines = fid.readlines()
183 index = search_line(lines, r'^{}:'.format(module), -1)
185 return get_owner(lines[index].split(":")[1].strip())
187 warning("cannot find modules info file")
191 # -----------------------------------------------------------------------------
192 def autodetect_owner(filename):
194 Auto-detect owner from file path.
197 filename (str): File path.
200 str: Owner; *None* if owner isn't detected.
202 filename = os.path.realpath(filename)
203 if os.path.exists(filename):
204 directory = os.path.dirname(filename)
205 while directory != '/':
206 config_file = os.path.join(directory, '.git', 'config')
207 if os.path.exists(config_file):
208 from ConfigParser import ConfigParser
209 from StringIO import StringIO
210 with open(config_file) as fid:
211 gitcfg = fid.readlines()
213 data = StringIO(''.join([l.lstrip() for l in gitcfg]))
215 url = cfg.get('remote "origin"', 'url')
216 module = os.path.split(url)[-1]
217 if module.endswith('.git'):
219 return get_module_owner(module)
221 directory = os.path.dirname(directory)
225 # -----------------------------------------------------------------------------
226 def autodetect_format(filename):
228 Auto-detect format from filename.
231 filename (str): File path.
234 str: Format of comments; *None* if format isn't detected.
237 'cpp': ('c', 'cpp', 'cxx', 'cc', 'c++',
238 'h', 'hxx', 'hpp', 'hh', 'h++',
240 'shell': ('sh', 'bash', 'csh', 'cmake', 'txt', 'cfg', 'ini', 'm4'),
243 if filename and os.path.isfile(filename):
244 extension = os.path.splitext(filename)[1][1:].lower()
245 if extension in ('in',):
246 name = os.path.splitext(filename)[0]
247 extension = os.path.splitext(name)[1][1:].lower()
248 for file_format in extensions:
249 if extension in extensions[file_format]:
254 # -----------------------------------------------------------------------------
255 def insert_copyright(filename, owner, year, file_format):
257 Insert copyright note to a file.
260 filename (str): File path.
261 owner (str): Copyright owner.
262 year (str): Copyright year(s).
263 file_format (str): Format of comments.
266 with open(filename) as fid:
267 lines = fid.readlines()
269 warning("cannot read file: {}".format(filename))
272 if file_format in ('auto',):
273 file_format = autodetect_format(filename)
275 if owner.lower() in ('auto',):
276 owner = autodetect_owner(filename) or get_owner('all')
278 owner = get_owner(owner)
280 comment = get_comment(file_format)
282 warning("cannot detect format")
285 shell_row = search_line(lines, r'^#!') \
286 if file_format in ('sh', 'bash', 'csh', 'py', 'python') else -1
287 coding_row = search_line(lines, r'coding:', 3) \
288 if file_format in ('py', 'python') else -1
289 insert_point = max(0, shell_row + 1, coding_row + 1)
291 the_copyright = get_copyright(comment, owner, year)
293 lines = lines[:insert_point] + the_copyright + ['\n'] \
294 + lines[insert_point:]
296 with open(filename, 'w') as fid:
300 warning("cannot write file: {}".format(filename))
304 # -----------------------------------------------------------------------------
308 # Parse command line.
309 description = "Command line tool to insert copyright notice to a file."
310 parser = argparse.ArgumentParser(description=description)
312 help_string = "copyright owner; if not specified, tool tries to " \
313 "autodetect an owner from the file path; if auto-detection fails, " \
314 "an owner is set to '{owner}'"
316 parser.add_argument("-o", "--owner", action="store",
317 dest="owner", default=owner,
318 help=help_string.format(owner=get_owner('all')))
319 help_string = "copyright year(s); default: current year ({year})"
320 year = str(time.localtime().tm_year)
321 parser.add_argument("-y", "--year", action="store",
322 dest="year", default=year,
323 help=help_string.format(year=year))
324 help_string = "format of comments ({choices}); default: {file_format}"
326 parser.add_argument("-f", "--format", action="store", choices=formats(),
327 dest="format", default=file_format,
328 help=help_string.format(file_format=file_format,
329 choices="|".join(formats())))
330 parser.add_argument('files', nargs='+', metavar='FILE')
332 args = parser.parse_args(sys.argv[1:])
336 file_format = args.format
339 for filename in files:
340 insert_copyright(filename, owner, year, file_format)
344 # -----------------------------------------------------------------------------
345 if __name__ == "__main__":