# Working with File-like Objects

The first argument passed to a `FileType` or `Metadata` can either be a file name or a file-like object, such as `BytesIO` and mutagen will figure out what to do.

    MP3("myfile.mp3")
    MP3(myfileobj)

If for some reason the automatic type detection fails, it’s possible to pass them using a named argument which skips the type guessing.

    MP3(filename="myfile.mp3")
    MP3(fileobj=myfileobj)

Mutagen expects the file offset to be at 0 for all file objects passed to it.

The file-like object has to implement the following interface (It’s a limited subset of real buffered file objects and StringIO/BytesIO)

    class IOInterface(object):
        """This is the interface mutagen expects from custom file-like
        objects.

        For loading read(), tell() and seek() have to be implemented. "name"
        is optional.

        For saving/deleting write(), flush() and truncate() have to be
        implemented in addition. fileno() is optional.
        """

        # For loading

        def tell(self):
            """Returns he current offset as int. Always >= 0.

            Raises IOError in case fetching the position is for some reason
            not possible.
            """

            raise NotImplementedError

        def read(self, size=-1):
            """Returns 'size' amount of bytes or less if there is no more data.
            If no size is given all data is returned. size can be >= 0.

            Raises IOError in case reading failed while data was available.
            """

            raise NotImplementedError

        def seek(self, offset, whence=0):
            """Move to a new offset either relative or absolute. whence=0 is
            absolute, whence=1 is relative, whence=2 is relative to the end.

            Any relative or absolute seek operation which would result in a
            negative position is undefined and that case can be ignored
            in the implementation.

            Any seek operation which moves the position after the stream
            should succeed. tell() should report that position and read()
            should return an empty bytes object.

            Returns Nothing.
            Raise IOError in case the seek operation asn't possible.
            """

            raise NotImplementedError

        # For loading, but optional

        @property
        def name(self):
            """Should return text. For example the file name.

            If not available the attribute can be missing or can return
            an empty string.

            Will be used for error messages and type detection.
            """

            raise NotImplementedError

        # For writing

        def write(self, data):
            """Write data to the file.

            Returns Nothing.
            Raises IOError
            """

            raise NotImplementedError

        def truncate(self, size=None):
            """Truncate to the current position or size if size is given.

            The current position or given size will never be larger than the
            file size.

            This has to flush write buffers in case writing is buffered.

            Returns Nothing.
            Raises IOError.
            """

            raise NotImplementedError

        def flush(self):
            """Flush the write buffer.

            Returns Nothing.
            Raises IOError.
            """

            raise NotImplementedError

        # For writing, but optional

        def fileno(self):
            """Returns the file descriptor (int) or raises IOError
            if there is none.

            Will be used for low level operations if available.
            """

            raise NotImplementedError

## Gio Example Implementation

The following implements a file-like object using PyGObject and Gio. It depends on the giofile Python library.

    import mutagen
    import giofile
    from gi.repository import Gio

    gio_file = Gio.File.new_for_uri(
        "http://people.xiph.org/~giles/2012/opus/ehren-paper_lights-96.opus")

    cancellable = Gio.Cancellable.new()
    with giofile.open(gio_file, "rb", cancellable=cancellable) as gfile:
        print(mutagen.File(gfile).pprint())

    $ python example.py
    Ogg Opus, 228.11 seconds (audio/ogg)
    ENCODER=opusenc from opus-tools 0.1.5
    artist=Ehren Starks
    title=Paper Lights
    album=Lines Build Walls
    date=2005-09-05
    copyright=Copyright 2005 Ehren Starks
    license=http://creativecommons.org/licenses/by-nc-sa/1.0/
    organization=magnatune.com