¿Cómo convertir un iterable a un flujo?

Si tengo un iterable que contiene cadenas, ¿hay una forma sencilla de convertirlo en una secuencia? Quiero hacer algo como esto:

def make_file(): yield "hello\n" yield "world\n" output = tarfile.TarFile(…) stream = iterable_to_stream(make_file()) output.addfile(…, stream) 

Aquí está mi iterador de transmisión, una twig experimental de urllib3 que admite la transmisión de solicitudes fragmentadas a través de iterables:

 class IterStreamer(object): """ File-like streaming iterator. """ def __init__(self, generator): self.generator = generator self.iterator = iter(generator) self.leftover = '' def __len__(self): return self.generator.__len__() def __iter__(self): return self.iterator def next(self): return self.iterator.next() def read(self, size): data = self.leftover count = len(self.leftover) if count < size: try: while count < size: chunk = self.next() data += chunk count += len(chunk) except StopIteration: pass self.leftover = data[size:] return data[:size] 

Fuente con contexto: https://github.com/shazow/urllib3/blob/filepost-stream/urllib3/filepost.py#L23

Pruebas unitarias relacionadas: https://github.com/shazow/urllib3/blob/filepost-stream/test/test_filepost.py#L9

Por desgracia, este código aún no se ha convertido en una twig estable, ya que las solicitudes fragmentadas de tamaño considerable no son compatibles, pero debería ser una buena base para lo que estás tratando de hacer. Vea el enlace de la fuente para ver ejemplos que muestran cómo se puede usar.

Python 3 tiene una nueva API de flujo de E / S ( documentos de la biblioteca ), que reemplaza el antiguo protocolo de objetos tipo archivo. (La nueva API también está disponible en Python 2 en el módulo io , y es compatible con el protocolo de objetos tipo archivo).

Aquí hay una implementación para la nueva API , en Python 2 y 3:

 import io def iterable_to_stream(iterable, buffer_size=io.DEFAULT_BUFFER_SIZE): """ Lets you use an iterable (eg a generator) that yields bytestrings as a read-only input stream. The stream implements Python 3's newer I/O API (available in Python 2's io module). For efficiency, the stream is buffered. """ class IterStream(io.RawIOBase): def __init__(self): self.leftover = None def readable(self): return True def readinto(self, b): try: l = len(b) # We're supposed to return at most this much chunk = self.leftover or next(iterable) output, self.leftover = chunk[:l], chunk[l:] b[:len(output)] = output return len(output) except StopIteration: return 0 # indicate EOF return io.BufferedReader(IterStream(), buffer_size=buffer_size) 

Ejemplo de uso:

 with iterable_to_stream(str(x**2).encode('utf8') for x in range(11)) as s: print(s.read()) 

Dado que no parece que haya una forma “estándar” de hacerlo, he golpeado una simple implementación:

 class iter_to_stream(object): def __init__(self, iterable): self.buffered = "" self.iter = iter(iterable) def read(self, size): result = "" while size > 0: data = self.buffered or next(self.iter, None) self.buffered = "" if data is None: break size -= len(data) if size < 0: data, self.buffered = data[:size], data[size:] result += data return result 

Un punto de partida:

 class iterable_to_stream: def __init__(self, iterable): self.iter = iter(iterable) def read(self): try: return self.iter.next() except StopIteration: return "" 

TarFile toma cualquier cosa que proporcione una interfaz similar a un archivo, por lo que puede usar StringIO ( io.StringIO si está usando Python 3.X) para obtener lo que necesita para TarFile.addfile() o puede crear su propia clase que proporciona una interfaz tipo archivo y produce lo que necesitas.