1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """
17 svnmailer stream objects
18 """
19 __author__ = "André Malo"
20 __docformat__ = "epytext en"
21 __all__ = [
22 'UnicodeStream', 'TruncatingStream', 'CuckooStream', 'SplittingStream',
23 'DevNullStream', 'BinaryOrUnicodeStream', 'CountStream'
24 ]
25
26
28 """ Base stream wrapper
29
30 @ivar stream: The wrapped stream
31 @type stream: file like object
32 """
33
35 """ Initialization
36
37 @param stream: The stream to wrap
38 @type stream: file like object
39 """
40 self.stream = stream
41
42
43 - def write(self, towrite):
44 """ Writes the data to the stream
45
46 @param towrite: stuff to write
47 @type towrite: C{str}
48 """
49 self.stream.write(towrite)
50
51
53 r"""Write a list of strings
54
55 @param lines: The lines to write (including \n)
56 @type lines: C{list}
57 """
58 for line in lines:
59 self.write(line)
60
61
65
66
68 """ Delegates all undefined attributes to the stream """
69 return getattr(self.stream, name)
70
71
73 """ Stream wrapper, which accepts unicode and a specified charset
74
75 @ivar decode: Decoder function for the input encoding
76 @type decode: callable
77
78 @ivar err: error handling advise
79 @type err: C{str}
80 """
81
82 - def __init__(self, stream, in_enc = 'utf-8', out_enc = 'utf-8',
83 errors = "replace"):
84 """ Initialization
85
86 @param stream: The stream to wrap
87 @type stream: file like object
88
89 @param in_enc: The input encoding, that should be assumed, if a
90 pure string is written
91 @type in_enc: C{str}
92
93 @param out_enc: The output encoding
94 @type out_enc: C{str}
95
96 @param errors: The error handling indicator, when an unicode error
97 occurs. (The default is quite lenient and writes replace
98 characters on errors)
99 @type errors: C{str}
100 """
101 import codecs
102
103 writer = codecs.lookup(out_enc)[3]
104 super(UnicodeStream, self).__init__(writer(stream, errors))
105
106 self.decode = codecs.lookup(in_enc)[1]
107 self.err = errors
108
109
110 - def write(self, towrite):
111 """ Write a string or unicode """
112 if isinstance(towrite, str):
113 super(UnicodeStream, self).write(self.decode(towrite, self.err)[0])
114 else:
115 super(UnicodeStream, self).write(towrite)
116
117
119 """ Stream wrapper, which accepts unicode or binary data
120
121 Unicode data is converted to UTF-8
122 """
123
124 - def write(self, towrite):
125 """ Write a string or unicode """
126 if isinstance(towrite, unicode):
127 towrite = towrite.encode('utf-8')
128
129 super(BinaryOrUnicodeStream, self).write(towrite)
130
131
133 """ stream wrapper, which truncates after a limit
134
135 @ivar maxsize: The maximum size in bytes
136 @type maxsize: C{int}
137
138 @ivar current: The number of bytes received
139 @type current: C{int}
140
141 @ivar trunced: The number of lines truncated (maybe actual-1)
142 @type trunced: C{int}
143
144 @ivar lastchar: The last character written
145 @type lastchar: C{str}
146 """
147
148 - def __init__(self, stream, maxsize, add_note = False):
149 """ Initialization
150
151 @param stream: The stream to wrap
152 @type stream: file like object
153
154 @param maxsize: The maximum size in bytes
155 @type maxsize: C{int}
156 """
157 super(TruncatingStream, self).__init__(stream)
158
159 self.maxsize = maxsize
160 self.current = 0
161 self.trunced = 0
162 self.lastchar = "\n"
163 self.add_note = add_note
164
165
166 - def write(self, towrite):
167 """ Writes a string up to the limit """
168 if self.current <= self.maxsize:
169 written = 0
170 for line in towrite.splitlines(True):
171 self.current += len(line)
172 if self.current <= self.maxsize:
173 super(TruncatingStream, self).write(line)
174 written += len(line)
175 else:
176 towrite = towrite[written:]
177 break
178
179 if self.current > self.maxsize:
180 self.trunced += towrite.count('\n')
181 self.lastchar = towrite[-1:]
182
183
185 """ Returns the number of truncated lines
186
187 @return: The line count
188 @rtype: C{int}
189 """
190 return self.trunced + (self.lastchar != "\n")
191
192
194 """ Writes without truncation
195
196 @param towrite: The data to write
197 @type towrite: C{str}
198 """
199 super(TruncatingStream, self).write(towrite)
200
201
202 - def seek(self, position, mode = 0):
203 """ Sets the file position """
204 if mode != 0 or position != 0:
205 raise NotImplementedError()
206
207 self.current = 0
208 self.trunced = 0
209 self.lastchar = "\n"
210 self.stream.seek(position, mode)
211
212
214 """ Returns the content """
215 cont = self.stream.getvalue()
216 if self.add_note:
217 num = self.getTruncatedLineCount()
218 if num:
219 cont = "%s\n[... %d lines stripped ...]\n" % (
220 cont, num
221 )
222
223 return cont
224
225
227 """ Truncating stream, which writes the truncating note on close """
228
238
239
241 """ Stream wrapper, which provides a method to replace the stream """
242
244 """ Replaces the stream with another
245
246 @param stream: The new stream
247 @type stream: file like object
248 """
249 self.stream.close()
250 self.stream = stream
251
252
254 """ Stream wrapper, which provides the ability to split the stream
255
256 @ivar current: The current byte counter
257 @type current: C{int}
258 """
259
261 """ Initialization
262
263 @param tempdir: specific temporary directory
264 @type tempdir: C{str}
265 """
266 import cStringIO
267 stream = cStringIO.StringIO()
268
269 super(SplittingStream, self).__init__(stream)
270
271 self.current = 0
272 self.tempfiles = []
273 self.tempdir = tempdir
274
275
276 - def write(self, towrite):
277 """ Writes to the current stream and counts the number of bytes """
278 self.current += len(towrite)
279 super(SplittingStream, self).write(towrite)
280
281
283 """ Splits the stream
284
285 This dumps the current content into a tempfile clears
286 the old stream.
287 """
288 if not self.current:
289 return
290
291 from svnmailer import util
292
293 tmpfile = util.TempFile(self.tempdir)
294 tmpfile.fp.write(self.getvalue())
295 tmpfile.close()
296 self.tempfiles.append(tmpfile)
297
298 self.current = 0
299 self.seek(0)
300 self.truncate()
301
302
304 """ Closes the stream and removes all tempfiles """
305 self.tempfiles = []
306 super(SplittingStream, self).close()
307
308
310 """ Returns the number of splitted parts
311
312 @return: The number
313 @rtype: C{int}
314 """
315 return len(self.tempfiles)
316
317
319 """ Returns the value of part C{idx}
320
321 @param idx: The part number
322 @type idx: C{int}
323
324 @return: The content of the particular part
325 @rtype: C{str}
326 """
327 try:
328 tmpfile = self.tempfiles[idx]
329 except IndexError:
330 return ''
331
332 return file(tmpfile.name, 'rb').read()
333
334
336 """ Dummy stream, which throws away all data """
337
339 """ Initialization """
340 import cStringIO
341 super(DevNullStream, self).__init__(cStringIO.StringIO())
342
343
344 - def write(self, towrite):
345 """ throw away stuff """
346 pass
347
348
350 """ throw away stuff """
351 pass
352
353
355 """ Dummy stream, which throws away all data """
356
358 """ Initialization """
359 self.size = 0
360 import cStringIO
361 super(CountStream, self).__init__(cStringIO.StringIO())
362
363
364 - def write(self, towrite):
365 """ throw away stuff and count the number of octets """
366 self.size += len(towrite)
367
368
370 """ write lines """
371 for line in lines:
372 self.write(line)
373