diff --git a/pvactools/tools/main.py b/pvactools/tools/main.py index 8efdcf9ca..d400ff9b5 100644 --- a/pvactools/tools/main.py +++ b/pvactools/tools/main.py @@ -82,12 +82,11 @@ def main(): if args[0].version is True: print(version('pvactools')) else: - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': diff --git a/pvactools/tools/pvacbind/main.py b/pvactools/tools/pvacbind/main.py index 6b1b31b00..a223e4ebf 100644 --- a/pvactools/tools/pvacbind/main.py +++ b/pvactools/tools/pvacbind/main.py @@ -106,12 +106,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': diff --git a/pvactools/tools/pvacfuse/main.py b/pvactools/tools/pvacfuse/main.py index 69951d59f..cefa7325e 100644 --- a/pvactools/tools/pvacfuse/main.py +++ b/pvactools/tools/pvacfuse/main.py @@ -126,12 +126,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': main() diff --git a/pvactools/tools/pvacseq/main.py b/pvactools/tools/pvacseq/main.py index 8675c0f0c..93e24a2d3 100644 --- a/pvactools/tools/pvacseq/main.py +++ b/pvactools/tools/pvacseq/main.py @@ -161,12 +161,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': diff --git a/pvactools/tools/pvacsplice/main.py b/pvactools/tools/pvacsplice/main.py index 9df54d386..5d4fc3d7d 100644 --- a/pvactools/tools/pvacsplice/main.py +++ b/pvactools/tools/pvacsplice/main.py @@ -137,12 +137,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': diff --git a/pvactools/tools/pvacvector/main.py b/pvactools/tools/pvacvector/main.py index 32c7c4fa5..eaf8f4fb6 100644 --- a/pvactools/tools/pvacvector/main.py +++ b/pvactools/tools/pvacvector/main.py @@ -33,12 +33,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': main() diff --git a/pvactools/tools/pvacview/main.py b/pvactools/tools/pvacview/main.py index cb74c5512..906c93be9 100644 --- a/pvactools/tools/pvacview/main.py +++ b/pvactools/tools/pvacview/main.py @@ -23,12 +23,11 @@ def define_parser(): def main(): parser = define_parser() args = parser.parse_known_args() - try: - args[0].func.main(args[1]) - except AttributeError as e: + if not hasattr(args[0], 'func'): parser.print_help() print("Error: No command specified") sys.exit(-1) + args[0].func.main(args[1]) if __name__ == '__main__': diff --git a/tests/test_cli_error_handling.py b/tests/test_cli_error_handling.py new file mode 100644 index 000000000..e476a40cf --- /dev/null +++ b/tests/test_cli_error_handling.py @@ -0,0 +1,50 @@ +from contextlib import redirect_stdout +import importlib +from io import StringIO +import sys +import unittest +from unittest.mock import patch + + +CLI_COMMANDS = [ + ('pvactools.tools.main', 'valid_alleles'), + ('pvactools.tools.pvacseq.main', 'run'), + ('pvactools.tools.pvacbind.main', 'run'), + ('pvactools.tools.pvacfuse.main', 'run'), + ('pvactools.tools.pvacsplice.main', 'run'), + ('pvactools.tools.pvacvector.main', 'run'), + ('pvactools.tools.pvacview.main', 'run'), +] + + +class CliErrorHandlingTests(unittest.TestCase): + def test_command_attribute_errors_are_not_hidden(self): + for module_name, command in CLI_COMMANDS: + with self.subTest(module=module_name): + cli_module = importlib.import_module(module_name) + command_module = getattr(cli_module, command) + argv = [module_name, command] + error = AttributeError('business failure') + with patch.object(sys, 'argv', argv), patch.object( + command_module, + 'main', + side_effect=error, + ): + with self.assertRaises(AttributeError) as context: + cli_module.main() + self.assertIs(context.exception, error) + + def test_missing_commands_report_cli_error(self): + for module_name, _ in CLI_COMMANDS: + with self.subTest(module=module_name): + cli_module = importlib.import_module(module_name) + output = StringIO() + with patch.object(sys, 'argv', [module_name]), redirect_stdout(output): + with self.assertRaises(SystemExit) as context: + cli_module.main() + self.assertEqual(context.exception.code, -1) + self.assertIn('Error: No command specified', output.getvalue()) + + +if __name__ == '__main__': + unittest.main()