Use the appropriate modules and run most of the work made by check_patch.py inside python, and not resorting to subprocesses. This way we get better control over the operations executed, better error management and more speed.
Signed-off-by: Lucas Meneghel Rodrigues <[email protected]> --- utils/check_patch.py | 271 ++++++++++++++++++++++++--------------------------- 1 file changed, 129 insertions(+), 142 deletions(-) diff --git a/utils/check_patch.py b/utils/check_patch.py index 79fd1cc..d55444e 100755 --- a/utils/check_patch.py +++ b/utils/check_patch.py @@ -22,19 +22,21 @@ Usage: check_patch.py -p [/path/to/patch] @author: Lucas Meneghel Rodrigues <[email protected]> """ -import os, stat, logging, sys, optparse, re, urllib +import os, stat, logging, sys, optparse, re, urllib, shutil, unittest try: import autotest.common as common except ImportError: import common + from autotest.client.shared import utils, error, logging_config from autotest.client.shared import logging_manager +from autotest.utils import run_pylint, reindent # Hostname of patchwork server to use PWHOST = "patchwork.virt.bos.redhat.com" class CheckPatchLoggingConfig(logging_config.LoggingConfig): - def configure_logging(self, results_dir=None, verbose=False): + def configure_logging(self, results_dir=None, verbose=True): super(CheckPatchLoggingConfig, self).configure_logging(use_console=True, verbose=verbose) p = os.path.join(os.getcwd(), 'check-patch.log') @@ -85,21 +87,21 @@ class VCS(object): return self.backend.get_modified_files() - def is_file_tracked(self, file): + def is_file_tracked(self, fl): """ Return whether a file is tracked by the VCS. """ - return self.backend.is_file_tracked(file) + return self.backend.is_file_tracked(fl) - def add_untracked_file(self, file): + def add_untracked_file(self, fl): """ Add an untracked file to version control. """ return self.backend.add_untracked_file(file) - def revert_file(self, file): + def revert_file(self, fl): """ Restore file according to the latest state on the reference repo. """ @@ -151,10 +153,10 @@ class SubVersionBackend(object): return modified_files - def is_file_tracked(self, file): + def is_file_tracked(self, fl): stdout = None try: - cmdresult = utils.run("svn status --ignore-externals %s" % file, + cmdresult = utils.run("svn status --ignore-externals %s" % fl, verbose=False) stdout = cmdresult.stdout except error.CmdError: @@ -172,29 +174,29 @@ class SubVersionBackend(object): return False - def add_untracked_file(self, file): + def add_untracked_file(self, fl): """ Add an untracked file under revision control. @param file: Path to untracked file. """ try: - utils.run('svn add %s' % file) + utils.run('svn add %s' % fl) except error.CmdError, e: - logging.error("Problem adding file %s to svn: %s", file, e) + logging.error("Problem adding file %s to svn: %s", fl, e) sys.exit(1) - def revert_file(self, file): + def revert_file(self, fl): """ Revert file against last revision. @param file: Path to file to be reverted. """ try: - utils.run('svn revert %s' % file) + utils.run('svn revert %s' % fl) except error.CmdError, e: - logging.error("Problem reverting file %s: %s", file, e) + logging.error("Problem reverting file %s: %s", fl, e) sys.exit(1) @@ -252,10 +254,10 @@ class GitBackend(object): return modified_files - def is_file_tracked(self, file): + def is_file_tracked(self, fl): stdout = None try: - cmdresult = utils.run("git status --porcelain %s" % file, + cmdresult = utils.run("git status --porcelain %s" % fl, verbose=False) stdout = cmdresult.stdout except error.CmdError: @@ -273,29 +275,29 @@ class GitBackend(object): return False - def add_untracked_file(self, file): + def add_untracked_file(self, fl): """ Add an untracked file under revision control. @param file: Path to untracked file. """ try: - utils.run('git add %s' % file) + utils.run('git add %s' % fl) except error.CmdError, e: - logging.error("Problem adding file %s to git: %s", file, e) + logging.error("Problem adding file %s to git: %s", fl, e) sys.exit(1) - def revert_file(self, file): + def revert_file(self, fl): """ Revert file against last revision. @param file: Path to file to be reverted. """ try: - utils.run('git checkout %s' % file) + utils.run('git checkout %s' % fl) except error.CmdError, e: - logging.error("Problem reverting file %s: %s", file, e) + logging.error("Problem reverting file %s: %s", fl, e) sys.exit(1) @@ -343,107 +345,95 @@ class FileChecker(object): self.vcs = vcs self.confirm = confirm self.basename = os.path.basename(self.path) + if self.basename.endswith('.py'): self.is_python = True else: self.is_python = False mode = os.stat(self.path)[stat.ST_MODE] - if mode & stat.S_IXUSR: - self.is_executable = True - else: - self.is_executable = False + self.is_executable = mode & stat.S_IXUSR checked_file = open(self.path, "r") self.first_line = checked_file.readline() checked_file.close() if "python" in self.first_line: self.is_python = True + self.corrective_actions = [] - self.indentation_exceptions = ['job_unittest.py'] + + self.indent_exception = 'cli/job_unittest.py' + + if self.is_python: + logging.debug("Checking file %s", + self.path.replace("./", "autotest/")) + if self.is_python and not self.path.endswith(".py"): + self.bkp_path = "%s-cp.py" % self.path + shutil.copyfile(self.path, self.bkp_path) + else: + self.bkp_path = None + + + def _get_checked_filename(self): + if self.bkp_path is not None: + return self.bkp_path + else: + return self.path def _check_indent(self): """ - Verifies the file with reindent.py. This tool performs the following - checks on python files: + Verifies the file with the reindent module + + This tool performs the following checks on python files: * Trailing whitespaces * Tabs * End of line * Incorrect indentation - For the purposes of checking, the dry run mode is used and no changes - are made. It is up to the user to decide if he wants to run reindent - to correct the issues. + And will automatically correct problems. """ success = True - indent_exception = 'cli/job_unittest.py' - if re.search (indent_exception, self.path): - return - if not self.path.endswith(".py"): - path = "%s-cp.py" % self.path - utils.run("cp %s %s" % (self.path, path), verbose=False) - else: - path = self.path + if re.search (self.indent_exception, self.path): + return success - this_path = os.path.abspath(sys.modules['__main__'].__file__) - reindent_path = os.path.join(os.path.dirname(this_path), - 'reindent.py') + path = self._get_checked_filename() try: - cmdstatus = utils.run('%s -v -d %s' % (reindent_path, - path), verbose=False) - except error.CmdError, e: - logging.error("Error executing reindent.py: %s" % e) - - if not "unchanged" in cmdstatus.stdout: - logging.info("File %s will be reindented" % self.path) - utils.run("%s -v %s" % (reindent_path, path), verbose=False) - if self.path != path: - utils.run("mv %s %s" % (path, self.path), verbose=False) - utils.run("rm %s.bak" % path, verbose=False) - logging.info("") + f = open(path) + except IOError, msg: + logging.error("Error opening %s: %s", path, str(msg)) + return False + + r = reindent.Reindenter(f) + f.close() + if r.run(): success = False + logging.info("Reindenting %s", path) + f = open(path) + r.write(f) + f.close() + + if not success and path != self.path: + shutil.copyfile(path, self.path) + return success def _check_code(self): """ - Verifies the file with run_pylint.py. This tool will call the static - code checker pylint using the special autotest conventions and warn - only on problems. If problems are found, a report will be generated. - Some of the problems reported might be bogus, but it's allways good - to look at them. + Verifies the file with run_pylint. + + This tool will call the static code checker pylint using the special + autotest conventions and warn about problems. Some of the problems + reported might be false positive, but it's allways good to look at them. """ success = True - non_py_ended = None - if not self.path.endswith(".py"): - path = "%s-cp.py" % self.path - utils.run("cp %s %s" % (self.path, path), verbose=False) - non_py_ended = " (filename has no .py)" - else: - path = self.path + path = self._get_checked_filename() - this_path = os.path.abspath(sys.modules['__main__'].__file__) - pylint_path = os.path.join(os.path.dirname(this_path), - 'run_pylint.py') - c_cmd = '%s %s' % (pylint_path, path) - try: - utils.run(c_cmd, verbose=False) - except error.CmdError, e: - e_msg = "Found syntax issues during '%s'" % c_cmd - if non_py_ended is not None: - e_msg += non_py_ended - utils.run("rm %s" % path, verbose=False) - logging.error(e_msg) - for stdout_line in e.result_obj.stdout.split("\n"): - if stdout_line: - logging.error(" [stdout]: %s", stdout_line) - for stderr_line in e.result_obj.stderr.split("\n"): - if stderr_line: - logging.error(" [stdout]: %s", stderr_line) - logging.error("") + if run_pylint.check_file(path): success = False + return success @@ -453,45 +443,54 @@ class FileChecker(object): unittest and report on any failures. This is important to keep our unit tests up to date. """ + success = True if "unittest" not in self.basename: - stripped_name = self.basename.strip(".py") + stripped_name = self.basename.rstrip(".py") unittest_name = stripped_name + "_unittest.py" unittest_path = self.path.replace(self.basename, unittest_name) if os.path.isfile(unittest_path): - unittest_cmd = 'python %s' % unittest_path - try: - utils.run(unittest_cmd, verbose=False) - except error.CmdError, e: - e_msg = ("Found unittest issues during '%s'" % - unittest_cmd) - logging.error(e_msg) - for stdout_line in e.result_obj.stdout.split("\n"): - if stdout_line: - logging.error(" [stdout]: %s", stdout_line) - for stderr_line in e.result_obj.stderr.split("\n"): - if stderr_line: - logging.error(" [stdout]: %s", stderr_line) - logging.error("") + module = unittest_path.rstrip(".py") + module = module.split("/") + module[0] = "autotest" + mod = common.setup_modules.import_module(module[-1], + ".".join(module[:-1])) + test = unittest.defaultTestLoader.loadTestsFromModule(mod) + suite = unittest.TestSuite(test) + runner = unittest.TextTestRunner() + result = runner.run(suite) + if result.errors or result.failures: + success = False + msg = '%s had %d failures and %d errors.' + msg = (msg % '.'.join(module), len(result.failures), + len(result.errors)) + logging.error(msg) + + return success def _check_permissions(self): """ - Verifies the execution permissions, specifically: + Verifies the execution permissions. + * Files with no shebang and execution permissions are reported. * Files with shebang and no execution permissions are reported. """ if self.first_line.startswith("#!"): if not self.is_executable: if self.vcs.type == "subversion": - self.corrective_actions.append("svn propset svn:executable ON %s" % self.path) + utils.run("svn propset svn:executable ON %s" % self.path, + ignore_status=True) elif self.vcs.type == "git": - self.corrective_actions.append("chmod +x %s" % self.path) + utils.run("chmod +x %s" % self.path, + ignore_status=True) else: if self.is_executable: if self.vcs.type == "subversion": - self.corrective_actions.append("svn propdel svn:executable %s" % self.path) + utils.run("svn propdel svn:executable %s" % self.path, + ignore_status=True) elif self.vcs.type == "git": - self.corrective_actions.append("chmod -x %s" % self.path) + utils.run("chmod -x %s" % self.path, + ignore_status=True) def report(self, skip_unittest=False): @@ -509,14 +508,10 @@ class FileChecker(object): if not skip_unittest: if not self._check_unittest(): success = False - if self.corrective_actions: - for action in self.corrective_actions: - answer = utils.ask("Would you like to execute %s?" % action, - auto=self.confirm) - if answer == "y": - rc = utils.system(action, ignore_status=True) - if rc != 0: - logging.error("Error executing %s" % action) + + if self.bkp_path is not None and os.path.isfile(self.bkp_path): + os.unlink(self.bkp_path) + return success @@ -562,15 +557,15 @@ class PatchChecker(object): self.vcs.update() - def _fetch_from_patchwork(self, id): + def _fetch_from_patchwork(self, pw_id): """ Gets a patch file from patchwork and puts it under the cwd so it can be applied. @param id: Patchwork patch id. """ - patch_url = "http://%s/patch/%s/mbox/" % (self.pwhost, id) - patch_dest = os.path.join(self.base_dir, 'patchwork-%s.patch' % id) + patch_url = "http://%s/patch/%s/mbox/" % (self.pwhost, pw_id) + patch_dest = os.path.join(self.base_dir, 'patchwork-%s.patch' % pw_id) patch = utils.get_file(patch_url, patch_dest) # Patchwork sometimes puts garbage on the path, such as long # sequences of underscores (_______). Get rid of those. @@ -585,15 +580,15 @@ class PatchChecker(object): return patch - def _fetch_from_github(self, id): + def _fetch_from_github(self, gh_id): """ Gets a patch file from patchwork and puts it under the cwd so it can be applied. - @param id: Patchwork patch id. + @param gh_id: Patchwork patch id. """ - patch_url = "https://github.com/autotest/autotest/pull/%s.patch" % id - patch_dest = os.path.join(self.base_dir, 'github-%s.patch' % id) + patch_url = "https://github.com/autotest/autotest/pull/%s.patch" % gh_id + patch_dest = os.path.join(self.base_dir, 'github-%s.patch' % gh_id) urllib.urlretrieve(patch_url, patch_dest) return patch_dest @@ -651,9 +646,9 @@ class PatchChecker(object): if __name__ == "__main__": parser = optparse.OptionParser() - parser.add_option('-p', '--patch', dest="local_patch", action='store', + parser.add_option('-l', '--patch', dest="local_patch", action='store', help='path to a patch file that will be checked') - parser.add_option('-i', '--patchwork-id', dest="id", action='store', + parser.add_option('-p', '--patchwork-id', dest="pw_id", action='store', help='id of a given patchwork patch') parser.add_option('-g', '--github-id', dest="gh_id", action='store', help='id of a given github patch') @@ -670,9 +665,10 @@ if __name__ == "__main__": options, args = parser.parse_args() local_patch = options.local_patch - id = options.id + pw_id = options.pw_id gh_id = options.gh_id debug = options.debug + run_pylint.set_verbosity(debug) full_check = options.full_check confirm = options.confirm pwhost = options.patchwork_host @@ -681,32 +677,27 @@ if __name__ == "__main__": vcs = None logging_manager.configure_logging(CheckPatchLoggingConfig(), verbose=debug) - ignore_list = ['common.py', ".svn", ".git", '.pyc', ".orig", ".rej", ".bak"] - files_failed_check = [] + if full_check: + run_pylint.set_verbosity(False) logging.info("Autotest full tree check") logging.info("") - for root, dirs, files in os.walk('.'): - for file in files: + for root, dirs, files in os.walk("."): + for fl in files: check = True - path = os.path.join(root, file) + path = os.path.join(root, fl) for pattern in ignore_list: if re.search(pattern, path): check = False if check: if vcs is not None: - if not vcs.is_file_tracked(file=path): + if not vcs.is_file_tracked(fl=path): check = False if check: file_checker = FileChecker(path=path, vcs=vcs, confirm=confirm) - if not file_checker.report(skip_unittest=True): - files_failed_check.append(file) - - if files_failed_check: - logging.error("List of files with problems: %s", files_failed_check) - sys.exit(-1) + file_checker.report(skip_unittest=False) else: if local_patch: @@ -714,10 +705,10 @@ if __name__ == "__main__": logging.info("") patch_checker = PatchChecker(patch=local_patch, vcs=vcs, confirm=confirm) - elif id: + elif pw_id: logging.info("Checking patchwork patch #%s", id) logging.info("") - patch_checker = PatchChecker(patchwork_id=id, pwhost=pwhost, + patch_checker = PatchChecker(patchwork_id=pw_id, pwhost=pwhost, vcs=vcs, confirm=confirm) elif gh_id: @@ -730,7 +721,3 @@ if __name__ == "__main__": sys.exit(1) (success, files_failed_check) = patch_checker.check() - - if not success: - logging.error("List of files with problems: %s", files_failed_check) - sys.exit(-1) -- 1.7.11.4 _______________________________________________ Autotest-kernel mailing list [email protected] https://www.redhat.com/mailman/listinfo/autotest-kernel
