practracker: add a --regen-overbroad option to remove overbroad exceptions.

Closes ticket 32372.
This commit is contained in:
Nick Mathewson 2020-02-03 13:02:12 -05:00
parent 1bf377f4b6
commit 2542a24b63
3 changed files with 47 additions and 5 deletions

4
changes/ticket32372 Normal file
View File

@ -0,0 +1,4 @@
o Minor features (best practices tracker):
- Practracker now supports a --regen-overbroad option to regenerate
the exceptions file, but only to revise exceptions to be _less_
tolerant of best-practices violations. Closes ticket 32372.

View File

@ -185,6 +185,9 @@ def main(argv):
help="Regenerate the exceptions file") help="Regenerate the exceptions file")
parser.add_argument("--list-overbroad", action="store_true", parser.add_argument("--list-overbroad", action="store_true",
help="List over-broad exceptions") help="List over-broad exceptions")
parser.add_argument("--regen-overbroad", action="store_true",
help="Regenerate the exceptions file, "
"removing over-broad exceptions.")
parser.add_argument("--exceptions", parser.add_argument("--exceptions",
help="Override the location for the exceptions file") help="Override the location for the exceptions file")
parser.add_argument("--strict", action="store_true", parser.add_argument("--strict", action="store_true",
@ -227,8 +230,9 @@ def main(argv):
filt.addThreshold(problem.DependencyViolationItem("*.c", int(args.max_dependency_violations))) filt.addThreshold(problem.DependencyViolationItem("*.c", int(args.max_dependency_violations)))
filt.addThreshold(problem.DependencyViolationItem("*.h", int(args.max_dependency_violations))) filt.addThreshold(problem.DependencyViolationItem("*.h", int(args.max_dependency_violations)))
if args.list_overbroad and args.regen: if args.list_overbroad + args.regen + args.regen_overbroad > 1:
print("Cannot use --regen with --list-overbroad", print("Cannot use more than one of --regen, --list-overbroad, and "
"--regen-overbroad.",
file=sys.stderr) file=sys.stderr)
sys.exit(1) sys.exit(1)
@ -247,13 +251,15 @@ def main(argv):
ProblemVault = problem.ProblemVault(exceptions_file) ProblemVault = problem.ProblemVault(exceptions_file)
problem_file = sys.stdout problem_file = sys.stdout
if args.list_overbroad: if args.list_overbroad or args.regen_overbroad:
# If we're listing overbroad exceptions, don't list problems. # If we're looking for overbroad exceptions, don't list problems
# immediately to the problem file.
problem_file = util.NullFile() problem_file = util.NullFile()
# 2.1) Adjust the exceptions so that we warn only about small problems, # 2.1) Adjust the exceptions so that we warn only about small problems,
# and produce errors on big ones. # and produce errors on big ones.
if not (args.regen or args.list_overbroad or args.strict): if not (args.regen or args.list_overbroad or args.regen_overbroad or
args.strict):
ProblemVault.set_tolerances(TOLERANCE_FNS) ProblemVault.set_tolerances(TOLERANCE_FNS)
# 3) Go through all the files and report problems if they are not exceptions # 3) Go through all the files and report problems if they are not exceptions
@ -272,6 +278,16 @@ def main(argv):
os.rename(tmpname, exceptions_file) os.rename(tmpname, exceptions_file)
sys.exit(0) sys.exit(0)
if args.regen_overbroad:
tmpname = exceptions_file + ".tmp"
tmpfile = open(tmpname, "w")
tmpfile.write(HEADER)
for item in ProblemVault.list_exceptions_without_overbroad():
print(item, file=tmpfile)
tmpfile.close()
os.rename(tmpname, exceptions_file)
sys.exit(0)
# If new issues were found, try to give out some advice to the developer on how to resolve it. # If new issues were found, try to give out some advice to the developer on how to resolve it.
if found_new_issues and not args.regen and not args.terse: if found_new_issues and not args.regen and not args.terse:
new_issues_str = """\ new_issues_str = """\
@ -296,6 +312,7 @@ variable.
else: else:
print(ex, "->", p.metric_value) print(ex, "->", p.metric_value)
sys.exit(found_new_issues) sys.exit(found_new_issues)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -29,6 +29,8 @@ class ProblemVault(object):
def __init__(self, exception_fname=None): def __init__(self, exception_fname=None):
# Exception dictionary: { problem.key() : Problem object } # Exception dictionary: { problem.key() : Problem object }
self.exceptions = {} self.exceptions = {}
# Exception list: list of Problem objects, in the order added.
self.exception_list = []
# Exception dictionary: maps key to the problem it was used to # Exception dictionary: maps key to the problem it was used to
# suppress. # suppress.
self.used_exception_for = {} self.used_exception_for = {}
@ -63,6 +65,7 @@ class ProblemVault(object):
sys.exit(1) sys.exit(1)
self.exceptions[problem.key()] = problem self.exceptions[problem.key()] = problem
self.exception_list.append(problem)
#print "Registering exception: %s" % problem #print "Registering exception: %s" % problem
def register_problem(self, problem): def register_problem(self, problem):
@ -98,6 +101,24 @@ class ProblemVault(object):
if p is None or e.is_worse_than(p): if p is None or e.is_worse_than(p):
yield (e, p) yield (e, p)
def list_exceptions_without_overbroad(self):
"""Return an iterator of new problems, such that overbroad
exceptions are replaced with minimally broad versions, or removed.
"""
for e in self.exception_list:
p = self.used_exception_for.get(e.key())
if p is None:
# This exception wasn't needed at all.
continue
if e.is_worse_than(p):
# The exception is worse than the problem we found.
# Yield the problem as the new exception value.
yield p
else:
# The problem is as bad as the exception, or worse.
# Yield the exception.
yield e
def set_tolerances(self, fns): def set_tolerances(self, fns):
"""Adjust the tolerances for the exceptions in this vault. Takes """Adjust the tolerances for the exceptions in this vault. Takes
a map of problem type to a function that adjusts the permitted a map of problem type to a function that adjusts the permitted