Import latest options.py from bup.
This commit is contained in:
parent
20951f9e6e
commit
75c3a0a46b
2 changed files with 86 additions and 16 deletions
100
options.py
100
options.py
|
|
@ -1,9 +1,72 @@
|
||||||
|
# Copyright 2011 Avery Pennarun and options.py contributors.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# (This license applies to this file but not necessarily the other files in
|
||||||
|
# this package.)
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in
|
||||||
|
# the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY AVERY PENNARUN ``AS IS'' AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||||
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
"""Command-line options parser.
|
"""Command-line options parser.
|
||||||
With the help of an options spec string, easily parse command-line options.
|
With the help of an options spec string, easily parse command-line options.
|
||||||
|
|
||||||
|
An options spec is made up of two parts, separated by a line with two dashes.
|
||||||
|
The first part is the synopsis of the command and the second one specifies
|
||||||
|
options, one per line.
|
||||||
|
|
||||||
|
Each non-empty line in the synopsis gives a set of options that can be used
|
||||||
|
together.
|
||||||
|
|
||||||
|
Option flags must be at the begining of the line and multiple flags are
|
||||||
|
separated by commas. Usually, options have a short, one character flag, and a
|
||||||
|
longer one, but the short one can be omitted.
|
||||||
|
|
||||||
|
Long option flags are used as the option's key for the OptDict produced when
|
||||||
|
parsing options.
|
||||||
|
|
||||||
|
When the flag definition is ended with an equal sign, the option takes one
|
||||||
|
string as an argument. Otherwise, the option does not take an argument and
|
||||||
|
corresponds to a boolean flag that is true when the option is given on the
|
||||||
|
command line.
|
||||||
|
|
||||||
|
The option's description is found at the right of its flags definition, after
|
||||||
|
one or more spaces. The description ends at the end of the line. If the
|
||||||
|
description contains text enclosed in square brackets, the enclosed text will
|
||||||
|
be used as the option's default value.
|
||||||
|
|
||||||
|
Options can be put in different groups. Options in the same group must be on
|
||||||
|
consecutive lines. Groups are formed by inserting a line that begins with a
|
||||||
|
space. The text on that line will be output after an empty line.
|
||||||
"""
|
"""
|
||||||
import sys, os, textwrap, getopt, re, struct
|
import sys, os, textwrap, getopt, re, struct
|
||||||
|
|
||||||
class OptDict:
|
class OptDict:
|
||||||
|
"""Dictionary that exposes keys as attributes.
|
||||||
|
|
||||||
|
Keys can bet set or accessed with a "no-" or "no_" prefix to negate the
|
||||||
|
value.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._opts = {}
|
self._opts = {}
|
||||||
|
|
||||||
|
|
@ -65,10 +128,9 @@ def _tty_width():
|
||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
"""Option parser.
|
"""Option parser.
|
||||||
When constructed, two strings are mandatory. The first one is the command
|
When constructed, a string called an option spec must be given. It
|
||||||
name showed before error messages. The second one is a string called an
|
specifies the synopsis and option flags and their description. For more
|
||||||
optspec that specifies the synopsis and option flags and their description.
|
information about option specs, see the docstring at the top of this file.
|
||||||
For more information about optspecs, consult the bup-options(1) man page.
|
|
||||||
|
|
||||||
Two optional arguments specify an alternative parsing function and an
|
Two optional arguments specify an alternative parsing function and an
|
||||||
alternative behaviour on abort (after having output the usage string).
|
alternative behaviour on abort (after having output the usage string).
|
||||||
|
|
@ -76,15 +138,14 @@ class Options:
|
||||||
By default, the parser function is getopt.gnu_getopt, and the abort
|
By default, the parser function is getopt.gnu_getopt, and the abort
|
||||||
behaviour is to exit the program.
|
behaviour is to exit the program.
|
||||||
"""
|
"""
|
||||||
def __init__(self, exe, optspec, optfunc=getopt.gnu_getopt,
|
def __init__(self, optspec, optfunc=getopt.gnu_getopt,
|
||||||
onabort=_default_onabort):
|
onabort=_default_onabort):
|
||||||
self.exe = exe
|
|
||||||
self.optspec = optspec
|
self.optspec = optspec
|
||||||
self._onabort = onabort
|
self._onabort = onabort
|
||||||
self.optfunc = optfunc
|
self.optfunc = optfunc
|
||||||
self._aliases = {}
|
self._aliases = {}
|
||||||
self._shortopts = 'h?'
|
self._shortopts = 'h?'
|
||||||
self._longopts = ['help']
|
self._longopts = ['help', 'usage']
|
||||||
self._hasparms = {}
|
self._hasparms = {}
|
||||||
self._defaults = {}
|
self._defaults = {}
|
||||||
self._usagestr = self._gen_usage()
|
self._usagestr = self._gen_usage()
|
||||||
|
|
@ -122,12 +183,15 @@ class Options:
|
||||||
defval = None
|
defval = None
|
||||||
flagl = flags.split(',')
|
flagl = flags.split(',')
|
||||||
flagl_nice = []
|
flagl_nice = []
|
||||||
for f in flagl:
|
for _f in flagl:
|
||||||
f,dvi = _remove_negative_kv(f, _intify(defval))
|
f,dvi = _remove_negative_kv(_f, _intify(defval))
|
||||||
self._aliases[f] = _remove_negative_k(flagl[0])
|
self._aliases[f] = _remove_negative_k(flagl[0])
|
||||||
self._hasparms[f] = has_parm
|
self._hasparms[f] = has_parm
|
||||||
self._defaults[f] = dvi
|
self._defaults[f] = dvi
|
||||||
if len(f) == 1:
|
if f == '#':
|
||||||
|
self._shortopts += '0123456789'
|
||||||
|
flagl_nice.append('-#')
|
||||||
|
elif len(f) == 1:
|
||||||
self._shortopts += f + (has_parm and ':' or '')
|
self._shortopts += f + (has_parm and ':' or '')
|
||||||
flagl_nice.append('-' + f)
|
flagl_nice.append('-' + f)
|
||||||
else:
|
else:
|
||||||
|
|
@ -135,7 +199,7 @@ class Options:
|
||||||
self._aliases[f_nice] = _remove_negative_k(flagl[0])
|
self._aliases[f_nice] = _remove_negative_k(flagl[0])
|
||||||
self._longopts.append(f + (has_parm and '=' or ''))
|
self._longopts.append(f + (has_parm and '=' or ''))
|
||||||
self._longopts.append('no-' + f)
|
self._longopts.append('no-' + f)
|
||||||
flagl_nice.append('--' + f)
|
flagl_nice.append('--' + _f)
|
||||||
flags_nice = ', '.join(flagl_nice)
|
flags_nice = ', '.join(flagl_nice)
|
||||||
if has_parm:
|
if has_parm:
|
||||||
flags_nice += ' ...'
|
flags_nice += ' ...'
|
||||||
|
|
@ -153,14 +217,15 @@ class Options:
|
||||||
def usage(self, msg=""):
|
def usage(self, msg=""):
|
||||||
"""Print usage string to stderr and abort."""
|
"""Print usage string to stderr and abort."""
|
||||||
sys.stderr.write(self._usagestr)
|
sys.stderr.write(self._usagestr)
|
||||||
|
if msg:
|
||||||
|
sys.stderr.write(msg)
|
||||||
e = self._onabort and self._onabort(msg) or None
|
e = self._onabort and self._onabort(msg) or None
|
||||||
if e:
|
if e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def fatal(self, s):
|
def fatal(self, msg):
|
||||||
"""Print an error message to stderr and abort with usage string."""
|
"""Print an error message to stderr and abort with usage string."""
|
||||||
msg = 'error: %s\n' % s
|
msg = '\nerror: %s\n' % msg
|
||||||
sys.stderr.write(msg)
|
|
||||||
return self.usage(msg)
|
return self.usage(msg)
|
||||||
|
|
||||||
def parse(self, args):
|
def parse(self, args):
|
||||||
|
|
@ -183,11 +248,16 @@ class Options:
|
||||||
|
|
||||||
for (k,v) in flags:
|
for (k,v) in flags:
|
||||||
k = k.lstrip('-')
|
k = k.lstrip('-')
|
||||||
if k in ('h', '?', 'help'):
|
if k in ('h', '?', 'help', 'usage'):
|
||||||
self.usage()
|
self.usage()
|
||||||
if k.startswith('no-'):
|
if k.startswith('no-'):
|
||||||
k = self._aliases[k[3:]]
|
k = self._aliases[k[3:]]
|
||||||
v = 0
|
v = 0
|
||||||
|
elif (self._aliases.get('#') and
|
||||||
|
k in ('0','1','2','3','4','5','6','7','8','9')):
|
||||||
|
v = int(k) # guaranteed to be exactly one digit
|
||||||
|
k = self._aliases['#']
|
||||||
|
opt['#'] = v
|
||||||
else:
|
else:
|
||||||
k = self._aliases[k]
|
k = self._aliases[k]
|
||||||
if not self._hasparms[k]:
|
if not self._hasparms[k]:
|
||||||
|
|
|
||||||
2
redo.py
2
redo.py
|
|
@ -16,7 +16,7 @@ debug-locks print messages about file locking (useful for debugging)
|
||||||
debug-pids print process ids as part of log messages (useful for debugging)
|
debug-pids print process ids as part of log messages (useful for debugging)
|
||||||
version print the current version and exit
|
version print the current version and exit
|
||||||
"""
|
"""
|
||||||
o = options.Options('redo', optspec)
|
o = options.Options(optspec)
|
||||||
(opt, flags, extra) = o.parse(sys.argv[1:])
|
(opt, flags, extra) = o.parse(sys.argv[1:])
|
||||||
|
|
||||||
targets = extra
|
targets = extra
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue