16

Given a file object, how do I determine whether it is opened in bytes mode (read returns bytes) or in text mode (read returns str)? It should work with reading and writing.

In other words:

>>> with open('filename', 'rb') as f:
...     is_binary(f)
...
True

>>> with open('filename', 'r') as f:
...     is_binary(f)
...
False

(Another question which sounds related is not. That question is about guessing whether a file is binary or not from it's contents.)

jdm
  • 9,470
  • 12
  • 58
  • 110

3 Answers3

31

File objects have a .mode attribute:

def is_binary(f):
    return 'b' in f.mode

This limits the test to files; in-memory file objects like TextIO and BytesIO do not have that attribute. You could also test for the appropriate abstract base classes:

import io

def is_binary(f):
    return isinstance(f, (io.RawIOBase, io.BufferedIOBase))

or the inverse

def is_binary(f):
    return not isinstance(f, io.TextIOBase)
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Excellent, and also thanks for the documentation link and pointing me to the io module. I only found the definition of [file object](https://docs.python.org/3.6/glossary.html#term-file-object) in the glossary which is very superficial.... Also, now that I think about it, another pythonic way would probably just to try reading, and catching an exception if it fails. – jdm Jun 16 '17 at 09:09
  • As the file may be a file-like object without a `.mode` attribute, a safer way to test it is: `if 'b' not in getattr(file, 'mode', 'b')` – Julien Palard Oct 27 '20 at 14:18
1

For streams opened as reading, perhaps the most reliable way to determine its mode is to actually read from it:

def is_binary(f):
    return isinstance(f.read(0), bytes)

Through it does have a caveat that it won't work if the stream was already closed (which may raise IOError) it would reliably determine binary-ness of any custom file-like objects neither extending from appropriate io ABCs nor providing the mode attribute.

If only Python 3 support is required, it is also possible to determine text/binary mode of writable streams given the clear distinction between bytes and text:

def is_binary(f):
    read = getattr(f, 'read', None)
    if read is not None:
        try:
            data = read(0)
        except (TypeError, ValueError):
            pass # ValueError is also a superclass of io.UnsupportedOperation
        else:
            return isinstance(data, bytes)
    try:
        # alternatively, replace with empty text literal
        # and swap the following True and False.
        f.write(b'')
    except TypeError:
        return False
    return True

Unless you are to frequently test if a stream is in binary mode or not (which is unnecessary since binary-ness of a stream should not change for the lifetime of the object), I suspect any performance drawbacks resulting from extensive usage of catching exceptions would be an issue (you could certainly optimize for the likelier path, though).

minmaxavg
  • 686
  • 6
  • 21
-4

There is one library called mimetypes where guess_type returns the The return value is a tuple (type, encoding) where type is None if the type can’t be guessed (missing or unknown suffix) or a string of the form 'type/subtype'

import mimetypes
file= mimetypes.guess_type(file)
Ankan Roy
  • 17
  • 6
  • 2
    This is not about the contents of the file. This is about how the file was opened. Please do read the question carefully, they specifically mention another question where this might be an answer, but *this one is not that question*. – Martijn Pieters Jun 16 '17 at 08:59