Как мне прочитать tar-файл из генератора?

Создать zip-файл из генератора на Python? описывает решение для записи .zip на диск из группы файлов.

У меня аналогичная проблема в обратном направлении. Мне дают генератор:

stream = attachment.iter_bytes()
print type(stream)

и я хотел бы передать его файловому объекту tar gunzip:

b = io.BytesIO(stream)
f = tarfile.open(mode='r:gz', fileobj = b)
f.list()

Но я не могу:

<type 'generator'>
Error: 'generator' does not have the buffer interface

Я могу решить это в оболочке так:

$ curl --options http://URL | tar zxf - ./path/to/interesting_file

Как я могу сделать то же самое в Python в данных условиях?


person Virgil Gheorghiu    schedule 25.08.2016    source источник


Ответы (1)


Мне пришлось заключить генератор в файловый объект, построенный поверх io модуль.

def generator_to_stream(generator, buffer_size=io.DEFAULT_BUFFER_SIZE):
    class GeneratorStream(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(generator)
                output, self.leftover = chunk[:l], chunk[l:]
                b[:len(output)] = output
                return len(output)
            except StopIteration:
                return 0  # : Indicate EOF
    return io.BufferedReader(GeneratorStream())

С его помощью вы можете открыть tar-файл и извлечь его содержимое.

stream = generator_to_stream(any_stream)
tar_file = tarfile.open(fileobj=stream, mode='r|*')
#: Do whatever you want with the tar_file now

for member in tar_file:
    member_file = tar_file.extractfile(member)
person Roberto Soares    schedule 26.07.2018
comment
Спасибо, Роберто! Важно подчеркнуть, что вы использовали режим 'r|*', а не 'r:*' в tarfile.open (), иначе вы получите исключение io.UnsupportedOperation: seek. - person Martin; 07.09.2018