diff --git a/awscli/arguments.py b/awscli/arguments.py index 96ac5de17150..90b9cbb1b6c7 100644 --- a/awscli/arguments.py +++ b/awscli/arguments.py @@ -449,7 +449,7 @@ def add_to_parser(self, parser): cli_name = self.cli_name parser.add_argument( cli_name, - help=self.documentation, + help=self.documentation.replace('%', '%%'), type=self.cli_type, required=self.required, ) @@ -600,7 +600,7 @@ def add_to_arg_table(self, argument_table): def add_to_parser(self, parser): parser.add_argument( self.cli_name, - help=self.documentation, + help=self.documentation.replace('%', '%%'), action=self._action, default=self._default, dest=self._destination, diff --git a/tests/unit/test_argprocess.py b/tests/unit/test_argprocess.py index fe5eb531f301..59c0d93ff83c 100644 --- a/tests/unit/test_argprocess.py +++ b/tests/unit/test_argprocess.py @@ -33,6 +33,7 @@ from awscli.help import OperationHelpCommand from awscli.paramfile import LOCAL_PREFIX_MAP, URIArgumentHandler from awscli.testutils import BaseCLIDriverTest, mock, temporary_file, unittest +from awscli.argparser import ArgTableArgParser # These tests use real service types so that we can @@ -968,5 +969,39 @@ def test_json_value_decode_error(self): unpack_cli_arg(self.p, value) +class TestArgumentPercentEscaping(BaseArgProcessTest): + def _test_percent_escaping(self, arg_type, arg_class, doc_string): + argument = self.create_argument( + { + 'Test': { + 'type': arg_type, + 'documentation': doc_string, + } + } + ) + arg = arg_class( + 'test-arg', + argument.argument_model.members['Test'], + mock.Mock(), + mock.Mock(), + is_required=False, + ) + arg_table = {arg.name: arg} + parser = ArgTableArgParser(arg_table) + help_output = parser.format_help() + self.assertIn(doc_string, help_output) + + def test_cli_argument_escapes_percent(self): + self._test_percent_escaping('string', CLIArgument, 'Symbols: % ^ & *') + + def test_boolean_argument_escapes_percent(self): + self._test_percent_escaping('boolean', BooleanArgument, 'Symbols: % ^ & *') + + def test_cli_argument_escapes_url_encoded_percent(self): + self._test_percent_escaping('string', CLIArgument, 'File: test%28file%29.png') + + def test_boolean_argument_escapes_url_encoded_percent(self): + self._test_percent_escaping('boolean', BooleanArgument, 'File: test%28file%29.png') + if __name__ == '__main__': unittest.main()