1 # Copyright 2010-2012 Avery Pennarun and options.py contributors.
4 # (This license applies to this file but not necessarily the other files in
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
11 # 1. Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in
16 # the documentation and/or other materials provided with the
19 # THIS SOFTWARE IS PROVIDED BY AVERY PENNARUN ``AS IS'' AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
23 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """Command-line options parser.
32 With the help of an options spec string, easily parse command-line options.
34 An options spec is made up of two parts, separated by a line with two dashes.
35 The first part is the synopsis of the command and the second one specifies
36 options, one per line.
38 Each non-empty line in the synopsis gives a set of options that can be used
41 Option flags must be at the begining of the line and multiple flags are
42 separated by commas. Usually, options have a short, one character flag, and a
43 longer one, but the short one can be omitted.
45 Long option flags are used as the option's key for the OptDict produced when
48 When the flag definition is ended with an equal sign, the option takes one
49 string as an argument. Otherwise, the option does not take an argument and
50 corresponds to a boolean flag that is true when the option is given on the
53 The option's description is found at the right of its flags definition, after
54 one or more spaces. The description ends at the end of the line. If the
55 description contains text enclosed in square brackets, the enclosed text will
56 be used as the option's default value.
58 Options can be put in different groups. Options in the same group must be on
59 consecutive lines. Groups are formed by inserting a line that begins with a
60 space. The text on that line will be output after an empty line.
62 import sys, os, textwrap, getopt, re, struct
65 """Dictionary that exposes keys as attributes.
67 Keys can bet set or accessed with a "no-" or "no_" prefix to negate the
73 def __setitem__(self, k, v):
74 if k.startswith('no-') or k.startswith('no_'):
79 def __getitem__(self, k):
80 if k.startswith('no-') or k.startswith('no_'):
81 return not self._opts[k[3:]]
84 def __getattr__(self, k):
88 def _default_onabort(msg):
109 def _remove_negative_kv(k, v):
110 if k.startswith('no-') or k.startswith('no_'):
114 def _remove_negative_k(k):
115 return _remove_negative_kv(k, None)[0]
119 s = struct.pack("HHHH", 0, 0, 0, 0)
121 import fcntl, termios
122 s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s)
123 except (IOError, ImportError):
124 return _atoi(os.environ.get('WIDTH')) or 70
125 (ysize,xsize,ypix,xpix) = struct.unpack('HHHH', s)
131 When constructed, a string called an option spec must be given. It
132 specifies the synopsis and option flags and their description. For more
133 information about option specs, see the docstring at the top of this file.
135 Two optional arguments specify an alternative parsing function and an
136 alternative behaviour on abort (after having output the usage string).
138 By default, the parser function is getopt.gnu_getopt, and the abort
139 behaviour is to exit the program.
141 def __init__(self, optspec, optfunc=getopt.gnu_getopt,
142 onabort=_default_onabort):
143 self.optspec = optspec
144 self._onabort = onabort
145 self.optfunc = optfunc
147 self._shortopts = 'h?'
148 self._longopts = ['help', 'usage']
151 self._usagestr = self._gen_usage()
153 def _gen_usage(self):
155 lines = self.optspec.strip().split('\n')
161 out.append('%s: %s\n' % (first_syn and 'usage' or ' or', l))
164 last_was_option = False
167 if l.startswith(' '):
168 out.append('%s%s\n' % (last_was_option and '\n' or '',
170 last_was_option = False
172 (flags,extra) = (l + ' ').split(' ', 1)
173 extra = extra.strip()
174 if flags.endswith('='):
179 g = re.search(r'\[([^\]]*)\]$', extra)
184 flagl = flags.split(',')
187 f,dvi = _remove_negative_kv(_f, _intify(defval))
188 self._aliases[f] = _remove_negative_k(flagl[0])
189 self._hasparms[f] = has_parm
190 self._defaults[f] = dvi
192 self._shortopts += '0123456789'
193 flagl_nice.append('-#')
195 self._shortopts += f + (has_parm and ':' or '')
196 flagl_nice.append('-' + f)
198 f_nice = re.sub(r'\W', '_', f)
199 self._aliases[f_nice] = _remove_negative_k(flagl[0])
200 self._longopts.append(f + (has_parm and '=' or ''))
201 self._longopts.append('no-' + f)
202 flagl_nice.append('--' + _f)
203 flags_nice = ', '.join(flagl_nice)
206 prefix = ' %-20s ' % flags_nice
207 argtext = '\n'.join(textwrap.wrap(extra, width=_tty_width(),
208 initial_indent=prefix,
209 subsequent_indent=' '*28))
210 out.append(argtext + '\n')
211 last_was_option = True
214 last_was_option = False
215 return ''.join(out).rstrip() + '\n'
217 def usage(self, msg=""):
218 """Print usage string to stderr and abort."""
219 sys.stderr.write(self._usagestr)
221 sys.stderr.write(msg)
222 e = self._onabort and self._onabort(msg) or None
226 def fatal(self, msg):
227 """Print an error message to stderr and abort with usage string."""
228 msg = '\nerror: %s\n' % msg
229 return self.usage(msg)
231 def parse(self, args):
232 """Parse a list of arguments and return (options, flags, extra).
234 In the returned tuple, "options" is an OptDict with known options,
235 "flags" is a list of option flags that were used on the command-line,
236 and "extra" is a list of positional arguments.
239 (flags,extra) = self.optfunc(args, self._shortopts, self._longopts)
240 except getopt.GetoptError, e:
245 for k,v in self._defaults.iteritems():
251 if k in ('h', '?', 'help', 'usage'):
253 if k.startswith('no-'):
254 k = self._aliases[k[3:]]
256 elif (self._aliases.get('#') and
257 k in ('0','1','2','3','4','5','6','7','8','9')):
258 v = int(k) # guaranteed to be exactly one digit
259 k = self._aliases['#']
263 if not self._hasparms[k]:
265 v = (opt._opts.get(k) or 0) + 1
269 for (f1,f2) in self._aliases.iteritems():
270 opt[f1] = opt._opts.get(f2)
271 return (opt,flags,extra)