17db96d56Sopenharmony_ci"""Parser for command line options.
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciThis module helps scripts to parse the command line arguments in
47db96d56Sopenharmony_cisys.argv.  It supports the same conventions as the Unix getopt()
57db96d56Sopenharmony_cifunction (including the special meanings of arguments of the form `-'
67db96d56Sopenharmony_ciand `--').  Long options similar to those supported by GNU software
77db96d56Sopenharmony_cimay be used as well via an optional third argument.  This module
87db96d56Sopenharmony_ciprovides two functions and an exception:
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_cigetopt() -- Parse command line options
117db96d56Sopenharmony_cignu_getopt() -- Like getopt(), but allow option and non-option arguments
127db96d56Sopenharmony_cito be intermixed.
137db96d56Sopenharmony_ciGetoptError -- exception (class) raised with 'opt' attribute, which is the
147db96d56Sopenharmony_cioption involved with the exception.
157db96d56Sopenharmony_ci"""
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci# Long option support added by Lars Wirzenius <liw@iki.fi>.
187db96d56Sopenharmony_ci#
197db96d56Sopenharmony_ci# Gerrit Holl <gerrit@nl.linux.org> moved the string-based exceptions
207db96d56Sopenharmony_ci# to class-based exceptions.
217db96d56Sopenharmony_ci#
227db96d56Sopenharmony_ci# Peter Åstrand <astrand@lysator.liu.se> added gnu_getopt().
237db96d56Sopenharmony_ci#
247db96d56Sopenharmony_ci# TODO for gnu_getopt():
257db96d56Sopenharmony_ci#
267db96d56Sopenharmony_ci# - GNU getopt_long_only mechanism
277db96d56Sopenharmony_ci# - allow the caller to specify ordering
287db96d56Sopenharmony_ci# - RETURN_IN_ORDER option
297db96d56Sopenharmony_ci# - GNU extension with '-' as first character of option string
307db96d56Sopenharmony_ci# - optional arguments, specified by double colons
317db96d56Sopenharmony_ci# - an option string with a W followed by semicolon should
327db96d56Sopenharmony_ci#   treat "-W foo" as "--foo"
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci__all__ = ["GetoptError","error","getopt","gnu_getopt"]
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_ciimport os
377db96d56Sopenharmony_citry:
387db96d56Sopenharmony_ci    from gettext import gettext as _
397db96d56Sopenharmony_ciexcept ImportError:
407db96d56Sopenharmony_ci    # Bootstrapping Python: gettext's dependencies not built yet
417db96d56Sopenharmony_ci    def _(s): return s
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_ciclass GetoptError(Exception):
447db96d56Sopenharmony_ci    opt = ''
457db96d56Sopenharmony_ci    msg = ''
467db96d56Sopenharmony_ci    def __init__(self, msg, opt=''):
477db96d56Sopenharmony_ci        self.msg = msg
487db96d56Sopenharmony_ci        self.opt = opt
497db96d56Sopenharmony_ci        Exception.__init__(self, msg, opt)
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ci    def __str__(self):
527db96d56Sopenharmony_ci        return self.msg
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_cierror = GetoptError # backward compatibility
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_cidef getopt(args, shortopts, longopts = []):
577db96d56Sopenharmony_ci    """getopt(args, options[, long_options]) -> opts, args
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci    Parses command line options and parameter list.  args is the
607db96d56Sopenharmony_ci    argument list to be parsed, without the leading reference to the
617db96d56Sopenharmony_ci    running program.  Typically, this means "sys.argv[1:]".  shortopts
627db96d56Sopenharmony_ci    is the string of option letters that the script wants to
637db96d56Sopenharmony_ci    recognize, with options that require an argument followed by a
647db96d56Sopenharmony_ci    colon (i.e., the same format that Unix getopt() uses).  If
657db96d56Sopenharmony_ci    specified, longopts is a list of strings with the names of the
667db96d56Sopenharmony_ci    long options which should be supported.  The leading '--'
677db96d56Sopenharmony_ci    characters should not be included in the option name.  Options
687db96d56Sopenharmony_ci    which require an argument should be followed by an equal sign
697db96d56Sopenharmony_ci    ('=').
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_ci    The return value consists of two elements: the first is a list of
727db96d56Sopenharmony_ci    (option, value) pairs; the second is the list of program arguments
737db96d56Sopenharmony_ci    left after the option list was stripped (this is a trailing slice
747db96d56Sopenharmony_ci    of the first argument).  Each option-and-value pair returned has
757db96d56Sopenharmony_ci    the option as its first element, prefixed with a hyphen (e.g.,
767db96d56Sopenharmony_ci    '-x'), and the option argument as its second element, or an empty
777db96d56Sopenharmony_ci    string if the option has no argument.  The options occur in the
787db96d56Sopenharmony_ci    list in the same order in which they were found, thus allowing
797db96d56Sopenharmony_ci    multiple occurrences.  Long and short options may be mixed.
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    """
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ci    opts = []
847db96d56Sopenharmony_ci    if type(longopts) == type(""):
857db96d56Sopenharmony_ci        longopts = [longopts]
867db96d56Sopenharmony_ci    else:
877db96d56Sopenharmony_ci        longopts = list(longopts)
887db96d56Sopenharmony_ci    while args and args[0].startswith('-') and args[0] != '-':
897db96d56Sopenharmony_ci        if args[0] == '--':
907db96d56Sopenharmony_ci            args = args[1:]
917db96d56Sopenharmony_ci            break
927db96d56Sopenharmony_ci        if args[0].startswith('--'):
937db96d56Sopenharmony_ci            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
947db96d56Sopenharmony_ci        else:
957db96d56Sopenharmony_ci            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci    return opts, args
987db96d56Sopenharmony_ci
997db96d56Sopenharmony_cidef gnu_getopt(args, shortopts, longopts = []):
1007db96d56Sopenharmony_ci    """getopt(args, options[, long_options]) -> opts, args
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ci    This function works like getopt(), except that GNU style scanning
1037db96d56Sopenharmony_ci    mode is used by default. This means that option and non-option
1047db96d56Sopenharmony_ci    arguments may be intermixed. The getopt() function stops
1057db96d56Sopenharmony_ci    processing options as soon as a non-option argument is
1067db96d56Sopenharmony_ci    encountered.
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci    If the first character of the option string is `+', or if the
1097db96d56Sopenharmony_ci    environment variable POSIXLY_CORRECT is set, then option
1107db96d56Sopenharmony_ci    processing stops as soon as a non-option argument is encountered.
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ci    """
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ci    opts = []
1157db96d56Sopenharmony_ci    prog_args = []
1167db96d56Sopenharmony_ci    if isinstance(longopts, str):
1177db96d56Sopenharmony_ci        longopts = [longopts]
1187db96d56Sopenharmony_ci    else:
1197db96d56Sopenharmony_ci        longopts = list(longopts)
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci    # Allow options after non-option arguments?
1227db96d56Sopenharmony_ci    if shortopts.startswith('+'):
1237db96d56Sopenharmony_ci        shortopts = shortopts[1:]
1247db96d56Sopenharmony_ci        all_options_first = True
1257db96d56Sopenharmony_ci    elif os.environ.get("POSIXLY_CORRECT"):
1267db96d56Sopenharmony_ci        all_options_first = True
1277db96d56Sopenharmony_ci    else:
1287db96d56Sopenharmony_ci        all_options_first = False
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci    while args:
1317db96d56Sopenharmony_ci        if args[0] == '--':
1327db96d56Sopenharmony_ci            prog_args += args[1:]
1337db96d56Sopenharmony_ci            break
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ci        if args[0][:2] == '--':
1367db96d56Sopenharmony_ci            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
1377db96d56Sopenharmony_ci        elif args[0][:1] == '-' and args[0] != '-':
1387db96d56Sopenharmony_ci            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
1397db96d56Sopenharmony_ci        else:
1407db96d56Sopenharmony_ci            if all_options_first:
1417db96d56Sopenharmony_ci                prog_args += args
1427db96d56Sopenharmony_ci                break
1437db96d56Sopenharmony_ci            else:
1447db96d56Sopenharmony_ci                prog_args.append(args[0])
1457db96d56Sopenharmony_ci                args = args[1:]
1467db96d56Sopenharmony_ci
1477db96d56Sopenharmony_ci    return opts, prog_args
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_cidef do_longs(opts, opt, longopts, args):
1507db96d56Sopenharmony_ci    try:
1517db96d56Sopenharmony_ci        i = opt.index('=')
1527db96d56Sopenharmony_ci    except ValueError:
1537db96d56Sopenharmony_ci        optarg = None
1547db96d56Sopenharmony_ci    else:
1557db96d56Sopenharmony_ci        opt, optarg = opt[:i], opt[i+1:]
1567db96d56Sopenharmony_ci
1577db96d56Sopenharmony_ci    has_arg, opt = long_has_args(opt, longopts)
1587db96d56Sopenharmony_ci    if has_arg:
1597db96d56Sopenharmony_ci        if optarg is None:
1607db96d56Sopenharmony_ci            if not args:
1617db96d56Sopenharmony_ci                raise GetoptError(_('option --%s requires argument') % opt, opt)
1627db96d56Sopenharmony_ci            optarg, args = args[0], args[1:]
1637db96d56Sopenharmony_ci    elif optarg is not None:
1647db96d56Sopenharmony_ci        raise GetoptError(_('option --%s must not have an argument') % opt, opt)
1657db96d56Sopenharmony_ci    opts.append(('--' + opt, optarg or ''))
1667db96d56Sopenharmony_ci    return opts, args
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_ci# Return:
1697db96d56Sopenharmony_ci#   has_arg?
1707db96d56Sopenharmony_ci#   full option name
1717db96d56Sopenharmony_cidef long_has_args(opt, longopts):
1727db96d56Sopenharmony_ci    possibilities = [o for o in longopts if o.startswith(opt)]
1737db96d56Sopenharmony_ci    if not possibilities:
1747db96d56Sopenharmony_ci        raise GetoptError(_('option --%s not recognized') % opt, opt)
1757db96d56Sopenharmony_ci    # Is there an exact match?
1767db96d56Sopenharmony_ci    if opt in possibilities:
1777db96d56Sopenharmony_ci        return False, opt
1787db96d56Sopenharmony_ci    elif opt + '=' in possibilities:
1797db96d56Sopenharmony_ci        return True, opt
1807db96d56Sopenharmony_ci    # No exact match, so better be unique.
1817db96d56Sopenharmony_ci    if len(possibilities) > 1:
1827db96d56Sopenharmony_ci        # XXX since possibilities contains all valid continuations, might be
1837db96d56Sopenharmony_ci        # nice to work them into the error msg
1847db96d56Sopenharmony_ci        raise GetoptError(_('option --%s not a unique prefix') % opt, opt)
1857db96d56Sopenharmony_ci    assert len(possibilities) == 1
1867db96d56Sopenharmony_ci    unique_match = possibilities[0]
1877db96d56Sopenharmony_ci    has_arg = unique_match.endswith('=')
1887db96d56Sopenharmony_ci    if has_arg:
1897db96d56Sopenharmony_ci        unique_match = unique_match[:-1]
1907db96d56Sopenharmony_ci    return has_arg, unique_match
1917db96d56Sopenharmony_ci
1927db96d56Sopenharmony_cidef do_shorts(opts, optstring, shortopts, args):
1937db96d56Sopenharmony_ci    while optstring != '':
1947db96d56Sopenharmony_ci        opt, optstring = optstring[0], optstring[1:]
1957db96d56Sopenharmony_ci        if short_has_arg(opt, shortopts):
1967db96d56Sopenharmony_ci            if optstring == '':
1977db96d56Sopenharmony_ci                if not args:
1987db96d56Sopenharmony_ci                    raise GetoptError(_('option -%s requires argument') % opt,
1997db96d56Sopenharmony_ci                                      opt)
2007db96d56Sopenharmony_ci                optstring, args = args[0], args[1:]
2017db96d56Sopenharmony_ci            optarg, optstring = optstring, ''
2027db96d56Sopenharmony_ci        else:
2037db96d56Sopenharmony_ci            optarg = ''
2047db96d56Sopenharmony_ci        opts.append(('-' + opt, optarg))
2057db96d56Sopenharmony_ci    return opts, args
2067db96d56Sopenharmony_ci
2077db96d56Sopenharmony_cidef short_has_arg(opt, shortopts):
2087db96d56Sopenharmony_ci    for i in range(len(shortopts)):
2097db96d56Sopenharmony_ci        if opt == shortopts[i] != ':':
2107db96d56Sopenharmony_ci            return shortopts.startswith(':', i+1)
2117db96d56Sopenharmony_ci    raise GetoptError(_('option -%s not recognized') % opt, opt)
2127db96d56Sopenharmony_ci
2137db96d56Sopenharmony_ciif __name__ == '__main__':
2147db96d56Sopenharmony_ci    import sys
2157db96d56Sopenharmony_ci    print(getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]))
216