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