3535 '__source__' , '__training__' ]
3636DUNDERS = BASE_DUNDERS + [
3737 '__date__' , '__details__' , '__description__' , '__doc__' , '__docformat__' , '__email__' , '__examples__' ,
38- '__functions__ ' , '__maximum_python_version__ ' , '__minimum_python_version__ ' , '__priority__ ' , '__product__ ' ,
39- '__script__' , '__status__' , '__version__' ,
38+ '__example_limit__ ' , '__functions__ ' , '__maximum_python_version__ ' , '__minimum_python_version__ ' , '__priority__ ' ,
39+ '__product__' , '__requires__' , ' __script__' , '__status__' , '__version__' ,
4040]
41- if sys .version_info >= (3 , 8 ):
42- DUNDERS .append ('__requires__' )
43-
4441DEFAULT_MAX_LEN = 20
4542DEFAULT_LST_MAX_LEN = 10
4643
@@ -158,14 +155,14 @@ def add_parser(self, name, **kwargs):
158155 choice_action .category = category
159156 self ._choices_actions .append (choice_action )
160157 # create the parser, but with another formatter and separating the help into an argument group
158+ kwargs .update (name = name , parent_action = self )
161159 parser = self ._parser_class (add_help = False , ** kwargs )
162- parser .name = name
163160 # add default extra arguments group
164161 i = parser .add_argument_group ("extra arguments" )
165162 i .add_argument ("-h" , action = "usage" , prefix = "show" , help = gt ("show usage message and exit" ))
166163 i .add_argument ("--help" , action = "help" , prefix = "show" , help = gt ("show this help message and exit" ))
167164 # add it to the map
168- self ._name_parser_map [name ] = parser
165+ self ._name_parser_map [parser . name ] = parser
169166 # make parser available under aliases also
170167 for alias in aliases :
171168 self ._name_parser_map [alias ] = parser
@@ -316,13 +313,16 @@ def __init__(self, *args, **kwargs):
316313 if not hasattr (ArgumentParser , "_globals_dict" ):
317314 ArgumentParser ._globals_dict = get_tool_globals ()
318315 gd = ArgumentParser ._globals_dict
319- if sys .version_info >= (3 , 8 ):
320- self ._check_requirements (gd .get ('__requires__' ))
316+ self ._check_requirements (gd .get ('__requires__' ))
321317 configure_docformat (gd )
322318 self ._config_parsed = False
323319 self ._docfmt = gd .get ('__docformat__' )
320+ self ._parent_action = kwargs .pop ('parent_action' , None )
321+ self ._parent = self ._parent_action ._parent if self ._parent_action else None
324322 self ._reparse_args = {'pos' : [], 'opt' : [], 'sub' : []}
323+ self .name = kwargs .pop ('name' , self .__class__ .name )
325324 self .examples = gd .get ('__examples__' , [])
325+ self .example_limit = gd .get ('__example_limit__' , {})
326326 script = basename (self .tokens [0 ])
327327 _stem = lambda p : splitext (p )[0 ]
328328 if gd .get ('__script__' ) is None :
@@ -344,6 +344,19 @@ def __init__(self, *args, **kwargs):
344344 l = ["{} {}" .format (ArgumentParser .prog , e ) for e in self .examples ]
345345 l = list (filter (lambda x : x .startswith (kwargs ['prog' ]), l ))
346346 if len (l ) > 0 :
347+ dest = self ._parent_action .dest if getattr (self , "_parent_action" , None ) else None
348+ if (n := self .example_limit if isinstance (self .example_limit , int ) else \
349+ self .example_limit .get (self .name , self .example_limit .get (dest , 0 ))):
350+ from random import shuffle
351+ from shlex import split
352+ shuffle (l )
353+ _tmp , new_l = set (), []
354+ for example in l :
355+ token = split (example )[self .depth + 1 ]
356+ if token not in _tmp :
357+ new_l .append (example )
358+ _tmp .add (token )
359+ l = sorted (new_l )
347360 kwargs ['epilog' ] = txt2title (gt ("Usage example{}" .format (["" , "s" ][len (l ) > 1 ])) + ":" )
348361 e = '\n ' .join (["\n " , " " ][self ._docfmt is None ] + txt2paragraph (e ) for e in l )
349362 kwargs ['epilog' ] += "\n " + e
@@ -603,6 +616,11 @@ def _sorted_actions(self):
603616 for a in filter (lambda _ : self .is_action (_ , 'parsers' ), self ._actions ):
604617 yield a
605618
619+ def add_subparsers (self , ** kwargs ):
620+ action = super (ArgumentParser , self ).add_subparsers (** kwargs )
621+ action ._parent = self
622+ return action
623+
606624 def config_args (self , section = "main" ):
607625 """ Additional method for feeding input arguments from a config file. """
608626 if self ._config_parsed :
@@ -730,19 +748,26 @@ def print_usage(self, file=None):
730748 self ._print_message (txt_terminal_render (self .format_usage ()), file or sys .stdout )
731749
732750 @property
733- def tokens (self ):
751+ def depth (self ):
752+ p , d = self , 0
753+ while (p := getattr (p , "_parent" , None )):
754+ d += 1
755+ return d
756+
757+ @property
758+ def root (self ):
734759 p = self
735- while hasattr (p , "_parent" ) and p . _parent is not None :
760+ while getattr (p , "_parent" , None ) :
736761 p = p ._parent
737- if hasattr (p , "_tokens" ):
738- return p ._tokens
762+ return p
763+
764+ @property
765+ def tokens (self ):
766+ return getattr (self .root , "_tokens" , None )
739767
740768 @tokens .setter
741769 def tokens (self , command ):
742- p = self
743- while hasattr (p , "_parent" ) and p ._parent is not None :
744- p = p ._parent
745- if hasattr (p , "_tokens" ):
770+ if hasattr (p := self .root , "_tokens" ):
746771 return
747772 p ._tokens = sys .argv if command is None else command
748773 if isinstance (p ._tokens , str ):
0 commit comments