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

Source Code for Module duplicity.librsync

  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 
  9  # under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your 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  """Provides a high-level interface to some librsync functions 
 23   
 24  This is a python wrapper around the lower-level _librsync module, 
 25  which is written in C.  The goal was to use C as little as possible... 
 26   
 27  """ 
 28   
 29  import _librsync 
 30  import types, array 
 31   
 32  blocksize = _librsync.RS_JOB_BLOCKSIZE 
 33   
34 -class librsyncError(Exception):
35 """Signifies error in internal librsync processing (bad signature, etc.) 36 37 underlying _librsync.librsyncError's are regenerated using this 38 class because the C-created exceptions are by default 39 unPickleable. There is probably a way to fix this in _librsync, 40 but this scheme was easier. 41 42 """ 43 pass
44 45
46 -class LikeFile:
47 """File-like object used by SigFile, DeltaFile, and PatchFile""" 48 mode = "rb" 49 50 # This will be replaced in subclasses by an object with 51 # appropriate cycle() method 52 maker = None 53
54 - def __init__(self, infile, need_seek = None):
55 """LikeFile initializer - zero buffers, set eofs off""" 56 self.check_file(infile, need_seek) 57 self.infile = infile 58 self.closed = self.infile_closed = None 59 self.inbuf = "" 60 self.outbuf = array.array('c') 61 self.eof = self.infile_eof = None
62
63 - def check_file(self, file, need_seek = None):
64 """Raise type error if file doesn't have necessary attributes""" 65 if not hasattr(file, "read"): 66 raise TypeError("Basis file must have a read() method") 67 if not hasattr(file, "close"): 68 raise TypeError("Basis file must have a close() method") 69 if need_seek and not hasattr(file, "seek"): 70 raise TypeError("Basis file must have a seek() method")
71
72 - def read(self, length = -1):
73 """Build up self.outbuf, return first length bytes""" 74 if length == -1: 75 while not self.eof: 76 self._add_to_outbuf_once() 77 real_len = len(self.outbuf) 78 else: 79 while not self.eof and len(self.outbuf) < length: 80 self._add_to_outbuf_once() 81 real_len = min(length, len(self.outbuf)) 82 83 return_val = self.outbuf[:real_len].tostring() 84 del self.outbuf[:real_len] 85 return return_val
86
87 - def _add_to_outbuf_once(self):
88 """Add one cycle's worth of output to self.outbuf""" 89 if not self.infile_eof: 90 self._add_to_inbuf() 91 try: 92 self.eof, len_inbuf_read, cycle_out = self.maker.cycle(self.inbuf) 93 except _librsync.librsyncError, e: 94 raise librsyncError(str(e)) 95 self.inbuf = self.inbuf[len_inbuf_read:] 96 self.outbuf.fromstring(cycle_out)
97
98 - def _add_to_inbuf(self):
99 """Make sure len(self.inbuf) >= blocksize""" 100 assert not self.infile_eof 101 while len(self.inbuf) < blocksize: 102 new_in = self.infile.read(blocksize) 103 if not new_in: 104 self.infile_eof = 1 105 assert not self.infile.close() 106 self.infile_closed = 1 107 break 108 self.inbuf += new_in
109
110 - def close(self):
111 """Close infile""" 112 if not self.infile_closed: 113 assert not self.infile.close() 114 self.closed = 1
115 116
117 -class SigFile(LikeFile):
118 """File-like object which incrementally generates a librsync signature"""
119 - def __init__(self, infile, blocksize = _librsync.RS_DEFAULT_BLOCK_LEN):
120 """SigFile initializer - takes basis file 121 122 basis file only needs to have read() and close() methods. It 123 will be closed when we come to the end of the signature. 124 125 """ 126 LikeFile.__init__(self, infile) 127 try: 128 self.maker = _librsync.new_sigmaker(blocksize) 129 except _librsync.librsyncError, e: 130 raise librsyncError(str(e))
131
132 -class DeltaFile(LikeFile):
133 """File-like object which incrementally generates a librsync delta"""
134 - def __init__(self, signature, new_file):
135 """DeltaFile initializer - call with signature and new file 136 137 Signature can either be a string or a file with read() and 138 close() methods. New_file also only needs to have read() and 139 close() methods. It will be closed when self is closed. 140 141 """ 142 LikeFile.__init__(self, new_file) 143 if type(signature) is types.StringType: 144 sig_string = signature 145 else: 146 self.check_file(signature) 147 sig_string = signature.read() 148 assert not signature.close() 149 try: 150 self.maker = _librsync.new_deltamaker(sig_string) 151 except _librsync.librsyncError, e: 152 raise librsyncError(str(e))
153 154
155 -class PatchedFile(LikeFile):
156 """File-like object which applies a librsync delta incrementally"""
157 - def __init__(self, basis_file, delta_file):
158 """PatchedFile initializer - call with basis delta 159 160 Here basis_file must be a true Python file, because we may 161 need to seek() around in it a lot, and this is done in C. 162 delta_file only needs read() and close() methods. 163 164 """ 165 LikeFile.__init__(self, delta_file) 166 if type(basis_file) is not types.FileType: 167 raise TypeError("basis_file must be a (true) file") 168 try: 169 self.maker = _librsync.new_patchmaker(basis_file) 170 except _librsync.librsyncError, e: 171 raise librsyncError(str(e))
172 173
174 -class SigGenerator:
175 """Calculate signature. 176 177 Input and output is same as SigFile, but the interface is like md5 178 module, not filelike object 179 180 """
181 - def __init__(self, blocksize = _librsync.RS_DEFAULT_BLOCK_LEN):
182 """Return new signature instance""" 183 try: 184 self.sig_maker = _librsync.new_sigmaker(blocksize) 185 except _librsync.librsyncError, e: 186 raise librsyncError(str(e)) 187 self.gotsig = None 188 self.buffer = "" 189 self.sigstring_list = []
190
191 - def update(self, buf):
192 """Add buf to data that signature will be calculated over""" 193 if self.gotsig: 194 raise librsyncError("SigGenerator already provided signature") 195 self.buffer += buf 196 while len(self.buffer) >= blocksize: 197 if self.process_buffer(): 198 raise librsyncError("Premature EOF received from sig_maker")
199
200 - def process_buffer(self):
201 """Run self.buffer through sig_maker, add to self.sig_string""" 202 try: 203 eof, len_buf_read, cycle_out = self.sig_maker.cycle(self.buffer) 204 except _librsync.librsyncError, e: 205 raise librsyncError(str(e)) 206 self.buffer = self.buffer[len_buf_read:] 207 self.sigstring_list.append(cycle_out) 208 return eof
209
210 - def getsig(self):
211 """Return signature over given data""" 212 while not self.process_buffer(): 213 pass # keep running until eof 214 return ''.join(self.sigstring_list)
215