@@ -345,21 +345,22 @@ def _format_usage(self, usage, actions, groups, prefix):
345
345
def get_lines (parts , indent , prefix = None ):
346
346
lines = []
347
347
line = []
348
+ indent_length = len (indent )
348
349
if prefix is not None :
349
350
line_len = len (prefix ) - 1
350
351
else :
351
- line_len = len ( indent ) - 1
352
+ line_len = indent_length - 1
352
353
for part in parts :
353
354
if line_len + 1 + len (part ) > text_width and line :
354
355
lines .append (indent + ' ' .join (line ))
355
356
line = []
356
- line_len = len ( indent ) - 1
357
+ line_len = indent_length - 1
357
358
line .append (part )
358
359
line_len += len (part ) + 1
359
360
if line :
360
361
lines .append (indent + ' ' .join (line ))
361
362
if prefix is not None :
362
- lines [0 ] = lines [0 ][len ( indent ) :]
363
+ lines [0 ] = lines [0 ][indent_length :]
363
364
return lines
364
365
365
366
# if prog is short, follow it with optionals or positionals
@@ -403,10 +404,18 @@ def _format_actions_usage(self, actions, groups):
403
404
except ValueError :
404
405
continue
405
406
else :
406
- end = start + len (group ._group_actions )
407
+ group_action_count = len (group ._group_actions )
408
+ end = start + group_action_count
407
409
if actions [start :end ] == group ._group_actions :
410
+
411
+ suppressed_actions_count = 0
408
412
for action in group ._group_actions :
409
413
group_actions .add (action )
414
+ if action .help is SUPPRESS :
415
+ suppressed_actions_count += 1
416
+
417
+ exposed_actions_count = group_action_count - suppressed_actions_count
418
+
410
419
if not group .required :
411
420
if start in inserts :
412
421
inserts [start ] += ' ['
@@ -416,7 +425,7 @@ def _format_actions_usage(self, actions, groups):
416
425
inserts [end ] += ']'
417
426
else :
418
427
inserts [end ] = ']'
419
- else :
428
+ elif exposed_actions_count > 1 :
420
429
if start in inserts :
421
430
inserts [start ] += ' ('
422
431
else :
@@ -490,7 +499,6 @@ def _format_actions_usage(self, actions, groups):
490
499
text = _re .sub (r'(%s) ' % open , r'\1' , text )
491
500
text = _re .sub (r' (%s)' % close , r'\1' , text )
492
501
text = _re .sub (r'%s *%s' % (open , close ), r'' , text )
493
- text = _re .sub (r'\(([^|]*)\)' , r'\1' , text )
494
502
text = text .strip ()
495
503
496
504
# return the text
@@ -875,16 +883,19 @@ def __call__(self, parser, namespace, values, option_string=None):
875
883
raise NotImplementedError (_ ('.__call__() not defined' ))
876
884
877
885
886
+ # FIXME: remove together with `BooleanOptionalAction` deprecated arguments.
887
+ _deprecated_default = object ()
888
+
878
889
class BooleanOptionalAction (Action ):
879
890
def __init__ (self ,
880
891
option_strings ,
881
892
dest ,
882
893
default = None ,
883
- type = None ,
884
- choices = None ,
894
+ type = _deprecated_default ,
895
+ choices = _deprecated_default ,
885
896
required = False ,
886
897
help = None ,
887
- metavar = None ):
898
+ metavar = _deprecated_default ):
888
899
889
900
_option_strings = []
890
901
for option_string in option_strings :
@@ -894,6 +905,24 @@ def __init__(self,
894
905
option_string = '--no-' + option_string [2 :]
895
906
_option_strings .append (option_string )
896
907
908
+ # We need `_deprecated` special value to ban explicit arguments that
909
+ # match default value. Like:
910
+ # parser.add_argument('-f', action=BooleanOptionalAction, type=int)
911
+ for field_name in ('type' , 'choices' , 'metavar' ):
912
+ if locals ()[field_name ] is not _deprecated_default :
913
+ warnings ._deprecated (
914
+ field_name ,
915
+ "{name!r} is deprecated as of Python 3.12 and will be "
916
+ "removed in Python {remove}." ,
917
+ remove = (3 , 14 ))
918
+
919
+ if type is _deprecated_default :
920
+ type = None
921
+ if choices is _deprecated_default :
922
+ choices = None
923
+ if metavar is _deprecated_default :
924
+ metavar = None
925
+
897
926
super ().__init__ (
898
927
option_strings = _option_strings ,
899
928
dest = dest ,
@@ -2165,7 +2194,9 @@ def _read_args_from_files(self, arg_strings):
2165
2194
# replace arguments referencing files with the file content
2166
2195
else :
2167
2196
try :
2168
- with open (arg_string [1 :]) as args_file :
2197
+ with open (arg_string [1 :],
2198
+ encoding = _sys .getfilesystemencoding (),
2199
+ errors = _sys .getfilesystemencodeerrors ()) as args_file :
2169
2200
arg_strings = []
2170
2201
for arg_line in args_file .read ().splitlines ():
2171
2202
for arg in self .convert_arg_line_to_args (arg_line ):
@@ -2479,9 +2510,11 @@ def _get_values(self, action, arg_strings):
2479
2510
not action .option_strings ):
2480
2511
if action .default is not None :
2481
2512
value = action .default
2513
+ self ._check_value (action , value )
2482
2514
else :
2515
+ # since arg_strings is always [] at this point
2516
+ # there is no need to use self._check_value(action, value)
2483
2517
value = arg_strings
2484
- self ._check_value (action , value )
2485
2518
2486
2519
# single argument or optional argument produces a single value
2487
2520
elif len (arg_strings ) == 1 and action .nargs in [None , OPTIONAL ]:
@@ -2523,7 +2556,6 @@ def _get_value(self, action, arg_string):
2523
2556
2524
2557
# ArgumentTypeErrors indicate errors
2525
2558
except ArgumentTypeError as err :
2526
- name = getattr (action .type , '__name__' , repr (action .type ))
2527
2559
msg = str (err )
2528
2560
raise ArgumentError (action , msg )
2529
2561
@@ -2595,9 +2627,11 @@ def print_help(self, file=None):
2595
2627
2596
2628
def _print_message (self , message , file = None ):
2597
2629
if message :
2598
- if file is None :
2599
- file = _sys .stderr
2600
- file .write (message )
2630
+ file = file or _sys .stderr
2631
+ try :
2632
+ file .write (message )
2633
+ except (AttributeError , OSError ):
2634
+ pass
2601
2635
2602
2636
# ===============
2603
2637
# Exiting methods
0 commit comments