Back to index

moin  1.9.0~rc2
iterio.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 r"""
00003     werkzeug.contrib.iterio
00004     ~~~~~~~~~~~~~~~~~~~~~~~
00005 
00006     This module implements a :class:`IterIO` that converts an iterator into
00007     a stream object and the other way round.  Converting streams into
00008     iterators requires the `greenlet`_ module.
00009 
00010     To convert an iterator into a stream all you have to do is to pass it
00011     directly to the :class:`IterIO` constructor.  In this example we pass it
00012     a newly created generator::
00013 
00014         def foo():
00015             yield "something\n"
00016             yield "otherthings"
00017         stream = IterIO(foo())
00018         print stream.read()         # read the whole iterator
00019 
00020     The other way round works a bit different because we have to ensure that
00021     the code execution doesn't take place yet.  An :class:`IterIO` call with a
00022     callable as first argument does two things.  The function itself is passed
00023     an :class:`IterIO` stream it can feed.  The object returned by the
00024     :class:`IterIO` constructor on the other hand is not an stream object but
00025     an iterator::
00026 
00027         def foo(stream):
00028             stream.write("some")
00029             stream.write("thing")
00030             stream.flush()
00031             stream.write("otherthing")
00032         iterator = IterIO(foo)
00033         print iterator.next()       # prints something
00034         print iterator.next()       # prints otherthing
00035         iterator.next()             # raises StopIteration
00036 
00037     .. _greenlet: http://codespeak.net/py/dist/greenlet.html
00038 
00039     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00040     :license: BSD, see LICENSE for more details.
00041 """
00042 try:
00043     from py.magic import greenlet
00044 except:
00045     greenlet = None
00046 
00047 
00048 class IterIO(object):
00049     """Instances of this object implement an interface compatible with the
00050     standard Python :class:`file` object.  Streams are either read-only or
00051     write-only depending on how the object is created.
00052     """
00053 
00054     def __new__(cls, obj):
00055         try:
00056             iterator = iter(obj)
00057         except TypeError:
00058             return IterI(obj)
00059         return IterO(iterator)
00060 
00061     def __iter__(self):
00062         return self
00063 
00064     def tell(self):
00065         if self.closed:
00066             raise ValueError('I/O operation on closed file')
00067         return self.pos
00068 
00069     def isatty(self):
00070         if self.closed:
00071             raise ValueError('I/O operation on closed file')
00072         return False
00073 
00074     def seek(self, pos, mode=0):
00075         if self.closed:
00076             raise ValueError('I/O operation on closed file')
00077         raise IOError(9, 'Bad file descriptor')
00078 
00079     def truncate(self, size=None):
00080         if self.closed:
00081             raise ValueError('I/O operation on closed file')
00082         raise IOError(9, 'Bad file descriptor')
00083 
00084     def write(self, s):
00085         if self.closed:
00086             raise ValueError('I/O operation on closed file')
00087         raise IOError(9, 'Bad file descriptor')
00088 
00089     def writelines(self, list):
00090         if self.closed:
00091             raise ValueError('I/O operation on closed file')
00092         raise IOError(9, 'Bad file descriptor')
00093 
00094     def read(self, n=-1):
00095         if self.closed:
00096             raise ValueError('I/O operation on closed file')
00097         raise IOError(9, 'Bad file descriptor')
00098 
00099     def readlines(self, sizehint=0):
00100         if self.closed:
00101             raise ValueError('I/O operation on closed file')
00102         raise IOError(9, 'Bad file descriptor')
00103 
00104     def readline(self, length=None):
00105         if self.closed:
00106             raise ValueError('I/O operation on closed file')
00107         raise IOError(9, 'Bad file descriptor')
00108 
00109     def flush(self):
00110         if self.closed:
00111             raise ValueError('I/O operation on closed file')
00112         raise IOError(9, 'Bad file descriptor')
00113 
00114     def next(self):
00115         if self.closed:
00116             raise StopIteration()
00117         line = self.readline()
00118         if not line:
00119             raise StopIteration()
00120         return line
00121 
00122 
00123 class IterI(IterIO):
00124     """Convert an stream into an iterator."""
00125 
00126     def __new__(cls, func):
00127         if greenlet is None:
00128             raise RuntimeError('IterI requires greenlet support')
00129         stream = object.__new__(cls)
00130         stream.__init__(greenlet.getcurrent())
00131 
00132         def run():
00133             func(stream)
00134             stream.flush()
00135 
00136         g = greenlet(run, stream._parent)
00137         while 1:
00138             rv = g.switch()
00139             if not rv:
00140                 return
00141             yield rv[0]
00142 
00143     def __init__(self, parent):
00144         self._parent = parent
00145         self._buffer = []
00146         self.closed = False
00147         self.pos = 0
00148 
00149     def close(self):
00150         if not self.closed:
00151             self.closed = True
00152 
00153     def write(self, s):
00154         if self.closed:
00155             raise ValueError('I/O operation on closed file')
00156         self.pos += len(s)
00157         self._buffer.append(s)
00158 
00159     def writelines(self, list):
00160         self.write(''.join(list))
00161 
00162     def flush(self):
00163         if self.closed:
00164             raise ValueError('I/O operation on closed file')
00165         data = ''.join(self._buffer)
00166         self._buffer = []
00167         self._parent.switch((data,))
00168 
00169 
00170 class IterO(IterIO):
00171     """Iter output.  Wrap an iterator and give it a stream like interface."""
00172 
00173     def __new__(cls, gen):
00174         return object.__new__(cls)
00175 
00176     def __init__(self, gen):
00177         self._gen = gen
00178         self._buf = ''
00179         self.closed = False
00180         self.pos = 0
00181 
00182     def __iter__(self):
00183         return self
00184 
00185     def close(self):
00186         if not self.closed:
00187             self.closed = True
00188             if hasattr(self._gen, 'close'):
00189                 self._gen.close()
00190 
00191     def seek(self, pos, mode=0):
00192         if self.closed:
00193             raise ValueError('I/O operation on closed file')
00194         if mode == 1:
00195             pos += self.pos
00196         elif mode == 2:
00197             self.read()
00198             self.pos = min(self.pos, self.pos + pos)
00199             return
00200         elif mode != 0:
00201             raise IOError('Invalid argument')
00202         buf = []
00203         try:
00204             tmp_end_pos = len(self._buf)
00205             while pos > tmp_end_pos:
00206                 item = self._gen.next()
00207                 tmp_end_pos += len(item)
00208                 buf.append(item)
00209         except StopIteration:
00210             pass
00211         if buf:
00212             self._buf += ''.join(buf)
00213         self.pos = max(0, pos)
00214 
00215     def read(self, n=-1):
00216         if self.closed:
00217             raise ValueError('I/O operation on closed file')
00218         if n < 0:
00219             self._buf += ''.join(self._gen)
00220             result = self._buf[self.pos:]
00221             self.pos += len(result)
00222             return result
00223         new_pos = self.pos + n
00224         buf = []
00225         try:
00226             tmp_end_pos = len(self._buf)
00227             while new_pos > tmp_end_pos:
00228                 item = self._gen.next()
00229                 tmp_end_pos += len(item)
00230                 buf.append(item)
00231         except StopIteration:
00232             pass
00233         if buf:
00234             self._buf += ''.join(buf)
00235         new_pos = max(0, new_pos)
00236         try:
00237             return self._buf[self.pos:new_pos]
00238         finally:
00239             self.pos = min(new_pos, len(self._buf))
00240 
00241     def readline(self, length=None):
00242         if self.closed:
00243             raise ValueError('I/O operation on closed file')
00244         nl_pos = self._buf.find('\n', self.pos)
00245         buf = []
00246         try:
00247             pos = self.pos
00248             while nl_pos < 0:
00249                 item = self._gen.next()
00250                 local_pos = item.find('\n')
00251                 buf.append(item)
00252                 if local_pos >= 0:
00253                     nl_pos = pos + local_pos
00254                     break
00255                 pos += len(item)
00256         except StopIteration:
00257             pass
00258         if buf:
00259             self._buf += ''.join(buf)
00260         if nl_pos < 0:
00261             new_pos = len(self._buf)
00262         else:
00263             new_pos = nl_pos + 1
00264         if length is not None and self.pos + length < new_pos:
00265             new_pos = self.pos + length
00266         try:
00267             return self._buf[self.pos:new_pos]
00268         finally:
00269             self.pos = min(new_pos, len(self._buf))
00270 
00271     def readlines(self, sizehint=0):
00272         total = 0
00273         lines = []
00274         line = self.readline()
00275         while line:
00276             lines.append(line)
00277             total += len(line)
00278             if 0 < sizehint <= total:
00279                 break
00280             line = self.readline()
00281         return lines