11# Author: Steven J. Bethard <steven.bethard@gmail.com>.
2+ # New maintainer as of 29 August 2019: Raymond Hettinger <raymond.hettinger@gmail.com>
23
34"""Command-line parsing library
45
6667 'ArgumentParser' ,
6768 'ArgumentError' ,
6869 'ArgumentTypeError' ,
70+ 'BooleanOptionalAction' ,
6971 'FileType' ,
7072 'HelpFormatter' ,
7173 'ArgumentDefaultsHelpFormatter' ,
@@ -127,7 +129,7 @@ def __repr__(self):
127129 return '%s(%s)' % (type_name , ', ' .join (arg_strings ))
128130
129131 def _get_kwargs (self ):
130- return sorted (self .__dict__ .items ())
132+ return list (self .__dict__ .items ())
131133
132134 def _get_args (self ):
133135 return []
@@ -164,15 +166,12 @@ def __init__(self,
164166
165167 # default setting for width
166168 if width is None :
167- try :
168- width = int (_os .environ ['COLUMNS' ])
169- except (KeyError , ValueError ):
170- width = 80
169+ import shutil
170+ width = shutil .get_terminal_size ().columns
171171 width -= 2
172172
173173 self ._prog = prog
174174 self ._indent_increment = indent_increment
175- self ._max_help_position = max_help_position
176175 self ._max_help_position = min (max_help_position ,
177176 max (width - 20 , indent_increment * 2 ))
178177 self ._width = width
@@ -265,7 +264,7 @@ def add_argument(self, action):
265264 invocations .append (get_invocation (subaction ))
266265
267266 # update the maximum item length
268- invocation_length = max ([ len ( s ) for s in invocations ] )
267+ invocation_length = max (map ( len , invocations ) )
269268 action_length = invocation_length + self ._current_indent
270269 self ._action_max_length = max (self ._action_max_length ,
271270 action_length )
@@ -407,13 +406,19 @@ def _format_actions_usage(self, actions, groups):
407406 inserts [start ] += ' ['
408407 else :
409408 inserts [start ] = '['
410- inserts [end ] = ']'
409+ if end in inserts :
410+ inserts [end ] += ']'
411+ else :
412+ inserts [end ] = ']'
411413 else :
412414 if start in inserts :
413415 inserts [start ] += ' ('
414416 else :
415417 inserts [start ] = '('
416- inserts [end ] = ')'
418+ if end in inserts :
419+ inserts [end ] += ')'
420+ else :
421+ inserts [end ] = ')'
417422 for i in range (start + 1 , end ):
418423 inserts [i ] = '|'
419424
@@ -450,7 +455,7 @@ def _format_actions_usage(self, actions, groups):
450455 # if the Optional doesn't take a value, format is:
451456 # -s or --long
452457 if action .nargs == 0 :
453- part = '%s' % option_string
458+ part = action . format_usage ()
454459
455460 # if the Optional takes a value, format is:
456461 # -s ARGS or --long ARGS
@@ -586,7 +591,11 @@ def _format_args(self, action, default_metavar):
586591 elif action .nargs == OPTIONAL :
587592 result = '[%s]' % get_metavar (1 )
588593 elif action .nargs == ZERO_OR_MORE :
589- result = '[%s [%s ...]]' % get_metavar (2 )
594+ metavar = get_metavar (1 )
595+ if len (metavar ) == 2 :
596+ result = '[%s [%s ...]]' % metavar
597+ else :
598+ result = '[%s ...]' % metavar
590599 elif action .nargs == ONE_OR_MORE :
591600 result = '%s [%s ...]' % get_metavar (2 )
592601 elif action .nargs == REMAINDER :
@@ -596,7 +605,10 @@ def _format_args(self, action, default_metavar):
596605 elif action .nargs == SUPPRESS :
597606 result = ''
598607 else :
599- formats = ['%s' for _ in range (action .nargs )]
608+ try :
609+ formats = ['%s' for _ in range (action .nargs )]
610+ except TypeError :
611+ raise ValueError ("invalid nargs value" ) from None
600612 result = ' ' .join (formats ) % get_metavar (action .nargs )
601613 return result
602614
@@ -835,9 +847,52 @@ def _get_kwargs(self):
835847 ]
836848 return [(name , getattr (self , name )) for name in names ]
837849
850+ def format_usage (self ):
851+ return self .option_strings [0 ]
852+
838853 def __call__ (self , parser , namespace , values , option_string = None ):
839854 raise NotImplementedError (_ ('.__call__() not defined' ))
840855
856+ class BooleanOptionalAction (Action ):
857+ def __init__ (self ,
858+ option_strings ,
859+ dest ,
860+ default = None ,
861+ type = None ,
862+ choices = None ,
863+ required = False ,
864+ help = None ,
865+ metavar = None ):
866+
867+ _option_strings = []
868+ for option_string in option_strings :
869+ _option_strings .append (option_string )
870+
871+ if option_string .startswith ('--' ):
872+ option_string = '--no-' + option_string [2 :]
873+ _option_strings .append (option_string )
874+
875+ if help is not None and default is not None :
876+ help += f" (default: { default } )"
877+
878+ super ().__init__ (
879+ option_strings = _option_strings ,
880+ dest = dest ,
881+ nargs = 0 ,
882+ default = default ,
883+ type = type ,
884+ choices = choices ,
885+ required = required ,
886+ help = help ,
887+ metavar = metavar )
888+
889+ def __call__ (self , parser , namespace , values , option_string = None ):
890+ if option_string in self .option_strings :
891+ setattr (namespace , self .dest , not option_string .startswith ('--no-' ))
892+
893+ def format_usage (self ):
894+ return ' | ' .join (self .option_strings )
895+
841896
842897class _StoreAction (Action ):
843898
@@ -853,7 +908,7 @@ def __init__(self,
853908 help = None ,
854909 metavar = None ):
855910 if nargs == 0 :
856- raise ValueError ('nargs for store actions must be > 0; if you '
911+ raise ValueError ('nargs for store actions must be != 0; if you '
857912 'have nothing to store, actions such as store '
858913 'true or store const may be more appropriate' )
859914 if const is not None and nargs != OPTIONAL :
@@ -945,7 +1000,7 @@ def __init__(self,
9451000 help = None ,
9461001 metavar = None ):
9471002 if nargs == 0 :
948- raise ValueError ('nargs for append actions must be > 0; if arg '
1003+ raise ValueError ('nargs for append actions must be != 0; if arg '
9491004 'strings are not supplying the value to append, '
9501005 'the append const action may be more appropriate' )
9511006 if const is not None and nargs != OPTIONAL :
@@ -1157,6 +1212,12 @@ def __call__(self, parser, namespace, values, option_string=None):
11571212 vars (namespace ).setdefault (_UNRECOGNIZED_ARGS_ATTR , [])
11581213 getattr (namespace , _UNRECOGNIZED_ARGS_ATTR ).extend (arg_strings )
11591214
1215+ class _ExtendAction (_AppendAction ):
1216+ def __call__ (self , parser , namespace , values , option_string = None ):
1217+ items = getattr (namespace , self .dest , None )
1218+ items = _copy_items (items )
1219+ items .extend (values )
1220+ setattr (namespace , self .dest , items )
11601221
11611222# ==============
11621223# Type classes
@@ -1201,8 +1262,9 @@ def __call__(self, string):
12011262 return open (string , self ._mode , self ._bufsize , self ._encoding ,
12021263 self ._errors )
12031264 except OSError as e :
1204- message = _ ("can't open '%s': %s" )
1205- raise ArgumentTypeError (message % (string , e ))
1265+ args = {'filename' : string , 'error' : e }
1266+ message = _ ("can't open '%(filename)s': %(error)s" )
1267+ raise ArgumentTypeError (message % args )
12061268
12071269 def __repr__ (self ):
12081270 args = self ._mode , self ._bufsize
@@ -1265,6 +1327,7 @@ def __init__(self,
12651327 self .register ('action' , 'help' , _HelpAction )
12661328 self .register ('action' , 'version' , _VersionAction )
12671329 self .register ('action' , 'parsers' , _SubParsersAction )
1330+ self .register ('action' , 'extend' , _ExtendAction )
12681331
12691332 # raise an exception if the conflict handler is invalid
12701333 self ._get_handler ()
@@ -1357,6 +1420,10 @@ def add_argument(self, *args, **kwargs):
13571420 if not callable (type_func ):
13581421 raise ValueError ('%r is not callable' % (type_func ,))
13591422
1423+ if type_func is FileType :
1424+ raise ValueError ('%r is a FileType class object, instance of it'
1425+ ' must be passed' % (type_func ,))
1426+
13601427 # raise an error if the metavar does not match the type
13611428 if hasattr (self , "_get_formatter" ):
13621429 try :
@@ -1471,10 +1538,8 @@ def _get_optional_kwargs(self, *args, **kwargs):
14711538
14721539 # strings starting with two prefix characters are long options
14731540 option_strings .append (option_string )
1474- if option_string [0 ] in self .prefix_chars :
1475- if len (option_string ) > 1 :
1476- if option_string [1 ] in self .prefix_chars :
1477- long_option_strings .append (option_string )
1541+ if len (option_string ) > 1 and option_string [1 ] in self .prefix_chars :
1542+ long_option_strings .append (option_string )
14781543
14791544 # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
14801545 dest = kwargs .pop ('dest' , None )
@@ -1614,6 +1679,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
16141679 - conflict_handler -- String indicating how to handle conflicts
16151680 - add_help -- Add a -h/-help option
16161681 - allow_abbrev -- Allow long options to be abbreviated unambiguously
1682+ - exit_on_error -- Determines whether or not ArgumentParser exits with
1683+ error info when an error occurs
16171684 """
16181685
16191686 def __init__ (self ,
@@ -1628,19 +1695,14 @@ def __init__(self,
16281695 argument_default = None ,
16291696 conflict_handler = 'error' ,
16301697 add_help = True ,
1631- allow_abbrev = True ):
1632- _ActionsContainer .__init__ (self ,
1633- description = description ,
1634- prefix_chars = prefix_chars ,
1635- argument_default = argument_default ,
1636- conflict_handler = conflict_handler )
1637- # FIXME: get multiple inheritance method resolution right so we can use
1638- # what's below instead of the modified version above
1639- # superinit = super(ArgumentParser, self).__init__
1640- # superinit(description=description,
1641- # prefix_chars=prefix_chars,
1642- # argument_default=argument_default,
1643- # conflict_handler=conflict_handler)
1698+ allow_abbrev = True ,
1699+ exit_on_error = True ):
1700+
1701+ superinit = super (ArgumentParser , self ).__init__
1702+ superinit (description = description ,
1703+ prefix_chars = prefix_chars ,
1704+ argument_default = argument_default ,
1705+ conflict_handler = conflict_handler )
16441706
16451707 # default setting for prog
16461708 if prog is None :
@@ -1653,6 +1715,7 @@ def __init__(self,
16531715 self .fromfile_prefix_chars = fromfile_prefix_chars
16541716 self .add_help = add_help
16551717 self .allow_abbrev = allow_abbrev
1718+ self .exit_on_error = exit_on_error
16561719
16571720 add_group = self .add_argument_group
16581721 self ._positionals = add_group (_ ('positional arguments' ))
@@ -1783,15 +1846,19 @@ def parse_known_args(self, args=None, namespace=None):
17831846 setattr (namespace , dest , self ._defaults [dest ])
17841847
17851848 # parse the arguments and exit if there are any errors
1786- try :
1849+ if self .exit_on_error :
1850+ try :
1851+ namespace , args = self ._parse_known_args (args , namespace )
1852+ except ArgumentError :
1853+ err = _sys .exc_info ()[1 ]
1854+ self .error (str (err ))
1855+ else :
17871856 namespace , args = self ._parse_known_args (args , namespace )
1788- if hasattr (namespace , _UNRECOGNIZED_ARGS_ATTR ):
1789- args .extend (getattr (namespace , _UNRECOGNIZED_ARGS_ATTR ))
1790- delattr (namespace , _UNRECOGNIZED_ARGS_ATTR )
1791- return namespace , args
1792- except ArgumentError :
1793- err = _sys .exc_info ()[1 ]
1794- self .error (str (err ))
1857+
1858+ if hasattr (namespace , _UNRECOGNIZED_ARGS_ATTR ):
1859+ args .extend (getattr (namespace , _UNRECOGNIZED_ARGS_ATTR ))
1860+ delattr (namespace , _UNRECOGNIZED_ARGS_ATTR )
1861+ return namespace , args
17951862
17961863 def _parse_known_args (self , arg_strings , namespace ):
17971864 # replace arg strings that are file references
@@ -2080,10 +2147,11 @@ def _match_argument(self, action, arg_strings_pattern):
20802147 OPTIONAL : _ ('expected at most one argument' ),
20812148 ONE_OR_MORE : _ ('expected at least one argument' ),
20822149 }
2083- default = ngettext ('expected %s argument' ,
2150+ msg = nargs_errors .get (action .nargs )
2151+ if msg is None :
2152+ msg = ngettext ('expected %s argument' ,
20842153 'expected %s arguments' ,
20852154 action .nargs ) % action .nargs
2086- msg = nargs_errors .get (action .nargs , default )
20872155 raise ArgumentError (action , msg )
20882156
20892157 # return the number of arguments matched
@@ -2130,24 +2198,23 @@ def _parse_optional(self, arg_string):
21302198 action = self ._option_string_actions [option_string ]
21312199 return action , option_string , explicit_arg
21322200
2133- if self .allow_abbrev :
2134- # search through all possible prefixes of the option string
2135- # and all actions in the parser for possible interpretations
2136- option_tuples = self ._get_option_tuples (arg_string )
2137-
2138- # if multiple actions match, the option string was ambiguous
2139- if len (option_tuples ) > 1 :
2140- options = ', ' .join ([option_string
2141- for action , option_string , explicit_arg in option_tuples ])
2142- args = {'option' : arg_string , 'matches' : options }
2143- msg = _ ('ambiguous option: %(option)s could match %(matches)s' )
2144- self .error (msg % args )
2145-
2146- # if exactly one action matched, this segmentation is good,
2147- # so return the parsed action
2148- elif len (option_tuples ) == 1 :
2149- option_tuple , = option_tuples
2150- return option_tuple
2201+ # search through all possible prefixes of the option string
2202+ # and all actions in the parser for possible interpretations
2203+ option_tuples = self ._get_option_tuples (arg_string )
2204+
2205+ # if multiple actions match, the option string was ambiguous
2206+ if len (option_tuples ) > 1 :
2207+ options = ', ' .join ([option_string
2208+ for action , option_string , explicit_arg in option_tuples ])
2209+ args = {'option' : arg_string , 'matches' : options }
2210+ msg = _ ('ambiguous option: %(option)s could match %(matches)s' )
2211+ self .error (msg % args )
2212+
2213+ # if exactly one action matched, this segmentation is good,
2214+ # so return the parsed action
2215+ elif len (option_tuples ) == 1 :
2216+ option_tuple , = option_tuples
2217+ return option_tuple
21512218
21522219 # if it was not found as an option, but it looks like a negative
21532220 # number, it was meant to be positional
@@ -2171,16 +2238,17 @@ def _get_option_tuples(self, option_string):
21712238 # split at the '='
21722239 chars = self .prefix_chars
21732240 if option_string [0 ] in chars and option_string [1 ] in chars :
2174- if '=' in option_string :
2175- option_prefix , explicit_arg = option_string .split ('=' , 1 )
2176- else :
2177- option_prefix = option_string
2178- explicit_arg = None
2179- for option_string in self ._option_string_actions :
2180- if option_string .startswith (option_prefix ):
2181- action = self ._option_string_actions [option_string ]
2182- tup = action , option_string , explicit_arg
2183- result .append (tup )
2241+ if self .allow_abbrev :
2242+ if '=' in option_string :
2243+ option_prefix , explicit_arg = option_string .split ('=' , 1 )
2244+ else :
2245+ option_prefix = option_string
2246+ explicit_arg = None
2247+ for option_string in self ._option_string_actions :
2248+ if option_string .startswith (option_prefix ):
2249+ action = self ._option_string_actions [option_string ]
2250+ tup = action , option_string , explicit_arg
2251+ result .append (tup )
21842252
21852253 # single character options can be concatenated with their arguments
21862254 # but multiple character options always have to have their argument
0 commit comments