Package svnmailer :: Module stream
[hide private]

Source Code for Module svnmailer.stream

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright 2004-2006 André Malo or his licensors, as applicable 
  4  # 
  5  # Licensed under the Apache License, Version 2.0 (the "License"); 
  6  # you may not use this file except in compliance with the License. 
  7  # You may obtain a copy of the License at 
  8  # 
  9  #     http://www.apache.org/licenses/LICENSE-2.0 
 10  # 
 11  # Unless required by applicable law or agreed to in writing, software 
 12  # distributed under the License is distributed on an "AS IS" BASIS, 
 13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 14  # See the License for the specific language governing permissions and 
 15  # limitations under the License. 
 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   
27 -class _BaseStream(object):
28 """ Base stream wrapper 29 30 @ivar stream: The wrapped stream 31 @type stream: file like object 32 """ 33
34 - def __init__(self, stream):
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
52 - def writelines(self, lines):
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
62 - def close(self):
63 """ Closes the stream """ 64 self.stream.close()
65 66
67 - def __getattr__(self, name):
68 """ Delegates all undefined attributes to the stream """ 69 return getattr(self.stream, name)
70 71
72 -class UnicodeStream(_BaseStream):
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
118 -class BinaryOrUnicodeStream(_BaseStream):
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
132 -class TruncatingStream(_BaseStream):
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
184 - def getTruncatedLineCount(self):
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
193 - def writeWithoutTruncation(self, towrite):
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
213 - def getvalue(self):
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
226 -class TruncatingFileStream(TruncatingStream):
227 """ Truncating stream, which writes the truncating note on close """ 228
229 - def close(self):
230 """ Closes the stream """ 231 if self.add_note: 232 num = self.getTruncatedLineCount() 233 if num: 234 self.writeWithoutTruncation( 235 "\n[... %d lines stripped ...]\n" % num 236 ) 237 super(TruncatingFileStream, self).close()
238 239
240 -class CuckooStream(_BaseStream):
241 """ Stream wrapper, which provides a method to replace the stream """ 242
243 - def replaceStream(self, stream):
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
253 -class SplittingStream(_BaseStream):
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
260 - def __init__(self, tempdir = None):
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
282 - def split(self):
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 # begin fresh 298 self.current = 0 299 self.seek(0) 300 self.truncate()
301 302
303 - def close(self):
304 """ Closes the stream and removes all tempfiles """ 305 self.tempfiles = [] 306 super(SplittingStream, self).close()
307 308
309 - def getPartCount(self):
310 """ Returns the number of splitted parts 311 312 @return: The number 313 @rtype: C{int} 314 """ 315 return len(self.tempfiles)
316 317
318 - def getPart(self, idx):
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
335 -class DevNullStream(_BaseStream):
336 """ Dummy stream, which throws away all data """ 337
338 - def __init__(self):
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
349 - def writelines(self, lines):
350 """ throw away stuff """ 351 pass
352 353
354 -class CountStream(_BaseStream):
355 """ Dummy stream, which throws away all data """ 356
357 - def __init__(self):
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
369 - def writelines(self, lines):
370 """ write lines """ 371 for line in lines: 372 self.write(line)
373