mercredi 23 décembre 2015

Wrap an open stream with io.TextIOWrapper

How can I wrap an open binary stream – a Python 2 file, a Python 3 io.BufferedReader, an io.BytesIO – in an io.TextIOWrapper?

I'm trying to write code that will work unchanged:

  • Running on Python 2.
  • Running on Python 3.
  • With binary streams generated from the standard library (i.e. I can't control what type they are)
  • With binary streams made to be test doubles (i.e. no file handle, can't re-open).

Example: wrapping the binary stream presented as the subprocess.Popen.stdout attribute:

import subprocess
import io

gnupg_subprocess = subprocess.Popen(
        ["gpg", "--version"], stdout=subprocess.PIPE)
gnupg_stdout = io.TextIOWrapper(gnupg_subprocess.stdout)

In unit tests, the stream is replaced with an io.BytesIO instance to control its content without touching any subprocesses or filesystems.

gnupg_subprocess.stdout = io.BytesIO("Lorem ipsum".encode("utf-8"))

That works fine on the streams created by Python 3's standard library. The same code, though, fails on streams generated by Python 2:

[Python 2]
>>> type(gnupg_subprocess.stdout)
<type 'file'>
>>> gnupg_stdout = io.TextIOWrapper(gnupg_subprocess.stdout)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'file' object has no attribute 'readable'

Some suggest re-opening (e.g. with io.open) the underlying file handle:

gnupg_stdout = io.open(gnupg_subprocess.stdout, mode='r', encoding="utf-8")

That fails in unit tests when the test double is an io.BytesIO instance:

>>> type(gnupg_subprocess.stdout)
<type '_io.BytesIO'>
>>> gnupg_stdout = io.open(gnupg_subprocess.stdout, mode='r', encoding="utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: invalid file: <_io.BytesIO object at 0x7f23162fb290>

So how can I write code that works for both Python 2 and Python 3, with both the test doubles and the real objects, which wraps the byte stream as a text stream?

Aucun commentaire:

Enregistrer un commentaire