]> arthur.barton.de Git - bup.git/commitdiff
Completely revamped option parsing based on git-shell-style.
authorAvery Pennarun <apenwarr@gmail.com>
Thu, 31 Dec 2009 22:06:15 +0000 (17:06 -0500)
committerAvery Pennarun <apenwarr@gmail.com>
Thu, 31 Dec 2009 22:09:33 +0000 (17:09 -0500)
This is in options.py.  Also added some wvtests for option parsing stuff.

Makefile
cmd-split.py
options.py [new file with mode: 0644]
t/__init__.py [new file with mode: 0644]
t/toptions.py [new file with mode: 0644]

index 1ecc388bef85e459538b7cb085fe1477ee6647f7..7138e851c23cac4d5a47903c27098d49f85d3508 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,8 +8,12 @@ randomgen: randomgen.o
 
 hashsplit.so: hashsplitmodule.o
        $(CC) -shared -o $@ $<
-
-test: all
+       
+runtests: all
+       ./wvtest.py $(wildcard t/t*.py)
+       
+runtests-cmdline: all
+       @echo "Testing \"$@\" in Makefile:"
        ./bup split <testfile1 >tags1
        ./bup split <testfile2 >tags2
        diff -u tags1 tags2 || true
@@ -19,6 +23,9 @@ test: all
        ./bup join <tags2 >out2
        diff -u testfile1 out1
        diff -u testfile2 out2
+       
+test: all runtests-cmdline
+       ./wvtestrun $(MAKE) runtests
 
 %: %.o
        gcc -o $@ $< $(LDFLAGS) $(LIBS)
index 2dbfa65d68b4afb6358b45bbd60158c51a12726b..04b6d7875a2be614999a2e969c758930f1c329a4 100755 (executable)
@@ -1,7 +1,6 @@
 #!/usr/bin/env python
-import sys, os, subprocess, errno, zlib, time, getopt
-import hashsplit
-import git
+import sys, os, subprocess, errno, zlib, time
+import hashsplit, git, options
 from helpers import *
 
 BLOB_LWM = 8192*2
@@ -74,41 +73,24 @@ def hashsplit_iter(f):
         if nv != lv:
             log('%d\t' % nv)
             lv = nv
-            
-            
-def usage():
-    log('Usage: bup split [-t] <filename\n')
-    exit(97)
-    
-gen_tree = False
-
-def argparse(usage, argv, shortopts, allow_extra):
-    try:
-        (flags,extra) = getopt.getopt(argv[1:], shortopts)
-    except getopt.GetoptError, e:
-        log('%s: %s\n' % (argv[0], e))
-        usage()
-    if extra and not allow_extra:
-        log('%s: invalid argument "%s"\n' % (argv[0], extra[0]))
-        usage()
-    return flags
-
-
-flags = argparse(usage, sys.argv, 't', False)
-for (flag,parm) in flags:
-    if flag == '-t':
-        gen_tree = True
 
+optspec = """
+bup split [-t] <filename
+--
+t,tree     output a tree instead of a series of blobs
+"""
+(opt, flags, extra) = options.Options('bup split', optspec).parse(sys.argv[1:])
 
 start_time = time.time()
 shalist = []
 
+ofs = 0
 for (ofs, size, sha) in hashsplit_iter(sys.stdin):
     #log('SPLIT @ %-8d size=%-8d\n' % (ofs, size))
-    if not gen_tree:
+    if not opt.tree:
         print sha
     shalist.append(('100644', '%016x.bupchunk' % ofs, sha))
-if gen_tree:
+if opt.tree:
     print git.gen_tree(shalist)
 
 secs = time.time() - start_time
diff --git a/options.py b/options.py
new file mode 100644 (file)
index 0000000..697ac05
--- /dev/null
@@ -0,0 +1,102 @@
+import textwrap, getopt
+from helpers import *
+
+class OptDict:
+    def __init__(self):
+        self._opts = {}
+
+    def __setitem__(self, k, v):
+        self._opts[k] = v
+        
+    def __getitem__(self, k):
+        return self._opts[k]
+
+    def __getattr__(self, k):
+        return self[k]
+
+
+class Options:
+    def __init__(self, exe, optspec):
+        self.exe = exe
+        self.optspec = optspec
+        self._aliases = {}
+        self._shortopts = 'h?'
+        self._longopts = ['help']
+        self._hasparms = {}
+        self._usagestr = self._gen_usage()
+        
+    def _gen_usage(self):
+        out = []
+        lines = self.optspec.strip().split('\n')
+        lines.reverse()
+        first_syn = True
+        while lines:
+            l = lines.pop()
+            if l == '--': break
+            out.append('%s: %s\n' % (first_syn and 'usage' or '   or', l))
+            first_syn = False
+        out.append('\n')
+        while lines:
+            l = lines.pop()
+            if l.startswith(' '):
+                out.append('\n%s\n' % l.lstrip())
+            elif l:
+                (flags, extra) = l.split(' ', 1)
+                extra = extra.strip()
+                if flags.endswith('='):
+                    flags = flags[:-1]
+                    has_parm = 1
+                else:
+                    has_parm = 0
+                flagl = flags.split(',')
+                flagl_nice = []
+                for f in flagl:
+                    self._aliases[f] = flagl[0]
+                    self._hasparms[f] = has_parm
+                    if len(f) == 1:
+                        self._shortopts += f + (has_parm and ':' or '')
+                        flagl_nice.append('-' + f)
+                    else:
+                        self._longopts.append(f + (has_parm and '=' or ''))
+                        flagl_nice.append('--' + f)
+                flags_nice = ', '.join(flagl_nice)
+                if has_parm:
+                    flags_nice += ' ...'
+                prefix = '    %-20s  ' % flags_nice
+                argtext = '\n'.join(textwrap.wrap(extra, width=70,
+                                                initial_indent=prefix,
+                                                subsequent_indent=' '*28))
+                out.append(argtext + '\n')
+            else:
+                out.append('\n')
+        return ''.join(out)
+    
+    def usage(self):
+        log(self._usagestr)
+        exit(97)
+        
+    def parse(self, args):
+        try:
+            (flags,extra) = getopt.gnu_getopt(args,
+                                              self._shortopts, self._longopts)
+        except getopt.GetoptError, e:
+            log('%s: %s\n' % (self.exe, e))
+            self.usage()
+
+        opt = OptDict()
+        for f in self._aliases.values():
+            opt[f] = None
+        for (k,v) in flags:
+            while k.startswith('-'):
+                k = k[1:]
+            if k in ['h', '?', 'help']:
+                self.usage()
+            k = self._aliases[k]
+            if not self._hasparms[k]:
+                assert(v == '')
+                opt[k] = (opt._opts.get(k) or 0) + 1
+            else:
+                opt[k] = v
+        for (f1,f2) in self._aliases.items():
+            opt[f1] = opt[f2]
+        return (opt,flags,extra)
diff --git a/t/__init__.py b/t/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/t/toptions.py b/t/toptions.py
new file mode 100644 (file)
index 0000000..5390883
--- /dev/null
@@ -0,0 +1,47 @@
+import options
+from wvtest import *
+
+@wvtest
+def test_optdict():
+    d = options.OptDict()
+    WVPASS('foo')
+    d['x'] = 5
+    d['y'] = 4
+    d['z'] = 99
+    WVPASSEQ(d.x, 5)
+    WVPASSEQ(d.y, 4)
+    WVPASSEQ(d.z, 99)
+    try:
+        print d.p
+    except:
+        WVPASS("invalid args don't match")
+    else:
+        WVFAIL("exception expected")
+
+
+optspec = """
+prog <optionset> [stuff...]
+prog [-t] <boggle>
+--
+t       test
+q,quiet   quiet
+l,longoption=   long option with parameters and a really really long description that will require wrapping
+p= short option with parameters
+onlylong  long option with no short
+neveropt never called options
+"""
+
+@wvtest
+def test_options():
+    o = options.Options('exename', optspec)
+    (opt,flags,extra) = o.parse(['-tttqp', 7, '--longoption', '19',
+                                 'hanky', '--onlylong'])
+    WVPASSEQ(flags[0], ('-t', ''))
+    WVPASSEQ(flags[1], ('-t', ''))
+    WVPASSEQ(flags[2], ('-t', ''))
+    WVPASSEQ(flags[3], ('-q', ''))
+    WVPASSEQ(flags[4], ('-p', 7))
+    WVPASSEQ(flags[5], ('--longoption', '19'))
+    WVPASSEQ(extra, ['hanky'])
+    WVPASSEQ((opt.t, opt.q, opt.p, opt.l, opt.onlylong,
+              opt.neveropt), (3,1,7,'19',1,None))