From a9b1762c3280b0b941489ca11f73fd13ee210ee5 Mon Sep 17 00:00:00 2001 From: David Wells Date: Thu, 4 May 2017 15:17:58 -0400 Subject: [PATCH] Add a script that finds deprecated items. --- contrib/utilities/print_deprecated.py | 140 ++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 contrib/utilities/print_deprecated.py diff --git a/contrib/utilities/print_deprecated.py b/contrib/utilities/print_deprecated.py new file mode 100644 index 0000000000..642173c135 --- /dev/null +++ b/contrib/utilities/print_deprecated.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +## --------------------------------------------------------------------- +## +## Copyright (C) 2017 by the deal.II authors +## +## This file is part of the deal.II library. +## +## The deal.II library is free software; you can use it, redistribute +## it, and/or modify it under the terms of the GNU Lesser General +## Public License as published by the Free Software Foundation; either +## version 2.1 of the License, or (at your option) any later version. +## The full text of the license can be found in the file LICENSE at +## the top level of the deal.II distribution. +## +## --------------------------------------------------------------------- +"""Search the ./include directory and print out a summary of each deprecated +object, class, function, etc. Compatile with emacs' compilation-mode. + +You may need to run + + git fetch --tags upstream + +to make the tag (i.e., which release corresponds to which commit) printing work. +""" + +from __future__ import print_function + +import datetime +import errno +import os +import re +import subprocess +import sys + + +def decode_and_split(string): + """Decode a string as unicode and split its lines. Works with python 2 and + python 3. + """ + if sys.version_info.major > 2: + return str(string, 'utf8').splitlines() + else: + return unicode(string, errors='replace').splitlines() + + +class DeprecatedDeclaration(object): + """A record of a deprecated declaration. The input line containing the + deprecation usually comes from some grep-like program and is assumed to have + a file name, line number, and contain DEAL_II_DEPRECATED, e.g., + + ./include/deal.II/base/utilities.h:457: bool job_supports_mpi() DEAL_II_DEPRECATED; + + is one such valid line. The file name and line number are used to query git + for information regarding when the deprecation notice appeared. The line + itself is printed out later. + """ + def __init__(self, line): + assert "DEAL_II_DEPRECATED" in line + # in case the line contains extra ':'s, split input based on where grep + # puts the line number: + line_number_re = re.search(":[0-9]+:", line) + if line_number_re: + line_number_range = (line_number_re.regs[0][0] + 1, + line_number_re.regs[0][1] - 1) + self.file_name = line[0:line_number_range[0] - 1] + self.line_n = int(line[slice(*line_number_range)]) + self.deprecation_line = line[line_number_range[1] + 1:].strip() + else: + raise ValueError("The given line does not contain a line number of " + "the form, e.g., :42:.") + + git_log_output = subprocess.check_output(["git", "blame", "-p", + "-L", "{0},{0}".format(self.line_n), + self.file_name]) + + self.git_log_output = decode_and_split(git_log_output) + self.commit_hash = self.git_log_output[0].split(' ')[0] + self.commit_summary = self.git_log_output[9][len("summary "):] + self.epoch_time = int(self.git_log_output[7][len("commiter-time "):]) + self.output_time = datetime.datetime.utcfromtimestamp(self.epoch_time) + + git_tag_output = subprocess.check_output(["git", "tag", "--contains", + self.commit_hash]) + git_tag_output = decode_and_split(git_tag_output) + relevant_tags = [tag for tag in git_tag_output if "rc" not in tag] + if relevant_tags: + self.release = relevant_tags[0][1:] # skip the prepended 'v' + else: + self.release = "" + + + def print_record(self): + """Print a description of the record to stdout. The 'Release' field + corresponds to the release number in which the declaration was first + marked as deprecated. + """ + print("File: " + self.file_name + ":" + str(self.line_n) + ": ") + print("Line: " + self.deprecation_line) + print("Date: " + str(self.output_time)) + print("Message: " + self.commit_summary) + print("Hash: " + self.commit_hash) + print("Release: " + self.release) + + +def main(): + # check that we are in the top directory + if not all((os.path.isdir(directory) + for directory in ["./include", "./tests", "./examples"])): + raise Exception("This script must be called from the top-level directory of deal.II.") + result = subprocess.check_output(["grep", "-R", "--line-number", "DEAL_II_DEPRECATED", + "./include/"]) + result = decode_and_split(result) + + deprecated_declarations = list() + for line in result: + try: + deprecated_declarations.append(DeprecatedDeclaration(line)) + except subprocess.CalledProcessError: # ignore errors coming from git + pass + + deprecated_declarations.sort(key=lambda u: u.epoch_time) + + try: + for record in deprecated_declarations: + record.print_record() + print("") + + sys.stderr.flush() + sys.stdout.flush() + except IOError as pipe_error: + if pipe_error.errno == errno.EPIPE: + # this is OK, it usually just means the user piped this script to + # head or some other screen capturing program that ignores some of + # its input + pass + + +if __name__ == '__main__': + main() -- 2.39.5