From 1fc2452569180d1589623359e0df1a68aeeab840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavl=C3=ADk=20Tom=C3=A1=C5=A1?= Date: Thu, 5 Dec 2019 12:49:50 +0100 Subject: [PATCH] Dash supported as first char of argument --- configargparse.py | 45 +++++++++++------------- setup.py | 2 +- tests/test_configargparse.py | 68 ++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/configargparse.py b/configargparse.py index 6f24108..be5df5a 100644 --- a/configargparse.py +++ b/configargparse.py @@ -397,17 +397,6 @@ def parse_known_args(self, args = None, namespace = None, else: args = list(args) - # normalize args by converting args like --key=value to --key value - normalized_args = [] - for arg in args: - if arg and arg[0] in self.prefix_chars and '=' in arg: - key, value = arg.split('=', 1) - normalized_args.append(key) - normalized_args.append(value) - else: - normalized_args.append(arg) - args = normalized_args - for a in self._actions: a.is_positional_arg = not a.option_strings @@ -438,7 +427,7 @@ def parse_known_args(self, args = None, namespace = None, nargs = False actions_with_env_var_values = [a for a in self._actions if not a.is_positional_arg and a.env_var and a.env_var in env_vars - and not already_on_command_line(args, a.option_strings)] + and not already_on_command_line(args, a.option_strings, self.prefix_chars)] for action in actions_with_env_var_values: key = action.env_var value = env_vars[key] @@ -497,13 +486,14 @@ def parse_known_args(self, args = None, namespace = None, if key in known_config_keys: action = known_config_keys[key] discard_this_key = already_on_command_line( - args, action.option_strings) + args, action.option_strings, self.prefix_chars) else: action = None discard_this_key = self._ignore_unknown_config_file_keys or \ already_on_command_line( args, - [self.get_command_line_key_for_unknown_config_file_setting(key)]) + [self.get_command_line_key_for_unknown_config_file_setting(key)], + self.prefix_chars) if not discard_this_key: config_args += self.convert_item_to_command_line_arg( @@ -526,7 +516,7 @@ def parse_known_args(self, args = None, namespace = None, for action in self._actions: cares_about_default_value = (not action.is_positional_arg or action.nargs in [OPTIONAL, ZERO_OR_MORE]) - if (already_on_command_line(args, action.option_strings) or + if (already_on_command_line(args, action.option_strings, self.prefix_chars) or not cares_about_default_value or action.default is None or action.default == SUPPRESS or @@ -617,7 +607,8 @@ def get_items_for_config_file_output(self, source_to_settings, config_file_keys = self.get_possible_config_keys(action) if config_file_keys and not action.is_positional_arg and \ already_on_command_line(existing_command_line_args, - action.option_strings): + action.option_strings, + self.prefix_chars): value = getattr(parsed_namespace, action.dest, None) if value is not None: if isinstance(value, bool): @@ -675,8 +666,7 @@ def convert_item_to_command_line_arg(self, action, key, value): elif isinstance(value, list): if action is None or isinstance(action, argparse._AppendAction): for list_elem in value: - args.append( command_line_key ) - args.append( str(list_elem) ) + args.append( "%s=%s" % (command_line_key, str(list_elem)) ) elif (isinstance(action, argparse._StoreAction) and action.nargs in ('+', '*')) or ( isinstance(action.nargs, int) and action.nargs > 1): args.append( command_line_key ) @@ -686,8 +676,7 @@ def convert_item_to_command_line_arg(self, action, key, value): self.error(("%s can't be set to a list '%s' unless its action type is changed " "to 'append' or nargs is set to '*', '+', or > 1") % (key, value)) elif isinstance(value, str): - args.append( command_line_key ) - args.append( value ) + args.append( "%s=%s" % (command_line_key, value) ) else: raise ValueError("Unexpected value type {} for value: {}".format( type(value), value)) @@ -719,7 +708,7 @@ def _open_config_files(self, command_line_args): constructor that are present on disk. Args: - command_line_args: List of all args (already split on spaces) + command_line_args: List of all args """ # open any default config files config_files = [open(f) for files in map(glob.glob, map(os.path.expanduser, self._default_config_files)) @@ -909,13 +898,21 @@ def add_argument(self, *args, **kwargs): return action -def already_on_command_line(existing_args_list, potential_command_line_args): +def already_on_command_line(existing_args_list, potential_command_line_args, prefix_chars): """Utility method for checking if any of the potential_command_line_args is already present in existing_args. """ - return any(potential_arg in existing_args_list - for potential_arg in potential_command_line_args) + arg_names = [] + for arg_string in existing_args_list: + if arg_string and arg_string[0] in prefix_chars and "=" in arg_string : + option_string, explicit_arg = arg_string.split("=", 1) + arg_names.append(option_string) + else: + arg_names.append(arg_string) + return any( + potential_arg in arg_names for potential_arg in potential_command_line_args + ) # wrap ArgumentParser's add_argument(..) method with the one above diff --git a/setup.py b/setup.py index ac5d9c6..92f575d 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ def launch_http_server(directory): setup( name='ConfigArgParse', - version="0.15.1", + version="0.15.2", description='A drop-in replacement for argparse that allows options to ' 'also be set via config files and/or environment variables.', long_description=long_description, diff --git a/tests/test_configargparse.py b/tests/test_configargparse.py index 9294e5d..630deb5 100644 --- a/tests/test_configargparse.py +++ b/tests/test_configargparse.py @@ -460,12 +460,12 @@ def testConfigFileSyntax(self): ns, args = self.parse_known("-x 10 --y 3.8", config_file_contents="bla=3", env_vars={"bla": "2"}) - self.assertListEqual(args, ["--bla", "3"]) + self.assertListEqual(args, ["--bla=3"]) self.initParser(ignore_unknown_config_file_keys=False) ns, args = self.parse_known(args="-x 1", config_file_contents="bla=3", env_vars={"bla": "2"}) - self.assertEqual(set(args), {"--bla", "3", "-x", "1"}) + self.assertEqual(set(args), {"--bla=3", "-x", "1"}) def testBooleanValuesCanBeExpressedAsNumbers(self): self.initParser() @@ -542,6 +542,68 @@ def testConfigOrEnvValueErrors(self): ns = self.parse("", env_vars = {"file": "[1,2,3]", "VERBOSE": "true"}) self.assertIsNone(ns.file) + def testValuesStartingWithDash(self): + self.initParser() + self.add_arg("--arg0") + self.add_arg("--arg1", env_var="ARG1") + self.add_arg("--arg2") + self.add_arg("--arg3", action='append') + self.add_arg("--arg4", action='append', env_var="ARG4") + self.add_arg("--arg5", action='append') + + ns = self.parse( + "--arg0=-foo --arg3=-foo --arg3=-bar", + config_file_contents="arg2: -foo\narg5: [-foo, -bar]", + env_vars={"ARG1": "-foo", "ARG4": "[-foo, -bar]"} + ) + self.assertEqual(ns.arg0, "-foo") + self.assertEqual(ns.arg1, "-foo") + self.assertEqual(ns.arg2, "-foo") + self.assertEqual(ns.arg3, ["-foo", "-bar"]) + self.assertEqual(ns.arg4, ["-foo", "-bar"]) + self.assertEqual(ns.arg5, ["-foo", "-bar"]) + + def testPriorityKnown(self): + self.initParser() + self.add_arg("--arg", env_var="ARG") + + ns = self.parse( + "--arg command_line_val", + config_file_contents="arg: config_val", + env_vars={"ARG": "env_val"} + ) + self.assertEqual(ns.arg, "command_line_val") + + ns = self.parse( + "--arg=command_line_val", + config_file_contents="arg: config_val", + env_vars={"ARG": "env_val"} + ) + self.assertEqual(ns.arg, "command_line_val") + + ns = self.parse( + "", + config_file_contents="arg: config_val", + env_vars={"ARG": "env_val"} + ) + self.assertEqual(ns.arg, "env_val") + + def testPriorityUnknown(self): + self.initParser() + + ns, args = self.parse_known( + "--arg command_line_val", + config_file_contents="arg: config_val", + env_vars={"arg": "env_val"} + ) + self.assertListEqual(args, ["--arg", "command_line_val"]) + + ns, args = self.parse_known( + "--arg=command_line_val", + config_file_contents="arg: config_val", + ) + self.assertListEqual(args, ["--arg=command_line_val"]) + def testAutoEnvVarPrefix(self): self.initParser(auto_env_var_prefix="TEST_") self.add_arg("-a", "--arg0", is_config_file_arg=True) @@ -719,7 +781,7 @@ def test_AbbrevConfigFileArgs(self): known, unknown = self.parse_known(command) - self.assertListEqual(unknown, ['--a2a', '0.5', '--a3a', '0.5']) + self.assertListEqual(unknown, ['--a2a=0.5', '--a3a=0.5']) def test_FormatHelp(self): self.initParser(args_for_setting_config_path=["-c", "--config"],