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 rev_extensions = {e: k for k, exts in extensions.items() for e in exts}
244 if filename and os.path.isfile(filename):
245 extension = os.path.splitext(filename)[1][1:].lower()
246 if extension in ('in',):
247 name = os.path.splitext(filename)[0]
248 extension = os.path.splitext(name)[1][1:].lower()
249 if extension in rev_extensions:
250 return rev_extensions[extension]
254 m = magic.open(magic.MAGIC_MIME_TYPE)
257 'cpp': ('text/x-c', 'text/x-c++'),
258 'shell': ('text/x-shellscript',),
259 'python': ('text/x-python',),
261 rev_file_formats = {f: k for k, ff in file_formats.items() for f in ff}
262 file_format = m.file(filename)
263 if file_format in rev_file_formats:
264 return rev_file_formats[file_format]
271 # -----------------------------------------------------------------------------
272 def insert_copyright(filename, owner, year, file_format):
274 Insert copyright note to a file.
277 filename (str): File path.
278 owner (str): Copyright owner.
279 year (str): Copyright year(s).
280 file_format (str): Format of comments.
283 with open(filename) as fid:
284 lines = fid.readlines()
286 warning("cannot read file: {}".format(filename))
289 if file_format in ('auto',):
290 file_format = autodetect_format(filename)
292 if owner.lower() in ('auto',):
293 owner = autodetect_owner(filename) or get_owner('all')
295 owner = get_owner(owner)
297 comment = get_comment(file_format)
299 warning("cannot detect format")
302 shell_row = search_line(lines, r'^#!') \
303 if file_format in ('sh', 'bash', 'csh', 'py', 'python') else -1
304 coding_row = search_line(lines, r'coding:', 3) \
305 if file_format in ('py', 'python') else -1
306 insert_point = max(0, shell_row + 1, coding_row + 1)
308 the_copyright = get_copyright(comment, owner, year)
310 lines = lines[:insert_point] + the_copyright + ['\n'] \
311 + lines[insert_point:]
313 with open(filename, 'w') as fid:
317 warning("cannot write file: {}".format(filename))
321 # -----------------------------------------------------------------------------
325 # Parse command line.
326 description = "Command line tool to insert copyright notice to a file."
327 parser = argparse.ArgumentParser(description=description)
329 help_string = "copyright owner; if not specified, tool tries to " \
330 "autodetect an owner from the file path; if auto-detection fails, " \
331 "an owner is set to '{owner}'"
333 parser.add_argument("-o", "--owner", action="store",
334 dest="owner", default=owner,
335 help=help_string.format(owner=get_owner('all')))
336 help_string = "copyright year(s); default: current year ({year})"
337 year = str(time.localtime().tm_year)
338 parser.add_argument("-y", "--year", action="store",
339 dest="year", default=year,
340 help=help_string.format(year=year))
341 help_string = "format of comments ({choices}); default: {file_format}"
343 parser.add_argument("-f", "--format", action="store", choices=formats(),
344 dest="format", default=file_format,
345 help=help_string.format(file_format=file_format,
346 choices="|".join(formats())))
347 parser.add_argument('files', nargs='+', metavar='FILE')
349 args = parser.parse_args(sys.argv[1:])
353 file_format = args.format
356 for filename in files:
357 insert_copyright(filename, owner, year, file_format)
361 # -----------------------------------------------------------------------------
362 if __name__ == "__main__":