1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
47 """File-like object used by SigFile, DeltaFile, and PatchFile"""
48 mode = "rb"
49
50
51
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
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
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
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
111 """Close infile"""
112 if not self.infile_closed:
113 assert not self.infile.close()
114 self.closed = 1
115
116
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
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
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
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
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
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
211 """Return signature over given data"""
212 while not self.process_buffer():
213 pass
214 return ''.join(self.sigstring_list)
215