class TestArgumentExperimental(unittest.TestCase): def setUp(self): from knack.help_files import helps class LoggerAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): print("Side-effect from some original action!", file=sys.stderr) class ExperimentalTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(ExperimentalTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('arg-test', 'example_arg_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, 'arg-test') as c: c.argument('arg1', help='Arg1', is_experimental=True, action=LoggerAction) super(ExperimentalTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI( commands_loader_cls=ExperimentalTestCommandLoader) @redirect_io def test_experimental_arguments_command_help(self): """ Ensure experimental arguments appear correctly in command help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test -h'.split()) actual = self.io.getvalue() expected = """ Arguments --arg1 [Experimental] [Required] : Arg1. Argument '--arg1' is experimental and not covered by customer support. Please use with discretion. """.format(self.cli_ctx.name) self.assertIn(remove_space(expected), remove_space(actual)) @redirect_io def test_experimental_arguments_execute(self): """ Ensure deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() experimental_expected = "Argument '--arg1' is experimental and not covered by customer support. " \ "Please use with discretion." self.assertIn(experimental_expected, actual) action_expected = "Side-effect from some original action!" self.assertIn(action_expected, actual)
class TestArgumentPreview(unittest.TestCase): def setUp(self): from knack.help_files import helps class PreviewTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(PreviewTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('arg-test', 'example_arg_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, 'arg-test') as c: c.argument('arg1', help='Arg1', is_preview=True) super(PreviewTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader) @redirect_io def test_preview_arguments_command_help(self): """ Ensure preview arguments appear correctly in command help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test -h'.split()) actual = self.io.getvalue() expected = """ Arguments --arg1 [Preview] [Required] : Arg1. Argument '--arg1' is in preview. It may be changed/removed in a future release. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io def test_preview_arguments_execute(self): """ Ensure deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() expected = "Argument '--arg1' is in preview. It may be changed/removed in a future release." self.assertIn(expected, actual)
class TestCommandWithConfiguredDefaults(unittest.TestCase): @classmethod def setUpClass(cls): # Ensure initialization has occurred correctly logging.basicConfig(level=logging.DEBUG) @classmethod def tearDownClass(cls): logging.shutdown() def _set_up_command_table(self, required): class TestCommandsLoader(CLICommandsLoader): def load_command_table(self, args): super(TestCommandsLoader, self).load_command_table(args) with CommandGroup(self, 'foo', '{}#{{}}'.format(__name__)) as g: g.command('list', 'list_foo') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, 'foo') as c: c.argument('my_param', options_list='--my-param', configured_default='param', required=required) super(TestCommandsLoader, self).load_arguments(command) self.cli_ctx = DummyCLI(commands_loader_cls=TestCommandsLoader) @mock.patch.dict(os.environ, {'CLI_DEFAULTS_PARAM': 'myVal'}) @redirect_io def test_apply_configured_defaults_on_required_arg(self): self._set_up_command_table(required=True) self.cli_ctx.invoke('foo list'.split()) actual = self.io.getvalue() expected = 'myVal' self.assertEqual(expected, actual) @redirect_io def test_no_configured_default_on_required_arg(self): self._set_up_command_table(required=True) with self.assertRaises(SystemExit): self.cli_ctx.invoke('foo list'.split()) actual = self.io.getvalue() expected = 'required: --my-param' if sys.version_info[0] == 2: expected = 'argument --my-param is required' self.assertEqual(expected in actual, True) @mock.patch.dict(os.environ, {'CLI_DEFAULTS_PARAM': 'myVal'}) @redirect_io def test_apply_configured_defaults_on_optional_arg(self): self._set_up_command_table(required=False) self.cli_ctx.invoke('foo list'.split()) actual = self.io.getvalue() expected = 'myVal' self.assertEqual(expected, actual)
class TestCommandPreview(unittest.TestCase): def setUp(self): from knack.help_files import helps class PreviewTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(PreviewTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('cmd1', 'example_handler', is_preview=True) with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), is_preview=True) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(PreviewTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader) @redirect_io def test_preview_command_group_help(self): """ Ensure preview commands appear correctly in group help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('-h'.split()) actual = self.io.getvalue() expected = u""" Group {} Subgroups: grp1 [Preview] : A group. Commands: cmd1 [Preview] : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_preview_command_plain_execute(self): """ Ensure general warning displayed when running preview command. """ self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() expected = "This command is in preview. It may be changed/removed in a future release." self.assertIn(expected, actual)
class TestCommandGroupPreview(unittest.TestCase): def setUp(self): from knack.help_files import helps class PreviewTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(PreviewTestCommandLoader, self).load_command_table(args) with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), is_preview=True) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(PreviewTestCommandLoader, self).load_arguments(command) helps['group1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader) @redirect_io def test_preview_command_group_help_plain(self): """ Ensure help warnings appear for preview command group help. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 -h'.split()) actual = self.io.getvalue() expected = """ Group cli group1 : A group. This command group is in preview. It may be changed/removed in a future release. Commands: cmd1 : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_preview_command_implicitly(self): """ Ensure help warning displayed for command in preview because of a preview parent group. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 cmd1 -h'.split()) actual = self.io.getvalue() expected = """ Command {} group1 cmd1 : Short summary here. Long summary here. Still long summary. Command group 'group1' is in preview. It may be changed/removed in a future release. """.format(self.cli_ctx.name) self.assertIn(expected, actual)
class TestHelp(unittest.TestCase): def setUp(self): from knack.help_files import helps class HelpTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(HelpTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('n1', 'example_handler') g.command('n2', 'example_handler') g.command('n3', 'example_handler') g.command('n4', 'example_handler') g.command('n5', 'example_handler') with CommandGroup(self, 'group alpha', '{}#{{}}'.format(__name__)) as g: g.command('n1', 'example_handler') with CommandGroup(self, 'group beta', '{}#{{}}'.format(__name__)) as g: g.command('n1', 'example_handler') return self.command_table def load_arguments(self, command): for scope in ['n1', 'group alpha', 'group beta']: with ArgumentsContext(self, scope) as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) for scope in ['n4', 'n5']: with ArgumentsContext(self, scope) as c: c.argument('arg1', options_list=['--foobar']) c.argument('arg2', options_list=['--foobar2'], required=True) c.argument('arg3', options_list=['--foobar3'], help='the foobar3') super(HelpTestCommandLoader, self).load_arguments(command) helps['n2'] = """ type: command short-summary: YAML short summary. long-summary: YAML long summary. More summary. """ helps['n3'] = """ type: command long-summary: | line1 line2 """ helps['n4'] = """ type: command parameters: - name: --foobar type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: --foobar2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) """ helps['n5'] = """ type: command short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 parameters: - name: --foobar type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: --foobar2 type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) examples: - name: foo example text: example details """ helps['group alpha'] = """ type: group short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 """ helps['group alpha n1'] = """ short-summary: this module does xyz one-line or so long-summary: | this module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2 parameters: - name: --arg -a type: string required: false short-summary: one line partial sentence long-summary: text, markdown, etc. populator-commands: - mycli abc xyz - default - name: -b type: string required: true short-summary: one line partial sentence long-summary: paragraph(s) examples: - name: foo example text: example details """ self.cli_ctx = DummyCLI(commands_loader_cls=HelpTestCommandLoader) @redirect_io def test_choice_list_with_ints(self): """ Ensure choice_list works with integer lists. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -h'.split()) actual = io.getvalue() expected = 'Allowed values: 1, 2, 3' self.assertIn(expected, actual) @redirect_io def test_help_param(self): """ Ensure both --help and -h produce the same output. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -h'.split()) with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 --help'.split()) @redirect_io def test_help_long_and_short_description_from_docstring(self): """ Ensure the first sentence of a docstring is parsed as the short summary and subsequent text is interpretted as the long summary. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -h'.split()) actual = io.getvalue() expected = '\nCommand\n {} n1 : Short summary here.\n Long summary here. Still long summary.'.format( self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_help_long_and_short_description_from_yaml(self): """ Ensure the YAML version of short and long summary display correctly and override any values that may have been obtained through reflection. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n2 -h'.split()) actual = io.getvalue() expected = '\nCommand\n {} n2 : YAML short summary.\n YAML long summary. More summary.'.format( self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_help_long_description_multi_line(self): """ Ensure that multi-line help in the YAML is displayed correctly. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n3 -h'.split()) actual = io.getvalue() expected = '\nCommand\n {} n3 : Short summary here.\n Line1\n line2.\n'.format( self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_help_params_documentations(self): """ Ensure argument help is rendered according to the YAML spec. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n4 -h'.split()) expected = """ Command {} n4 : Short summary here. Arguments --foobar [Required] : One line partial sentence. Values from: mycli abc xyz, default. Text, markdown, etc. --foobar2 [Required] : One line partial sentence. Paragraph(s). --foobar3 : The foobar3. """ actual = io.getvalue() expected = expected.format(self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_help_full_documentations(self): """ Test all features of YAML format. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n5 -h'.split()) expected = """ Command {} n5 : This module does xyz one-line or so. This module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2. Arguments --foobar [Required] : One line partial sentence. Values from: mycli abc xyz, default. Text, markdown, etc. --foobar2 [Required] : One line partial sentence. Paragraph(s). --foobar3 : The foobar3. Global Arguments --debug : Increase logging verbosity to show all debug logs. --help -h : Show this help message and exit. --output -o : Output format. Allowed values: json, jsonc, table, tsv. Default: json. --query : JMESPath query string. See http://jmespath.org/ for more information and examples. --verbose : Increase logging verbosity. Use --debug for full debug logs. Examples foo example example details """ actual = io.getvalue() expected = expected.format(self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_help_with_param_specified(self): """ Ensure help appears even if some arguments are specified. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 --arg 1 -h'.split()) expected = """ Command {} n1 : Short summary here. Long summary here. Still long summary. Arguments -b [Required] : Allowed values: a, b, c. --arg -a : Allowed values: 1, 2, 3. --arg3 Global Arguments --debug : Increase logging verbosity to show all debug logs. --help -h : Show this help message and exit. --output -o : Output format. Allowed values: json, jsonc, table, tsv. Default: json. --query : JMESPath query string. See http://jmespath.org/ for more information and examples. --verbose : Increase logging verbosity. Use --debug for full debug logs. """ actual = io.getvalue() expected = expected.format(self.cli_ctx.name) self.assertEqual(actual, expected) @redirect_io def test_help_group_children(self): """ Ensure subgroups appear correctly. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group -h'.split()) expected = """ Group {} group Subgroups: alpha : This module does xyz one-line or so. beta """ actual = io.getvalue() expected = expected.format(self.cli_ctx.name) self.assertEqual(actual, expected) @redirect_io def test_help_missing_params(self): """ Ensure the appropriate error is thrown when a required argument is missing. """ # work around an argparse behavior where output is not printed and SystemExit # is not raised on Python 2.7.9 if sys.version_info < (2, 7, 10): try: self.cli_ctx.invoke('n1 -a 1 --arg 2'.split()) except SystemExit: pass else: with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -a 1 --arg 2'.split()) actual = io.getvalue() self.assertTrue('required' in actual and '-b' in actual) @redirect_io def test_help_extra_params(self): """ Ensure appropriate error is thrown when an extra argument is used. """ # work around an argparse behavior where output is not printed and SystemExit # is not raised on Python 2.7.9 if sys.version_info < (2, 7, 10): try: self.cli_ctx.invoke('n1 -a 1 -b c -c extra'.split()) except SystemExit: pass else: with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -a 1 -b c -c extra'.split()) actual = io.getvalue() expected = 'unrecognized arguments: -c extra' self.assertIn(expected, actual) @redirect_io def test_help_group_help(self): """ Ensure group help appears correctly. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group alpha -h'.split()) expected = """ Group {} group alpha : This module does xyz one-line or so. This module.... kjsdflkj... klsfkj paragraph1 this module.... kjsdflkj... klsfkj paragraph2. Commands: n1 : This module does xyz one-line or so. """ actual = io.getvalue() expected = expected.format(self.cli_ctx.name) self.assertEqual(actual, expected) @redirect_io @mock.patch('knack.cli.CLI.register_event') def test_help_global_params(self, _): """ Ensure global parameters can be added and display correctly. """ def register_globals(_, **kwargs): arg_group = kwargs.get('arg_group') arg_group.add_argument('--exampl', help='This is a new global argument.') self.cli_ctx._event_handlers[EVENT_PARSER_GLOBAL_CREATE].append( register_globals) # pylint: disable=protected-access with self.assertRaises(SystemExit): self.cli_ctx.invoke('n1 -h'.split()) s = """ Command {} n1 : Short summary here. Long summary here. Still long summary. Arguments -b [Required] : Allowed values: a, b, c. --arg -a : Allowed values: 1, 2, 3. --arg3 Global Arguments --debug : Increase logging verbosity to show all debug logs. --exampl : This is a new global argument. --help -h : Show this help message and exit. --output -o : Output format. Allowed values: json, jsonc, table, tsv. Default: json. --query : JMESPath query string. See http://jmespath.org/ for more information and examples. --verbose : Increase logging verbosity. Use --debug for full debug logs. """ actual = io.getvalue() expected = s.format(self.cli_ctx.name) self.assertEqual(actual, expected)
class TestCommandDeprecation(unittest.TestCase): def setUp(self): from knack.help_files import helps class DeprecationTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(DeprecationTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('cmd1', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd1')) g.command('cmd2', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd2', hide='1.0.0')) g.command('cmd3', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd3', hide='0.1.0')) g.command('cmd4', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd4', expiration='1.0.0')) g.command('cmd5', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd5', expiration='0.1.0')) with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-grp1')) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(DeprecationTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader) @redirect_io def test_deprecate_command_group_help(self): """ Ensure deprecated commands appear (or don't appear) correctly in group help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('-h'.split()) actual = self.io.getvalue() expected = u""" Group {} Subgroups: grp1 [Deprecated] : A group. Commands: cmd1 [Deprecated] : Short summary here. cmd2 [Deprecated] : Short summary here. cmd4 [Deprecated] : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_deprecate_command_help_hidden(self): """ Ensure hidden deprecated command can be used. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('cmd3 -h'.split()) actual = self.io.getvalue() expected = """ Command {} cmd3 : Short summary here. Long summary here. Still long summary. This command has been deprecated and will be removed in a future release. Use 'alt- cmd3' instead. Arguments -b [Required] : Allowed values: a, b, c. --arg -a : Allowed values: 1, 2, 3. --arg3 """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io def test_deprecate_command_plain_execute(self): """ Ensure general warning displayed when running deprecated command. """ self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() expected = "This command has been deprecated and will be removed in a future release. Use 'alt-cmd1' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_command_hidden_execute(self): """ Ensure general warning displayed when running hidden deprecated command. """ self.cli_ctx.invoke('cmd3 -b b'.split()) actual = self.io.getvalue() expected = "This command has been deprecated and will be removed in a future release. Use 'alt-cmd3' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_command_expiring_execute(self): """ Ensure specific warning displayed when running expiring deprecated command. """ self.cli_ctx.invoke('cmd4 -b b'.split()) actual = self.io.getvalue() expected = "This command has been deprecated and will be removed in version '1.0.0'. Use 'alt-cmd4' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_command_expiring_execute_no_color(self): """ Ensure warning is displayed without color. """ self.cli_ctx.enable_color = False self.cli_ctx.invoke('cmd4 -b b'.split()) actual = self.io.getvalue() expected = "WARNING: This command has been deprecated and will be removed in version '1.0.0'" self.assertIn(expected, actual) @redirect_io def test_deprecate_command_expired_execute(self): """ Ensure expired command cannot be reached. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('cmd5 -h'.split()) actual = self.io.getvalue() expected = """cli: 'cmd5' is not in the 'cli' command group.""" self.assertIn(expected, actual) @redirect_io @disable_color def test_deprecate_command_expired_execute_no_color(self): """ Ensure error is displayed without color. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('cmd5 -h'.split()) actual = self.io.getvalue() expected = """ERROR: cli: 'cmd5' is not in the 'cli' command group.""" self.assertIn(expected, actual)
class TestArgumentDeprecation(unittest.TestCase): def setUp(self): from knack.help_files import helps class DeprecationTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(DeprecationTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('arg-test', 'example_arg_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, 'arg-test') as c: c.argument('arg1', help='Arg1', deprecate_info=c.deprecate()) c.argument('opt1', help='Opt1', options_list=['--opt1', c.deprecate(redirect='--opt1', target='--alt1')]) c.argument('arg2', help='Arg2', deprecate_info=c.deprecate(hide='1.0.0')) c.argument('opt2', help='Opt2', options_list=['--opt2', c.deprecate(redirect='--opt2', target='--alt2', hide='1.0.0')]) c.argument('arg3', help='Arg3', deprecate_info=c.deprecate(hide='0.1.0')) c.argument('opt3', help='Opt3', options_list=['--opt3', c.deprecate(redirect='--opt3', target='--alt3', hide='0.1.0')]) c.argument('arg4', deprecate_info=c.deprecate(expiration='1.0.0')) c.argument('opt4', options_list=['--opt4', c.deprecate(redirect='--opt4', target='--alt4', expiration='1.0.0')]) c.argument('arg5', deprecate_info=c.deprecate(expiration='0.1.0')) c.argument('opt5', options_list=['--opt5', c.deprecate(redirect='--opt5', target='--alt5', expiration='0.1.0')]) super(DeprecationTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader) @redirect_io def test_deprecate_arguments_command_help(self): """ Ensure deprecated arguments and options appear (or don't appear) correctly in command help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test -h'.split()) actual = self.io.getvalue() expected = """ Command {} arg-test Arguments --alt1 [Deprecated] [Required] : Opt1. Option '--alt1' has been deprecated and will be removed in a future release. Use '-- opt1' instead. --arg1 [Deprecated] [Required] : Arg1. Argument 'arg1' has been deprecated and will be removed in a future release. --opt1 [Required] : Opt1. --alt2 [Deprecated] : Opt2. Option '--alt2' has been deprecated and will be removed in a future release. Use '-- opt2' instead. --alt4 [Deprecated] Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '-- opt4' instead. --arg2 [Deprecated] : Arg2. Argument 'arg2' has been deprecated and will be removed in a future release. --arg4 [Deprecated] Argument 'arg4' has been deprecated and will be removed in version '1.0.0'. --opt2 : Opt2. --opt3 : Opt3. --opt4 --opt5 """.format(self.cli_ctx.name) self.assertTrue(actual.startswith(expected)) @redirect_io def test_deprecate_arguments_execute(self): """ Ensure deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() expected = "Argument 'arg1' has been deprecated and will be removed in a future release." self.assertIn(expected, actual) @redirect_io def test_deprecate_arguments_execute_hidden(self): """ Ensure hidden deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg3 bar'.split()) actual = self.io.getvalue() expected = "Argument 'arg3' has been deprecated and will be removed in a future release." self.assertIn(expected, actual) @redirect_io def test_deprecate_arguments_execute_expiring(self): """ Ensure hidden deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg4 bar'.split()) actual = self.io.getvalue() expected = "Argument 'arg4' has been deprecated and will be removed in version '1.0.0'." self.assertIn(expected, actual) @redirect_io def test_deprecate_arguments_execute_expired(self): """ Ensure expired deprecated arguments can't be used. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg5 foo'.split()) actual = self.io.getvalue() expected = 'unrecognized arguments: --arg5 foo' self.assertIn(expected, actual) @redirect_io def test_deprecate_options_execute(self): """ Ensure deprecated options can be used with a warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --alt1 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt1' has been deprecated and will be removed in a future release. Use '--opt1' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_options_execute_non_deprecated(self): """ Ensure non-deprecated options don't show warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt1' has been deprecated and will be removed in a future release. Use '--opt1' instead." self.assertNotIn(expected, actual) @redirect_io def test_deprecate_options_execute_hidden(self): """ Ensure hidden deprecated options can be used with warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt3 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt3' has been deprecated and will be removed in a future release. Use '--opt3' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_options_execute_hidden_non_deprecated(self): """ Ensure hidden non-deprecated optionss can be used without warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt3 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt3' has been deprecated and will be removed in a future release. Use '--opt3' instead." self.assertNotIn(expected, actual) @redirect_io def test_deprecate_options_execute_expired(self): """ Ensure expired deprecated options can't be used. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt5 foo'.split()) actual = self.io.getvalue() expected = 'unrecognized arguments: --alt5 foo' self.assertIn(expected, actual) @redirect_io def test_deprecate_options_execute_expired_non_deprecated(self): """ Ensure non-expired options can be used without warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt5 foo'.split()) actual = self.io.getvalue() self.assertTrue(u'--alt5' not in actual and u'--opt5' not in actual) @redirect_io def test_deprecate_options_execute_expiring(self): """ Ensure expiring options can be used with warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt4 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead." self.assertIn(expected, actual) @redirect_io @disable_color def test_deprecate_options_execute_expiring_no_color(self): """ Ensure error is displayed without color. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt4 bar'.split()) actual = self.io.getvalue() expected = "WARNING: Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead." self.assertIn(expected, actual) @redirect_io def test_deprecate_options_execute_expiring_non_deprecated(self): """ Ensure non-expiring options can be used without warning. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt4 bar'.split()) actual = self.io.getvalue() expected = "Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead." self.assertNotIn(expected, actual)
class TestCommandGroupDeprecation(unittest.TestCase): def setUp(self): from knack.help_files import helps class DeprecationTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(DeprecationTestCommandLoader, self).load_command_table(args) with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group1')) as g: g.command('cmd1', 'example_handler') with CommandGroup(self, 'group2', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group2', hide='1.0.0')) as g: g.command('cmd1', 'example_handler') with CommandGroup(self, 'group3', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group3', hide='0.1.0')) as g: g.command('cmd1', 'example_handler') with CommandGroup(self, 'group4', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group4', expiration='1.0.0')) as g: g.command('cmd1', 'example_handler') with CommandGroup(self, 'group5', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group5', expiration='0.1.0')) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(DeprecationTestCommandLoader, self).load_arguments(command) helps['group1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader) @redirect_io def test_deprecate_command_group_help_plain(self): """ Ensure help warnings appear for deprecated command group help. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 -h'.split()) actual = self.io.getvalue() expected = """ Group cli group1 : A group. This command group has been deprecated and will be removed in a future release. Use 'alt-group1' instead. Commands: cmd1 : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_deprecate_command_group_help_hidden(self): """ Ensure hidden deprecated command can be used. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group3 -h'.split()) actual = self.io.getvalue() expected = """ Group {} group3 This command group has been deprecated and will be removed in a future release. Use 'alt-group3' instead. Commands: cmd1 : Short summary here. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io def test_deprecate_command_group_help_expiring(self): """ Ensure specific warning displayed when running expiring deprecated command. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group4 -h'.split()) actual = self.io.getvalue() expected = """ Group {} group4 This command group has been deprecated and will be removed in version '1.0.0'. Use 'alt-group4' instead. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io @disable_color def test_deprecate_command_group_help_expiring_no_color(self): """ Ensure warning is displayed without color. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group4 -h'.split()) actual = self.io.getvalue() expected = """ Group cli group4 WARNING: This command group has been deprecated and will be removed in version \'1.0.0\'. Use 'alt-group4' instead. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io def test_deprecate_command_group_expired(self): """ Ensure expired command cannot be reached. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group5 -h'.split()) actual = self.io.getvalue() expected = """The most similar choices to 'group5'""" self.assertIn(expected, actual) @redirect_io def test_deprecate_command_implicitly(self): """ Ensure help warning displayed for command deprecated because of a deprecated parent group. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 cmd1 -h'.split()) actual = self.io.getvalue() expected = """ Command {} group1 cmd1 : Short summary here. Long summary here. Still long summary. This command is implicitly deprecated because command group 'group1' is deprecated and will be removed in a future release. Use 'alt-group1' instead. """.format(self.cli_ctx.name) self.assertIn(expected, actual)
class TestCommandExperimental(unittest.TestCase): def setUp(self): from knack.help_files import helps class ExperimentalTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(ExperimentalTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('cmd1', 'example_handler', is_experimental=True) with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), is_experimental=True) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(ExperimentalTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI( commands_loader_cls=ExperimentalTestCommandLoader) @redirect_io def test_experimental_command_implicitly_execute(self): """ Ensure general warning displayed when running command from an experimental parent group. """ self.cli_ctx.invoke('grp1 cmd1 -b b'.split()) actual = self.io.getvalue() expected = "Command group 'grp1' is experimental and not covered by customer support. " \ "Please use with discretion." self.assertIn(remove_space(expected), remove_space(actual)) @redirect_io def test_experimental_command_group_help(self): """ Ensure experimental commands appear correctly in group help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('-h'.split()) actual = self.io.getvalue() expected = u""" Group {} Subgroups: grp1 [Experimental] : A group. Commands: cmd1 [Experimental] : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_experimental_command_plain_execute(self): """ Ensure general warning displayed when running experimental command. """ self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() expected = "This command is experimental and not covered by customer support. Please use with discretion." self.assertIn(remove_space(expected), remove_space(actual))
class TestCommandGroupExperimental(unittest.TestCase): def setUp(self): from knack.help_files import helps class ExperimentalTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(ExperimentalTestCommandLoader, self).load_command_table(args) with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), is_experimental=True) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super(ExperimentalTestCommandLoader, self).load_arguments(command) helps['group1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI( commands_loader_cls=ExperimentalTestCommandLoader) @redirect_io def test_experimental_command_group_help_plain(self): """ Ensure help warnings appear for experimental command group help. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 -h'.split()) actual = self.io.getvalue() expected = """ Group cli group1 : A group. This command group is experimental and not covered by customer support. Please use with discretion. Commands: cmd1 : Short summary here. """.format(self.cli_ctx.name) self.assertIn(remove_space(expected), remove_space(actual)) @redirect_io def test_experimental_command_implicitly(self): """ Ensure help warning displayed for command in experimental because of a experimental parent group. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('group1 cmd1 -h'.split()) actual = self.io.getvalue() expected = """ Command {} group1 cmd1 : Short summary here. Long summary here. Still long summary. Command group 'group1' is experimental and not covered by customer support. Please use with discretion. """.format(self.cli_ctx.name) self.assertIn(remove_space(expected), remove_space(actual))
class TestArgumentPreview(unittest.TestCase): def setUp(self): from knack.help_files import helps class LoggerAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): print("Side-effect from some original action!", file=sys.stderr) class PreviewTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super(PreviewTestCommandLoader, self).load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('arg-test', 'example_arg_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, 'arg-test') as c: c.argument('arg1', help='Arg1', is_preview=True, action=LoggerAction) super(PreviewTestCommandLoader, self).load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader) @redirect_io def test_preview_arguments_command_help(self): """ Ensure preview arguments appear correctly in command help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test -h'.split()) actual = self.io.getvalue() expected = """ Arguments --arg1 [Preview] [Required] : Arg1. Argument '--arg1' is in preview. It may be changed/removed in a future release. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io @disable_color def test_preview_arguments_command_help_no_color(self): """ Ensure warning is displayed without color. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('arg-test -h'.split()) actual = self.io.getvalue() expected = """ Arguments --arg1 [Preview] [Required] : Arg1. WARNING: Argument '--arg1' is in preview. It may be changed/removed in a future release. """.format(self.cli_ctx.name) self.assertIn(expected, actual) @redirect_io def test_preview_arguments_execute(self): """ Ensure deprecated arguments can be used. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() preview_expected = "Argument '--arg1' is in preview. It may be changed/removed in a future release." self.assertIn(preview_expected, actual) action_expected = "Side-effect from some original action!" self.assertIn(action_expected, actual) @redirect_io @disable_color def test_preview_arguments_execute_no_color(self): """ Ensure warning is displayed without color. """ self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split()) actual = self.io.getvalue() preview_expected = "WARNING: Argument '--arg1' is in preview. It may be changed/removed in a future release." self.assertIn(preview_expected, actual) action_expected = "Side-effect from some original action!" self.assertIn(action_expected, actual) @redirect_io def test_preview_arguments_execute_only_show_error(self): """ Ensure warning is suppressed when using preview arguments. """ self.cli_ctx.invoke( 'arg-test --arg1 foo --opt1 bar --only-show-errors'.split()) actual = self.io.getvalue() self.assertNotIn("preview", actual) action_expected = "Side-effect from some original action!" self.assertIn(action_expected, actual)
class TestCommandPreview(unittest.TestCase): def setUp(self): from knack.help_files import helps class PreviewTestCommandLoader(CLICommandsLoader): def load_command_table(self, args): super().load_command_table(args) with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g: g.command('cmd1', 'example_handler', is_preview=True) with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), is_preview=True) as g: g.command('cmd1', 'example_handler') return self.command_table def load_arguments(self, command): with ArgumentsContext(self, '') as c: c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3]) c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c']) super().load_arguments(command) helps['grp1'] = """ type: group short-summary: A group. """ self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader) @redirect_io def test_preview_command_group_help(self): """ Ensure preview commands appear correctly in group help view. """ with self.assertRaises(SystemExit): self.cli_ctx.invoke('-h'.split()) actual = self.io.getvalue() expected = u""" Group {} Subgroups: grp1 [Preview] : A group. Commands: cmd1 [Preview] : Short summary here. """.format(self.cli_ctx.name) self.assertEqual(expected, actual) @redirect_io def test_preview_command_plain_execute(self): """ Ensure general warning displayed when running preview command. """ self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() expected = "This command is in preview. It may be changed/removed in a future release." self.assertIn(expected, actual) @redirect_io def test_preview_command_plain_execute_only_show_error(self): """ Ensure warning is suppressed when running preview command. """ # Directly use --only-show-errors self.cli_ctx.invoke('cmd1 -b b --only-show-errors'.split()) actual = self.io.getvalue() self.assertNotIn("preview", actual) # Apply --only-show-errors with config self.cli_ctx.only_show_errors = True self.cli_ctx.config.set_value('core', 'only_show_errors', 'True') self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() self.assertNotIn("preview", actual) self.cli_ctx.config.set_value('core', 'only_show_errors', '') self.cli_ctx.only_show_errors = False @redirect_io @disable_color def test_preview_command_plain_execute_no_color(self): """ Ensure warning is displayed without color. """ self.cli_ctx.invoke('cmd1 -b b'.split()) actual = self.io.getvalue() self.assertIn( "WARNING: This command is in preview. It may be changed/removed in a future release.", actual) @redirect_io def test_preview_command_implicitly_execute(self): """ Ensure general warning displayed when running command from a preview parent group. """ self.cli_ctx.invoke('grp1 cmd1 -b b'.split()) actual = self.io.getvalue() expected = "Command group 'grp1' is in preview. It may be changed/removed in a future release." self.assertIn(expected, actual) @redirect_io @disable_color def test_preview_command_implicitly_no_color(self): """ Ensure warning is displayed without color. """ self.cli_ctx.invoke('grp1 cmd1 -b b'.split()) actual = self.io.getvalue() expected = "WARNING: Command group 'grp1' is in preview. It may be changed/removed in a future release." self.assertIn(expected, actual)