diff --git a/bob/io/base/__init__.py b/bob/io/base/__init__.py index d84289b2a75e4f931e5d45ace768a23299d83605..ca1d2581da304c66a55dc0306521346539e21da4 100644 --- a/bob/io/base/__init__.py +++ b/bob/io/base/__init__.py @@ -6,7 +6,7 @@ import bob.core import bob.extension bob.extension.load_bob_library('bob.io.base', __file__) -from ._library import File as File_C, HDF5File as HDF5File_C, extensions +from ._library import File as _File_C, HDF5File as _HDF5File_C, extensions from . import version from .version import module as __version__ from .version import api as __api_version__ @@ -14,8 +14,8 @@ from .version import api as __api_version__ import os -class File(File_C): - __doc__ = File_C.__doc__ +class File(_File_C): + __doc__ = _File_C.__doc__ def __enter__(self): return self @@ -24,24 +24,51 @@ class File(File_C): self.close() -class HDF5File(HDF5File_C): - __doc__ = HDF5File_C.__doc__ +class HDF5File(_HDF5File_C): + __doc__ = _HDF5File_C.__doc__ def __enter__(self): return self def __exit__(self, type, value, traceback): - self.close() + return self.close() def __contains__(self, x): - """Since .has_key() is deprecated, implement the ``in`` operator to avoid - PEP8 W601. - """ + __doc__ = self.has_key.__doc__ return self.has_key(x) def __iter__(self): + __doc__ = self.keys.__doc__ return iter(self.keys()) + def __getitem__(self, name): + __doc__ = self.get.__doc__ + return self.get(name) + + def __setitem__(self, name, value): + __doc__ = self.set.__doc__ + return self.set(name, value) + + def values(self): + '''Yields the datasets contained in the current directory. + + Yields + ------- + object + The datasets that are being read. + ''' + return (self[key] for key in self) + + def items(self): + '''Yields the keys and the datasets contained in the current directory. + + Yields + ------- + tuple + The key and the datasets that are being read in a tuple. + ''' + return ((key, self[key]) for key in self) + def _is_string(s): """Returns ``True`` if the given object is a string diff --git a/bob/io/base/test_hdf5.py b/bob/io/base/test_hdf5.py index fcc5811ce60903393cfc6a8695e6ebc682e8db88..64d5a6bb353d09a95c4de7b41a24e11841ac9a0e 100644 --- a/bob/io/base/test_hdf5.py +++ b/bob/io/base/test_hdf5.py @@ -477,10 +477,8 @@ def test_copy_constructor(): assert shallow.has_group("/Test") assert shallow.has_key("/Test/Data") - assert "/Test/Data" in shallow assert hdf5.filename == shallow.filename assert hdf5.keys() == shallow.keys() - assert [key for key in hdf5] == shallow.keys() assert hdf5.cwd == shallow.cwd assert not deep.has_group("/Test") @@ -512,3 +510,23 @@ def test_copy_constructor(): finally: os.unlink(tmpname) os.unlink(tmpname2) + + +def test_python_interfaces(): + try: + tmpname = test_utils.temporary_filename() + with HDF5File(tmpname, 'w') as hdf5: + a = numpy.arange(10) + b = a * 2 + hdf5['Data'] = a + hdf5.create_group('Group1') + hdf5['Group1/Data'] = b + assert "/Group1/Data" in hdf5 + assert [key for key in hdf5] == hdf5.keys() + assert numpy.allclose(hdf5['Data'], hdf5.get('Data')) + assert all(numpy.allclose(c, d) for c, d in zip(hdf5.values(), (a, b))) + for key, value in hdf5.items(): + assert numpy.allclose(value, hdf5[key]) + + finally: + os.unlink(tmpname) diff --git a/doc/guide.rst b/doc/guide.rst index 7ecdd8c09f1fd12511505506baf8e1a4a6eb3558..5533f6bc52ecc26512f83e6bdddb21fbba93496c 100644 --- a/doc/guide.rst +++ b/doc/guide.rst @@ -157,9 +157,9 @@ is an example: >>> A = numpy.array(range(4), 'int8').reshape(2,2) >>> f = bob.io.base.HDF5File('testfile1.hdf5', 'a') >>> f.set('my_array', A) - >>> del f + >>> f.close() -The result of running ``h5dump`` on the file ``testfile3.hdf5`` should be: +The result of running ``h5dump`` on the file ``testfile1.hdf5`` should be: .. code-block:: none @@ -261,23 +261,49 @@ would return a 1D uint8 array instead of a 2D array. Pythonic operations on HDF5 files --------------------------------- -You can use some Pythonic opertations on :py:class:`bob.io.base.HDF5File`. -You can iterate over :py:class:`bob.io.base.HDF5File` objects to get an -iterable of keys instead of calling -:py:meth:`bob.io.base.HDF5File.keys`. You can also use the ``in`` keyword -instead of calling :py:meth:`bob.io.base.HDF5File.has_key`. For example: +You can use some Pythonic opertations on :py:class:`bob.io.base.HDF5File`: + +* Use the ``with`` statement to open (and automatically close) HDF5 files. +* iterate over :py:class:`bob.io.base.HDF5File` objects to get an + iterable of keys instead of calling :py:meth:`bob.io.base.HDF5File.keys`. +* use the ``in`` keyword instead of calling + :py:meth:`bob.io.base.HDF5File.has_key`. +* use Python's dictionary syntax instead of :py:meth:`bob.io.base.HDF5File.set` + and :py:meth:`bob.io.base.HDF5File.get`. + +For example: + .. doctest:: - >>> keys = f.keys() # Get a list of keys - >>> keys == [key for key in f] # instead you can also iterate over keys - True - >>> f.has_key('arrayset') + >>> f = bob.io.base.HDF5File('testfile3.hdf5', 'w') + >>> array = numpy.arange(5) + >>> f['my_array'] = array # f.set('my_array', array) + >>> f['my_array'] # f.get('my_array') + array([0, 1, 2, 3, 4]) + >>> 'my_array' in f # f.has_key('my_array') True - >>> 'arrayset' in f # you can use the `in` operator instead of `has_key` + >>> [key for key in f] # f.keys() + ['/my_array'] + >>> f.create_group('group1') + >>> f.cd('group1') + >>> f['my_array_in_group'] = array + >>> f.cd('/') + >>> # keys(), values(), and items() just like a dictionary + >>> [key for key in f.keys()] + ['/my_array', '/group1/my_array_in_group'] + >>> [value for value in f.values()] + [array([0, 1, 2, 3, 4]), array([0, 1, 2, 3, 4])] + >>> [(key, value) for key, value in f.items()] + [('/my_array', array([0, 1, 2, 3, 4])), ('/group1/my_array_in_group', array([0, 1, 2, 3, 4]))] + >>> f.close() + >>> # using a with statement to open and close files + >>> with bob.io.base.HDF5File('testfile3.hdf5', 'a') as f: + ... f['second_array'] = array + >>> f = bob.io.base.HDF5File('testfile3.hdf5', 'r') + >>> 'second_array' in f True - Array interfaces ---------------- @@ -356,35 +382,6 @@ the :py:class:`bob.io.base.File` container: the read and write operations. Have a look at the manual section for :py:mod:`bob.io.base` for more details and other shortcuts available. -.. _audiosignal: - -Loading and saving audio files ------------------------------- - -|project| does not yet support audio files (no wav codec). However, it is -possible to use the `SciPy`_ module :py:mod:`scipy.io.wavfile` to do the job. -For instance, to read a wave file, just use the -:py:func:`scipy.io.wavfile.read` function. - -.. code-block:: python - - >>> import scipy.io.wavfile - >>> filename = '/home/user/sample.wav' - >>> samplerate, data = scipy.io.wavfile.read(filename) - >>> print(type(data)) - <... 'numpy.ndarray'> - >>> print(data.shape) - (132474, 2) - -In the above example, the stereo audio signal is represented as a 2D `NumPy` -:py:class:`numpy.ndarray`. The first dimension corresponds to the time index -(132474 frames) and the second dimesnion correpsonds to one of the audio -channel (2 channels, stereo). The values in the array correpsond to the wave -magnitudes. - -To save a `NumPy` :py:class:`numpy.ndarray` into a wave file, the -:py:func:`scipy.io.wavfile.write` could be used, which also requires the -framerate to be specified. .. Place here your external references .. include:: links.rst diff --git a/doc/py_api.rst b/doc/py_api.rst index 47fdfaab8236b008e4121d92db9528bfdd4da6ff..bd8a9a713b3346583274afbca3bbb92306cf8ddf 100644 --- a/doc/py_api.rst +++ b/doc/py_api.rst @@ -49,5 +49,4 @@ Details .. automodule:: bob.io.base :inherited-members: - .. automodule:: bob.io.base.test_utils