:mod:`dip.io`
=============
.. module:: dip.io

The :mod:`dip.io` module implements the infrastructure for defining and
interfacing with :term:`storage`.

Most of the functionality provided by the module is used by the rest of dip.

Applications will, typically, only need to define an implementation of the
:class:`~dip.io.ICodecsFactory` interface for the data the application wants
to store.  The :mod:`dip.io.codecs` module contains an appropriate XML decoder
and encoder which do much of the work if the data is to be stored as XML and
is implemented as a :class:`~dip.model.Model`.

dip includes support for using the filesystem as storage.  Some applications
or environments may also need to define additional types of storage.  This is
done by providing appropriate implementations of the
:class:`~dip.io.IStreamingStorageFactory` or
:class:`~dip.io.IStructuredStorageFactory` interfaces.


:exc:`FormatError`
------------------
.. exception:: FormatError

    Base exception: :class:`~dip.io.StorageError`

    The FormatError class is an exception raised when there is an error
    decoding or encoding an object at a particular storage location.


:class:`ICodecsFactory`
-----------------------
.. class:: ICodecsFactory

    Base class: :class:`~dip.io.IFormat`

    The ICodecsFactory interface defines the API of factories that create
    object decoders and encoders.

    .. attribute:: filter = Str()

        The filter string in the format used by
        :class:`~PyQt4.QtGui.QFileDialog`.

    .. method:: ICodecsFactory.decoder()

        A new instance of the object decoder is created and returned.
        
        :return:
            an implementation of :class:`~dip.io.IDecoder`.

    .. method:: ICodecsFactory.encoder()

        A new instance of the object encoder is created and returned.
        
        :return:
            an implementation of :class:`~dip.io.IEncoder`.


:class:`IDecoder`
-----------------
.. class:: IDecoder

    Base class: :class:`~dip.model.Interface`

    The IDecoder interface defines the API of an object decoder. 

    .. attribute:: factory = Instance(ICodecsFactory)

        The codecs factory that created this decoder.

    .. method:: IDecoder.decode(obj, source, location)

        An object is decoded from a byte stream.
        
        :param obj:
            is the object to populate from the decoded byte stream.
        :param source:
            is an iterator that will return the byte stream to be decoded.
        :param location:
            is the storage location where the encoded object is being read
            from.  It is mainly used for error reporting.
        :return:
            the decoded object.  This may be the original object populated
            from the byte stream, or it may be a different object (of an
            appropriate type) created from the byte stream.


:class:`IEncoder`
-----------------
.. class:: IEncoder

    Base class: :class:`~dip.model.Interface`

    The IEncoder interface defines the API of an object encoder. 

    .. attribute:: factory = Instance(ICodecsFactory)

        The codecs factory that created this encoder.

    .. method:: IEncoder.encode(obj, location)

        An object is encoded as a byte stream.  This should be implemented
        as a generator as it will be called in a loop.  This allows large
        objects to be encoded without requiring the whole byte stream to be
        held in memory.
        
        :param obj:
            is the object to encode.
        :param location:
            is the storage location where the encoded object will be written
            to.  It is mainly used for error reporting.
        :return:
            the next section of the encoded byte stream.


:class:`IFormat`
----------------
.. class:: IFormat

    Base class: :class:`~dip.model.Interface`

    The IFormat interface is implemented by objects that need to impose a
    format on an object to see if the object can be stored.

    .. attribute:: id = Str()

        The identifier of the format.

    .. attribute:: name = Str()

        The name of the format.

    .. method:: IFormat.decodes(obj)

        Determines if an object can be decoded from this format.
        
        :param obj:
            is the object.
        :return:
            ``True`` if the object can be decoded from this format.

    .. method:: IFormat.encodes(obj)

        Determines if an object can be encoded to this format.
        
        :param obj:
            is the object.
        :return:
            ``True`` if the object can be encoded to this format.


:class:`IIOManager`
-------------------
.. class:: IIOManager

    Base class: :class:`~dip.model.Interface`

    The IIOManager interface defines the API of an i/o manager. 

    .. attribute:: codecs_factories = List(ICodecsFactory)

        The available codecs factories.

    .. attribute:: policy = Instance(IStoragePolicy)

        The policy that determines if an object is allowed to be stored in
        persistent storage represented by a particular implementation of
        :class:`~dip.io.IStorage`.

    .. attribute:: storage_factories = List(IStorageFactory)

        The available storage factories.

    .. method:: IIOManager.readable_storage(obj, exclude_format=None, include_format=None, storage_factories=None)

        Get the list of the storage instances that an object can be read
        from.
        
        :param obj:
            is the object.
        :param exclude_format:
            is the optional identifier of a format that will be used to
            exclude factories that can handle it.
        :param include_format:
            is the optional identifier of a format that will be used to
            include factories that can handle it.
        :param storage_factories:
            is the optional list of storage factories that are considered.  If
            it is ``None`` then all storage factories are considered.
        :return:
            the list of storage instances.

    .. method:: IIOManager.storage_factories_for_location(location)

        Get the list of storage factories for which a location specified
        as a string is valid.
        
        :param location:
            is the location as a string.
        :return:
            the list of storage factories.

    .. method:: IIOManager.writeable_storage(obj, exclude_format=None, include_format=None, storage_factories=None)

        Get the list of the storage instances that an object can be
        written to.
        
        :param obj:
            is the object.
        :param exclude_format:
            is the optional identifier of a format that will be used to
            exclude factories that can handle it.
        :param include_format:
            is the optional identifier of a format that will be used to
            include factories that can handle it.
        :param storage_factories:
            is the optional list of storage factories that are considered.  If
            it is ``None`` then all storage factories are considered.
        :return:
            the list of storage instances.


:class:`IStorage`
-----------------
.. class:: IStorage

    Base class: :class:`~dip.model.Interface`

    The IStorage interface defines the API of a particular type of
    :term:`storage`.

    .. attribute:: factory = Instance(IStorageFactory)

        The storage factory that created this storage.

    .. attribute:: ui = Instance(IStorageUi)

        The user interface used to obtain storage locations from the user.

    .. method:: IStorage.explicit_location(location='')

        If the storage has explicit storage locations (i.e. a location is
        independent on the value of an object) then convert such a location
        specified as a string to an :class:`~dip.io.IStorageLocation`
        instance.
        
        :param location:
            is the location as a string.  The string may be empty, but
            otherwise should be valid for the storage.
        :return:
            the :class:`~dip.io.IStorageLocation` instance or ``None`` if the
            storage does not have explicit storage locations.

    .. method:: IStorage.implicit_location(obj)

        If the storage has implicit storage locations (i.e. a location is
        dependent on the value of an object) then create such a location for
        an object.
        
        :param obj:
            is the object to create the location for.
        :return:
            the location or ``None`` if the storage does not have implicit
            storage locations.

    .. method:: IStorage.read(obj, location)

        Read an object from a storage location.
        
        :param obj:
            is the object to populate from the storage location.
        :param location:
            is the storage location where the object is read from.
        :return:
            the read object.  This may be the original object populated
            from the storage location, or it may be a different object (of an
            appropriate type) created from the storage location.

    .. method:: IStorage.write(obj, location)

        Write an object to a storage location.
        
        :param obj:
            is the object to write.
        :param location:
            is the storage location where the object is written to.


:class:`IStorageFactory`
------------------------
.. class:: IStorageFactory

    Base class: :class:`~dip.model.Interface`

    The IStorageFactory interface defines the API of storage factories.
        

    .. method:: IStorageFactory.__call__()

        A new instance of the storage is created and returned.
        
        :return:
            a new instance of the storage.

    .. method:: IStorageFactory.valid_location(location)

        Check if a location specified as a string is valid for this
        storage.
        
        :param location:
            is the location as a string.
        
        :return:
            ``True`` if the location is valid.


:class:`IStorageLocation`
-------------------------
.. class:: IStorageLocation

    Base class: :class:`~dip.model.Interface`

    The IStorageLocation interface defines the API of a
    :term:`storage location`.

    .. attribute:: location = Str()

        The string representation of the location as seen by the user.  It must
        be unique for a particular location and be updated when the location
        changes.

    .. attribute:: storage = Instance(IStorage)

        The storage that the location refers to.


:class:`IStoragePolicy`
-----------------------
.. class:: IStoragePolicy

    Base class: :class:`~dip.model.Interface`

    The IStoragePolicy interface defines the API of a storage policy.
        

    .. method:: IStoragePolicy.__call__(obj, storage)

        The policy determines if an object is allowed to be stored in a
        particular type of :term:`storage`.
        
        :param obj:
            is the object to be stored.
        :param storage:
            is the :class:`~dip.io.IStorageFactory` implementation for the
            storage that can store the object, but may not be allowed to.
        :return:
            ``True`` if the object is allowed to be stored.


:class:`IStorageUi`
-------------------
.. class:: IStorageUi

    Base class: :class:`~dip.model.Interface`

    The IStorageUi interface defines the API used to implement the user
    interface that selects an explicit :term:`storage location`.

    .. attribute:: storage = Instance(dip.io.IStorage)

        The storage that this is the user interface for.

    .. method:: IStorageUi.get_read_location(toolkit, location)

        Get an updated location from the user where an object should be
        read from.
        
        :param toolkit:
            is the :class:`~dip.ui.IToolkit` implementation that can be used to
            create any user interface.
        :param location:
            is the location that is updated by the user.  If the user
            cancelled then its :attr:`~dip.io.IStorageLocation.location`
            attribute is set to an empty string.

    .. method:: IStorageUi.get_write_location(toolkit, location)

        Get an updated location from the user where an object should be
        written to.
        
        :param toolkit:
            is the :class:`~dip.ui.IToolkit` implementation that can be used to
            create any user interface.
        :param location:
            is the location that is updated by the user.  If the user
            cancelled then its :attr:`~dip.io.IStorageLocation.location`
            attribute is set to an empty string.

    .. method:: IStorageUi.read_browser(toolkit, location)

        Create a widget that allows the user to browse the storage and
        select a read location.
        
        :param toolkit:
            is the :class:`~dip.ui.IToolkit` implementation that can be used to
            create the browser.
        :param location:
            is the location that is updated by the user.
        :return:
            the :class:`~PyQt4.QtGui.QWidget` that implements the browser.

    .. method:: IStorageUi.write_browser(toolkit, location)

        Create a widget that allows the user to browse the storage and
        select a write location.
        
        :param toolkit:
            is the :class:`~dip.ui.IToolkit` implementation that can be used to
            create the browser.
        :param location:
            is the location that is updated by the user.
        :return:
            the :class:`~PyQt4.QtGui.QWidget` that implements the browser.


:class:`IStreamingStorage`
--------------------------
.. class:: IStreamingStorage

    Base class: :class:`~dip.io.IStorage`

    The IStreamingStorage interface defines the API of
    :term:`streaming storage`.

    .. attribute:: codecs_factory = Instance(ICodecsFactory)

        The factory used to create the decoder and encoder that converts an
        object from and to a stream of bytes.


:class:`IStreamingStorageFactory`
---------------------------------
.. class:: IStreamingStorageFactory

    Base class: :class:`~dip.io.IStorageFactory`

    The IStreamingStorageFactory interface defines the API of
    :term:`streaming storage` factories.

    .. attribute:: name = Str()

        The name of the storage.


:class:`IStructuredStorage`
---------------------------
.. class:: IStructuredStorage

    Base class: :class:`~dip.io.IStorage`

    The IStructuredStorage interface defines the API of
    :term:`structured storage`.


:class:`IStructuredStorageFactory`
----------------------------------
.. class:: IStructuredStorageFactory

    Base classes: :class:`~dip.io.IStorageFactory`, :class:`~dip.io.IFormat`

    The IStorageFactory interface defines the API of
    :term:`structured storage` factories.


:exc:`StorageError`
-------------------
.. exception:: StorageError

    Base exception: :class:`~builtins.Exception`

    The StorageError class is an exception raised when there is an error
    accessing a particular storage location.
