test_attrs.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. Attributes testing module
  11. Covers all operations which access the .attrs property, with the
  12. exception of data read/write and type conversion. Those operations
  13. are tested by module test_attrs_data.
  14. """
  15. import numpy as np
  16. from collections.abc import MutableMapping
  17. from .common import TestCase, make_name
  18. import h5py
  19. from h5py import File
  20. from h5py import h5a, h5t
  21. from h5py import AttributeManager
  22. class BaseAttrs(TestCase):
  23. def setUp(self):
  24. self.f = File(self.mktemp(), 'w')
  25. def tearDown(self):
  26. if self.f:
  27. self.f.close()
  28. class TestRepr(TestCase):
  29. """ Feature: AttributeManager provide a helpful
  30. __repr__ string
  31. """
  32. def test_repr(self):
  33. grp = self.f.create_group(make_name())
  34. grp.attrs.create('att', 1)
  35. self.assertIsInstance(repr(grp.attrs), str)
  36. grp.id.close()
  37. self.assertIsInstance(repr(grp.attrs), str)
  38. class TestAccess(BaseAttrs):
  39. """
  40. Feature: Attribute creation/retrieval via special methods
  41. """
  42. def test_create(self):
  43. """ Attribute creation by direct assignment """
  44. name = make_name()
  45. self.f.attrs[name] = 4.0
  46. assert name in self.f.attrs
  47. self.assertEqual(self.f.attrs[name], 4.0)
  48. def test_create_2(self):
  49. """ Attribute creation by create() method """
  50. name = make_name()
  51. self.f.attrs.create(name, 4.0)
  52. assert name in self.f.attrs
  53. self.assertEqual(self.f.attrs[name], 4.0)
  54. def test_modify(self):
  55. """ Attributes are modified by direct assignment"""
  56. name = make_name()
  57. self.f.attrs[name] = 3
  58. assert name in self.f.attrs
  59. self.assertEqual(self.f.attrs[name], 3)
  60. self.f.attrs[name] = 4
  61. self.assertEqual(self.f.attrs[name], 4)
  62. def test_modify_2(self):
  63. """ Attributes are modified by modify() method """
  64. a = make_name("a")
  65. b = make_name("b")
  66. self.f.attrs.modify(a, 3)
  67. assert a in self.f.attrs
  68. self.assertEqual(self.f.attrs[a], 3)
  69. self.f.attrs.modify(a, 4)
  70. assert a in self.f.attrs
  71. self.assertEqual(self.f.attrs[a], 4)
  72. # If the attribute doesn't exist, create new
  73. self.f.attrs.modify(b, 5)
  74. assert a in self.f.attrs
  75. assert b in self.f.attrs
  76. self.assertEqual(self.f.attrs[a], 4)
  77. self.assertEqual(self.f.attrs[b], 5)
  78. # Shape of new value is incompatible with the previous
  79. new_value = np.arange(5)
  80. with self.assertRaises(TypeError):
  81. self.f.attrs.modify(b, new_value)
  82. def test_overwrite(self):
  83. """ Attributes are silently overwritten """
  84. name = make_name()
  85. self.f.attrs[name] = 4.0
  86. self.f.attrs[name] = 5.0
  87. self.assertEqual(self.f.attrs[name], 5.0)
  88. def test_rank(self):
  89. """ Attribute rank is preserved """
  90. name = make_name()
  91. self.f.attrs[name] = (4.0, 5.0)
  92. self.assertEqual(self.f.attrs[name].shape, (2,))
  93. self.assertArrayEqual(self.f.attrs[name], np.array((4.0,5.0)))
  94. def test_single(self):
  95. """ Attributes of shape (1,) don't become scalars """
  96. name = make_name()
  97. self.f.attrs[name] = np.ones((1,))
  98. out = self.f.attrs[name]
  99. self.assertEqual(out.shape, (1,))
  100. self.assertEqual(out[()], 1)
  101. def test_access_exc(self):
  102. """ Attempt to access missing item raises KeyError """
  103. with self.assertRaises(KeyError):
  104. self.f.attrs['notexist']
  105. def test_get_id(self):
  106. name = make_name()
  107. self.f.attrs[name] = 4.0
  108. aid = self.f.attrs.get_id(name)
  109. assert isinstance(aid, h5a.AttrID)
  110. with self.assertRaises(KeyError):
  111. self.f.attrs.get_id("notexist")
  112. class TestDelete(BaseAttrs):
  113. """
  114. Feature: Deletion of attributes using __delitem__
  115. """
  116. def test_delete(self):
  117. """ Deletion via "del" """
  118. name = make_name()
  119. self.f.attrs[name] = 4.0
  120. self.assertIn(name, self.f.attrs)
  121. del self.f.attrs[name]
  122. self.assertNotIn(name, self.f.attrs)
  123. def test_delete_exc(self):
  124. """ Attempt to delete missing item raises KeyError """
  125. with self.assertRaises(KeyError):
  126. del self.f.attrs['notexist']
  127. class TestUnicode(BaseAttrs):
  128. """
  129. Feature: Attributes can be accessed via Unicode or byte strings
  130. """
  131. def test_ascii(self):
  132. """ Access via pure-ASCII byte string """
  133. name = make_name("ascii").encode("ascii")
  134. self.f.attrs[name] = 42
  135. out = self.f.attrs[name]
  136. self.assertEqual(out, 42)
  137. def test_raw(self):
  138. """ Access via non-ASCII byte string """
  139. name = make_name("non-ascii\xfe").encode("utf-8")
  140. self.f.attrs[name] = 42
  141. out = self.f.attrs[name]
  142. self.assertEqual(out, 42)
  143. def test_unicode(self):
  144. """ Access via Unicode string with non-ascii characters """
  145. name = make_name("Omega" + chr(0x03A9))
  146. self.f.attrs[name] = 42
  147. out = self.f.attrs[name]
  148. self.assertEqual(out, 42)
  149. class TestCreate(BaseAttrs):
  150. """
  151. Options for explicit attribute creation
  152. """
  153. def test_named(self):
  154. """ Attributes created from named types link to the source type object
  155. """
  156. t = make_name("t")
  157. x = make_name("x")
  158. self.f[t] = np.dtype('u8')
  159. self.f.attrs.create(x, 42, dtype=self.f[t])
  160. self.assertEqual(self.f.attrs[x], 42)
  161. aid = h5a.open(self.f.id, x.encode('utf-8'))
  162. htype = aid.get_type()
  163. htype2 = self.f[t].id
  164. self.assertEqual(htype, htype2)
  165. self.assertTrue(htype.committed())
  166. def test_empty(self):
  167. # https://github.com/h5py/h5py/issues/1540
  168. """ Create attribute with h5py.Empty value
  169. """
  170. name = make_name()
  171. self.f.attrs.create(name, h5py.Empty('f'))
  172. self.assertEqual(self.f.attrs[name], h5py.Empty('f'))
  173. self.f.attrs.create(name, h5py.Empty(None))
  174. self.assertEqual(self.f.attrs[name], h5py.Empty(None))
  175. class TestMutableMapping(BaseAttrs):
  176. '''Tests if the registration of AttributeManager as a MutableMapping
  177. behaves as expected
  178. '''
  179. def test_resolution(self):
  180. assert issubclass(AttributeManager, MutableMapping)
  181. assert isinstance(self.f.attrs, MutableMapping)
  182. def test_validity(self):
  183. '''
  184. Test that the required functions are implemented.
  185. '''
  186. AttributeManager.__getitem__
  187. AttributeManager.__setitem__
  188. AttributeManager.__delitem__
  189. AttributeManager.__iter__
  190. AttributeManager.__len__
  191. class TestVlen(BaseAttrs):
  192. def test_vlen(self):
  193. name = make_name()
  194. a = np.array([np.arange(3), np.arange(4)],
  195. dtype=h5t.vlen_dtype(int))
  196. self.f.attrs[name] = a
  197. self.assertArrayEqual(self.f.attrs[name][0], a[0])
  198. def test_vlen_s1(self):
  199. name = make_name()
  200. dt = h5py.vlen_dtype(np.dtype('S1'))
  201. a = np.empty((1,), dtype=dt)
  202. a[0] = np.array([b'a', b'b'], dtype='S1')
  203. self.f.attrs.create(name, a)
  204. self.assertArrayEqual(self.f.attrs[name][0], a[0])
  205. class TestTrackOrder(BaseAttrs):
  206. def fill_attrs(self, track_order):
  207. attrs = self.f.create_group(make_name(), track_order=track_order).attrs
  208. for i in range(100):
  209. attrs[str(i)] = i
  210. return attrs
  211. # https://forum.hdfgroup.org/t/bug-h5arename-fails-unexpectedly/4881
  212. def test_track_order(self):
  213. attrs = self.fill_attrs(track_order=True) # creation order
  214. self.assertEqual(list(attrs),
  215. [str(i) for i in range(100)])
  216. def test_no_track_order(self):
  217. attrs = self.fill_attrs(track_order=False) # name alphanumeric
  218. self.assertEqual(list(attrs),
  219. sorted([str(i) for i in range(100)]))
  220. def test_track_order_overwrite_delete(self):
  221. # issue 1385
  222. group = self.f.create_group(make_name(), track_order=True)
  223. for i in range(12):
  224. group.attrs[str(i)] = i
  225. self.assertEqual(group.attrs["11"], 11)
  226. # overwrite attribute
  227. group.attrs['11'] = 42.0
  228. self.assertEqual(group.attrs["11"], 42.0)
  229. # delete attribute
  230. self.assertIn('10', group.attrs)
  231. del group.attrs['10']
  232. self.assertNotIn('10', group.attrs)
  233. class TestDatatype(BaseAttrs):
  234. def test_datatype(self):
  235. name = make_name()
  236. self.f[name] = np.dtype('f')
  237. dt = self.f[name]
  238. self.assertEqual(list(dt.attrs.keys()), [])
  239. dt.attrs.create('a', 4.0)
  240. self.assertEqual(list(dt.attrs.keys()), ['a'])
  241. self.assertEqual(list(dt.attrs.values()), [4.0])
  242. def test_python_int_uint64(writable_file):
  243. f = writable_file
  244. name = make_name()
  245. data = [np.iinfo(np.int64).max, np.iinfo(np.int64).max + 1]
  246. # Check creating a new attribute
  247. f.attrs.create(name, data, dtype=np.uint64)
  248. assert f.attrs[name].dtype == np.dtype(np.uint64)
  249. np.testing.assert_array_equal(f.attrs[name], np.array(data, dtype=np.uint64))
  250. # Check modifying an existing attribute
  251. f.attrs.modify(name, data)
  252. np.testing.assert_array_equal(f.attrs[name], np.array(data, dtype=np.uint64))