Skip to content

Commit bceffce

Browse files
committed
Move addheader to ./bin/ with various refactoring and lint style fixes.
* Change constants to `UPPER_CASE` as suggested by linters and PEP8 * Wrap action in main function for better isolation * Switch to fileio helpers instead of raw open+read/write+close * Automatic python module load path lookup from cmd parent path if insuffient * Simplify some `if X: return True` constructs as suggested by linters * Normalize path patterns to actually make the ../X ones work Adjust the addition of shebang interpreter and encoding lines to actually do what the label says. Add new bin, sbin and mig/lib dirs to projcode paths.
1 parent 83d6591 commit bceffce

File tree

2 files changed

+80
-62
lines changed

2 files changed

+80
-62
lines changed

addheader.py renamed to bin/addheader.py

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# addheader - add license header to all code modules.
7-
# Copyright (C) 2009-2024 The MiG Project by the Science HPC Center at UCPH
7+
# Copyright (C) 2009-2025 The MiG Project by the Science HPC Center at UCPH
88
#
99
# This file is part of MiG.
1010
#
@@ -26,7 +26,8 @@
2626
# --- END_HEADER ---
2727
#
2828

29-
"""Search code tree and add the required header to all python modules"""
29+
"""Search code tree and add the required header to all python modules."""
30+
3031
from __future__ import print_function
3132
from __future__ import absolute_import
3233

@@ -35,25 +36,39 @@
3536
import os
3637
import sys
3738

39+
# Try to import mig to assure we have a suitable python module load path
40+
try:
41+
import mig
42+
except ImportError:
43+
mig = None # type: ignore[assignment]
44+
45+
if mig is None:
46+
# NOTE: include cmd parent path in search path for mig.X imports to work
47+
MIG_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
48+
print("Using mig installation in %s" % MIG_ROOT)
49+
sys.path.append(MIG_ROOT)
50+
51+
from mig.shared.fileio import read_head_lines, read_file_lines, write_file_lines
3852
from mig.shared.projcode import code_root, py_code_files, sh_code_files, \
3953
js_code_files
4054

4155
# Modify these to fit actual project
42-
proj_vars = {}
43-
proj_vars['project_name'] = "MiG"
44-
proj_vars['authors'] = 'The MiG Project by the Science HPC Center at UCPH'
56+
PROJ_CONSTS = {}
57+
PROJ_CONSTS['project_name'] = "MiG"
58+
PROJ_CONSTS['authors'] = 'The MiG Project by the Science HPC Center at UCPH'
4559

46-
proj_vars['copyright_year'] = '2003-%d' % datetime.date.today().year
60+
PROJ_CONSTS['copyright_year'] = '2003-%d' % datetime.date.today().year
4761

4862
# Set interpreter path and file encoding if not already set in source files
4963
# Use empty string to leave them alone.
50-
proj_vars['interpreter_path'] = '/usr/bin/python'
51-
proj_vars['module_encoding'] = 'utf-8'
64+
PROJ_CONSTS['interpreter_path'] = '/usr/bin/env python'
65+
PROJ_CONSTS['module_encoding'] = 'utf-8'
5266

53-
begin_marker, end_marker = "--- BEGIN_HEADER ---", "--- END_HEADER ---"
67+
BEGIN_MARKER, END_MARKER = "--- BEGIN_HEADER ---", "--- END_HEADER ---"
68+
BACKUP_MARKER = ".unlicensed"
5469

5570
# Mandatory copyright notice for any license
56-
license_text = """#
71+
LICENSE_TEXT = """#
5772
# %(module_name)s - [optionally add short module description on this line]
5873
# Copyright (C) %(copyright_year)s %(authors)s
5974
"""
@@ -65,7 +80,7 @@
6580
# with a verbatim copy of the license text:
6681
# wget -O COPYING http://www.gnu.org/licenses/gpl2.txt
6782

68-
license_text += """#
83+
LICENSE_TEXT += """#
6984
# This file is part of %(project_name)s.
7085
#
7186
# %(project_name)s is free software: you can redistribute it and/or modify
@@ -84,18 +99,13 @@
8499
# USA."""
85100

86101

87-
def check_header(path, var_dict, preamble_size=4096):
102+
def check_header(path, var_dict, preamble_lines=100):
88103
"""Check if path already has a credible license header. Only looks inside
89104
the first preamble_size bytes of the file.
90105
"""
91-
module_fd = open(path, 'r')
92-
module_preamble = module_fd.read(4096)
93-
module_fd.close()
94-
if begin_marker in module_preamble or \
95-
proj_vars['authors'] in module_preamble:
96-
return True
97-
else:
98-
return False
106+
module_preamble = '\n'.join(read_head_lines(path, preamble_lines, None))
107+
return (BEGIN_MARKER in module_preamble or
108+
var_dict['authors'] in module_preamble)
99109

100110

101111
def add_header(path, var_dict, explicit_border=True, block_wrap=False):
@@ -109,33 +119,29 @@ def add_header(path, var_dict, explicit_border=True, block_wrap=False):
109119
Creates a '.unlicensed' backup copy of each file changed.
110120
"""
111121

112-
module_fd = open(path, 'r')
113-
module_lines = module_fd.readlines()
114-
module_fd.close()
115-
backup_fd = open(path + '.unlicensed', 'w')
116-
backup_fd.writelines(module_lines)
117-
backup_fd.close()
118-
# Do not truncate any existing unix executable hint and encoding
119-
act = '#!%(interpreter_path)s' % var_dict
122+
module_lines = read_file_lines(path, None)
123+
if not write_file_lines(module_lines, path + BACKUP_MARKER, None):
124+
print("Failed to create backup of %s - skip!" % path)
125+
return False
126+
# Do not truncate any existing unix executable hint (shebang) and encoding
127+
act = '#!%(interpreter_path)s\n' % var_dict
120128
if block_wrap:
121129
enc = ''
122130
else:
123131
enc = '# -*- coding: %(module_encoding)s -*-' % var_dict
124-
lic = license_text % var_dict
132+
lic = LICENSE_TEXT % var_dict
125133
module_header = []
126-
if var_dict['interpreter_path']:
134+
if module_lines and module_lines[0].startswith("#!"):
135+
module_header.append(module_lines[0])
136+
module_lines = module_lines[1:]
137+
elif var_dict['interpreter_path']:
127138
module_header.append(act)
128-
if module_lines and module_lines[0].startswith("#!"):
129-
module_lines = module_lines[1:]
130-
else:
131-
if module_lines and module_lines[0].startswith("#!"):
132-
module_header.append(module_lines[0].strip())
133-
module_lines = module_lines[1:]
134139

135-
if var_dict['module_encoding']:
140+
if module_lines and module_lines[0].startswith("# -*- coding"):
141+
module_header.append(module_lines[0])
142+
module_lines = module_lines[1:]
143+
elif var_dict['module_encoding']:
136144
module_header.append(enc)
137-
if module_lines and module_lines[0].startswith("# -*- coding"):
138-
module_lines = module_lines[1:]
139145

140146
if explicit_border:
141147
lic = """
@@ -146,7 +152,7 @@ def add_header(path, var_dict, explicit_border=True, block_wrap=False):
146152
#
147153
# %s
148154
#
149-
""" % (begin_marker, lic, end_marker)
155+
""" % (BEGIN_MARKER, lic, END_MARKER)
150156
if block_wrap:
151157
lic = """
152158
/*
@@ -155,23 +161,26 @@ def add_header(path, var_dict, explicit_border=True, block_wrap=False):
155161
""" % lic
156162

157163
module_header.append(lic)
158-
module_text = '\n'.join(module_header) % var_dict + '\n'\
159-
+ ''.join(module_lines)
160164

161-
module_fd = open(path, 'w')
162-
module_fd.write(module_text)
163-
module_fd.close()
165+
updated_lines = [i % var_dict for i in module_header + [''] + module_lines]
166+
167+
if not write_file_lines(updated_lines, path, None):
168+
print("Failed to write %s with added headers!" % path)
169+
return False
170+
#print("DEBUG: wrote %s with added headers!" % path)
171+
return True
164172

165173

166-
if __name__ == '__main__':
174+
def main(argv):
175+
"""Run header addition for given argv"""
167176
target = os.getcwd()
168-
if len(sys.argv) > 1:
169-
target = os.path.abspath(sys.argv[1])
177+
if len(argv) > 1:
178+
target = os.path.abspath(argv[1])
170179
mig_code_base = target
171-
if len(sys.argv) > 2:
172-
mig_code_base = os.path.abspath(sys.argv[2])
180+
if len(argv) > 2:
181+
mig_code_base = os.path.abspath(argv[2])
173182

174-
for (root, dirs, files) in os.walk(target):
183+
for (root, _, files) in os.walk(target):
175184

176185
# skip all dot dirs - they are from repos etc and _not_ jobs
177186

@@ -181,27 +190,33 @@ def add_header(path, var_dict, explicit_border=True, block_wrap=False):
181190
src_path = os.path.join(root, name)
182191
if os.path.islink(src_path):
183192
continue
193+
if src_path.endswith(BACKUP_MARKER):
194+
continue
184195
print('Inspecting %s' % src_path)
185196
for pattern in py_code_files + sh_code_files + js_code_files:
186-
if pattern in js_code_files:
187-
needs_block = True
188-
else:
189-
needs_block = False
190-
191-
pattern = os.path.join(mig_code_base, code_root, pattern)
197+
needs_block = (pattern in js_code_files)
198+
pattern = os.path.normpath(os.path.join(
199+
mig_code_base, code_root, pattern))
192200

193-
# print "Testing %s against %s" % (src_path, pattern)
201+
#print("DEBUG: Testing %s against %s" % (src_path, pattern))
194202

195203
if src_path == pattern or fnmatch.fnmatch(src_path, pattern):
196204
print('Matched %s against %s' % (src_path, pattern))
197-
proj_vars['module_name'] = name.replace('.py', '')
198-
if check_header(src_path, proj_vars):
205+
PROJ_CONSTS['module_name'] = name.replace('.py', '')
206+
if check_header(src_path, PROJ_CONSTS):
199207
print('Skip %s with existing header' % src_path)
200208
continue
201-
add_header(src_path, proj_vars, block_wrap=needs_block)
209+
add_header(src_path, PROJ_CONSTS, block_wrap=needs_block)
210+
# else:
211+
# print('DEBUG: %s does not match %s' % (src_path, pattern))
212+
202213
print()
203214
print("Added license headers to code in %s" % target)
204215
print()
205216
print("Don't forget to include COPYING file in root of source, e.g. run:")
206217
print("wget -O COPYING http://www.gnu.org/licenses/gpl2.txt")
207218
print("if using the default GPL v2 license here.")
219+
220+
221+
if __name__ == '__main__':
222+
main(sys.argv)

mig/shared/projcode.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33
#
44
# projcode - simple helpers for transforming or searching project code
5-
# Copyright (C) 2003-2021 The MiG Project lead by Brian Vinter
5+
# Copyright (C) 2009-2025 The MiG Project by the Science HPC Center at UCPH
66
#
77
# This file is part of MiG.
88
#
@@ -31,7 +31,10 @@
3131
py_code_files = [
3232
# a few scripts are in parent dir of code_root
3333
'../%s' % plain,
34+
'../bin/%s' % plain,
35+
'../sbin/%s' % plain,
3436
'%s' % plain,
37+
'lib/%s' % plain,
3538
'cgi-bin/%s' % plain,
3639
'cgi-sid/%s' % plain,
3740
'install/%s' % plain,

0 commit comments

Comments
 (0)