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