fancyopts.py
159 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
/ mercurial / fancyopts.py
Martin Geisler
|
r8230 | # fancyopts.py - better command line parsing | ||
# | ||||
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Martin Geisler
|
r8230 | |||
Gregory Szorc
|
r25947 | from __future__ import absolute_import | ||
from .i18n import _ | ||||
Pulkit Goyal
|
r30578 | from . import ( | ||
error, | ||||
pycompat, | ||||
) | ||||
mpm@selenic.com
|
r0 | |||
Augie Fackler
|
r29947 | # Set of flags to not apply boolean negation logic on | ||
Martin von Zweigbergk
|
r32291 | nevernegate = { | ||
Augie Fackler
|
r29947 | # avoid --no-noninteractive | ||
'noninteractive', | ||||
# These two flags are special because they cause hg to do one | ||||
# thing and then exit, and so aren't suitable for use in things | ||||
# like aliases anyway. | ||||
'help', | ||||
'version', | ||||
Martin von Zweigbergk
|
r32291 | } | ||
Augie Fackler
|
r29947 | |||
Augie Fackler
|
r7772 | def gnugetopt(args, options, longoptions): | ||
"""Parse options mostly like getopt.gnu_getopt. | ||||
This is different from getopt.gnu_getopt in that an argument of - will | ||||
become an argument of - instead of vanishing completely. | ||||
""" | ||||
extraargs = [] | ||||
if '--' in args: | ||||
stopindex = args.index('--') | ||||
Matt Mackall
|
r10282 | extraargs = args[stopindex + 1:] | ||
Augie Fackler
|
r7772 | args = args[:stopindex] | ||
Pulkit Goyal
|
r30578 | opts, parseargs = pycompat.getoptb(args, options, longoptions) | ||
Augie Fackler
|
r7772 | args = [] | ||
while parseargs: | ||||
arg = parseargs.pop(0) | ||||
Pulkit Goyal
|
r33103 | if arg and arg[0:1] == '-' and len(arg) > 1: | ||
Augie Fackler
|
r7772 | parseargs.insert(0, arg) | ||
Pulkit Goyal
|
r30578 | topts, newparseargs = pycompat.getoptb(parseargs,\ | ||
options, longoptions) | ||||
Augie Fackler
|
r7772 | opts = opts + topts | ||
parseargs = newparseargs | ||||
else: | ||||
args.append(arg) | ||||
args.extend(extraargs) | ||||
return opts, args | ||||
def fancyopts(args, options, state, gnu=False): | ||||
Matt Mackall
|
r5638 | """ | ||
read args, parse options, and store options in state | ||||
each option is a tuple of: | ||||
short option or '' | ||||
long option | ||||
default value | ||||
description | ||||
FUJIWARA Katsunori
|
r11321 | option value label(optional) | ||
Matt Mackall
|
r5638 | |||
option types include: | ||||
boolean or none - option sets variable in state to true | ||||
string - parameter string is stored in state | ||||
list - parameter string is added to a list | ||||
integer - parameter strings is stored as int | ||||
function - call function with parameter | ||||
mpm@selenic.com
|
r0 | |||
Matt Mackall
|
r5638 | non-option args are returned | ||
""" | ||||
namelist = [] | ||||
shortlist = '' | ||||
argmap = {} | ||||
defmap = {} | ||||
Augie Fackler
|
r29947 | negations = {} | ||
alllong = set(o[1] for o in options) | ||||
Matt Mackall
|
r5638 | |||
FUJIWARA Katsunori
|
r11321 | for option in options: | ||
if len(option) == 5: | ||||
short, name, default, comment, dummy = option | ||||
else: | ||||
short, name, default, comment = option | ||||
Matt Mackall
|
r5638 | # convert opts to getopt format | ||
oname = name | ||||
name = name.replace('-', '_') | ||||
argmap['-' + short] = argmap['--' + oname] = name | ||||
defmap[name] = default | ||||
# copy defaults to state | ||||
if isinstance(default, list): | ||||
state[name] = default[:] | ||||
Augie Fackler
|
r21794 | elif callable(default): | ||
Matt Mackall
|
r5638 | state[name] = None | ||
Thomas Arendsen Hein
|
r5093 | else: | ||
Matt Mackall
|
r5638 | state[name] = default | ||
mpm@selenic.com
|
r0 | |||
Matt Mackall
|
r5638 | # does it take a parameter? | ||
if not (default is None or default is True or default is False): | ||||
Matt Mackall
|
r10282 | if short: | ||
short += ':' | ||||
if oname: | ||||
oname += '=' | ||||
Augie Fackler
|
r29947 | elif oname not in nevernegate: | ||
if oname.startswith('no-'): | ||||
insert = oname[3:] | ||||
else: | ||||
insert = 'no-' + oname | ||||
# backout (as a practical example) has both --commit and | ||||
# --no-commit options, so we don't want to allow the | ||||
# negations of those flags. | ||||
if insert not in alllong: | ||||
assert ('--' + oname) not in negations | ||||
negations['--' + insert] = '--' + oname | ||||
namelist.append(insert) | ||||
Matt Mackall
|
r5638 | if short: | ||
shortlist += short | ||||
if name: | ||||
namelist.append(oname) | ||||
# parse arguments | ||||
Augie Fackler
|
r7772 | if gnu: | ||
parse = gnugetopt | ||||
else: | ||||
Pulkit Goyal
|
r30578 | parse = pycompat.getoptb | ||
Augie Fackler
|
r7772 | opts, args = parse(args, shortlist, namelist) | ||
mpm@selenic.com
|
r0 | |||
Matt Mackall
|
r5638 | # transfer result to state | ||
for opt, val in opts: | ||||
Augie Fackler
|
r29947 | boolval = True | ||
negation = negations.get(opt, False) | ||||
if negation: | ||||
opt = negation | ||||
boolval = False | ||||
Matt Mackall
|
r5638 | name = argmap[opt] | ||
introom
|
r25563 | obj = defmap[name] | ||
t = type(obj) | ||||
if callable(obj): | ||||
Matt Mackall
|
r5638 | state[name] = defmap[name](val) | ||
elif t is type(1): | ||||
Idan Kamara
|
r17712 | try: | ||
state[name] = int(val) | ||||
except ValueError: | ||||
Pierre-Yves David
|
r26587 | raise error.Abort(_('invalid value %r for option %s, ' | ||
Idan Kamara
|
r17712 | 'expected int') % (val, opt)) | ||
Matt Mackall
|
r5638 | elif t is type(''): | ||
state[name] = val | ||||
elif t is type([]): | ||||
state[name].append(val) | ||||
elif t is type(None) or t is type(False): | ||||
Augie Fackler
|
r29947 | state[name] = boolval | ||
mpm@selenic.com
|
r209 | |||
Matt Mackall
|
r5638 | # return unparsed args | ||
mpm@selenic.com
|
r0 | return args | ||