@@ -143,12 +143,14 @@ def wordify(string):
143143
144144def get_bash_commands (root_parser , root_prefix , choice_functions = None ):
145145 """
146- Recursive subcommand parser traversal, printing bash helper syntax.
146+ Recursive subcommand parser traversal, returning lists of information on
147+ commands (formatted for output to the completions script).
148+ printing bash helper syntax.
147149
148150 Returns:
149- commands : list of subparsers for each parser
151+ subparsers : list of subparsers for each parser
150152 option_strings : list of options strings for each parser
151- compgens : list of shtab completion functions corresponding to actions
153+ compgens : list of shtab `.complete` functions corresponding to actions
152154 choices : list of choices corresponding to actions
153155 nargs : list of number of args allowed for each action (if not 0 or 1)
154156 """
@@ -167,35 +169,29 @@ def get_option_strings(parser):
167169 [],
168170 )
169171
170- # For the given parser, return a tuple of lists of information on commands,
171- # option strings, compgens, choices, and nargs values (where applicable)
172- # formatted for output to the completions script.
173- # Also recurse through any subparsers, adding their results to the lists.
174172 def recurse (parser , prefix ):
173+ """recurse through subparsers, appending to the return lists"""
175174 subparsers = []
176175 option_strings = []
177176 compgens = []
178177 choices = []
179178 nargs = []
180179
181- # These lists are generated from recursive calls to subparsers but aren't
182- # added to the main list until just before returning, so that the order in
183- # output makes sense with the structure of the argparser object.
180+ # temp lists for recursion results
184181 sub_subparsers = []
185182 sub_option_strings = []
186183 sub_compgens = []
187184 sub_choices = []
188185 sub_nargs = []
189186
190- # Add relevant information for all the positional arguments (where applicable).
187+ # positional arguments
191188 discovered_subparsers = []
192189 for i , positional in enumerate (parser ._get_positional_actions ()):
193-
194190 if positional .help == SUPPRESS :
195191 continue
196192
197- # Check for shtab completion functions.
198193 if hasattr (positional , "complete" ):
194+ # shtab `.complete = ...` functions
199195 compgens .append (
200196 u"{}_pos_{}_COMPGEN={}" .format (
201197 prefix ,
@@ -204,18 +200,15 @@ def recurse(parser, prefix):
204200 )
205201 )
206202
207- # Check for choices (including calls to subparsers and shtab
208- # completion functions)
209203 if positional .choices :
204+ # choices (including subparsers & shtab `.complete` functions)
210205 log .debug ("choices:{}:{}" .format (prefix , sorted (positional .choices )))
211206
212207 this_positional_choices = []
213208 for choice in positional .choices :
214- # If the choice is a special completion type, add its info to
215- # compgens.
216- # NOTE: if compgens were specified in the "complete" attribute,
217- # this will overwrite them.
218209 if isinstance (choice , Choice ):
210+ # append special completion type to `compgens`
211+ # NOTE: overrides `.complete` attribute
219212 log .debug (
220213 "Choice.{}:{}:{}" .format (
221214 choice .type , prefix , positional .dest
@@ -226,9 +219,8 @@ def recurse(parser, prefix):
226219 prefix , i , choice_type2fn [choice .type ]
227220 )
228221 )
229- # If the choice is a dict, it represents a call to a subparser,
230- # so add to the list of subparsers & recursively call the subparser
231222 elif isinstance (positional .choices , dict ):
223+ # subparser, so append to list of subparsers & recurse
232224 log .debug ("subcommand:%s" , choice )
233225 if positional .choices [choice ].add_help :
234226 discovered_subparsers .append (str (choice ))
@@ -250,8 +242,8 @@ def recurse(parser, prefix):
250242 sub_nargs .extend (new_nargs )
251243 else :
252244 log .debug ("skip:subcommand:%s" , choice )
253- # Otherwise, it's just a simple choice.
254245 else :
246+ # simple choice
255247 this_positional_choices .append (str (choice ))
256248
257249 if this_positional_choices :
@@ -261,7 +253,7 @@ def recurse(parser, prefix):
261253 )
262254 )
263255
264- # Lastly, skip default `nargs` values
256+ # skip default `nargs` values
265257 if positional .nargs not in (None , "1" , "?" ):
266258 nargs .append (u"{}_pos_{}_nargs={}" .format (prefix , i , positional .nargs ))
267259
@@ -273,72 +265,68 @@ def recurse(parser, prefix):
273265 )
274266 log .debug ("subcommands:{}:{}" .format (prefix , discovered_subparsers ))
275267
276- # Add relevant information for the optional arguments.
268+ # optional arguments
277269 option_strings .append (
278270 u"{}_option_strings=('{}')" .format (
279271 prefix , "' '" .join (get_option_strings (parser ))
280272 )
281273 )
282274 for optional in parser ._get_optional_actions ():
283- if optional ! = SUPPRESS :
284- for option_string in optional . option_strings :
275+ if optional = = SUPPRESS :
276+ continue
285277
286- # Check for shtab completion functions.
287- if hasattr (optional , "complete" ):
288- compgens .append (
289- u"{}_{}_COMPGEN={}" .format (
290- prefix ,
291- wordify (option_string ),
292- complete2pattern (
293- optional .complete , "bash" , choice_type2fn
294- ),
295- )
278+ for option_string in optional .option_strings :
279+ if hasattr (optional , "complete" ):
280+ # shtab `.complete = ...` functions
281+ compgens .append (
282+ u"{}_{}_COMPGEN={}" .format (
283+ prefix ,
284+ wordify (option_string ),
285+ complete2pattern (optional .complete , "bash" , choice_type2fn ),
296286 )
287+ )
297288
298- # Check for choices.
299- if optional .choices :
300- this_optional_choices = []
301- for choice in optional .choices :
302- # If the choice is a special completion type,
303- # add its info to compgens.
304- # NOTE: if compgens were specified in the "complete"
305- # attribute, this will overwrite them.
306- if isinstance (choice , Choice ):
307- log .debug (
308- "Choice.{}:{}:{}" .format (
309- choice .type , prefix , optional .dest
310- )
311- )
312- compgens .append (
313- u"{}_{}_COMPGEN={}" .format (
314- prefix ,
315- wordify (option_string ),
316- choice_type2fn [choice .type ],
317- )
289+ if optional .choices :
290+ # choices (including shtab `.complete` functions)
291+ this_optional_choices = []
292+ for choice in optional .choices :
293+ # append special completion type to `compgens`
294+ # NOTE: overrides `.complete` attribute
295+ if isinstance (choice , Choice ):
296+ log .debug (
297+ "Choice.{}:{}:{}" .format (
298+ choice .type , prefix , optional .dest
318299 )
319-
320- # Otherwise, it's just a simple choice.
321- else :
322- this_optional_choices .append (str (choice ))
323-
324- if this_optional_choices :
325- choices .append (
326- u"{}_{}_choices='{}'" .format (
300+ )
301+ compgens .append (
302+ u"{}_{}_COMPGEN={}" .format (
327303 prefix ,
328304 wordify (option_string ),
329- " " . join ( this_optional_choices ) ,
305+ choice_type2fn [ choice . type ] ,
330306 )
331307 )
308+ else :
309+ # simple choice
310+ this_optional_choices .append (str (choice ))
332311
333- # Check for nargs.
334- if optional .nargs is not None and optional .nargs != 1 :
335- nargs .append (
336- u"{}_{}_nargs={}" .format (
337- prefix , wordify (option_string ), optional .nargs
312+ if this_optional_choices :
313+ choices .append (
314+ u"{}_{}_choices='{}'" .format (
315+ prefix ,
316+ wordify (option_string ),
317+ " " .join (this_optional_choices ),
338318 )
339319 )
340320
341- # Add on the information obtained from subparsers.
321+ # Check for nargs.
322+ if optional .nargs is not None and optional .nargs != 1 :
323+ nargs .append (
324+ u"{}_{}_nargs={}" .format (
325+ prefix , wordify (option_string ), optional .nargs
326+ )
327+ )
328+
329+ # append recursion results
342330 subparsers .extend (sub_subparsers )
343331 option_strings .extend (sub_option_strings )
344332 compgens .extend (sub_compgens )
@@ -351,9 +339,7 @@ def recurse(parser, prefix):
351339
352340
353341@mark_completer ("bash" )
354- def complete_bash (
355- parser , root_prefix = None , preamble = "" , choice_functions = None ,
356- ):
342+ def complete_bash (parser , root_prefix = None , preamble = "" , choice_functions = None ):
357343 """
358344 Returns bash syntax autocompletion script.
359345
@@ -400,8 +386,7 @@ def complete_bash(
400386 echo "${1//[^[:word:]]/_}"
401387}
402388
403- # This function is called for the initial parser and any
404- # subparsers that are found, to set default values.
389+ # set default values (called for the initial parser & any subparsers)
405390_set_parser_defaults() {
406391 local subparsers_var="${prefix}_subparsers[@]"
407392 subparsers=${!subparsers_var}
@@ -415,9 +400,8 @@ def complete_bash(
415400}
416401
417402# $1=action identifier
418- # $2=is positional action (boolean)
419- # This function is called when a new action is encountered
420- # to set all the identifiers for that action's parameters.
403+ # $2=positional action (bool)
404+ # set all identifiers for an action's parameters
421405_set_new_action() {
422406 current_action="${prefix}_$(_shtab_replace_nonword $1)"
423407
@@ -434,19 +418,18 @@ def complete_bash(
434418 current_action_nargs=1
435419 fi
436420
437- current_action_args_start_index=$(($word_index+1 ))
421+ current_action_args_start_index=$(( $word_index + 1 ))
438422
439423 current_action_is_positional=$2
440424}
441425
442426# Notes:
443- # `COMPREPLY` contains what will be rendered after completion is triggered
444- # `completing_word` refers to the currently typed word to generate completions for
445- # `${!var}` is to evaluate the content of `var`
446- # and expand its content as a variable
447- # hello="world"
448- # x="hello"
449- # ${!x} -> ${hello} -> "world"
427+ # `COMPREPLY`: what will be rendered after completion is triggered
428+ # `completing_word`: currently typed word to generate completions for
429+ # `${!var}`: evaluates the content of `var` and expand its content as a variable
430+ # hello="world"
431+ # x="hello"
432+ # ${!x} -> ${hello} -> "world"
450433{root_prefix}() {
451434 local completing_word="${COMP_WORDS[COMP_CWORD]}"
452435 COMPREPLY=()
@@ -456,34 +439,30 @@ def complete_bash(
456439 _set_parser_defaults
457440 word_index=1
458441
459- # determined what arguments are appropriate for the current state
442+ # determine what arguments are appropriate for the current state
460443 # of the arg parser
461- while [ $word_index -ne $COMP_CWORD ]
462- do
463-
444+ while [ $word_index -ne $COMP_CWORD ]; do
464445 local this_word="${COMP_WORDS[$word_index]}"
465446
466- # If we encounter a valid subcommand,
467- # add it to the prefix and reset the current action
468447 if [[ -n $subparsers && " ${subparsers[@]} " =~ " ${this_word} " ]]; then
448+ # valid subcommand: add it to the prefix & reset the current action
469449 prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
470450 _set_parser_defaults
471451 fi
472452
473- # Check to see if a new action should be acquired,
474- # either because an option string is recognized,
475- # or because no more input is expected from the current action,
476- # indicating that the next positional action can fill in here.
477453 if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
454+ # a new action should be acquired (due to recognised option string or
455+ # no more input expected from current action);
456+ # the next positional action can fill in here
478457 _set_new_action $this_word false
479458 fi
480459
481460 if [[ "$current_action_nargs" != "*" ]] && \\
482461 [[ "$current_action_nargs" != "+" ]] && \\
483462 [[ "$current_action_nargs" != *"..." ]] && \\
484- (( $word_index+1- $current_action_args_start_index >= \\
485- $current_action_nargs )); then
486- $current_action_is_positional && let "completed_positional_actions+= 1"
463+ (( $word_index + 1 - $current_action_args_start_index >= \\
464+ $current_action_nargs )); then
465+ $current_action_is_positional && let "completed_positional_actions += 1"
487466 _set_new_action "pos_${completed_positional_actions}" true
488467 fi
489468
@@ -492,25 +471,14 @@ def complete_bash(
492471
493472 # Generate the completions
494473
495- # Uncomment below for debugging.
496- # echo ""
497- # echo "Current subparsers: ${subparsers[@]}"
498- # echo "Current option strings: ${current_option_strings[@]}"
499- # echo "Current action: ${current_action}"
500- # echo "Current action nargs: ${current_action_nargs}"
501- # echo "Current action completed arguments:"
502- # echo " $(( $word_index-$current_action_args_start_index ))"
503- # echo "Current action choices: ${current_action_choices}"
504- # echo "Current action compgen: ${current_action_compgen}"
505-
506- # If an optional argument has started, use option strings
507474 if [[ "${completing_word}" == -* ]]; then
475+ # optional argument started: use option strings
508476 COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
509- # Otherwise, use choices and compgen
510477 else
478+ # use choices & compgen
511479 COMPREPLY=( $(compgen -W "${current_action_choices}" -- "${completing_word}"; \\
512- [ -n "${current_action_compgen}" ] \\
513- && "${current_action_compgen}" "${completing_word}") )
480+ [ -n "${current_action_compgen}" ] \\
481+ && "${current_action_compgen}" "${completing_word}") )
514482 fi
515483
516484 return 0
@@ -701,7 +669,7 @@ def format_positional(opt):
701669 ),
702670 commands_case = "\n " .join (
703671 "{cmd_orig}) _arguments ${root_prefix}_{cmd} ;;" .format (
704- cmd_orig = cmd , cmd = wordify (cmd ), root_prefix = root_prefix ,
672+ cmd_orig = cmd , cmd = wordify (cmd ), root_prefix = root_prefix
705673 )
706674 for cmd in sorted (subcommands )
707675 ),
@@ -725,7 +693,7 @@ def format_positional(opt):
725693
726694
727695def complete (
728- parser , shell = "bash" , root_prefix = None , preamble = "" , choice_functions = None ,
696+ parser , shell = "bash" , root_prefix = None , preamble = "" , choice_functions = None
729697):
730698 """
731699 parser : argparse.ArgumentParser
0 commit comments