@@ -200,6 +200,41 @@ def __init__(self, code, component_trace):
200200 self .trace = component_trace
201201
202202
203+ def _IsHelpShortcut (component_trace , remaining_args ):
204+ """Determines if the user is trying to access help without '--' separator.
205+
206+ For example, mycmd.py --help instead of mycmd.py -- --help.
207+
208+ Args:
209+ component_trace: (FireTrace) The trace for the Fire command.
210+ remaining_args: List of remaining args that haven't been consumed yet.
211+ Returns:
212+ True if help is requested, False otherwise.
213+ """
214+ show_help = False
215+ if remaining_args :
216+ target = remaining_args [0 ]
217+ if target == '-h' :
218+ show_help = True
219+ elif target == '--help' :
220+ # Check if --help would be consumed as a keyword argument, or is a member.
221+ component = component_trace .GetResult ()
222+ if inspect .isclass (component ) or inspect .isroutine (component ):
223+ fn_spec = inspectutils .GetFullArgSpec (component )
224+ _ , remaining_kwargs , _ = _ParseKeywordArgs (remaining_args , fn_spec )
225+ show_help = target in remaining_kwargs
226+ else :
227+ members = dict (inspect .getmembers (component ))
228+ show_help = target not in members
229+
230+ if show_help :
231+ component_trace .show_help = True
232+ command = '{cmd} -- --help' .format (cmd = component_trace .GetCommand ())
233+ print ('INFO: Showing help with the command {cmd}.\n ' .format (
234+ cmd = pipes .quote (command )), file = sys .stderr )
235+ return show_help
236+
237+
203238def _PrintResult (component_trace , verbose = False ):
204239 """Prints the result of the Fire call to stdout in a human readable way."""
205240 # TODO: Design human readable deserializable serialization method
@@ -231,20 +266,25 @@ def _DictAsString(result, verbose=False):
231266 Returns:
232267 A string representing the dict
233268 """
234- result = {key : value for key , value in result .items ()
235- if _ComponentVisible (key , verbose )}
236269
237- if not result :
270+ # We need to do 2 iterations over the items in the result dict
271+ # 1) Getting visible items and the longest key for output formatting
272+ # 2) Actually construct the output lines
273+ result_visible = {key : value for key , value in result .items ()
274+ if _ComponentVisible (key , verbose )}
275+
276+ if not result_visible :
238277 return '{}'
239278
240- longest_key = max (len (str (key )) for key in result .keys ())
279+ longest_key = max (len (str (key )) for key in result_visible .keys ())
241280 format_string = '{{key:{padding}s}} {{value}}' .format (padding = longest_key + 1 )
242281
243282 lines = []
244283 for key , value in result .items ():
245- line = format_string .format (key = str (key ) + ':' ,
246- value = _OneLineResult (value ))
247- lines .append (line )
284+ if _ComponentVisible (key , verbose ):
285+ line = format_string .format (key = str (key ) + ':' ,
286+ value = _OneLineResult (value ))
287+ lines .append (line )
248288 return '\n ' .join (lines )
249289
250290
@@ -344,6 +384,10 @@ def _Fire(component, args, context, name=None):
344384 # there's a separator after it, and instead process the current component.
345385 break
346386
387+ if _IsHelpShortcut (component_trace , remaining_args ):
388+ remaining_args = []
389+ break
390+
347391 saved_args = []
348392 used_separator = False
349393 if separator in remaining_args :
@@ -556,7 +600,6 @@ def _MakeParseFn(fn):
556600 the leftover args from the arguments to the parse function.
557601 """
558602 fn_spec = inspectutils .GetFullArgSpec (fn )
559- all_args = fn_spec .args + fn_spec .kwonlyargs
560603 metadata = decorators .GetMetadata (fn )
561604
562605 # Note: num_required_args is the number of positional arguments without
@@ -566,8 +609,7 @@ def _MakeParseFn(fn):
566609
567610 def _ParseFn (args ):
568611 """Parses the list of `args` into (varargs, kwargs), remaining_args."""
569- kwargs , remaining_kwargs , remaining_args = _ParseKeywordArgs (
570- args , all_args , fn_spec .varkw )
612+ kwargs , remaining_kwargs , remaining_args = _ParseKeywordArgs (args , fn_spec )
571613
572614 # Note: _ParseArgs modifies kwargs.
573615 parsed_args , kwargs , remaining_args , capacity = _ParseArgs (
@@ -663,7 +705,7 @@ def _ParseArgs(fn_args, fn_defaults, num_required_args, kwargs,
663705 return parsed_args , kwargs , remaining_args , capacity
664706
665707
666- def _ParseKeywordArgs (args , fn_args , fn_keywords ):
708+ def _ParseKeywordArgs (args , fn_spec ):
667709 """Parses the supplied arguments for keyword arguments.
668710
669711 Given a list of arguments, finds occurences of --name value, and uses 'name'
@@ -677,11 +719,8 @@ def _ParseKeywordArgs(args, fn_args, fn_keywords):
677719 _ParseArgs, which converts them to the appropriate type.
678720
679721 Args:
680- args: A list of arguments
681- fn_args: A list of argument names that the target function accepts,
682- including positional and named arguments, but not the varargs or kwargs
683- names.
684- fn_keywords: The argument name for **kwargs, or None if **kwargs not used
722+ args: A list of arguments.
723+ fn_spec: The inspectutils.FullArgSpec describing the given callable.
685724 Returns:
686725 kwargs: A dictionary mapping keywords to values.
687726 remaining_kwargs: A list of the unused kwargs from the original args.
@@ -690,6 +729,8 @@ def _ParseKeywordArgs(args, fn_args, fn_keywords):
690729 kwargs = {}
691730 remaining_kwargs = []
692731 remaining_args = []
732+ fn_keywords = fn_spec .varkw
733+ fn_args = fn_spec .args + fn_spec .kwonlyargs
693734
694735 if not args :
695736 return kwargs , remaining_kwargs , remaining_args
0 commit comments