diff --git a/bob/io/base/__init__.py b/bob/io/base/__init__.py index b205ba79fa8acd58c30a009b010f31d66e3c2c2d..340178fa32af8b7b5cbf5408a6692611d3d12b8e 100644 --- a/bob/io/base/__init__.py +++ b/bob/io/base/__init__.py @@ -12,7 +12,7 @@ from .version import api as __api_version__ import os -def __is_string__(s): +def _is_string(s): """Returns ``True`` if the given object is a string This method can be used with Python-2.x or 3.x and returns a string @@ -30,13 +30,13 @@ def create_directories_safe(directory, dryrun=False): If the dryrun option is selected, it does not actually create the directory, but just writes the (Linux) command that would have been executed. - Parameters: + **Parameters:** - directory + ``directory`` : str The directory that you want to create. - dryrun - Only write the command, but do not execute it. + ``dryrun`` : bool + Only ``print`` the command to console, but do not execute it. """ try: if dryrun: @@ -51,12 +51,14 @@ def create_directories_safe(directory, dryrun=False): def load(inputs): - """Loads the contents of a file, an iterable of files, or an iterable of + """load(inputs) -> data + + Loads the contents of a file, an iterable of files, or an iterable of :py:class:`bob.io.base.File`'s into a :py:class:`numpy.ndarray`. - Parameters: + **Parameters:** - inputs + ``inputs`` : various types This might represent several different entities: @@ -66,24 +68,29 @@ def load(inputs): 2. An iterable of filenames to be loaded in memory. In this case, this would assume that each file contains a single 1D sample or a set of 1D samples, load them in memory and concatenate them into a single and - returned 2D numpy ndarray. - 3. An iterable of :py:class:`bob.io.base.File`. In this case, this would assume - that each :py:class:`bob.io.base.File` contains a single 1D sample or a set + returned 2D :py:class:`numpy.ndarray`. + 3. An iterable of :py:class:`File`. In this case, this would assume + that each :py:class:`File` contains a single 1D sample or a set of 1D samples, load them in memory if required and concatenate them into - a single and returned 2D numpy ndarray. - 4. An iterable with mixed filenames and :py:class:`bob.io.base.File`. In this + a single and returned 2D :py:class:`numpy.ndarray`. + 4. An iterable with mixed filenames and :py:class:`File`. In this case, this would returned a 2D :py:class:`numpy.ndarray`, as described by points 2 and 3 above. + + **Returns:** + + ``data`` : :py:class:`numpy.ndarray` + The data loaded from the given ``inputs``. """ from collections import Iterable import numpy - if __is_string__(inputs): + if _is_string(inputs): return File(inputs, 'r').read() elif isinstance(inputs, Iterable): retval = [] for obj in inputs: - if __is_string__(obj): + if _is_string(obj): retval.append(load(obj)) elif isinstance(obj, File): retval.append(obj.read()) @@ -94,19 +101,27 @@ def load(inputs): raise TypeError("Unexpected input object. This function is expecting a filename, or an iterable of filenames and/or bob.io.base.File's") def merge(filenames): - """Converts an iterable of filenames into an iterable over read-only - bob.io.base.File's. + """merge(filenames) -> files - Parameters: + Converts an iterable of filenames into an iterable over read-only + :py:class`bob.io.base.File`'s. + + **Parameters:** - filenames + ``filenames`` : str or [str] + A list of file names. This might represent: 1. A single filename. In this case, an iterable with a single - :py:class:`bob.io.base.File` is returned. + :py:class:`File` is returned. 2. An iterable of filenames to be converted into an iterable of - :py:class:`bob.io.base.File`'s. + :py:class:`File`'s. + + **Returns:** + + ``files`` : [:py:class:`File`] + The list of files. """ from collections import Iterable @@ -121,20 +136,20 @@ def merge(filenames): def save(array, filename, create_directories = False): """Saves the contents of an array-like object to file. - Effectively, this is the same as creating a :py:class:`bob.io.base.File` object - with the mode flag set to `w` (write with truncation) and calling - :py:meth:`bob.io.base.File.write` passing `array` as parameter. + Effectively, this is the same as creating a :py:class:`File` object + with the mode flag set to ``'w'`` (write with truncation) and calling + :py:meth:`File.write` passing ``array`` as parameter. Parameters: - array + ``array`` : array_like The array-like object to be saved on the file - filename + ``filename`` : str The name of the file where you need the contents saved to - create_directories - Automatically generate the directories if required + ``create_directories`` : bool + Automatically generate the directories if required (defaults to ``False`` because of compatibility reasons; might change in future to default to ``True``) """ # create directory if not existent yet if create_directories: @@ -147,47 +162,66 @@ write = save read = load def append(array, filename): - """Appends the contents of an array-like object to file. + """append(array, filename) -> position - Effectively, this is the same as creating a :py:class:`bob.io.base.File` object - with the mode flag set to `a` (append) and calling - :py:meth:`bob.io.base.File.append` passing `array` as parameter. + Appends the contents of an array-like object to file. - Parameters: + Effectively, this is the same as creating a :py:class:`File` object + with the mode flag set to ``'a'`` (append) and calling + :py:meth:`File.append` passing ``array`` as parameter. - array + **Parameters:** + + ``array`` : array_like The array-like object to be saved on the file - filename + ``filename`` : str The name of the file where you need the contents saved to + + **Returns:** + + ``position`` : int + See :py:meth:`File.append` """ return File(filename, 'a').append(array) def peek(filename): - """Returns the type of array (frame or sample) saved in the given file. + """peek(filename) -> dtype, shape, stride - Effectively, this is the same as creating a :py:class:`bob.io.base.File` object - with the mode flag set to `r` (read-only) and returning - :py:func:`bob.io.base.File.describe`. + Returns the type of array (frame or sample) saved in the given file. - Parameters: + Effectively, this is the same as creating a :py:class:`File` object + with the mode flag set to `r` (read-only) and calling + :py:meth:`File.describe`. + + **Parameters**: - filename + ``filename`` : str The name of the file to peek information from + + **Returns:** + + ``dtype, shape, stride`` : see :py:meth:`File.describe` """ return File(filename, 'r').describe() def peek_all(filename): - """Returns the type of array (for full readouts) saved in the given file. + """peek_all(filename) -> dtype, shape, stride - Effectively, this is the same as creating a :py:class:`bob.io.base.File` object - with the mode flag set to `r` (read-only) and returning - ``bob.io.base.File.describe(all=True)``. + Returns the type of array (for full readouts) saved in the given file. - Parameters: + Effectively, this is the same as creating a :py:class:`File` object + with the mode flag set to ``'r'`` (read-only) and returning + ``File.describe`` with its parameter ``all`` set to ``True``. + + **Parameters:** - filename + ``filename`` : str The name of the file to peek information from + + **Returns:** + + ``dtype, shape, stride`` : see :py:meth:`File.describe` """ return File(filename, 'r').describe(all=True) @@ -201,8 +235,18 @@ def get_config(): def get_include_directories(): - """Returns a list of include directories for dependent libraries, such as HDF5.""" - from bob.extension import pkgconfig + """get_include_directories() -> includes + + Returns a list of include directories for dependent libraries, such as HDF5. + This function is automatically used by :py:func:`bob.extension.get_bob_libraries` to retrieve the non-standard include directories that are required to use the C bindings of this library in dependent classes. + You shouldn't normally need to call this function by hand. + + **Returns:** + + ``includes`` : [str] + The list of non-standard include directories required to use the C bindings of this class. + For now, only the directory for the HDF5 headers are returned. + """ # try to use pkg_config first try: from bob.extension.utils import find_header @@ -214,6 +258,7 @@ def get_include_directories(): return [os.path.dirname(candidates[0])] except RuntimeError: + from bob.extension import pkgconfig pkg = pkgconfig('hdf5') return pkg.include_directories() diff --git a/bob/io/base/file.cpp b/bob/io/base/file.cpp index 164b8644d91ee0e81caeb338ebe1e69777bb1050..585481db3270a023fcea4e7123044572f26bfc5e 100644 --- a/bob/io/base/file.cpp +++ b/bob/io/base/file.cpp @@ -396,7 +396,7 @@ static auto s_write = bob::extension::FunctionDoc( true ) .add_prototype("data") -.add_parameter("data", "array_like", "[optional] The array to be written into the file; it can be a :py:class:`numpy.array`, a :py:class:`bob.blitz.array` or any other object which can be converted to either of them") +.add_parameter("data", "array_like", "The array to be written into the file; it can be a :py:class:`numpy.array`, a :py:class:`bob.blitz.array` or any other object which can be converted to either of them") ; static PyObject* PyBobIoFile_write(PyBobIoFileObject* self, PyObject *args, PyObject* kwds) { BOB_TRY @@ -424,7 +424,7 @@ static auto s_append = bob::extension::FunctionDoc( true ) .add_prototype("data", "position") -.add_parameter("data", "array_like", "[optional] The array to be written into the file; it can be a :py:class:`numpy.array`, a :py:class:`bob.blitz.array` or any other object which can be converted to either of them") +.add_parameter("data", "array_like", "The array to be written into the file; it can be a :py:class:`numpy.array`, a :py:class:`bob.blitz.array` or any other object which can be converted to either of them") .add_return("position", "int", "The current position of the newly written data") ; static PyObject* PyBobIoFile_append(PyBobIoFileObject* self, PyObject *args, PyObject* kwds) { diff --git a/bob/io/base/test_utils.py b/bob/io/base/test_utils.py index 6f1e3072bcd45255dccaabbb3066e6740a349edb..8e83915644dd7821572fea0eb8f1c4b751f9a432 100644 --- a/bob/io/base/test_utils.py +++ b/bob/io/base/test_utils.py @@ -11,46 +11,92 @@ import os import functools import nose.plugins.skip -from distutils.version import StrictVersion as SV def datafile(f, module=None, path='data'): - """Returns the test file on the "data" subdirectory of the current module. + """datafile(f, [module], [data]) -> filename - Keyword attributes + Returns the test file on the "data" subdirectory of the current module. - f: str - This is the filename of the file you want to retrieve. Something like - ``'movie.avi'``. + **Parameters:** - module: string, optional - This is the python-style package name of the module you want to retrieve - the data from. This should be something like ``bob.io.test``, but you + ``f`` : str + This is the filename of the file you want to retrieve. Something like ``'movie.avi'``. + + ``module``: str + [optional] This is the python-style package name of the module you want to retrieve + the data from. This should be something like ``bob.io.base``, but you normally refer it using the ``__name__`` property of the module you want to find the path relative to. - path: str, optional - This is the subdirectory where the datafile will be taken from inside the - module. Normally (the default) ``data``. It can be set to ``None`` if it - should be taken from the module path root (where the ``__init__.py`` file - sits). + ``path``: str + [Default: ``'data'``] The subdirectory where the datafile will be taken from inside the module. + It can be set to ``None`` if it should be taken from the module path root (where the ``__init__.py`` file sits). + + **Returns:** - Returns the full path of the file. + ``filename`` : str + The full path of the file """ resource = __name__ if module is None else module final_path = f if path is None else os.path.join(path, f) - return __import__('pkg_resources').resource_filename(resource, final_path) + import pkg_resources + return pkg_resources.resource_filename(resource, final_path) + def temporary_filename(prefix='bobtest_', suffix='.hdf5'): - """Generates a temporary filename to be used in tests""" + """temporary_filename([prefix], [suffix]) -> filename + + Generates a temporary filename to be used in tests, using the default ``temp`` directory (on Unix-like systems, usually ``/tmp``). + Please note that you are responsible for deleting the file after your test finished. + A common way to assure the file to be deleted is: + + .. code-block:: py + + import bob.io.base.test_utils + temp = bob.io.base.test_utils.temporary_filename() + try: + # use the temp file + ... + finally: + if os.path.exist(temp): os.remove(temp) + + **Parameters:** + + ``prefix`` : str + [Default: ``'bobtest_'``] The file name prefix to be added in front of the random file name + + ``suffix`` : str + [Default: ``'.hdf5'``] The file name extension of the temporary file name - (fd, name) = __import__('tempfile').mkstemp(suffix, prefix) + **Returns:** + + ``filename`` : str + The name of a temporary file that you can use in your test. + Don't forget to delete! + + """ + import tempfile + fd, name = tempfile.mkstemp(suffix, prefix) os.close(fd) os.unlink(name) return name + def extension_available(extension): - '''Decorator to check if a extension is available before enabling a test''' + '''Decorator to check if a extension is available before enabling a test + + This decorator is mainly used to decorate a test function, in order to skip tests when the extension is not available. + The syntax is: + + .. code-block:: py + + import bob.io.base.test_utils + + @bob.io.base.test_utils.extension_available('.ext') + def my_test(): + ... + ''' def test_wrapper(test): diff --git a/doc/c_cpp_api.rst b/doc/c_cpp_api.rst index 35c7a1fd47cddf4d3bb59c3b7faad6289b906792..da4f8fc61673079825426079481f066c78062e18 100644 --- a/doc/c_cpp_api.rst +++ b/doc/c_cpp_api.rst @@ -49,7 +49,7 @@ Generic Functions Converts the input Bob element type into a ``NPY_<TYPE>`` enumeration value. Returns ``NPY_NOTYPE`` in case of problems, and sets a - :py:class:`RuntimeError`. + :py:exc:`RuntimeError`. .. cpp:function:: PyObject* PyBobIo_TypeInfoAsTuple (const bob::core::array::typeinfo& ti) diff --git a/doc/conf.py b/doc/conf.py index 34108fbbb9cf0f0c5f5db25d498f2072f3c85806..f5a21b88efeb5b9f322248f1853327a194a7481d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -251,7 +251,7 @@ autodoc_default_flags = ['members', 'undoc-members', 'inherited-members', 'show- # For inter-documentation mapping: from bob.extension.utils import link_documentation -intersphinx_mapping = link_documentation() +intersphinx_mapping = link_documentation(['python', 'numpy', 'scipy', 'bob.io.image', 'bob.io.video', 'bob.io.matlab']) def setup(app): diff --git a/doc/index.rst b/doc/index.rst index a95b29006910b7b0b5a3667f0fda09a5fdf81f11..ba226b3964a456e262caf1379cde0d3af295d4bf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,11 +10,15 @@ Bob's Core I/O Routines ========================= -.. todolist:: +This module contains a basic interface to read and write files of various types. +It provides generic functions :py:func:`bob.io.base.save` and :py:func:`bob.io.base.load` to write and read various types of data. +In this interface, data is mainly written using the :py:class:`bob.io.base.HDF5File` interface. +To enable further types of IO, please import one of the following packages (the list might not be exhaustive): + +* :ref:`bob.io.image <bob.io.image>` to load and save images of various kinds +* :ref:`bob.io.video <bob.io.video>` to load and save videos of various types +* :ref:`bob.io.matlab <bob.io.matlab>` to load and save matrices in basic matlab ``.mat`` files -This module contains base functionality from Bob bound to Python, available in -the C++ counter-part ``bob::io``. It includes input and output operations to -and from files. Documentation ------------- @@ -26,6 +30,11 @@ Documentation py_api c_cpp_api +TODO +---- + +.. todolist:: + Indices and tables ------------------