Подтвердить что ты не робот

Как преобразовать итерируемое в поток?

Если у меня есть итерируемые содержащие строки, есть ли простой способ превратить его в поток? Я хочу сделать что-то вроде этого:

def make_file():
    yield "hello\n"
    yield "world\n"

output = tarfile.TarFile(…)
stream = iterable_to_stream(make_file())
output.addfile(…, stream)
4b9b3361

Ответ 1

Здесь мой потоковый итератор - экспериментальная ветвь urllib3, поддерживающая потоковый запрос с помощью 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]

Источник с контекстом: https://github.com/shazow/urllib3/blob/filepost-stream/urllib3/filepost.py#L23

Связанные модульные тесты: https://github.com/shazow/urllib3/blob/filepost-stream/test/test_filepost.py#L9

Увы, этот код еще не попал в стабильную ветвь, так как безупречные chunked-запросы плохо поддерживаются, но это должна быть хорошая основа для того, что вы пытаетесь сделать. См. Ссылку источника для примеров, показывающих, как ее можно использовать.

Ответ 2

Python 3 имеет новый API потока ввода-вывода (docs), заменив старый файл -подобный объектный протокол. (Новый API также доступен в Python 2 в модуле io, и он обратно совместим с файловым протоколом объектов.)

Здесь реализована реализация для нового API в Python 2 и 3:

import io

def iterable_to_stream(iterable, buffer_size=io.DEFAULT_BUFFER_SIZE):
    """
    Lets you use an iterable (e.g. a generator) that yields bytestrings as a read-only
    input stream.

    The stream implements Python 3 newer I/O API (available in Python 2 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)

Пример использования:

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

Ответ 3

Так как это не похоже на "стандартный" способ сделать это, я ударил простую реализацию:

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

Ответ 4

Начальная точка:

class iterable_to_stream:
    def __init__(self, iterable):
        self.iter = iter(iterable)

    def read(self):
        try:
            return self.iter.next()
        except StopIteration:
            return ""

Ответ 5

TarFile берет все, что обеспечивает файловый интерфейс - чтобы вы могли либо использовать StringIO (io.StringIO, если вы используете Python 3.X), чтобы получить то, что вам нужно TarFile.addfile() или вы можете создать свой собственный класс, который предоставляет файловый интерфейс и дает то, что вам нужно.