test_attrs.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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, ut
  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('grp')
  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. self.f.attrs['a'] = 4.0
  45. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  46. self.assertEqual(self.f.attrs['a'], 4.0)
  47. def test_create_2(self):
  48. """ Attribute creation by create() method """
  49. self.f.attrs.create('a', 4.0)
  50. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  51. self.assertEqual(self.f.attrs['a'], 4.0)
  52. def test_modify(self):
  53. """ Attributes are modified by direct assignment"""
  54. self.f.attrs['a'] = 3
  55. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  56. self.assertEqual(self.f.attrs['a'], 3)
  57. self.f.attrs['a'] = 4
  58. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  59. self.assertEqual(self.f.attrs['a'], 4)
  60. def test_modify_2(self):
  61. """ Attributes are modified by modify() method """
  62. self.f.attrs.modify('a',3)
  63. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  64. self.assertEqual(self.f.attrs['a'], 3)
  65. self.f.attrs.modify('a', 4)
  66. self.assertEqual(list(self.f.attrs.keys()), ['a'])
  67. self.assertEqual(self.f.attrs['a'], 4)
  68. # If the attribute doesn't exist, create new
  69. self.f.attrs.modify('b', 5)
  70. self.assertEqual(list(self.f.attrs.keys()), ['a', 'b'])
  71. self.assertEqual(self.f.attrs['a'], 4)
  72. self.assertEqual(self.f.attrs['b'], 5)
  73. # Shape of new value is incompatible with the previous
  74. new_value = np.arange(5)
  75. with self.assertRaises(TypeError):
  76. self.f.attrs.modify('b', new_value)
  77. def test_overwrite(self):
  78. """ Attributes are silently overwritten """
  79. self.f.attrs['a'] = 4.0
  80. self.f.attrs['a'] = 5.0
  81. self.assertEqual(self.f.attrs['a'], 5.0)
  82. def test_rank(self):
  83. """ Attribute rank is preserved """
  84. self.f.attrs['a'] = (4.0, 5.0)
  85. self.assertEqual(self.f.attrs['a'].shape, (2,))
  86. self.assertArrayEqual(self.f.attrs['a'], np.array((4.0,5.0)))
  87. def test_single(self):
  88. """ Attributes of shape (1,) don't become scalars """
  89. self.f.attrs['a'] = np.ones((1,))
  90. out = self.f.attrs['a']
  91. self.assertEqual(out.shape, (1,))
  92. self.assertEqual(out[()], 1)
  93. def test_access_exc(self):
  94. """ Attempt to access missing item raises KeyError """
  95. with self.assertRaises(KeyError):
  96. self.f.attrs['a']
  97. def test_get_id(self):
  98. self.f.attrs['a'] = 4.0
  99. aid = self.f.attrs.get_id('a')
  100. assert isinstance(aid, h5a.AttrID)
  101. with self.assertRaises(KeyError):
  102. self.f.attrs.get_id('b')
  103. class TestDelete(BaseAttrs):
  104. """
  105. Feature: Deletion of attributes using __delitem__
  106. """
  107. def test_delete(self):
  108. """ Deletion via "del" """
  109. self.f.attrs['a'] = 4.0
  110. self.assertIn('a', self.f.attrs)
  111. del self.f.attrs['a']
  112. self.assertNotIn('a', self.f.attrs)
  113. def test_delete_exc(self):
  114. """ Attempt to delete missing item raises KeyError """
  115. with self.assertRaises(KeyError):
  116. del self.f.attrs['a']
  117. class TestUnicode(BaseAttrs):
  118. """
  119. Feature: Attributes can be accessed via Unicode or byte strings
  120. """
  121. def test_ascii(self):
  122. """ Access via pure-ASCII byte string """
  123. self.f.attrs[b"ascii"] = 42
  124. out = self.f.attrs[b"ascii"]
  125. self.assertEqual(out, 42)
  126. def test_raw(self):
  127. """ Access via non-ASCII byte string """
  128. name = b"non-ascii\xfe"
  129. self.f.attrs[name] = 42
  130. out = self.f.attrs[name]
  131. self.assertEqual(out, 42)
  132. def test_unicode(self):
  133. """ Access via Unicode string with non-ascii characters """
  134. name = "Omega" + chr(0x03A9)
  135. self.f.attrs[name] = 42
  136. out = self.f.attrs[name]
  137. self.assertEqual(out, 42)
  138. class TestCreate(BaseAttrs):
  139. """
  140. Options for explicit attribute creation
  141. """
  142. def test_named(self):
  143. """ Attributes created from named types link to the source type object
  144. """
  145. self.f['type'] = np.dtype('u8')
  146. self.f.attrs.create('x', 42, dtype=self.f['type'])
  147. self.assertEqual(self.f.attrs['x'], 42)
  148. aid = h5a.open(self.f.id, b'x')
  149. htype = aid.get_type()
  150. htype2 = self.f['type'].id
  151. self.assertEqual(htype, htype2)
  152. self.assertTrue(htype.committed())
  153. def test_empty(self):
  154. # https://github.com/h5py/h5py/issues/1540
  155. """ Create attribute with h5py.Empty value
  156. """
  157. self.f.attrs.create('empty', h5py.Empty('f'))
  158. self.assertEqual(self.f.attrs['empty'], h5py.Empty('f'))
  159. self.f.attrs.create('empty', h5py.Empty(None))
  160. self.assertEqual(self.f.attrs['empty'], h5py.Empty(None))
  161. class TestMutableMapping(BaseAttrs):
  162. '''Tests if the registration of AttributeManager as a MutableMapping
  163. behaves as expected
  164. '''
  165. def test_resolution(self):
  166. assert issubclass(AttributeManager, MutableMapping)
  167. assert isinstance(self.f.attrs, MutableMapping)
  168. def test_validity(self):
  169. '''
  170. Test that the required functions are implemented.
  171. '''
  172. AttributeManager.__getitem__
  173. AttributeManager.__setitem__
  174. AttributeManager.__delitem__
  175. AttributeManager.__iter__
  176. AttributeManager.__len__
  177. class TestVlen(BaseAttrs):
  178. def test_vlen(self):
  179. a = np.array([np.arange(3), np.arange(4)],
  180. dtype=h5t.vlen_dtype(int))
  181. self.f.attrs['a'] = a
  182. self.assertArrayEqual(self.f.attrs['a'][0], a[0])
  183. def test_vlen_s1(self):
  184. dt = h5py.vlen_dtype(np.dtype('S1'))
  185. a = np.empty((1,), dtype=dt)
  186. a[0] = np.array([b'a', b'b'], dtype='S1')
  187. self.f.attrs.create('test', a)
  188. self.assertArrayEqual(self.f.attrs['test'][0], a[0])
  189. class TestTrackOrder(BaseAttrs):
  190. def fill_attrs(self, track_order):
  191. attrs = self.f.create_group('test', track_order=track_order).attrs
  192. for i in range(100):
  193. attrs[str(i)] = i
  194. return attrs
  195. @ut.skipUnless(h5py.version.hdf5_version_tuple >= (1, 10, 6), 'HDF5 1.10.6 required')
  196. # https://forum.hdfgroup.org/t/bug-h5arename-fails-unexpectedly/4881
  197. def test_track_order(self):
  198. attrs = self.fill_attrs(track_order=True) # creation order
  199. self.assertEqual(list(attrs),
  200. [str(i) for i in range(100)])
  201. def test_no_track_order(self):
  202. attrs = self.fill_attrs(track_order=False) # name alphanumeric
  203. self.assertEqual(list(attrs),
  204. sorted([str(i) for i in range(100)]))
  205. class TestDatatype(BaseAttrs):
  206. def test_datatype(self):
  207. self.f['foo'] = np.dtype('f')
  208. dt = self.f['foo']
  209. self.assertEqual(list(dt.attrs.keys()), [])
  210. dt.attrs.create('a', 4.0)
  211. self.assertEqual(list(dt.attrs.keys()), ['a'])
  212. self.assertEqual(list(dt.attrs.values()), [4.0])
  213. def test_python_int_uint64(writable_file):
  214. f = writable_file
  215. data = [np.iinfo(np.int64).max, np.iinfo(np.int64).max + 1]
  216. # Check creating a new attribute
  217. f.attrs.create('a', data, dtype=np.uint64)
  218. assert f.attrs['a'].dtype == np.dtype(np.uint64)
  219. np.testing.assert_array_equal(f.attrs['a'], np.array(data, dtype=np.uint64))
  220. # Check modifying an existing attribute
  221. f.attrs.modify('a', data)
  222. np.testing.assert_array_equal(f.attrs['a'], np.array(data, dtype=np.uint64))