pyfec: more progress indicators, handling of already-existent outfile
authorZooko O'Whielacronx zooko@zooko.com <zooko@zooko.com>
Sat, 14 Apr 2007 23:00:59 +0000 (04:30 +0530)
committerZooko O'Whielacronx zooko@zooko.com <zooko@zooko.com>
Sat, 14 Apr 2007 23:00:59 +0000 (04:30 +0530)
darcs-hash:40abdd9f4629d7ffe462a000e7aebd990e8b04a2

pyfec/bin/fec
pyfec/bin/unfec
pyfec/fec/filefec.py

index 4b29862fa93ac7128fa952f85c297c15f4908d92..a70cff4b6322bfd2360172b47bfed5fce971a2ef 100755 (executable)
@@ -19,11 +19,11 @@ if '-V' in sys.argv or '--version' in sys.argv:
 parser = argparse.ArgumentParser(description="Encode a file into a set of share files, a subset of which can later be used to recover the original file.")
 
 parser.add_argument('inputfile', help='file to encode or "-" for stdin', type=argparse.FileType('rb'), metavar='INF')
-parser.add_argument('-d', '--output-dir', help='directory in which share file names will be created', default='.', metavar='D')
+parser.add_argument('-d', '--output-dir', help='directory in which share file names will be created (default ".")', default='.', metavar='D')
 parser.add_argument('-p', '--prefix', help='prefix for share file names; If omitted, the name of the input file will be used.', metavar='P')
-parser.add_argument('-s', '--suffix', help='suffix for share file names', default='.fec', metavar='S')
-parser.add_argument('-m', '--totalshares', help='the total number of share files created', default=16, metavar='M')
-parser.add_argument('-k', '--requiredshares', help='the number of share files required to reconstruct', default=4, metavar='K')
+parser.add_argument('-s', '--suffix', help='suffix for share file names (default ".fec")', default='.fec', metavar='S')
+parser.add_argument('-m', '--totalshares', help='the total number of share files created (default 16)', default=16, metavar='M')
+parser.add_argument('-k', '--requiredshares', help='the number of share files required to reconstruct (default 4)', default=4, metavar='K')
 parser.add_argument('-v', '--verbose', help='print out messages about progress', action='store_true')
 parser.add_argument('-V', '--version', help='print out version number and exit', action='store_true')
 args = parser.parse_args()
index 8ef5c1fb2a346dc8f2caf05e3e2ab1abcaaba739..9f212377f9b687836f0b29fd1e334669ef5a54f9 100755 (executable)
@@ -3,7 +3,7 @@
 # import bindann
 # import bindann.monkeypatch.all
 
-import sys
+import os, sys
 
 from fec.util import argparse
 
@@ -19,9 +19,10 @@ if '-V' in sys.argv or '--version' in sys.argv:
   
 parser = argparse.ArgumentParser(description="Decode data from share files.")
 
-parser.add_argument('outputfile', help='file to write the resulting data to, or "-" for stdout', type=argparse.FileType('wb'), metavar='OUTF')
-parser.add_argument('sharefiles', nargs='+', help='shares file to read the encoded data from', type=argparse.FileType('rb'), metavar='SHAREFILE')
+parser.add_argument('-o', '--outputfile', required=True, help='file to write the resulting data to, or "-" for stdout', type=str, metavar='OUTF')
+parser.add_argument('sharefiles', nargs='*', help='shares file to read the encoded data from', type=argparse.FileType('rb'), metavar='SHAREFILE')
 parser.add_argument('-v', '--verbose', help='print out messages about progress', action='store_true')
+parser.add_argument('-f', '--force', help='overwrite any file which already in place of the output file', action='store_true')
 parser.add_argument('-V', '--version', help='print out version number and exit', action='store_true')
 args = parser.parse_args()
 
@@ -29,6 +30,16 @@ if len(args.sharefiles) < 2:
     print "At least two sharefiles are required."
     sys.exit(1)
 
-ret = filefec.decode_from_files(args.outputfile, args.sharefiles, args.verbose)
+if args.force:
+    outf = open(args.outputfile, 'wb')
+else:
+    try:
+        outfd = os.open(args.outputfile, os.O_WRONLY|os.O_CREAT|os.O_EXCL)
+    except OSError:
+        print "There is already a file named %r -- aborting.  Use --force to overwrite." % (args.outputfile,)
+        sys.exit(2)
+    outf = os.fdopen(outfd, "wb")
+
+ret = filefec.decode_from_files(outf, args.sharefiles, args.verbose)
 
 sys.exit(ret)
index f86abce8ba557610638cb0401767f08734add465..e83ab2d80a869dedded37896dc37e2005b716706 100644 (file)
@@ -188,10 +188,13 @@ def encode_to_files(inf, fsize, dirname, prefix, k, m, suffix=".fec", verbose=Fa
             fns.append(fn)
         sumlen = [0]
         def cb(blocks, length):
-            if verbose:
-                print "Writing %d bytes into share files..." % (length,)
             assert len(blocks) == len(fs)
+            oldsumlen = sumlen[0]
             sumlen[0] += length
+            if verbose:
+                if int((float(oldsumlen) / fsize) * 10) != int((float(sumlen[0]) / fsize) * 10):
+                    print str(int((float(sumlen[0]) / fsize) * 10) * 10) + "% ...",
+            
             if sumlen[0] > fsize:
                 raise IOError("Wrong file size -- possibly the size of the file changed during encoding.  Original size: %d, observed size at least: %s" % (fsize, sumlen[0],))
             for i in range(len(blocks)):
@@ -214,9 +217,16 @@ def encode_to_files(inf, fsize, dirname, prefix, k, m, suffix=".fec", verbose=Fa
             fileutil.remove_if_possible(fn)
         return 1
     if verbose:
+        print 
         print "Done!"
     return 0
 
+# Note: if you really prefer base-2 and you change this code, then please
+# denote 2^20 as "MiB" instead of "MB" in order to avoid ambiguity.
+# Thanks.
+# http://en.wikipedia.org/wiki/Megabyte
+MILLION_BYTES=10**6
+
 def decode_from_files(outf, infiles, verbose=False):
     """
     Decode from the first k files in infiles, writing the results to outf.
@@ -228,6 +238,7 @@ def decode_from_files(outf, infiles, verbose=False):
     k = None
     padlen = None
 
+    byteswritten = 0
     for f in infiles:
         (nm, nk, npadlen, shnum,) = _parse_header(f)
         if not (m is None or m == nm):
@@ -257,11 +268,18 @@ def decode_from_files(outf, infiles, verbose=False):
             # Then this was a full read, so we're still in the sharefiles.
             resultdata = dec.decode(chunks, shnums, padlen=0)
             outf.write(resultdata)
+            byteswritten += len(resultdata)
+            if verbose:
+                if ((byteswritten - len(resultdata)) / (10*MILLION_BYTES)) != (byteswritten / (10*MILLION_BYTES)):
+                    print str(byteswritten / MILLION_BYTES) + " MB ...",
         else:
             # Then this was a short read, so we've reached the end of the sharefiles.
             resultdata = dec.decode(chunks, shnums, padlen)
             outf.write(resultdata)
             return # Done.
+    if verbose:
+        print
+        print "Done!"
 
 def encode_file(inf, cb, k, m, chunksize=4096):
     """