From a0e61539f31b5fa9d409f71b6040bd3fb20b5334 Mon Sep 17 00:00:00 2001
From: Andre Anjos <andre.dos.anjos@gmail.com>
Date: Fri, 15 Nov 2013 16:58:28 +0100
Subject: [PATCH] Finished implementation of all bindings

---
 xbob/io/hdf5.cpp | 414 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 410 insertions(+), 4 deletions(-)

diff --git a/xbob/io/hdf5.cpp b/xbob/io/hdf5.cpp
index 62e7d81..f3be2d8 100644
--- a/xbob/io/hdf5.cpp
+++ b/xbob/io/hdf5.cpp
@@ -1120,10 +1120,12 @@ static PyObject* PyBobIoHDF5File_Replace(PyBobIoHDF5FileObject* self, PyObject*
   }
   catch (std::exception& e) {
     PyErr_SetString(PyExc_RuntimeError, e.what());
+    Py_XDECREF(converted);
     return 0;
   }
   catch (...) {
     PyErr_Format(PyExc_RuntimeError, "cannot replace object in position %" PY_FORMAT_SIZE_T "d at HDF5 file `%s': unknown exception caught", pos, self->f->filename().c_str());
+    Py_XDECREF(converted);
     return 0;
   }
 
@@ -1531,6 +1533,116 @@ file\n\
 \n\
 ");
 
+template <typename T> static PyObject* PyBobIoHDF5File_ReadScalarAttribute
+(PyBobIoHDF5FileObject* self, const char* path, const char* name,
+ const bob::io::HDF5Type& type) {
+  T value;
+  try {
+    self->f->read_attribute(path, name, type, static_cast<void*>(&value));
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading attribute `%s' at resource `%s' with descriptor `%s' from HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+    return 0;
+  }
+  return PyBlitzArrayCxx_FromCScalar(value);
+}
+
+template <> PyObject* PyBobIoHDF5File_ReadScalarAttribute<const char*>
+(PyBobIoHDF5FileObject* self, const char* path, const char* name,
+ const bob::io::HDF5Type& type) {
+  std::string retval;
+  try {
+    self->f->getAttribute(path, name, retval);
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading string attribute `%s' at resource `%s' with descriptor `%s' from HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+    return 0;
+  }
+  return Py_BuildValue("s", retval.c_str());
+}
+
+static PyObject* PyBobIoHDF5File_ReadAttribute(PyBobIoHDF5FileObject* self,
+    const char* path, const char* name, const bob::io::HDF5Type& type) {
+
+  //no error detection: this should be done before reaching this method
+
+  const bob::io::HDF5Shape& shape = type.shape();
+
+  if (type.type() == bob::io::s || (shape.n() == 1 && shape[0] == 1)) {
+    //read as scalar
+    switch(type.type()) {
+      case bob::io::s:
+        return PyBobIoHDF5File_ReadScalarAttribute<const char*>(self, path, name, type);
+      case bob::io::b:
+        return PyBobIoHDF5File_ReadScalarAttribute<bool>(self, path, name, type);
+      case bob::io::i8:
+        return PyBobIoHDF5File_ReadScalarAttribute<int8_t>(self, path, name, type);
+      case bob::io::i16:
+        return PyBobIoHDF5File_ReadScalarAttribute<int16_t>(self, path, name, type);
+      case bob::io::i32:
+        return PyBobIoHDF5File_ReadScalarAttribute<int32_t>(self, path, name, type);
+      case bob::io::i64:
+        return PyBobIoHDF5File_ReadScalarAttribute<int64_t>(self, path, name, type);
+      case bob::io::u8:
+        return PyBobIoHDF5File_ReadScalarAttribute<uint8_t>(self, path, name, type);
+      case bob::io::u16:
+        return PyBobIoHDF5File_ReadScalarAttribute<uint16_t>(self, path, name, type);
+      case bob::io::u32:
+        return PyBobIoHDF5File_ReadScalarAttribute<uint32_t>(self, path, name, type);
+      case bob::io::u64:
+        return PyBobIoHDF5File_ReadScalarAttribute<uint64_t>(self, path, name, type);
+      case bob::io::f32:
+        return PyBobIoHDF5File_ReadScalarAttribute<float>(self, path, name, type);
+      case bob::io::f64:
+        return PyBobIoHDF5File_ReadScalarAttribute<double>(self, path, name, type);
+      case bob::io::f128:
+        return PyBobIoHDF5File_ReadScalarAttribute<long double>(self, path, name, type);
+      case bob::io::c64:
+        return PyBobIoHDF5File_ReadScalarAttribute<std::complex<float> >(self, path, name, type);
+      case bob::io::c128:
+        return PyBobIoHDF5File_ReadScalarAttribute<std::complex<double> >(self, path, name, type);
+      case bob::io::c256:
+        return PyBobIoHDF5File_ReadScalarAttribute<std::complex<long double> >(self, path, name, type);
+      default:
+        break;
+    }
+  }
+
+  //read as an numpy array
+  int type_num = PyBobIo_H5AsTypenum(type.type());
+  if (type_num == NPY_NOTYPE) return 0; ///< failure
+
+  npy_intp pyshape[NPY_MAXDIMS];
+  for (int k=0; k<shape.n(); ++k) pyshape[k] = shape.get()[k];
+
+  PyObject* retval = PyArray_SimpleNew(shape.n(), pyshape, type_num);
+  if (!retval) return 0;
+
+  try {
+    self->f->read_attribute(path, name, type, PyArray_DATA((PyArrayObject*)retval));
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    Py_DECREF(retval);
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading array attribute `%s' at resource `%s' with descriptor `%s' from HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+    Py_DECREF(retval);
+    return 0;
+  }
+
+  return retval;
+}
+
 static PyObject* PyBobIoHDF5File_GetAttribute(PyBobIoHDF5FileObject* self, PyObject *args, PyObject* kwds) {
   
   /* Parses input arguments in a single shot */
@@ -1541,7 +1653,28 @@ static PyObject* PyBobIoHDF5File_GetAttribute(PyBobIoHDF5FileObject* self, PyObj
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &name, &path)) return 0;
 
-  Py_RETURN_NONE;
+  bob::io::HDF5Type type;
+
+  try {
+    self->f->getAttributeType(path, name, type);
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while getting type for attribute `%s' at resource `%s' from HDF5 file `%s'", name, path, self->f->filename().c_str());
+    return 0;
+  }
+
+  if (type.type() == bob::io::unsupported) {
+    boost::format m("unsupported HDF5 data type detected for attribute `%s' at path `%s' of file `%s' - returning None");
+    m % name % path % self->f->filename();
+    PyErr_Warn(PyExc_UserWarning, m.str().c_str());
+    Py_RETURN_NONE;
+  }
+
+  return PyBobIoHDF5File_ReadAttribute(self, path, name, type);
 }
 
 PyDoc_STRVAR(s_get_attribute_str, "get_attribute");
@@ -1577,7 +1710,30 @@ static PyObject* PyBobIoHDF5File_GetAttributes(PyBobIoHDF5FileObject* self, PyOb
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &path)) return 0;
 
-  Py_RETURN_NONE;
+  std::map<std::string, bob::io::HDF5Type> attributes;
+  self->f->listAttributes(path, attributes);
+  PyObject* retval = PyDict_New();
+  for (auto k=attributes.begin(); k!=attributes.end(); ++k) {
+    PyObject* item = 0;
+    if (k->second.type() == bob::io::unsupported) {
+      boost::format m("unsupported HDF5 data type detected for attribute `%s' at path `%s' of file `%s' - returning None");
+      m % k->first % k->second.str() % self->f->filename();
+      PyErr_Warn(PyExc_UserWarning, m.str().c_str());
+      item = Py_None;
+      Py_INCREF(item);
+      Py_INCREF(Py_None);
+    }
+    else item = PyBobIoHDF5File_ReadAttribute(self, path, k->first.c_str(), k->second);
+    int status = PyDict_SetItemString(retval, k->first.c_str(), item);
+    Py_DECREF(item);
+    if (status != 0) {
+      Py_DECREF(retval);
+      return 0;
+    }
+  }
+
+  return retval;
+
 }
 
 PyDoc_STRVAR(s_get_attributes_str, "get_attributes");
@@ -1600,6 +1756,137 @@ to the value stored inside the HDF5 file. To retrieve only\n\
 a specific attribute, use :py:meth:`HDF5File.get_attribute()`.\n\
 ");
 
+template <typename T> PyObject* PyBobIoHDF5File_WriteScalarAttribute
+(PyBobIoHDF5FileObject* self, const char* path, const char* name,
+ const bob::io::HDF5Type& type, PyObject* o) {
+
+  T value = PyBlitzArrayCxx_AsCScalar<T>(o);
+  if (PyErr_Occurred()) return 0;
+
+  try {
+    self->f->write_attribute(path, name, type, static_cast<void*>(&value));
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while writing attribute `%s' at resource `%s' with descriptor `%s' at HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+    return 0;
+  }
+
+  Py_RETURN_NONE;
+
+}
+
+template <> PyObject* PyBobIoHDF5File_WriteScalarAttribute<const char*>
+(PyBobIoHDF5FileObject* self, const char* path, const char* name,
+ const bob::io::HDF5Type& type, PyObject* o) {
+
+  const char* value = PyBobIo_GetString(o);
+  if (!value) return 0;
+
+  try {
+    self->f->write_attribute(path, name, type, static_cast<const void*>(value));
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "caught unknown exception while writing string attribute `%s' at resource `%s' with descriptor `%s' at HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+    return 0;
+  }
+
+  Py_RETURN_NONE;
+
+}
+
+static PyObject* PyBobIoHDF5File_WriteAttribute(PyBobIoHDF5FileObject* self,
+    const char* path, const char* name, const bob::io::HDF5Type& type,
+    PyObject* o, int is_array, PyObject* converted) {
+
+  //no error detection: this should be done before reaching this method
+
+  if (!is_array) { //write as a scalar
+    switch(type.type()) {
+      case bob::io::s:
+        return PyBobIoHDF5File_WriteScalarAttribute<const char*>(self, path, name, type, o);
+      case bob::io::b:
+        return PyBobIoHDF5File_WriteScalarAttribute<bool>(self, path, name, type, o);
+      case bob::io::i8:
+        return PyBobIoHDF5File_WriteScalarAttribute<int8_t>(self, path, name, type, o);
+      case bob::io::i16:
+        return PyBobIoHDF5File_WriteScalarAttribute<int16_t>(self, path, name, type, o);
+      case bob::io::i32:
+        return PyBobIoHDF5File_WriteScalarAttribute<int32_t>(self, path, name, type, o);
+      case bob::io::i64:
+        return PyBobIoHDF5File_WriteScalarAttribute<int64_t>(self, path, name, type, o);
+      case bob::io::u8:
+        return PyBobIoHDF5File_WriteScalarAttribute<uint8_t>(self, path, name, type, o);
+      case bob::io::u16:
+        return PyBobIoHDF5File_WriteScalarAttribute<uint16_t>(self, path, name, type, o);
+      case bob::io::u32:
+        return PyBobIoHDF5File_WriteScalarAttribute<uint32_t>(self, path, name, type, o);
+      case bob::io::u64:
+        return PyBobIoHDF5File_WriteScalarAttribute<uint64_t>(self, path, name, type, o);
+      case bob::io::f32:
+        return PyBobIoHDF5File_WriteScalarAttribute<float>(self, path, name, type, o);
+      case bob::io::f64:
+        return PyBobIoHDF5File_WriteScalarAttribute<double>(self, path, name, type, o);
+      case bob::io::f128:
+        return PyBobIoHDF5File_WriteScalarAttribute<long double>(self, path, name, type, o);
+      case bob::io::c64:
+        return PyBobIoHDF5File_WriteScalarAttribute<std::complex<float> >(self, path, name, type, o);
+      case bob::io::c128:
+        return PyBobIoHDF5File_WriteScalarAttribute<std::complex<double> >(self, path, name, type, o);
+      case bob::io::c256:
+        return PyBobIoHDF5File_WriteScalarAttribute<std::complex<long double> >(self, path, name, type, o);
+      default:
+        break;
+    }
+  }
+
+  else { //write as an numpy array
+     
+    try {
+      switch (is_array) {
+
+        case 1: //blitz.array
+          self->f->write_attribute(path, name, type, ((PyBlitzArrayObject*)o)->data);
+          break;
+
+        case 2: //numpy.ndarray
+          self->f->write_attribute(path, name, type, PyArray_DATA((PyArrayObject*)o));
+          break;
+
+        case 3: //converted numpy.ndarray
+          self->f->write_attribute(path, name, type, PyArray_DATA((PyArrayObject*)converted));
+          Py_DECREF(converted);
+          break;
+
+        default:
+          PyErr_Format(PyExc_NotImplementedError, "error setting attribute `%s' at resource `%s' of HDF5 file `%s': HDF5 attribute setting function is uncovered for array type %d (DEBUG ME)", name, path, self->f->filename().c_str(), is_array);
+          return 0;
+      }
+    }
+    catch (std::exception& e) {
+      PyErr_SetString(PyExc_RuntimeError, e.what());
+      Py_XDECREF(converted);
+      return 0;
+    }
+    catch (...) {
+      PyErr_Format(PyExc_RuntimeError, "caught unknown exception while writing array attribute `%s' at resource `%s' with descriptor `%s' at HDF5 file `%s'", name, path, type.str().c_str(), self->f->filename().c_str());
+      Py_XDECREF(converted);
+      return 0;
+    }
+
+  }
+
+  Py_RETURN_NONE;
+
+}
+
 static PyObject* PyBobIoHDF5File_SetAttribute(PyBobIoHDF5FileObject* self, PyObject *args, PyObject* kwds) {
   
   /* Parses input arguments in a single shot */
@@ -1611,7 +1898,16 @@ static PyObject* PyBobIoHDF5File_SetAttribute(PyBobIoHDF5FileObject* self, PyObj
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|s", kwlist, &name, &value, &path)) return 0;
 
-  Py_RETURN_NONE;
+  bob::io::HDF5Type type;
+  PyObject* converted = 0;
+  int is_array = PyBobIoHDF5File_GetObjectType(value, type, &converted);
+  if (is_array < 0) { ///< error condition, signal
+    PyErr_Format(PyExc_TypeError, "error setting attribute `%s' of resource `%s' at HDF5 file `%s': no support for storing objects of type `%s' on HDF5 files", name, path, self->f->filename().c_str(), value->ob_type->tp_name);
+    return 0;
+  }
+
+  return PyBobIoHDF5File_WriteAttribute(self, path, name, type, value, is_array, converted);
+
 }
 
 PyDoc_STRVAR(s_set_attribute_str, "set_attribute");
@@ -1660,7 +1956,34 @@ static PyObject* PyBobIoHDF5File_SetAttributes(PyBobIoHDF5FileObject* self, PyOb
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s", kwlist, &attrs, &path)) return 0;
 
+  if (!PyDict_Check(attrs)) {
+    PyErr_SetString(PyExc_TypeError, "parameter `attrs' should be a dictionary where keys are strings and values are the attribute values");
+    return 0;
+  }
+
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+  while (PyDict_Next(attrs, &pos, &key, &value)) {
+    bob::io::HDF5Type type;
+    PyObject* converted = 0;
+
+    const char* name = PyBobIo_GetString(key);
+    if (!name) return 0;
+
+    int is_array = PyBobIoHDF5File_GetObjectType(value, type, &converted);
+    if (is_array < 0) { ///< error condition, signal
+      PyErr_Format(PyExc_TypeError, "error setting attribute `%s' of resource `%s' at HDF5 file `%s': no support for storing objects of type `%s' on HDF5 files", name, path, self->f->filename().c_str(), value->ob_type->tp_name);
+      return 0;
+    }
+
+    PyObject* retval = PyBobIoHDF5File_WriteAttribute(self, path, name, type, value, is_array, converted);
+    if (!retval) return 0;
+    Py_DECREF(retval);
+
+  }
+
   Py_RETURN_NONE;
+
 }
 
 PyDoc_STRVAR(s_set_attributes_str, "set_attributes");
@@ -1706,6 +2029,18 @@ static PyObject* PyBobIoHDF5File_DelAttribute(PyBobIoHDF5FileObject* self, PyObj
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &name, &path)) return 0;
 
+  try {
+    self->f->deleteAttribute(path, name);
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "cannot delete attribute `%s' at resource `%s' of HDF5 file `%s': unknown exception caught", name, path, self->f->filename().c_str());
+    return 0;
+  }
+
   Py_RETURN_NONE;
 }
 
@@ -1741,6 +2076,66 @@ static PyObject* PyBobIoHDF5File_DelAttributes(PyBobIoHDF5FileObject* self, PyOb
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Os", kwlist, &attrs, &path)) return 0;
 
+  if (attrs && !PyIter_Check(attrs)) {
+    PyErr_SetString(PyExc_TypeError, "parameter `attrs', if set, must be an iterable of strings");
+    return 0;
+  }
+
+  if (attrs) {
+    PyObject* iter = PyObject_GetIter(attrs);
+    if (!iter) return 0;
+    while (PyObject* item = PyIter_Next(iter)) {
+      const char* name = PyBobIo_GetString(item);
+      Py_DECREF(item);
+      if (!name) {
+        Py_DECREF(iter);
+        return 0;
+      }
+      try {
+        self->f->deleteAttribute(path, name);
+      }
+      catch (std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        Py_DECREF(iter);
+        return 0;
+      }
+      catch (...) {
+        PyErr_Format(PyExc_RuntimeError, "cannot delete attribute `%s' at resource `%s' of HDF5 file `%s': unknown exception caught", name, path, self->f->filename().c_str());
+        Py_DECREF(iter);
+        return 0;
+      }
+    }
+    Py_DECREF(iter);
+    Py_RETURN_NONE;
+  }
+
+  //else, find the attributes and remove all of them
+  std::map<std::string, bob::io::HDF5Type> attributes;
+  try {
+    self->f->listAttributes(path, attributes);
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "cannot list attributes at resource `%s' of HDF5 file `%s': unknown exception caught", path, self->f->filename().c_str());
+    return 0;
+  }
+  for (auto k=attributes.begin(); k!=attributes.end(); ++k) {
+    try {
+      self->f->deleteAttribute(path, k->first);
+    }
+    catch (std::exception& e) {
+      PyErr_SetString(PyExc_RuntimeError, e.what());
+      return 0;
+    }
+    catch (...) {
+      PyErr_Format(PyExc_RuntimeError, "cannot delete attribute `%s' at resource `%s' of HDF5 file `%s': unknown exception caught", k->first.c_str(), path, self->f->filename().c_str());
+      return 0;
+    }
+  }
+
   Py_RETURN_NONE;
 }
 
@@ -1775,7 +2170,18 @@ static PyObject* PyBobIoHDF5File_HasAttribute(PyBobIoHDF5FileObject* self, PyObj
   const char* path = ".";
   if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &name, &path)) return 0;
 
-  Py_RETURN_NONE;
+  try {
+    if (self->f->hasAttribute(path, name)) Py_RETURN_TRUE;
+    Py_RETURN_FALSE;
+  }
+  catch (std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return 0;
+  }
+  catch (...) {
+    PyErr_Format(PyExc_RuntimeError, "cannot verify existence of attribute `%s' at resource `%s' of HDF5 file `%s': unknown exception caught", name, path, self->f->filename().c_str());
+    return 0;
+  }
 }
 
 PyDoc_STRVAR(s_has_attribute_str, "has_attribute");
-- 
GitLab