##// END OF EJS Templates
merge: cut import cycle at merge -> extensions...
merge: cut import cycle at merge -> extensions This seems less bad because checking fsmonitor looks like a temporary hack, and cmdutil -> mergemod should be a valid dependency.

File last commit:

r35227:98a5aa55 default
r36027:46a54de9 default
Show More
fancyopts.py
321 lines | 9.6 KiB | text/x-python | PythonLexer
Martin Geisler
fancyopts: add copyright and license header
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
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
fancyopts: add copyright and license header
r8230
Gregory Szorc
fancyopts: use absolute_import
r25947 from __future__ import absolute_import
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 import functools
Gregory Szorc
fancyopts: use absolute_import
r25947 from .i18n import _
Pulkit Goyal
py3: make a bytes version of getopt.getopt()...
r30578 from . import (
error,
pycompat,
)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Augie Fackler
flags: allow specifying --no-boolean-flag on the command line (BC)...
r29947 # Set of flags to not apply boolean negation logic on
Martin von Zweigbergk
cleanup: use set literals...
r32291 nevernegate = {
Augie Fackler
flags: allow specifying --no-boolean-flag on the command line (BC)...
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
cleanup: use set literals...
r32291 }
Augie Fackler
flags: allow specifying --no-boolean-flag on the command line (BC)...
r29947
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 def _earlyoptarg(arg, shortlist, namelist):
"""Check if the given arg is a valid unabbreviated option
Returns (flag_str, has_embedded_value?, embedded_value, takes_value?)
>>> def opt(arg):
... return _earlyoptarg(arg, b'R:q', [b'cwd=', b'debugger'])
long form:
>>> opt(b'--cwd')
('--cwd', False, '', True)
>>> opt(b'--cwd=')
('--cwd', True, '', True)
>>> opt(b'--cwd=foo')
('--cwd', True, 'foo', True)
>>> opt(b'--debugger')
('--debugger', False, '', False)
>>> opt(b'--debugger=') # invalid but parsable
('--debugger', True, '', False)
short form:
>>> opt(b'-R')
('-R', False, '', True)
>>> opt(b'-Rfoo')
('-R', True, 'foo', True)
>>> opt(b'-q')
('-q', False, '', False)
>>> opt(b'-qfoo') # invalid but parsable
('-q', True, 'foo', False)
unknown or invalid:
>>> opt(b'--unknown')
('', False, '', False)
>>> opt(b'-u')
('', False, '', False)
>>> opt(b'-ufoo')
('', False, '', False)
>>> opt(b'--')
('', False, '', False)
>>> opt(b'-')
('', False, '', False)
>>> opt(b'-:')
('', False, '', False)
>>> opt(b'-:foo')
('', False, '', False)
"""
if arg.startswith('--'):
flag, eq, val = arg.partition('=')
if flag[2:] in namelist:
return flag, bool(eq), val, False
if flag[2:] + '=' in namelist:
return flag, bool(eq), val, True
elif arg.startswith('-') and arg != '-' and not arg.startswith('-:'):
flag, val = arg[:2], arg[2:]
i = shortlist.find(flag[1:])
if i >= 0:
return flag, bool(val), val, shortlist.startswith(':', i + 1)
return '', False, '', False
def earlygetopt(args, shortlist, namelist, gnu=False, keepsep=False):
"""Parse options like getopt, but ignores unknown options and abbreviated
forms
If gnu=False, this stops processing options as soon as a non/unknown-option
argument is encountered. Otherwise, option and non-option arguments may be
intermixed, and unknown-option arguments are taken as non-option.
If keepsep=True, '--' won't be removed from the list of arguments left.
This is useful for stripping early options from a full command arguments.
>>> def get(args, gnu=False, keepsep=False):
... return earlygetopt(args, b'R:q', [b'cwd=', b'debugger'],
... gnu=gnu, keepsep=keepsep)
default parsing rules for early options:
>>> get([b'x', b'--cwd', b'foo', b'-Rbar', b'-q', b'y'], gnu=True)
([('--cwd', 'foo'), ('-R', 'bar'), ('-q', '')], ['x', 'y'])
>>> get([b'x', b'--cwd=foo', b'y', b'-R', b'bar', b'--debugger'], gnu=True)
([('--cwd', 'foo'), ('-R', 'bar'), ('--debugger', '')], ['x', 'y'])
>>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=True)
([('--cwd', 'foo')], ['--unknown', '--debugger'])
restricted parsing rules (early options must come first):
>>> get([b'--cwd', b'foo', b'-Rbar', b'x', b'-q', b'y'], gnu=False)
([('--cwd', 'foo'), ('-R', 'bar')], ['x', '-q', 'y'])
>>> get([b'--cwd=foo', b'x', b'y', b'-R', b'bar', b'--debugger'], gnu=False)
([('--cwd', 'foo')], ['x', 'y', '-R', 'bar', '--debugger'])
>>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=False)
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 ([], ['--unknown', '--cwd=foo', '--', '--debugger'])
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179
stripping early options (without loosing '--'):
>>> get([b'x', b'-Rbar', b'--', '--debugger'], gnu=True, keepsep=True)[1]
['x', '--', '--debugger']
last argument:
>>> get([b'--cwd'])
([], ['--cwd'])
>>> get([b'--cwd=foo'])
([('--cwd', 'foo')], [])
>>> get([b'-R'])
([], ['-R'])
>>> get([b'-Rbar'])
([('-R', 'bar')], [])
>>> get([b'-q'])
([('-q', '')], [])
>>> get([b'-q', b'--'])
([('-q', '')], [])
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 '--' may be a value:
>>> get([b'-R', b'--', b'x'])
([('-R', '--')], ['x'])
>>> get([b'--cwd', b'--', b'x'])
([('--cwd', '--')], ['x'])
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 value passed to bool options:
>>> get([b'--debugger=foo', b'x'])
([], ['--debugger=foo', 'x'])
>>> get([b'-qfoo', b'x'])
([], ['-qfoo', 'x'])
short option isn't separated with '=':
>>> get([b'-R=bar'])
([('-R', '=bar')], [])
':' may be in shortlist, but shouldn't be taken as an option letter:
>>> get([b'-:', b'y'])
([], ['-:', 'y'])
'-' is a valid non-option argument:
>>> get([b'-', b'y'])
([], ['-', 'y'])
"""
parsedopts = []
parsedargs = []
pos = 0
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 while pos < len(args):
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 arg = args[pos]
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 if arg == '--':
pos += not keepsep
break
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 flag, hasval, val, takeval = _earlyoptarg(arg, shortlist, namelist)
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 if not hasval and takeval and pos + 1 >= len(args):
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 # missing last argument
break
if not flag or hasval and not takeval:
# non-option argument or -b/--bool=INVALID_VALUE
if gnu:
parsedargs.append(arg)
pos += 1
else:
break
elif hasval == takeval:
# -b/--bool or -s/--str=VALUE
parsedopts.append((flag, val))
pos += 1
else:
# -s/--str VALUE
parsedopts.append((flag, args[pos + 1]))
pos += 2
Yuya Nishihara
fancyopts: fix handling of "--" value in earlygetopt()
r35227 parsedargs.extend(args[pos:])
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 return parsedopts, parsedargs
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 def fancyopts(args, options, state, gnu=False, early=False, optaliases=None):
Matt Mackall
fancyopts: lots of cleanups
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
help: show value requirement and multiple occurrence of options...
r11321 option value label(optional)
Matt Mackall
fancyopts: lots of cleanups
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
Add back links from file revisions to changeset revisions...
r0
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 optaliases is a mapping from a canonical option name to a list of
additional long options. This exists for preserving backward compatibility
of early options. If we want to use it extensively, please consider moving
the functionality to the options table (e.g separate long options by '|'.)
Matt Mackall
fancyopts: lots of cleanups
r5638 non-option args are returned
"""
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 if optaliases is None:
optaliases = {}
Matt Mackall
fancyopts: lots of cleanups
r5638 namelist = []
shortlist = ''
argmap = {}
defmap = {}
Augie Fackler
flags: allow specifying --no-boolean-flag on the command line (BC)...
r29947 negations = {}
alllong = set(o[1] for o in options)
Matt Mackall
fancyopts: lots of cleanups
r5638
FUJIWARA Katsunori
help: show value requirement and multiple occurrence of options...
r11321 for option in options:
if len(option) == 5:
short, name, default, comment, dummy = option
else:
short, name, default, comment = option
Matt Mackall
fancyopts: lots of cleanups
r5638 # convert opts to getopt format
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 onames = [name]
onames.extend(optaliases.get(name, []))
Matt Mackall
fancyopts: lots of cleanups
r5638 name = name.replace('-', '_')
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 argmap['-' + short] = name
for n in onames:
argmap['--' + n] = name
Matt Mackall
fancyopts: lots of cleanups
r5638 defmap[name] = default
# copy defaults to state
if isinstance(default, list):
state[name] = default[:]
Augie Fackler
fancyopts: restore use of callable() since it was readded in Python 3.2
r21794 elif callable(default):
Matt Mackall
fancyopts: lots of cleanups
r5638 state[name] = None
Thomas Arendsen Hein
fancyopts: Copy list arguments in command table before modifying....
r5093 else:
Matt Mackall
fancyopts: lots of cleanups
r5638 state[name] = default
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Matt Mackall
fancyopts: lots of cleanups
r5638 # does it take a parameter?
if not (default is None or default is True or default is False):
Matt Mackall
many, many trivial check-code fixups
r10282 if short:
short += ':'
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 onames = [n + '=' for n in onames]
elif name not in nevernegate:
for n in onames:
if n.startswith('no-'):
insert = n[3:]
else:
insert = 'no-' + n
# 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 ('--' + n) not in negations
negations['--' + insert] = '--' + n
namelist.append(insert)
Matt Mackall
fancyopts: lots of cleanups
r5638 if short:
shortlist += short
if name:
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 namelist.extend(onames)
Matt Mackall
fancyopts: lots of cleanups
r5638
# parse arguments
Yuya Nishihara
fancyopts: add early-options parser compatible with getopt()...
r35179 if early:
parse = functools.partial(earlygetopt, gnu=gnu)
elif gnu:
Yuya Nishihara
fancyopts: use getopt.gnu_getopt()...
r35226 parse = pycompat.gnugetoptb
Augie Fackler
fancyopts: Parse options that occur after arguments....
r7772 else:
Pulkit Goyal
py3: make a bytes version of getopt.getopt()...
r30578 parse = pycompat.getoptb
Augie Fackler
fancyopts: Parse options that occur after arguments....
r7772 opts, args = parse(args, shortlist, namelist)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Matt Mackall
fancyopts: lots of cleanups
r5638 # transfer result to state
for opt, val in opts:
Augie Fackler
flags: allow specifying --no-boolean-flag on the command line (BC)...
r29947 boolval = True
negation = negations.get(opt, False)
if negation:
opt = negation
boolval = False
Matt Mackall
fancyopts: lots of cleanups
r5638 name = argmap[opt]
introom
fancyopts: allow all callable as default parameter value...
r25563 obj = defmap[name]
t = type(obj)
if callable(obj):
Matt Mackall
fancyopts: lots of cleanups
r5638 state[name] = defmap[name](val)
elif t is type(1):
Idan Kamara
fancyopts: don't show a traceback on invalid integer values
r17712 try:
state[name] = int(val)
except ValueError:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('invalid value %r for option %s, '
Idan Kamara
fancyopts: don't show a traceback on invalid integer values
r17712 'expected int') % (val, opt))
Matt Mackall
fancyopts: lots of cleanups
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
flags: allow specifying --no-boolean-flag on the command line (BC)...
r29947 state[name] = boolval
mpm@selenic.com
Beginning of new command parsing interface...
r209
Matt Mackall
fancyopts: lots of cleanups
r5638 # return unparsed args
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 return args