Skip to content

Commit b7e9fd1

Browse files
committed
Updated check-copyright to python3.7 and added a --patch switch
- Legacy-Id: 16372
1 parent cfd4025 commit b7e9fd1

1 file changed

Lines changed: 78 additions & 27 deletions

File tree

bin/check-copyright

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#!/usr/bin/env python
2-
# -*- python -*-
1+
#!/usr/bin/env python3.7
2+
# -*- mode: python; coding: utf-8 -*-
33
# Copyright The IETF Trust 2019, All Rights Reserved
44
"""
55
NAME
@@ -10,11 +10,17 @@ SYNOPSIS
1010
1111
DESCRIPTION
1212
Given a list of files or filename wildcard patterns, check all for
13-
an IETF Trust copyright notice with the current year.
13+
an IETF Trust copyright notice with the current year. Optionally
14+
generate a diff on standard out which can be used by 'patch'.
1415
15-
%(options)s
16+
An invocation similar to the following can be particularly useful with
17+
a set of changed version-controlled files, as it will fix up the
18+
Copyright statements of any python files with pending changes:
19+
20+
$ check-copyright -p $(svn st | cut -c 9- | grep '\.py$' ) | patch -p0
1621
17-
FILES
22+
23+
%(options)s
1824
1925
AUTHOR
2026
Written by Henrik Levkowetz, <henrik@tools.ietf.org>
@@ -47,6 +53,8 @@ version = "0.10"
4753
program = os.path.basename(sys.argv[0])
4854
progdir = os.path.dirname(sys.argv[0])
4955

56+
debug.debug = True
57+
5058
# ----------------------------------------------------------------------
5159
# Parse options
5260

@@ -63,8 +71,8 @@ if len(sys.argv) < 1:
6371
sys.exit(1)
6472

6573
try:
66-
opts, files = getopt.gnu_getopt(sys.argv[1:], "hvV", ["help", "version", "verbose",])
67-
except Exception, e:
74+
opts, files = getopt.gnu_getopt(sys.argv[1:], "hpvV", ["help", "patch", "version", "verbose",])
75+
except Exception as e:
6876
print( "%s: %s" % (program, e))
6977
sys.exit(1)
7078

@@ -73,12 +81,15 @@ except Exception, e:
7381

7482
# set default values, if any
7583
opt_verbose = 0
84+
opt_patch = False
7685

7786
# handle individual options
7887
for opt, value in opts:
7988
if opt in ["-h", "--help"]: # Output this help, then exit
8089
print( __doc__ % locals() )
8190
sys.exit(1)
91+
elif opt in ["-p", "--patch"]: # Generate patch output rather than error messages
92+
opt_patch = True
8293
elif opt in ["-V", "--version"]: # Output version information, then exit
8394
print( program, version )
8495
sys.exit(0)
@@ -107,7 +118,7 @@ def pipe(cmd, inp=None):
107118
args = shlex.split(cmd)
108119
bufsize = 4096
109120
stdin = PIPE if inp else None
110-
pipe = Popen(args, stdin=stdin, stdout=PIPE, stderr=PIPE, bufsize=bufsize)
121+
pipe = Popen(args, stdin=stdin, stdout=PIPE, stderr=PIPE, bufsize=bufsize, encoding='utf-8', universal_newlines=True)
111122
out, err = pipe.communicate(inp)
112123
code = pipe.returncode
113124
if code != 0:
@@ -156,9 +167,6 @@ import json
156167

157168
cwd = os.getcwd()
158169

159-
if cwd.split(os.path.sep)[-1] != 'trunk':
160-
die("Expected to run this operation in trunk, but the current\ndirectory is '%s'" % cwd)
161-
162170
# Get current initinfo from cache and svn
163171
cachefn = os.path.join(os.environ.get('HOME', '.'), '.initinfo')
164172

@@ -178,23 +186,66 @@ loginfo_format = r'^r[0-9]+ \| [^@]+@[^@]+ \| \d\d\d\d-\d\d-\d\d '
178186

179187
year = time.strftime('%Y')
180188
for path in files:
181-
note("Checking path %s" % path)
182-
if not path in initinfo:
183-
initinfo.update(get_first_commit(path))
184-
write_cache = True
185-
date = initinfo[path]['date']
186-
init = date[:4]
187-
copyright = "(?i)Copyright The IETF Trust (%s-)?%s, All Rights Reserved" % (init, year)
188-
with open(path) as file:
189-
chunk = file.read(4000)
190-
if os.path.basename(path) == '__init__.py' and len(chunk)==0:
189+
try:
190+
if not os.path.exists(path):
191+
note("File does not exist: %s" % path)
191192
continue
192-
if not re.search(copyright, chunk):
193-
sys.stdout.write("%s(1): Error: Missing or bad copyright. " % path)
194-
if year == init:
195-
print(" Expected: Copyright The IETF Trust %s, All Rights Reserved" % year)
196-
else:
197-
print(" Expected: Copyright The IETF Trust %s-%s, All Rights Reserved" % (init, year))
193+
note("Checking path %s" % path)
194+
if not path in initinfo:
195+
initinfo.update(get_first_commit(path))
196+
write_cache = True
197+
date = initinfo[path]['date']
198+
init = date[:4]
199+
200+
copyright_re = r"(?i)Copyright The IETF Trust (\d+-)?\d+, All Rights Reserved"
201+
copyright_year_re = r"(?i)Copyright The IETF Trust (%s-)?%s, All Rights Reserved" % (init, year)
202+
with open(path) as file:
203+
try:
204+
chunk = file.read(4000)
205+
except UnicodeDecodeError as e:
206+
sys.stderr.write(f'Error when reading {file.name}: {e}\n')
207+
raise
208+
if os.path.basename(path) == '__init__.py' and len(chunk)==0:
209+
continue
210+
if not re.search(copyright_year_re, chunk):
211+
if year == init:
212+
copyright = f"Copyright The IETF Trust {year}, All Rights Reserved"
213+
else:
214+
copyright = f"Copyright The IETF Trust {init}-{year}, All Rights Reserved"
215+
if opt_patch:
216+
print(f"--- {file.name}\t(original)")
217+
print(f"+++ {file.name}\t(modified)")
218+
if not re.search(copyright_re, chunk):
219+
# Simple case, just insert copyright at the top
220+
print( "@@ -1,3 +1,4 @@")
221+
print(f"+# {copyright}")
222+
for i, line in list(enumerate(chunk.splitlines()))[:3]:
223+
print(f" {line}")
224+
else:
225+
# Find old copyright, then emit preceding lines,
226+
# change, and following lines.
227+
pos = None
228+
for i, line in enumerate(chunk.splitlines(), start=1):
229+
if re.search(copyright_re, line):
230+
pos = i
231+
break
232+
if not pos:
233+
raise RuntimeError("Unexpected state: Expected a copyright line, but found none")
234+
print(f"@@ -1,{pos+3} +1,{pos+3} @@")
235+
for i, line in list(enumerate(chunk.splitlines(), start=1))[:pos+3]:
236+
if i == pos:
237+
print(f"-{line}")
238+
print(f"+# {copyright}")
239+
else:
240+
print(f" {line}")
241+
else:
242+
sys.stderr.write(f"{path}(1): Error: Missing or bad copyright. Expected: {copyright}")
243+
except Exception:
244+
if write_cache:
245+
cache = initinfo
246+
with open(cachefn, "w") as file:
247+
json.dump(cache, file, indent=2, sort_keys=True)
248+
raise
198249

199250
if write_cache:
200251
cache = initinfo

0 commit comments

Comments
 (0)