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