gh-126374: Add support of options with optional arguments in the getopt module (GH-126375)

This commit is contained in:
Serhiy Storchaka 2024-11-11 18:29:28 +02:00 committed by GitHub
parent 79805d2284
commit 25aee21aa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 25 deletions

View file

@ -27,7 +27,6 @@ option involved with the exception.
# - allow the caller to specify ordering
# - RETURN_IN_ORDER option
# - GNU extension with '-' as first character of option string
# - optional arguments, specified by double colons
# - an option string with a W followed by semicolon should
# treat "-W foo" as "--foo"
@ -58,12 +57,14 @@ def getopt(args, shortopts, longopts = []):
running program. Typically, this means "sys.argv[1:]". shortopts
is the string of option letters that the script wants to
recognize, with options that require an argument followed by a
colon (i.e., the same format that Unix getopt() uses). If
colon and options that accept an optional argument followed by
two colons (i.e., the same format that Unix getopt() uses). If
specified, longopts is a list of strings with the names of the
long options which should be supported. The leading '--'
characters should not be included in the option name. Options
which require an argument should be followed by an equal sign
('=').
('='). Options which acept an optional argument should be
followed by an equal sign and question mark ('=?').
The return value consists of two elements: the first is a list of
(option, value) pairs; the second is the list of program arguments
@ -153,7 +154,7 @@ def do_longs(opts, opt, longopts, args):
has_arg, opt = long_has_args(opt, longopts)
if has_arg:
if optarg is None:
if optarg is None and has_arg != '?':
if not args:
raise GetoptError(_('option --%s requires argument') % opt, opt)
optarg, args = args[0], args[1:]
@ -174,6 +175,8 @@ def long_has_args(opt, longopts):
return False, opt
elif opt + '=' in possibilities:
return True, opt
elif opt + '=?' in possibilities:
return '?', opt
# No exact match, so better be unique.
if len(possibilities) > 1:
# XXX since possibilities contains all valid continuations, might be
@ -181,6 +184,8 @@ def long_has_args(opt, longopts):
raise GetoptError(_('option --%s not a unique prefix') % opt, opt)
assert len(possibilities) == 1
unique_match = possibilities[0]
if unique_match.endswith('=?'):
return '?', unique_match[:-2]
has_arg = unique_match.endswith('=')
if has_arg:
unique_match = unique_match[:-1]
@ -189,8 +194,9 @@ def long_has_args(opt, longopts):
def do_shorts(opts, optstring, shortopts, args):
while optstring != '':
opt, optstring = optstring[0], optstring[1:]
if short_has_arg(opt, shortopts):
if optstring == '':
has_arg = short_has_arg(opt, shortopts)
if has_arg:
if optstring == '' and has_arg != '?':
if not args:
raise GetoptError(_('option -%s requires argument') % opt,
opt)
@ -204,7 +210,11 @@ def do_shorts(opts, optstring, shortopts, args):
def short_has_arg(opt, shortopts):
for i in range(len(shortopts)):
if opt == shortopts[i] != ':':
return shortopts.startswith(':', i+1)
if not shortopts.startswith(':', i+1):
return False
if shortopts.startswith('::', i+1):
return '?'
return True
raise GetoptError(_('option -%s not recognized') % opt, opt)
if __name__ == '__main__':