diff --git a/docs/bin/generate_man.py b/docs/bin/generate_man.py index 80253fcd23b..d4b1ad27152 100755 --- a/docs/bin/generate_man.py +++ b/docs/bin/generate_man.py @@ -116,7 +116,7 @@ def opts_docs(cli_class_name, cli_module_name): # instantiate each cli and ask its options cli_klass = getattr(__import__("ansible.cli.%s" % cli_module_name, fromlist=[cli_class_name]), cli_class_name) - cli = cli_klass([]) + cli = cli_klass([cli_name]) # parse the common options try: @@ -124,8 +124,6 @@ def opts_docs(cli_class_name, cli_module_name): except Exception: pass - cli.parser.prog = cli_name - # base/common cli info docs = { 'cli': cli_module_name, diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py index 8cc04a9276b..37fc2fee483 100644 --- a/lib/ansible/cli/__init__.py +++ b/lib/ansible/cli/__init__.py @@ -61,6 +61,9 @@ class CLI(with_metaclass(ABCMeta, object)): Base init method for all command line programs """ + if not args: + raise ValueError('A non-empty list for args is required') + self.args = args self.parser = None self.callback = callback @@ -275,7 +278,7 @@ class CLI(with_metaclass(ABCMeta, object)): ansible.arguments.option_helpers.add_runas_options(self.parser) self.parser.add_option('--my-option', dest='my_option', action='store') """ - self.parser = opt_help.create_base_parser(usage=usage, desc=desc, epilog=epilog) + self.parser = opt_help.create_base_parser(os.path.basename(self.args[0]), usage=usage, desc=desc, epilog=epilog, ) @abstractmethod def post_process_args(self, options): diff --git a/lib/ansible/cli/arguments/option_helpers.py b/lib/ansible/cli/arguments/option_helpers.py index 7ea478c5b00..46b71171853 100644 --- a/lib/ansible/cli/arguments/option_helpers.py +++ b/lib/ansible/cli/arguments/option_helpers.py @@ -29,6 +29,13 @@ class SortingHelpFormatter(argparse.HelpFormatter): super(SortingHelpFormatter, self).add_arguments(actions) +class AnsibleVersion(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + ansible_version = to_native(version(getattr(parser, 'prog'))) + print(ansible_version) + parser.exit() + + class PrependListAction(argparse.Action): """A near clone of ``argparse._AppendAction``, but designed to prepend list values instead of appending. @@ -171,12 +178,13 @@ def version(prog=None): # Functions to add pre-canned options to an OptionParser # -def create_base_parser(usage="", desc=None, epilog=None): +def create_base_parser(prog, usage="", desc=None, epilog=None): """ Create an options parser for all ansible scripts """ # base opts parser = argparse.ArgumentParser( + prog=prog, formatter_class=SortingHelpFormatter, epilog=epilog, description=desc, @@ -184,7 +192,8 @@ def create_base_parser(usage="", desc=None, epilog=None): ) version_help = "show program's version number, config file location, configured module search path," \ " module location, executable location and exit" - parser.add_argument('--version', action='version', version=to_native(version("%(prog)s")), help=version_help) + + parser.add_argument('--version', action=AnsibleVersion, nargs=0, help=version_help) add_verbosity_options(parser) return parser diff --git a/test/units/cli/test_adhoc.py b/test/units/cli/test_adhoc.py index 98dd24762bb..0e7475c603a 100644 --- a/test/units/cli/test_adhoc.py +++ b/test/units/cli/test_adhoc.py @@ -5,6 +5,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest +import re from ansible import context from ansible.cli.adhoc import AdHocCLI, display @@ -13,8 +14,11 @@ from ansible.errors import AnsibleOptionsError def test_parse(): """ Test adhoc parse""" - adhoc_cli = AdHocCLI([]) - with pytest.raises(SystemExit) as exec_info: + with pytest.raises(ValueError, match='A non-empty list for args is required'): + adhoc_cli = AdHocCLI([]) + + adhoc_cli = AdHocCLI(['ansibletest']) + with pytest.raises(SystemExit): adhoc_cli.parse() @@ -87,3 +91,23 @@ def test_run_no_extra_vars(): with pytest.raises(SystemExit) as exec_info: adhoc_cli.parse() assert exec_info.value.code == 2 + + +def test_ansible_version(capsys, mocker): + adhoc_cli = AdHocCLI(args=['/bin/ansible', '--version']) + with pytest.raises(SystemExit): + adhoc_cli.run() + version = capsys.readouterr() + try: + version_lines = version.out.splitlines() + except AttributeError: + # Python 2.6 does return a named tuple, so get the first item + version_lines = version[0].splitlines() + + assert len(version_lines) == 6, 'Incorrect number of lines in "ansible --version" output' + assert re.match('ansible [0-9.a-z]+$', version_lines[0]), 'Incorrect ansible version line in "ansible --version" output' + assert re.match(' config file = .*$', version_lines[1]), 'Incorrect config file line in "ansible --version" output' + assert re.match(' configured module search path = .*$', version_lines[2]), 'Incorrect module search path in "ansible --version" output' + assert re.match(' ansible python module location = .*$', version_lines[3]), 'Incorrect python module location in "ansible --version" output' + assert re.match(' executable location = .*$', version_lines[4]), 'Incorrect executable locaction in "ansible --version" output' + assert re.match(' python version = .*$', version_lines[5]), 'Incorrect python version in "ansible --version" output' diff --git a/test/units/cli/test_console.py b/test/units/cli/test_console.py index c22dd55ba40..3acc4faaaff 100644 --- a/test/units/cli/test_console.py +++ b/test/units/cli/test_console.py @@ -27,12 +27,12 @@ from ansible.cli.console import ConsoleCLI class TestConsoleCLI(unittest.TestCase): def test_parse(self): - cli = ConsoleCLI([]) + cli = ConsoleCLI(['ansible test']) cli.parse() self.assertTrue(cli.parser is not None) def test_module_args(self): - cli = ConsoleCLI([]) + cli = ConsoleCLI(['ansible test']) cli.parse() res = cli.module_args('copy') self.assertTrue(cli.parser is not None) @@ -42,7 +42,7 @@ class TestConsoleCLI(unittest.TestCase): @patch('ansible.utils.display.Display.display') def test_helpdefault(self, mock_display): - cli = ConsoleCLI([]) + cli = ConsoleCLI(['ansible test']) cli.parse() cli.modules = set(['copy']) cli.helpdefault('copy') diff --git a/test/units/cli/test_vault.py b/test/units/cli/test_vault.py index 7b7e857844c..864ce0aeab6 100644 --- a/test/units/cli/test_vault.py +++ b/test/units/cli/test_vault.py @@ -40,7 +40,7 @@ class TestVaultCli(unittest.TestCase): self.tty_patcher.stop() def test_parse_empty(self): - cli = VaultCLI([]) + cli = VaultCLI(['vaultcli']) self.assertRaises(SystemExit, cli.parse) diff --git a/test/units/playbook/test_play_context.py b/test/units/playbook/test_play_context.py index f01113bc63b..aaf257ecde0 100644 --- a/test/units/playbook/test_play_context.py +++ b/test/units/playbook/test_play_context.py @@ -22,7 +22,7 @@ from ansible.utils import context_objects as co @pytest.fixture def parser(): - parser = opt_help.create_base_parser() + parser = opt_help.create_base_parser('testparser') opt_help.add_runas_options(parser) opt_help.add_meta_options(parser)