fileinput.input does fundamentally different things depending on whether it gets a gzipped file or not. For text files, it opens with regular open, which effectively opens in text mode by default. For gzip.open, the default mode is binary, which is sensible for compressed files of unknown content.
The binary-only restriction is artificially imposed by fileinput.FileInput. From the code of the __init__ method:
# restrict mode argument to reading modes
if mode not in ('r', 'rU', 'U', 'rb'):
raise ValueError("FileInput opening mode must be one of "
"'r', 'rU', 'U' and 'rb'")
if 'U' in mode:
import warnings
warnings.warn("'U' mode is deprecated",
DeprecationWarning, 2)
self._mode = mode
This gives you two options for a workaround.
Option 1
Set the _mode attribute after __init__. To avoid adding extra lines of code to your usage, you can subclass fileinput.FileInput and use the class directly:
class TextFileInput(fileinput.FileInput):
def __init__(*args, **kwargs):
if 'mode' in kwargs and 't' in kwargs['mode']:
mode = kwargs.pop['mode']
else:
mode = ''
super().__init__(*args, **kwargs)
if mode:
self._mode = mode
for line in TextFileInput(filenames, openhook=fileinput.hook_compressed, mode='rt'):
...
Option 2
Messing with undocumented leading-underscore is pretty hacky, so instead, you can create a custom hook for zip files. This is actually pretty easy, since you can use the code for fileinput.hook_compressed as a template:
def my_hook_compressed(filename, mode):
if 'b' not in mode:
mode += 't'
ext = os.path.splitext(filename)[1]
if ext == '.gz':
import gzip
return gzip.open(filename, mode)
elif ext == '.bz2':
import bz2
return bz2.open(filename, mode)
else:
return open(filename, mode)
Option 3
Finally, you can always decode your bytes to unicode strings. This is clearly not the preferable option.