88import types as _types
99import typing as _typing
1010import argparse as _argparse
11+ import collections .abc
1112from enum import Enum as _Enum
12- from typing import (Tuple as _Tuple , Mapping as _Mapping , Optional as _Optional ,
13- Sequence as _Sequence )
13+ from typing import (Tuple as _Tuple , Mapping as _Mapping , NoReturn ,
14+ Optional as _Optional , Sequence as _Sequence )
1415from gettext import gettext as _gettext
1516from textwrap import TextWrapper as _TextWrapper
1617from collections import OrderedDict as _OrderedDict
@@ -78,7 +79,7 @@ def __init__(
7879 if key in self .colors :
7980 self .colors [key ] = value
8081
81- class _Section ( object ) :
82+ class _Section :
8283
8384 def __init__ (self , formatter , parent , heading = None ):
8485 self .formatter = formatter
@@ -561,36 +562,43 @@ def _validate_func_type(self, action, func_type, kwargs, level: int = 0) -> _Tup
561562 and isinstance (func_type , _typing ._LiteralGenericAlias )): # type: ignore
562563 func_type = Literal (func_type )
563564
565+ # Handle `Union` and `Optional` as a type (e.g. `Union[int, str]` and
566+ # `Optional[int]`)
567+ elif (hasattr (_typing , '_UnionGenericAlias' )
568+ and isinstance (func_type , _typing ._UnionGenericAlias )): # type: ignore
569+ # Optional[T] is just Union[T, NoneType]
570+ # Optional
571+ if (hasattr (_types , 'NoneType' ) and len (func_type .__args__ ) == 2
572+ and func_type .__args__ [1 ] is _types .NoneType ):
573+ action .required = False
574+ func_type = func_type .__args__ [0 ]
575+ # Union
576+ else :
577+ func_type = func_type .__args__ # type: ignore
578+
564579 # Handle `List` as a type (e.g. `List[int]`)
565580 elif (hasattr (_typing , '_GenericAlias' )
566581 and isinstance (func_type , _typing ._GenericAlias ) # type: ignore
567- and func_type .__origin__ is list ):
582+ and getattr (func_type , '__origin__' ) is list ):
583+ func_type = func_type .__args__ [0 ]
584+ if kwargs .get ('nargs' ) is None :
585+ action .nargs = '+'
586+
587+ # Handle `Sequence` as a type (e.g. `Sequence[int]`)
588+ elif (hasattr (_typing , '_GenericAlias' )
589+ and isinstance (func_type , _typing ._GenericAlias ) # type: ignore
590+ and getattr (func_type , '__origin__' ) is collections .abc .Sequence ):
568591 func_type = func_type .__args__ [0 ]
569592 if kwargs .get ('nargs' ) is None :
570593 action .nargs = '+'
571594
572595 # Handle `Required` as a type (e.g. `Required[int]`)
573596 elif (hasattr (_typing , 'Required' ) and hasattr (_typing , '_GenericAlias' )
574597 and isinstance (func_type , _typing ._GenericAlias ) # type: ignore
575- and func_type .__origin__ is _typing .Required
576- ):
598+ and getattr (func_type , '__origin__' ) is _typing .Required ):
577599 func_type = func_type .__args__ [0 ]
578600 action .required = True
579601
580- # Handle `Union` and `Optional` as a type (e.g. `Union[int, str]` and
581- # `Optional[int]`)
582- elif (hasattr (_types , 'NoneType' ) and hasattr (_typing , '_UnionGenericAlias' )
583- and isinstance (func_type , _typing ._UnionGenericAlias )): # type: ignore
584- # Optional[T] is just Union[T, NoneType]
585- # Optional
586- if (len (func_type .__args__ ) == 2
587- and func_type .__args__ [1 ] is _types .NoneType ):
588- action .required = False
589- func_type = func_type .__args__ [0 ]
590- # Union
591- else :
592- func_type = func_type .__args__ # type: ignore
593-
594602 # Handle Enum as a type
595603 elif callable (func_type ) and isinstance (func_type , type ) and issubclass (
596604 func_type , _Enum ) and action .choices is None and level == 0 :
@@ -747,16 +755,15 @@ def error(self, message):
747755 f'%(prog)s: { _gc ("r" )} error{ _gc ("lr" )} :{ _gc ("rst" )} %(message)s\n '
748756 ) % args
749757 )
758+ return NoReturn
750759
751760 def _get_formatter (self ):
752761 if hasattr (self .formatter_class , 'colors' ):
753762 return self .formatter_class (prog = self .prog , colors = self .colors )
754- else :
755- return self .formatter_class (prog = self .prog )
763+ return self .formatter_class (prog = self .prog )
756764
757765 def _get_value (self , action , arg_string ):
758- """Override _get_value to add support for types such as Union and
759- Literal."""
766+ """Override _get_value to add support for types such as Union and Literal."""
760767
761768 func_type = self ._registry_get ('type' , action .type , action .type )
762769 if not callable (func_type ) and not isinstance (func_type , tuple ):
@@ -842,13 +849,14 @@ def _read_args_from_files(self, arg_strings):
842849 for arg_string in arg_strings :
843850
844851 # for regular arguments, just add them back into the list
845- if not arg_string or arg_string [0 ] not in self .fromfile_prefix_chars :
852+ if not arg_string or arg_string [0 ] not in (self .fromfile_prefix_chars
853+ or '' ):
846854 new_arg_strings .append (arg_string )
847855
848856 # replace arguments referencing files with the file content
849857 else :
850858 try :
851- with open (arg_string [1 :]) as args_file :
859+ with open (arg_string [1 :], encoding = 'utf-8' ) as args_file :
852860 arg_strings = []
853861 for arg_line in args_file .read ().splitlines ():
854862 for arg in self .convert_arg_line_to_args (arg_line ):
@@ -1110,7 +1118,11 @@ def consume_positionals(start_index):
11101118 if action .help is not _argparse .SUPPRESS
11111119 ]
11121120 self .error (
1113- _gettext (f'one of the arguments { " " .join (names )} is required' )
1121+ _gettext (
1122+ 'one of the arguments ' +
1123+ ' ' .join (name for name in names if name is not None ) +
1124+ ' is required'
1125+ )
11141126 )
11151127
11161128 for group in self ._action_groups :
@@ -1127,7 +1139,11 @@ def consume_positionals(start_index):
11271139 if action .help is not _argparse .SUPPRESS
11281140 ]
11291141 self .error (
1130- _gettext (f'one of the arguments { " " .join (names )} is required' )
1142+ _gettext (
1143+ 'one of the arguments ' +
1144+ ' ' .join (name for name in names if name is not None ) +
1145+ ' is required'
1146+ )
11311147 )
11321148
11331149 # return the updated namespace and the extra arguments
0 commit comments