1010from typing import Any
1111from typing import final
1212from typing import Literal
13- from typing import NoReturn
1413
1514from .exceptions import UsageError
1615import _pytest ._io
@@ -42,7 +41,14 @@ def __init__(
4241
4342 self ._processopt = processopt
4443 self .extra_info : dict [str , Any ] = {}
45- self .optparser = PytestArgumentParser (usage , self .extra_info )
44+ self .optparser = argparse .ArgumentParser (
45+ usage = usage ,
46+ add_help = False ,
47+ exit_on_error = False ,
48+ formatter_class = DropShorterLongHelpFormatter ,
49+ allow_abbrev = False ,
50+ fromfile_prefix_chars = "@" ,
51+ )
4652 anonymous_arggroup = self .optparser .add_argument_group ("Custom options" )
4753 self ._anonymous = OptionGroup (
4854 anonymous_arggroup , "_anonymous" , self , _ispytest = True
@@ -117,6 +123,14 @@ def addoption(self, *opts: str, **attrs: Any) -> None:
117123 """
118124 self ._anonymous .addoption (* opts , ** attrs )
119125
126+ def _format_usage_error (self , e : argparse .ArgumentError ) -> str :
127+ msg = f"{ self .prog } : error: { e } "
128+ if self .extra_info :
129+ msg += "\n " + "\n " .join (
130+ f" { k } : { v } " for k , v in sorted (self .extra_info .items ())
131+ )
132+ return self .optparser .format_usage () + msg
133+
120134 def parse (
121135 self ,
122136 args : Sequence [str | os .PathLike [str ]],
@@ -135,9 +149,11 @@ def parse(
135149 strargs = [os .fspath (x ) for x in args ]
136150 if namespace is None :
137151 namespace = argparse .Namespace ()
152+ namespace ._raise_print_help = True
138153 try :
139- namespace ._raise_print_help = True
140154 return self .optparser .parse_intermixed_args (strargs , namespace = namespace )
155+ except argparse .ArgumentError as e :
156+ raise UsageError (self ._format_usage_error (e )) from None
141157 finally :
142158 del namespace ._raise_print_help
143159
@@ -165,17 +181,20 @@ def parse_known_and_unknown_args(
165181 arguments, and a list of unknown flag arguments.
166182 """
167183 strargs = [os .fspath (x ) for x in args ]
168- if sys .version_info < (3 , 12 ):
169- # Older argparse have a bugged parse_known_intermixed_args.
170- namespace , unknown = self .optparser .parse_known_args (strargs , namespace )
171- assert namespace is not None
172- file_or_dir = getattr (namespace , FILE_OR_DIR )
173- unknown_flags : list [str ] = []
174- for arg in unknown :
175- (unknown_flags if arg .startswith ("-" ) else file_or_dir ).append (arg )
176- return namespace , unknown_flags
177- else :
178- return self .optparser .parse_known_intermixed_args (strargs , namespace )
184+ try :
185+ if sys .version_info < (3 , 12 ):
186+ # Older argparse have a bugged parse_known_intermixed_args.
187+ namespace , unknown = self .optparser .parse_known_args (strargs , namespace )
188+ assert namespace is not None
189+ file_or_dir = getattr (namespace , FILE_OR_DIR )
190+ unknown_flags : list [str ] = []
191+ for arg in unknown :
192+ (unknown_flags if arg .startswith ("-" ) else file_or_dir ).append (arg )
193+ return namespace , unknown_flags
194+ else :
195+ return self .optparser .parse_known_intermixed_args (strargs , namespace )
196+ except argparse .ArgumentError as e :
197+ raise UsageError (self ._format_usage_error (e )) from None
179198
180199 def addini (
181200 self ,
@@ -375,33 +394,6 @@ def _addoption_inner(
375394 self .parser .processoption (option )
376395
377396
378- class PytestArgumentParser (argparse .ArgumentParser ):
379- def __init__ (
380- self ,
381- usage : str | None ,
382- extra_info : dict [str , str ],
383- ) -> None :
384- super ().__init__ (
385- usage = usage ,
386- add_help = False ,
387- formatter_class = DropShorterLongHelpFormatter ,
388- allow_abbrev = False ,
389- fromfile_prefix_chars = "@" ,
390- )
391- # extra_info is a dict of (param -> value) to display if there's
392- # an usage error to provide more contextual information to the user.
393- self .extra_info = extra_info
394-
395- def error (self , message : str ) -> NoReturn :
396- """Transform argparse error message into UsageError."""
397- msg = f"{ self .prog } : error: { message } "
398- if self .extra_info :
399- msg += "\n " + "\n " .join (
400- f" { k } : { v } " for k , v in sorted (self .extra_info .items ())
401- )
402- raise UsageError (self .format_usage () + msg )
403-
404-
405397class DropShorterLongHelpFormatter (argparse .HelpFormatter ):
406398 """Shorten help for long options that differ only in extra hyphens.
407399
0 commit comments