1 """Command-line options parser.
2 With the help of an options spec string, easily parse command-line options.
4 An options spec is made up of two parts, separated by a line with two dashes.
5 The first part is the synopsis of the command and the second one specifies
8 Each non-empty line in the synopsis gives a set of options that can be used
11 Option flags must be at the begining of the line and multiple flags are
12 separated by commas. Usually, options have a short, one character flag, and a
13 longer one, but the short one can be omitted.
15 Long option flags are used as the option's key for the OptDict produced when
18 When the flag definition is ended with an equal sign, the option takes one
19 string as an argument. Otherwise, the option does not take an argument and
20 corresponds to a boolean flag that is true when the option is given on the
23 The option's description is found at the right of its flags definition, after
24 one or more spaces. The description ends at the end of the line. If the
25 description contains text enclosed in square brackets, the enclosed text will
26 be used as the option's default value.
28 Options can be put in different groups. Options in the same group must be on
29 consecutive lines. Groups are formed by inserting a line that begins with a
30 space. The text on that line will be output after an empty line.
32 import sys, os, textwrap, getopt, re, struct
35 """Dictionary that exposes keys as attributes.
37 Keys can bet set or accessed with a "no-" or "no_" prefix to negate the
43 def __setitem__(self, k, v):
44 if k.startswith('no-') or k.startswith('no_'):
49 def __getitem__(self, k):
50 if k.startswith('no-') or k.startswith('no_'):
51 return not self._opts[k[3:]]
54 def __getattr__(self, k):
58 def _default_onabort(msg):
79 def _remove_negative_kv(k, v):
80 if k.startswith('no-') or k.startswith('no_'):
84 def _remove_negative_k(k):
85 return _remove_negative_kv(k, None)[0]
89 s = struct.pack("HHHH", 0, 0, 0, 0)
92 s = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, s)
93 except (IOError, ImportError):
94 return _atoi(os.environ.get('WIDTH')) or 70
95 (ysize,xsize,ypix,xpix) = struct.unpack('HHHH', s)
101 When constructed, a string called an option spec must be given. It
102 specifies the synopsis and option flags and their description. For more
103 information about option specs, see the docstring at the top of this file.
105 Two optional arguments specify an alternative parsing function and an
106 alternative behaviour on abort (after having output the usage string).
108 By default, the parser function is getopt.gnu_getopt, and the abort
109 behaviour is to exit the program.
111 def __init__(self, optspec, optfunc=getopt.gnu_getopt,
112 onabort=_default_onabort):
113 self.optspec = optspec
114 self._onabort = onabort
115 self.optfunc = optfunc
117 self._shortopts = 'h?'
118 self._longopts = ['help', 'usage']
121 self._usagestr = self._gen_usage()
123 def _gen_usage(self):
125 lines = self.optspec.strip().split('\n')
131 out.append('%s: %s\n' % (first_syn and 'usage' or ' or', l))
134 last_was_option = False
137 if l.startswith(' '):
138 out.append('%s%s\n' % (last_was_option and '\n' or '',
140 last_was_option = False
142 (flags, extra) = l.split(' ', 1)
143 extra = extra.strip()
144 if flags.endswith('='):
149 g = re.search(r'\[([^\]]*)\]$', extra)
154 flagl = flags.split(',')
157 f,dvi = _remove_negative_kv(_f, _intify(defval))
158 self._aliases[f] = _remove_negative_k(flagl[0])
159 self._hasparms[f] = has_parm
160 self._defaults[f] = dvi
162 self._shortopts += '0123456789'
163 flagl_nice.append('-#')
165 self._shortopts += f + (has_parm and ':' or '')
166 flagl_nice.append('-' + f)
168 f_nice = re.sub(r'\W', '_', f)
169 self._aliases[f_nice] = _remove_negative_k(flagl[0])
170 self._longopts.append(f + (has_parm and '=' or ''))
171 self._longopts.append('no-' + f)
172 flagl_nice.append('--' + _f)
173 flags_nice = ', '.join(flagl_nice)
176 prefix = ' %-20s ' % flags_nice
177 argtext = '\n'.join(textwrap.wrap(extra, width=_tty_width(),
178 initial_indent=prefix,
179 subsequent_indent=' '*28))
180 out.append(argtext + '\n')
181 last_was_option = True
184 last_was_option = False
185 return ''.join(out).rstrip() + '\n'
187 def usage(self, msg=""):
188 """Print usage string to stderr and abort."""
189 sys.stderr.write(self._usagestr)
191 sys.stderr.write(msg)
192 e = self._onabort and self._onabort(msg) or None
196 def fatal(self, msg):
197 """Print an error message to stderr and abort with usage string."""
198 msg = '\nerror: %s\n' % msg
199 return self.usage(msg)
201 def parse(self, args):
202 """Parse a list of arguments and return (options, flags, extra).
204 In the returned tuple, "options" is an OptDict with known options,
205 "flags" is a list of option flags that were used on the command-line,
206 and "extra" is a list of positional arguments.
209 (flags,extra) = self.optfunc(args, self._shortopts, self._longopts)
210 except getopt.GetoptError, e:
215 for k,v in self._defaults.iteritems():
221 if k in ('h', '?', 'help', 'usage'):
223 if k.startswith('no-'):
224 k = self._aliases[k[3:]]
226 elif (self._aliases.get('#') and
227 k in ('0','1','2','3','4','5','6','7','8','9')):
228 v = int(k) # guaranteed to be exactly one digit
229 k = self._aliases['#']
233 if not self._hasparms[k]:
235 v = (opt._opts.get(k) or 0) + 1
239 for (f1,f2) in self._aliases.iteritems():
240 opt[f1] = opt._opts.get(f2)
241 return (opt,flags,extra)