Почему mypy жалуется на то, что TextIOWrapper получает GzipFile в качестве аргумента 1? - PullRequest
1 голос
/ 15 октября 2019

Я записываю материал в двоичный поток в памяти, чтобы загрузить материал на S3, не сохраняя его в локальном файле (у меня больше памяти, чем места на диске). Следующий код работает, но mypy mvce.py завершается неудачно с

mvce.py:6: error: Argument 1 to "TextIOWrapper" has incompatible type "GzipFile";
expected "IO[bytes]"
Found 1 error in 1 file (checked 1 source file)

mvce.py

from io import BytesIO, TextIOWrapper
import gzip

inmem = BytesIO()
with gzip.GzipFile(fileobj=inmem, mode="wb") as gzip_handler, TextIOWrapper(
    gzip_handler, encoding="utf-8"
) as wrapper:
    wrapper.write("some test string")


# Check if this actually worked
with open("foobar.gzip", "wb") as f1:
    inmem.seek(0)
    f1.write(inmem.read())


with gzip.open("foobar.gzip", "rb") as f2:
    data = f2.read()

print(data)

Вопрос

Почему происходит сбой mypy и как мне его сделатьработай? Есть ли скрытые потенциальные проблемы?

1 Ответ

1 голос
/ 16 октября 2019

Почему MyPy дает сбой?

MyPy использует набор заглушек типов, называемый Typeshed , чтобы определить типы для стандартной библиотеки. В Typeshed gzip.GzipFile не наследуется от typing.IO[bytes].

Иерархия классов: gzip.GzipFile -> _compression.BaseStream -> io.BufferedIOBase -> io.IOBase.

Как мне заставить это работать?

Вы можетеиспользуйте typing.cast(IO[bytes], gzip_handler), чтобы намекнуть MyPy, что экземпляр GzipFile следует считать двоичным файловым объектом. См. документацию для получения дополнительной информации о приведениях.

В качестве альтернативы, вы можете использовать gzip.open(inmem, mode='wt', encoding="utf-8") для непосредственного получения объекта текстового файла (по сути, того же, что вы делаете, см. Ниже),Эта функция имеет тип возврата IO[Any] в Typeshed.

Есть ли скрытые потенциальные проблемы?

Документация gzip говорит об этой функции gzip.open():

Для текстового режима создается объект GzipFile, который оборачивается в экземпляр io.TextIOWrapper с указанным кодированием, поведением обработки ошибок иконец (и) строк.

Таким образом, ваш код должен работать на практике.

Можно ли это исправить в Typeshed?

Я пытался добавить IO[bytes] каксуперкласс GZipFile в Typeshed, и я получил одну ошибку в тестах:

stdlib/3/gzip.pyi:17: error: Definition of "__enter__" in base class "IOBase" is incompatible with definition in base class "IO"

Решение этой проблемы оставлено в качестве упражнения для читателя.

...