diff --git a/lib/pavilion/commands/result.py b/lib/pavilion/commands/result.py index 87ebeaf72..4e646ac56 100644 --- a/lib/pavilion/commands/result.py +++ b/lib/pavilion/commands/result.py @@ -2,10 +2,9 @@ import datetime import errno +import json import io import pathlib -import pprint -import shutil from typing import List, IO import pavilion.exceptions @@ -36,10 +35,8 @@ def __init__(self): def _setup_arguments(self, parser): parser.add_argument( - "-j", "--json", - action="store_true", default=False, - help="Give the results in json." - ) + '--outfile', '-o', action='store', default=self.outfile, + help='Send output to file, type dependent on extension (json).') group = parser.add_mutually_exclusive_group() group.add_argument( "-k", "--key", type=str, default='', @@ -105,7 +102,12 @@ def run(self, pav_cfg, args): field_info = {} - if args.list_keys: + if isinstance(args.outfile, str): + if args.outfile.endswith('json'): + with open(args.outfile, 'w') as json_file: + output.json_dump(results, json_file) + + elif args.list_keys: flat_keys = result_utils.keylist(flat_results) flatter_keys = result_utils.make_key_table(flat_keys) @@ -113,32 +115,26 @@ def run(self, pav_cfg, args): test_fields = [f for f in flat_keys.keys() if f not in fields] fields = fields + sorted(test_fields) - output.draw_table(outfile=self.outfile, + output.draw_table(outfile=args.outfile, field_info=field_info, fields=fields, rows=flatter_keys, border=True, title="AVAILABLE KEYS") - elif args.json or args.full: + elif args.full: if not results: output.fprint("Could not find any matching tests.", - color=output.RED, file=self.outfile) + color=output.RED, file=args.outfile) return errno.EINVAL - width = shutil.get_terminal_size().columns or 80 - for result in results: result['finish_date'] = output.get_relative_timestamp( result['finished'], fullstamp=True) try: - if args.json: - output.json_dump(results, self.outfile) - else: - pprint.pprint(results, # ext-print: ignore - stream=self.outfile, width=width, - compact=True) + json_string = json.dumps(results, indent=2) + output.fprint(json_string, width=None, file=self.outfile) except OSError: # It's ok if this fails. Generally means we're piping to # another command. @@ -167,7 +163,7 @@ def run(self, pav_cfg, args): } output.draw_table( - outfile=self.outfile, + outfile=args.outfile, field_info=field_info, fields=fields, rows=flat_results, @@ -176,7 +172,7 @@ def run(self, pav_cfg, args): if args.show_log: if log_file is not None: - output.fprint(log_file.getvalue(), file=self.outfile, + output.fprint(log_file.getvalue(), file=args.outfile, color=output.GREY) else: if len(results) > 1: @@ -188,15 +184,15 @@ def run(self, pav_cfg, args): result_set = results[0] log_path = pathlib.Path(result_set['results_log']) output.fprint("\nResult logs for test {}\n" - .format(result_set['name']), file=self.outfile) + .format(result_set['name']), file=args.outfile) if log_path.exists(): with log_path.open() as log_file: output.fprint( log_file.read(), color=output.GREY, - file=self.outfile) + file=args.outfile) else: output.fprint("Log file '{}' missing>".format(log_path), - file=self.outfile, color=output.YELLOW) + file=args.outfile, color=output.YELLOW) return 0 diff --git a/lib/pavilion/commands/status.py b/lib/pavilion/commands/status.py index 273b94625..d21ca4d02 100644 --- a/lib/pavilion/commands/status.py +++ b/lib/pavilion/commands/status.py @@ -2,6 +2,7 @@ other commands to print statuses.""" import errno +import sys from pavilion import cmd_utils from pavilion import filters @@ -21,9 +22,8 @@ def __init__(self): def _setup_arguments(self, parser): parser.add_argument( - '-j', '--json', action='store_true', default=False, - help='Give output as json, rather than as standard human readable.' - ) + '--outfile', '-o', action='store', default=self.outfile, + help='Send output to file, type dependent on extension (json).') parser.add_argument( '--series', action='store_true', default=False, help='Show the series the test belongs to.') @@ -68,7 +68,7 @@ def run(self, pav_cfg, args): file=self.errfile, color=output.RED) return 1 - return status_utils.print_status_history(tests[-1], self.outfile, args.json) + return status_utils.print_status_history(tests[-1], args.outfile) tests = cmd_utils.get_tests_by_paths(pav_cfg, test_paths, self.errfile) @@ -76,8 +76,8 @@ def run(self, pav_cfg, args): if args.summary: return self.print_summary(statuses) else: - return status_utils.print_status(statuses, self.outfile, json=args.json, - series=args.series, note=args.note) + return status_utils.print_status(statuses, args.outfile, series=args.series, + note=args.note) def print_summary(self, statuses): """Print_summary takes in a list of test statuses. diff --git a/lib/pavilion/status_utils.py b/lib/pavilion/status_utils.py index b170c929c..5406bf2e8 100644 --- a/lib/pavilion/status_utils.py +++ b/lib/pavilion/status_utils.py @@ -118,7 +118,7 @@ def get_statuses(pav_cfg, tests: List[TestRun]): return list(pool.map(get_this_status, tests)) -def print_status(statuses: List[dict], outfile, note=False, series=False, json=False): +def print_status(statuses: List[dict], outfile, note=False, series=False): """Prints the statuses provided in the statuses parameter. :param statuses: list of dictionary objects containing the test_id, @@ -133,25 +133,28 @@ def print_status(statuses: List[dict], outfile, note=False, series=False, json=F :rtype: int """ - if json: - json_data = {'statuses': statuses} - output.json_dump(json_data, outfile) - else: - fields = ['test_id', 'job_id', 'name', 'nodes', 'state', 'result', 'time'] - if series: - fields.insert(0, 'series') - if note: - fields.append('note') - - output.draw_table( - outfile=outfile, - field_info={ - 'time': {'transform': output.get_relative_timestamp}, - 'test_id': {'title': 'Test'}, - }, - fields=fields, - rows=statuses, - title='Test statuses') + if isinstance(outfile, str): + if outfile.endswith('json'): + json_data = {'statuses': statuses} + with open(outfile, 'w') as json_file: + output.json_dump(json_data, json_file) + return 0 + + fields = ['test_id', 'job_id', 'name', 'nodes', 'state', 'result', 'time'] + if series: + fields.insert(0, 'series') + if note: + fields.append('note') + + output.draw_table( + outfile=outfile, + field_info={ + 'time': {'transform': output.get_relative_timestamp}, + 'test_id': {'title': 'Test'}, + }, + fields=fields, + rows=statuses, + title='Test statuses') return 0 @@ -195,7 +198,7 @@ def status_history_from_test_obj(test: TestRun) -> List[dict]: return status_history -def print_status_history(test: TestRun, outfile: TextIO, json: bool = False): +def print_status_history(test: TestRun, outfile: TextIO): """Print the status history for a given test object. :param test: Single test object. @@ -210,18 +213,22 @@ def print_status_history(test: TestRun, outfile: TextIO, json: bool = False): for status in status_history: if status['note'] != "Test not found.": ret_val = 0 - if json: - json_data = {'status_history': status_history} - output.json_dump(json_data, outfile) - else: - fields = ['state', 'time', 'note'] - output.draw_table( - outfile=outfile, - field_info={ - 'time': {'transform': output.get_relative_timestamp} - }, - fields=fields, - rows=status_history, - title='Test {} Status History ({})'.format(test.id, test.name)) + + if isinstance(outfile, str): + if outfile.endswith('json'): + json_data = {'status_history': status_history} + with open(outfile, 'w') as json_file: + output.json_dump(json_data, json_file) + return 0 + + fields = ['state', 'time', 'note'] + output.draw_table( + outfile=outfile, + field_info={ + 'time': {'transform': output.get_relative_timestamp} + }, + fields=fields, + rows=status_history, + title='Test {} Status History ({})'.format(test.id, test.name)) return ret_val diff --git a/test/tests/result_tests.py b/test/tests/result_tests.py index e8bb8d0d4..f7f6cde91 100644 --- a/test/tests/result_tests.py +++ b/test/tests/result_tests.py @@ -597,7 +597,7 @@ def test_result_command(self): # Check that the changed results are what we expected. result_cmd.clear_output() res_args = arg_parser.parse_args( - ('result', '--re-run', '--json') + + ('result', '--re-run', '--full') + tuple(t.full_id for t in run_cmd.last_tests)) result_cmd.run(rerun_cfg, res_args) diff --git a/test/tests/status_cmd_tests.py b/test/tests/status_cmd_tests.py index 924582182..13e135115 100644 --- a/test/tests/status_cmd_tests.py +++ b/test/tests/status_cmd_tests.py @@ -28,15 +28,13 @@ def test_status_arguments(self): self.assertEqual(args.tests[0], 'test1') self.assertEqual(args.tests[1], 'test2') - self.assertEqual(args.json, False) parser = argparse.ArgumentParser() status_cmd._setup_arguments(parser) - args = parser.parse_args(['-j', 'test0', 'test9']) + args = parser.parse_args(['test0', 'test9']) self.assertEqual(args.tests[0], 'test0') self.assertEqual(args.tests[1], 'test9') - self.assertEqual(args.json, True) def test_status_command(self): """Test status command by generating a suite of tests.""" @@ -95,19 +93,20 @@ def test_status_command(self): status_cmd = commands.get_command('status') status_cmd.outfile = io.StringIO() + jsontest_file = ['-o', 'test.json'] # Testing for individual tests with json output for test in series.tests.values(): parser = argparse.ArgumentParser() status_cmd._setup_arguments(parser) - arg_list = ['-j', test.full_id] + arg_list = jsontest_file + [test.full_id] args = parser.parse_args(arg_list) self.assertEqual(status_cmd.run(self.pav_cfg, args), 0) # Testing for multiple tests with json output parser = argparse.ArgumentParser() status_cmd._setup_arguments(parser) - arg_list = ['-j'] + test_str.split() + arg_list = jsontest_file + test_str.split() args = parser.parse_args(arg_list) self.assertEqual(status_cmd.run(self.pav_cfg, args), 0) @@ -216,6 +215,7 @@ def test_status_command_with_sched(self): status_cmd = commands.get_command('status') status_cmd.silence() + jsontest_file = ['-o', 'test.json'] parser = argparse.ArgumentParser() status_cmd._setup_arguments(parser) @@ -226,7 +226,7 @@ def test_status_command_with_sched(self): parser = argparse.ArgumentParser() status_cmd._setup_arguments(parser) - args = parser.parse_args(['-j', 'test.{}'.format(test.id)]) + args = parser.parse_args(jsontest_file + ['test.{}'.format(test.id)]) test.status.set(status_file.STATES.SCHEDULED, "faker") self.assertEqual(status_cmd.run(self.pav_cfg, args), 0)