-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Do not treat arguments that start with '--' as string. #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -565,7 +565,8 @@ def _MakeParseFn(fn): | |
|
|
||
| def _ParseFn(args): | ||
| """Parses the list of `args` into (varargs, kwargs), remaining_args.""" | ||
| kwargs, remaining_args = _ParseKeywordArgs(args, all_args, fn_spec.varkw) | ||
| kwargs, remaining_kwargs, remaining_args = \ | ||
| _ParseKeywordArgs(args, all_args, fn_spec.varkw) | ||
|
|
||
| # Note: _ParseArgs modifies kwargs. | ||
| parsed_args, kwargs, remaining_args, capacity = _ParseArgs( | ||
|
|
@@ -594,6 +595,7 @@ def _ParseFn(args): | |
| varargs[index] = _ParseValue(value, None, None, metadata) | ||
|
|
||
| varargs = parsed_args + varargs | ||
| remaining_args += remaining_kwargs | ||
|
|
||
| consumed_args = args[:len(args) - len(remaining_args)] | ||
| return (varargs, kwargs), consumed_args, remaining_args, capacity | ||
|
|
@@ -681,64 +683,72 @@ def _ParseKeywordArgs(args, fn_args, fn_keywords): | |
| fn_keywords: The argument name for **kwargs, or None if **kwargs not used | ||
| Returns: | ||
| kwargs: A dictionary mapping keywords to values. | ||
| remaining_kwargs: A list of the unused kwargs from the original args. | ||
| remaining_args: A list of the unused arguments from the original args. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| """ | ||
| kwargs = {} | ||
| if args: | ||
| remaining_args = [] | ||
| skip_argument = False | ||
|
|
||
| for index, argument in enumerate(args): | ||
| if skip_argument: | ||
| skip_argument = False | ||
| continue | ||
|
|
||
| arg_consumed = False | ||
| if argument.startswith('--'): | ||
| # This is a named argument; get its value from this arg or the next. | ||
| got_argument = False | ||
|
|
||
| keyword = argument[2:] | ||
| contains_equals = '=' in keyword | ||
| is_bool_syntax = ( | ||
| not contains_equals and | ||
| (index + 1 == len(args) or args[index + 1].startswith('--'))) | ||
| if contains_equals: | ||
| keyword, value = keyword.split('=', 1) | ||
| got_argument = True | ||
| elif is_bool_syntax: | ||
| # Since there's no next arg or the next arg is a Flag, we consider | ||
| # this flag to be a boolean. | ||
| got_argument = True | ||
| if keyword in fn_args: | ||
| value = 'True' | ||
| elif keyword.startswith('no'): | ||
| keyword = keyword[2:] | ||
| value = 'False' | ||
| else: | ||
| value = 'True' | ||
| remaining_kwargs = [] | ||
| remaining_args = [] | ||
|
|
||
| if not args: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I add a return statement here because pylint complains about "Too many nested blocks (6/5)". Not sure if I should do it this way.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems ok to me. Another possibility would be moving some of the inner blocks into their own functions, but I don't see a super clean way to do that. |
||
| return kwargs, remaining_kwargs, remaining_args | ||
|
|
||
| skip_argument = False | ||
|
|
||
| for index, argument in enumerate(args): | ||
| if skip_argument: | ||
| skip_argument = False | ||
| continue | ||
|
|
||
| arg_consumed = False | ||
| if argument.startswith('--'): | ||
| # This is a named argument; get its value from this arg or the next. | ||
| got_argument = False | ||
|
|
||
| keyword = argument[2:] | ||
| contains_equals = '=' in keyword | ||
| is_bool_syntax = ( | ||
| not contains_equals and | ||
| (index + 1 == len(args) or args[index + 1].startswith('--'))) | ||
| if contains_equals: | ||
| keyword, value = keyword.split('=', 1) | ||
| got_argument = True | ||
| elif is_bool_syntax: | ||
| # Since there's no next arg or the next arg is a Flag, we consider | ||
| # this flag to be a boolean. | ||
| got_argument = True | ||
| if keyword in fn_args: | ||
| value = 'True' | ||
| elif keyword.startswith('no'): | ||
| keyword = keyword[2:] | ||
| value = 'False' | ||
| else: | ||
| if index + 1 < len(args): | ||
| value = args[index + 1] | ||
| got_argument = True | ||
| value = 'True' | ||
| else: | ||
| if index + 1 < len(args): | ||
| value = args[index + 1] | ||
| got_argument = True | ||
|
|
||
| keyword = keyword.replace('-', '_') | ||
| keyword = keyword.replace('-', '_') | ||
|
|
||
| # In order for us to consume the argument as a keyword arg, we either: | ||
| # Need to be explicitly expecting the keyword, or we need to be | ||
| # accepting **kwargs. | ||
| if got_argument and (keyword in fn_args or fn_keywords): | ||
| # In order for us to consume the argument as a keyword arg, we either: | ||
| # Need to be explicitly expecting the keyword, or we need to be | ||
| # accepting **kwargs. | ||
| if got_argument: | ||
| skip_argument = not contains_equals and not is_bool_syntax | ||
| arg_consumed = True | ||
| if keyword in fn_args or fn_keywords: | ||
| kwargs[keyword] = value | ||
| skip_argument = not contains_equals and not is_bool_syntax | ||
| arg_consumed = True | ||
| else: | ||
| remaining_kwargs.append(argument) | ||
| if skip_argument: | ||
| remaining_kwargs.append(args[index + 1]) | ||
|
|
||
| if not arg_consumed: | ||
| # The argument was not consumed, so it is still a remaining argument. | ||
| remaining_args.append(argument) | ||
| else: | ||
| remaining_args = args | ||
| if not arg_consumed: | ||
| # The argument was not consumed, so it is still a remaining argument. | ||
| remaining_args.append(argument) | ||
|
|
||
| return kwargs, remaining_args | ||
| return kwargs, remaining_kwargs, remaining_args | ||
|
|
||
|
|
||
| def _ParseValue(value, index, arg, metadata): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -368,10 +368,15 @@ def testBoolParsingLessExpectedCases(self): | |
| fire.Fire(tc.MixedDefaults, | ||
| command=['identity', 'True', '10']), (True, 10)) | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing that may be worthwhile to test is the behavior in the presence of separators / chained function calls. For example, with these components: What is the behavior of these calls? fire.Fire(example_component, command=['value1', '-', '--kwarg3', 'value3']) == 'value3' |
||
| # Note: Does not return ('--test', '0'). | ||
| self.assertEqual(fire.Fire(tc.MixedDefaults, | ||
| command=['identity', '--alpha', '--test']), | ||
| (True, '--test')) | ||
| # Note: Does not return (True, '--test') or ('--test', 0). | ||
| with self.assertRaisesFireExit(2): | ||
| fire.Fire(tc.MixedDefaults, command=['identity', '--alpha', '--test']) | ||
|
|
||
| self.assertEqual( | ||
| fire.Fire( | ||
| tc.MixedDefaults, | ||
| command=['identity', '--alpha', 'True', '"--test"']), | ||
| (True, '--test')) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lgtm. |
||
| # To get ('--test', '0'), use one of the following: | ||
| self.assertEqual(fire.Fire(tc.MixedDefaults, | ||
| command=['identity', '--alpha=--test']), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: for consistency w/ codebase, let's do: