2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2017-2019 OPEN CASCADE
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License Version 3 as
7 # published by the Free Software Foundation.
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, you may download a copy of license
16 # from https://www.gnu.org/licenses/gpl-3.0.
19 Command line tool to insert copyright notice to a file.
20 Usage: type "insert_copyright --help" to learn how to use tool.
29 # -----------------------------------------------------------------------------
44 # -----------------------------------------------------------------------------
47 Print error message to stderr and exit.
50 msg (str): Error message.
52 sys.stderr.write("ERROR: {}\n".format(msg))
56 # -----------------------------------------------------------------------------
59 Print wating message to stderr.
62 msg (str): Warning message.
64 sys.stderr.write("WARNING: {}\n".format(msg))
67 # -----------------------------------------------------------------------------
70 Get supported formats of comments.
73 list[str]: List of formats.
75 return list(_COMMENTS)
78 # -----------------------------------------------------------------------------
79 def search_line(lines, rex, depth=1):
81 Search regexp in given lines.
84 lines (list[str]): List of strings.
85 regex (str): Regular expression.
86 depth (Optional[int]): Depth of search. Defaults to 1 line.
89 int: Index of first matched line.
91 for i in range(depth if depth >= 0 else len(lines)):
93 if re.search(rex, lines[i]):
98 # -----------------------------------------------------------------------------
104 owner (str): Owner's name of alias or list of owners separated
110 if owner.lower() in 'all':
111 return get_owner('cea,edf,occ')
113 owners = [i.strip() for i in owner.split(',')]
116 i = _OWNERS.get(i.lower(), i)
119 return ', '.join(result)
122 # -----------------------------------------------------------------------------
123 def get_comment(file_format):
125 Get comment for given format.
128 format (str): Format of comments.
131 str: Comment signature for given format; *None* for unsupported
134 return _COMMENTS.get(file_format) if file_format else None
137 # -----------------------------------------------------------------------------
138 def get_copyright(comment, owner, year):
140 Generate copyright from template.
143 comment (str): Comment signature.
144 owner (str): Copyright owner.
145 year (str): Copyright year(s).
148 list[str]: List of strings with copyright data.
150 template = osp.join(osp.dirname(sys.argv[0]), 'copyright.template')
152 with open(template) as fid:
153 cp_notice = [i.strip() for i in fid.readlines()]
154 cp_notice = [i % {'year' : year, 'owner' : owner} for i in cp_notice]
155 cp_notice = [comment + ' ' + i if i else comment for i in cp_notice]
156 return [i + '\n' for i in cp_notice] + ['\n']
158 error_exit("cannot find copyright template")
162 # -----------------------------------------------------------------------------
163 def get_module_owner(module):
165 Get owner of given module.
168 module (str): Module name.
173 modules_info = osp.join(osp.dirname(sys.argv[0]), 'modules.info')
176 with open(modules_info) as fid:
177 lines = fid.readlines()
178 index = search_line(lines, r'^{}:'.format(module), -1)
180 return get_owner(lines[index].split(":")[1].strip())
182 warning("cannot find modules info file")
186 # -----------------------------------------------------------------------------
187 def autodetect_owner(filename):
189 Auto-detect owner from file path.
192 filename (str): File path.
195 str: Owner; *None* if owner isn't detected.
197 filename = osp.realpath(filename)
198 if osp.exists(filename):
199 directory = osp.dirname(filename)
200 while directory != '/':
201 config_file = osp.join(directory, '.git', 'config')
202 if osp.exists(config_file):
204 from ConfigParser import ConfigParser
206 from configparser import ConfigParser
208 from StringIO import StringIO
210 from io import StringIO
211 with open(config_file) as fid:
212 gitcfg = fid.readlines()
214 data = StringIO(''.join([l.lstrip() for l in gitcfg]))
215 cfg.readfp(data) # pragma pylint: disable=deprecated-method
216 url = cfg.get('remote "origin"', 'url')
217 module = osp.split(url)[-1]
218 if module.endswith('.git'):
220 return get_module_owner(module)
222 directory = osp.dirname(directory)
226 # -----------------------------------------------------------------------------
227 def autodetect_format(filename):
229 Auto-detect format from filename.
232 filename (str): File path.
235 str: Format of comments; *None* if format isn't detected.
238 'cpp': ('c', 'cpp', 'cxx', 'cc', 'c++',
239 'h', 'hxx', 'hpp', 'hh', 'h++',
241 'shell': ('sh', 'bash', 'csh', 'cmake', 'txt', 'cfg', 'ini', 'm4'),
244 rev_extensions = {e: k for k, exts in extensions.items() for e in exts}
245 if filename and osp.isfile(filename):
246 extension = osp.splitext(filename)[1][1:].lower()
247 if extension in ('in',):
248 name = osp.splitext(filename)[0]
249 extension = osp.splitext(name)[1][1:].lower()
250 if extension in rev_extensions:
251 return rev_extensions[extension]
255 mtool = magic.open(magic.MAGIC_MIME_TYPE)
258 'cpp': ('text/x-c', 'text/x-c++'),
259 'shell': ('text/x-shellscript',),
260 'python': ('text/x-python',),
262 rev_file_formats = {f: k for k, ff in file_formats.items() for f in ff}
263 file_format = mtool.file(filename)
264 if file_format in rev_file_formats:
265 return rev_file_formats[file_format]
272 # -----------------------------------------------------------------------------
273 def insert_copyright(filename, owner, year, file_format):
275 Insert copyright note to a file.
278 filename (str): File path.
279 owner (str): Copyright owner.
280 year (str): Copyright year(s).
281 file_format (str): Format of comments.
284 with open(filename) as fid:
285 lines = fid.readlines()
287 warning("cannot read file: {}".format(filename))
290 if file_format in ('auto',):
291 file_format = autodetect_format(filename)
293 if owner.lower() in ('auto',):
294 owner = autodetect_owner(filename) or get_owner('all')
296 owner = get_owner(owner)
298 comment = get_comment(file_format)
300 warning("cannot detect format")
303 shell_row = search_line(lines, r'^#!') \
304 if file_format in ('sh', 'bash', 'csh', 'py', 'python') else -1
305 coding_row = search_line(lines, r'coding:', 3) \
306 if file_format in ('py', 'python') else -1
307 insert_point = max(0, shell_row + 1, coding_row + 1)
309 cp_notice = get_copyright(comment, owner, year)
311 lines[insert_point:insert_point] = cp_notice
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 file(s)."
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 help_string = "file where to insert copyright notice"
348 parser.add_argument('files', nargs='+', metavar='FILE', help=help_string)
350 args = parser.parse_args(sys.argv[1:])
354 file_format = args.format
357 for filename in files:
358 insert_copyright(filename, owner, year, file_format)
362 # -----------------------------------------------------------------------------
363 if __name__ == "__main__":