Package duplicity :: Module misc
[hide private]
[frames] | no frames]

Source Code for Module duplicity.misc

  1  # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- 
  2  # 
  3  # Copyright 2002 Ben Escoto <ben@emerose.org> 
  4  # Copyright 2007 Kenneth Loafman <kenneth@loafman.com> 
  5  # 
  6  # This file is part of duplicity. 
  7  # 
  8  # Duplicity is free software; you can redistribute it and/or modify it 
  9  # under the terms of the GNU General Public License as published by the 
 10  # Free Software Foundation; either version 2 of the License, or (at your 
 11  # option) any later version. 
 12  # 
 13  # Duplicity is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with duplicity; if not, write to the Free Software Foundation, 
 20  # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21   
 22  """Miscellaneous classes and methods""" 
 23   
 24  import os 
 25   
 26  from duplicity import log 
 27   
 28   
29 -class MiscError(Exception):
30 """Signifies a miscellaneous error...""" 31 pass
32 33
34 -class FileVolumeWriter:
35 """Split up an incoming fileobj into multiple volumes on disk 36 37 This class can also be used as an iterator. It returns the 38 filenames of the files it writes. 39 40 """ 41 volume_size = 50 * 1024 * 1024 42 blocksize = 64 * 1024
43 - def __init__(self, infp, file_prefix):
44 """FileVolumeWriter initializer 45 46 infp is a file object opened for reading. It will be closed 47 at end. file_prefix is the full path of the volumes that will 48 be written. If more than one is required, it will be appended 49 with .1, .2, etc. 50 51 """ 52 self.infp = infp 53 self.prefix = file_prefix 54 self.current_index = 1 55 self.finished = None # set to true when completely done 56 self.buffer = "" # holds data that belongs in next volume
57
58 - def get_initial_buf(self):
59 """Get first value of buffer, from self.buffer or infp""" 60 if self.buffer: 61 buf = self.buffer 62 self.buffer = "" 63 return buf 64 else: 65 return self.infp.read(self.blocksize)
66
67 - def write_volume(self, outfp):
68 """Write self.volume_size bytes from self.infp to outfp 69 70 Return None if we have reached end of infp without reaching 71 volume size, and false otherwise. 72 73 """ 74 bytes_written, buf = 0, self.get_initial_buf() 75 while len(buf) + bytes_written <= self.volume_size: 76 if not buf: 77 # reached end of input 78 outfp.close() 79 return None 80 if len(buf) + bytes_written > self.volume_size: 81 break 82 outfp.write(buf) 83 bytes_written += len(buf) 84 buf = self.infp.read(self.blocksize) 85 86 remainder = self.volume_size - bytes_written 87 assert remainder < len(buf) 88 outfp.write(buf[:remainder]) 89 outfp.close() 90 self.buffer = buf[remainder:] 91 return 1
92
93 - def next(self):
94 """Write next file, return filename""" 95 if self.finished: 96 raise StopIteration 97 98 filename = "%s.%d" % (self.prefix, self.current_index) 99 log.Info(_("Starting to write %s") % filename) 100 outfp = open(filename, "wb") 101 102 if not self.write_volume(outfp): 103 # end of input 104 self.finished = 1 105 if self.current_index == 1: 106 # special case first index 107 log.Notice(_("One only volume required.\n" 108 "Renaming %s to %s") % (filename, self.prefix)) 109 os.rename(filename, self.prefix) 110 return self.prefix 111 else: 112 self.current_index += 1 113 return filename
114
115 - def __iter__(self):
116 return self
117 118
119 -class BufferedFile:
120 """Buffer file open for reading, so reads will happen in fixed sizes 121 122 This is currently used to buffer a GzipFile, because that class 123 apparently doesn't respond well to arbitrary read sizes. 124 125 """
126 - def __init__(self, fileobj, blocksize = 32 * 1024):
127 self.fileobj = fileobj 128 self.buffer = "" 129 self.blocksize = blocksize
130
131 - def read(self, length = -1):
132 """Return length bytes, or all if length < 0""" 133 if length < 0: 134 while 1: 135 buf = self.fileobj.read(self.blocksize) 136 if not buf: 137 break 138 self.buffer += buf 139 real_length = len(self.buffer) 140 else: 141 while len(self.buffer) < length: 142 buf = self.fileobj.read(self.blocksize) 143 if not buf: 144 break 145 self.buffer += buf 146 real_length = min(length, len(self.buffer)) 147 result = self.buffer[:real_length] 148 self.buffer = self.buffer[real_length:] 149 return result
150
151 - def close(self):
152 self.fileobj.close()
153 154
155 -def copyfileobj(infp, outfp, byte_count = -1):
156 """Copy byte_count bytes from infp to outfp, or all if byte_count < 0 157 158 Returns the number of bytes actually written (may be less than 159 byte_count if find eof. Does not close either fileobj. 160 161 """ 162 blocksize = 64 * 1024 163 bytes_written = 0 164 if byte_count < 0: 165 while 1: 166 buf = infp.read(blocksize) 167 if not buf: 168 break 169 bytes_written += len(buf) 170 outfp.write(buf) 171 else: 172 while bytes_written + blocksize <= byte_count: 173 buf = infp.read(blocksize) 174 if not buf: 175 break 176 bytes_written += len(buf) 177 outfp.write(buf) 178 buf = infp.read(byte_count - bytes_written) 179 bytes_written += len(buf) 180 outfp.write(buf) 181 return bytes_written
182
183 -def copyfileobj_close(infp, outfp):
184 """Copy infp to outfp, closing afterwards""" 185 copyfileobj(infp, outfp) 186 if infp.close(): 187 raise MiscError("Error closing input file") 188 if outfp.close(): 189 raise MiscError("Error closing output file")
190