test_attrs_data.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. # This file is part of h5py, a Python interface to the HDF5 library.
  2. #
  3. # http://www.h5py.org
  4. #
  5. # Copyright 2008-2013 Andrew Collette and contributors
  6. #
  7. # License: Standard 3-clause BSD; see "license.txt" for full license terms
  8. # and contributor agreement.
  9. """
  10. Attribute data transfer testing module
  11. Covers all data read/write and type-conversion operations for attributes.
  12. """
  13. import numpy as np
  14. from .common import TestCase, make_name
  15. import h5py
  16. from h5py import h5a, h5s, h5t
  17. from h5py import File
  18. from h5py._hl.base import is_empty_dataspace
  19. class BaseAttrs(TestCase):
  20. def setUp(self):
  21. self.f = File(self.mktemp(), 'w')
  22. def tearDown(self):
  23. if self.f:
  24. self.f.close()
  25. class TestScalar(BaseAttrs):
  26. """
  27. Feature: Scalar types map correctly to array scalars
  28. """
  29. def test_int(self):
  30. """ Integers are read as correct NumPy type """
  31. name = make_name()
  32. self.f.attrs[name] = np.array(1, dtype=np.int8)
  33. out = self.f.attrs[name]
  34. self.assertIsInstance(out, np.int8)
  35. def test_compound(self):
  36. """ Compound scalars are read as numpy.void """
  37. name = make_name()
  38. dt = np.dtype([('a', 'i'), ('b', 'f')])
  39. data = np.array((1, 4.2), dtype=dt)
  40. self.f.attrs[name] = data
  41. out = self.f.attrs[name]
  42. self.assertIsInstance(out, np.void)
  43. self.assertEqual(out, data)
  44. self.assertEqual(out['b'], data['b'])
  45. def test_compound_with_vlen_fields(self):
  46. """ Compound scalars with vlen fields can be written and read """
  47. name = make_name()
  48. dt = np.dtype([('a', h5py.vlen_dtype(np.int32)),
  49. ('b', h5py.vlen_dtype(np.int32))])
  50. data = np.array((np.array(list(range(1, 5)), dtype=np.int32),
  51. np.array(list(range(8, 10)), dtype=np.int32)), dtype=dt)[()]
  52. self.f.attrs[name] = data
  53. out = self.f.attrs[name]
  54. # Specifying check_alignment=False because vlen fields have 8 bytes of padding
  55. # because the vlen datatype in hdf5 occupies 16 bytes
  56. self.assertArrayEqual(out, data, check_alignment=False)
  57. def test_nesting_compound_with_vlen_fields(self):
  58. """ Compound scalars with nested compound vlen fields can be written and read """
  59. dt_inner = np.dtype([('a', h5py.vlen_dtype(np.int32)),
  60. ('b', h5py.vlen_dtype(np.int32))])
  61. dt = np.dtype([('f1', h5py.vlen_dtype(dt_inner)),
  62. ('f2', np.int64)])
  63. inner1 = (np.array(range(1, 3), dtype=np.int32),
  64. np.array(range(6, 9), dtype=np.int32))
  65. inner2 = (np.array(range(10, 14), dtype=np.int32),
  66. np.array(range(16, 20), dtype=np.int32))
  67. data = np.array((np.array([inner1, inner2], dtype=dt_inner),
  68. 2),
  69. dtype=dt)[()]
  70. name = make_name()
  71. self.f.attrs[name] = data
  72. out = self.f.attrs[name]
  73. self.assertArrayEqual(out, data, check_alignment=False)
  74. def test_vlen_compound_with_vlen_string(self):
  75. """ Compound scalars with vlen compounds containing vlen strings can be written and read """
  76. dt_inner = np.dtype([('a', h5py.string_dtype()),
  77. ('b', h5py.string_dtype())])
  78. dt = np.dtype([('f', h5py.vlen_dtype(dt_inner))])
  79. name = make_name()
  80. data = np.array((np.array([(b"apples", b"bananas"), (b"peaches", b"oranges")], dtype=dt_inner),),dtype=dt)[()]
  81. self.f.attrs[name] = data
  82. out = self.f.attrs[name]
  83. self.assertArrayEqual(out, data, check_alignment=False)
  84. class TestArray(BaseAttrs):
  85. """
  86. Feature: Non-scalar types are correctly retrieved as ndarrays
  87. """
  88. def test_single(self):
  89. """ Single-element arrays are correctly recovered """
  90. name = make_name()
  91. data = np.ndarray((1,), dtype='f')
  92. self.f.attrs[name] = data
  93. out = self.f.attrs[name]
  94. self.assertIsInstance(out, np.ndarray)
  95. self.assertEqual(out.shape, (1,))
  96. def test_multi(self):
  97. """ Rank-1 arrays are correctly recovered """
  98. name = make_name()
  99. data = np.ndarray((42,), dtype='f')
  100. data[:] = 42.0
  101. data[10:35] = -47.0
  102. self.f.attrs[name] = data
  103. out = self.f.attrs[name]
  104. self.assertIsInstance(out, np.ndarray)
  105. self.assertEqual(out.shape, (42,))
  106. self.assertArrayEqual(out, data)
  107. class TestTypes(BaseAttrs):
  108. """
  109. Feature: All supported types can be stored in attributes
  110. """
  111. def test_int(self):
  112. """ Storage of integer types """
  113. name = make_name()
  114. dtypes = (np.int8, np.int16, np.int32, np.int64,
  115. np.uint8, np.uint16, np.uint32, np.uint64)
  116. for dt in dtypes:
  117. data = np.ndarray((1,), dtype=dt)
  118. data[...] = 42
  119. self.f.attrs[name] = data
  120. out = self.f.attrs[name]
  121. self.assertEqual(out.dtype, dt)
  122. self.assertArrayEqual(out, data)
  123. def test_float(self):
  124. """ Storage of floating point types """
  125. name = make_name()
  126. dtypes = tuple(np.dtype(x) for x in ('<f4', '>f4', '>f8', '<f8'))
  127. for dt in dtypes:
  128. data = np.ndarray((1,), dtype=dt)
  129. data[...] = 42.3
  130. self.f.attrs[name] = data
  131. out = self.f.attrs[name]
  132. self.assertEqual(out.dtype, dt)
  133. self.assertArrayEqual(out, data)
  134. def test_complex(self):
  135. """ Storage of complex types """
  136. name = make_name()
  137. dtypes = tuple(np.dtype(x) for x in ('<c8', '>c8', '<c16', '>c16'))
  138. for dt in dtypes:
  139. data = np.ndarray((1,), dtype=dt)
  140. data[...] = -4.2j + 35.9
  141. self.f.attrs[name] = data
  142. out = self.f.attrs[name]
  143. self.assertEqual(out.dtype, dt)
  144. self.assertArrayEqual(out, data)
  145. def test_string(self):
  146. """ Storage of fixed-length strings """
  147. name = make_name()
  148. dtypes = tuple(np.dtype(x) for x in ('|S1', '|S10'))
  149. for dt in dtypes:
  150. data = np.ndarray((1,), dtype=dt)
  151. data[...] = 'h'
  152. self.f.attrs[name] = data
  153. out = self.f.attrs[name]
  154. self.assertEqual(out.dtype, dt)
  155. self.assertEqual(out[0], data[0])
  156. def test_bool(self):
  157. """ Storage of NumPy booleans """
  158. name = make_name()
  159. data = np.ndarray((2,), dtype=np.bool_)
  160. data[...] = True, False
  161. self.f.attrs[name] = data
  162. out = self.f.attrs[name]
  163. self.assertEqual(out.dtype, data.dtype)
  164. self.assertEqual(out[0], data[0])
  165. self.assertEqual(out[1], data[1])
  166. def test_vlen_string_array(self):
  167. """ Storage of vlen byte string arrays"""
  168. name = make_name()
  169. dt = h5py.string_dtype(encoding='ascii')
  170. data = np.ndarray((2,), dtype=dt)
  171. data[...] = "Hello", "Hi there! This is HDF5!"
  172. self.f.attrs[name] = data
  173. out = self.f.attrs[name]
  174. self.assertEqual(out.dtype, dt)
  175. self.assertEqual(out[0], data[0])
  176. self.assertEqual(out[1], data[1])
  177. def test_string_scalar(self):
  178. """ Storage of variable-length byte string scalars (auto-creation) """
  179. name = make_name()
  180. self.f.attrs[name] = b'Hello'
  181. out = self.f.attrs[name]
  182. self.assertEqual(out, 'Hello')
  183. self.assertEqual(type(out), str)
  184. aid = h5py.h5a.open(self.f.id, name.encode('utf-8'))
  185. tid = aid.get_type()
  186. self.assertEqual(type(tid), h5py.h5t.TypeStringID)
  187. self.assertEqual(tid.get_cset(), h5py.h5t.CSET_ASCII)
  188. self.assertTrue(tid.is_variable_str())
  189. def test_unicode_scalar(self):
  190. """ Storage of variable-length unicode strings (auto-creation) """
  191. name = make_name()
  192. self.f.attrs[name] = u"Hello" + chr(0x2340) + u"!!"
  193. out = self.f.attrs[name]
  194. self.assertEqual(out, u"Hello" + chr(0x2340) + u"!!")
  195. self.assertEqual(type(out), str)
  196. aid = h5py.h5a.open(self.f.id, name.encode('utf-8'))
  197. tid = aid.get_type()
  198. self.assertEqual(type(tid), h5py.h5t.TypeStringID)
  199. self.assertEqual(tid.get_cset(), h5py.h5t.CSET_UTF8)
  200. self.assertTrue(tid.is_variable_str())
  201. class TestEmpty(BaseAttrs):
  202. def setUp(self):
  203. BaseAttrs.setUp(self)
  204. sid = h5s.create(h5s.NULL)
  205. tid = h5t.C_S1.copy()
  206. tid.set_size(10)
  207. aid = h5a.create(self.f.id, b'x', tid, sid)
  208. self.empty_obj = h5py.Empty(np.dtype("S10"))
  209. def test_read(self):
  210. self.assertEqual(
  211. self.empty_obj, self.f.attrs['x']
  212. )
  213. def test_write(self):
  214. name = make_name()
  215. self.f.attrs[name] = self.empty_obj
  216. self.assertTrue(
  217. is_empty_dataspace(h5a.open(self.f.id, name.encode("utf-8")))
  218. )
  219. def test_modify(self):
  220. with self.assertRaises(OSError):
  221. self.f.attrs.modify('x', 1)
  222. def test_values(self):
  223. # list() is for Py3 where these are iterators
  224. values = list(self.f.attrs.values())
  225. self.assertEqual(
  226. [self.empty_obj], values
  227. )
  228. def test_items(self):
  229. items = list(self.f.attrs.items())
  230. self.assertEqual(
  231. [(u"x", self.empty_obj)], items
  232. )
  233. def test_itervalues(self):
  234. values = list(self.f.attrs.values())
  235. self.assertEqual(
  236. [self.empty_obj], values
  237. )
  238. def test_iteritems(self):
  239. items = list(self.f.attrs.items())
  240. self.assertEqual(
  241. [(u"x", self.empty_obj)], items
  242. )
  243. class TestWriteException(BaseAttrs):
  244. """
  245. Ensure failed attribute writes don't leave garbage behind.
  246. """
  247. def test_write(self):
  248. """ ValueError on string write wipes out attribute """
  249. s = b"Hello\x00Hello"
  250. with self.assertRaises(ValueError):
  251. self.f.attrs["x"] = s
  252. with self.assertRaises(KeyError):
  253. self.f.attrs["x"]