1# -*- coding: utf-8 -*- 2# This file is part of h5py, a Python interface to the HDF5 library. 3# 4# http://www.h5py.org 5# 6# Copyright 2008-2013 Andrew Collette and contributors 7# 8# License: Standard 3-clause BSD; see "license.txt" for full license terms 9# and contributor agreement. 10 11""" 12 Group test module. 13 14 Tests all methods and properties of Group objects, with the following 15 exceptions: 16 17 1. Method create_dataset is tested in module test_dataset 18""" 19 20import numpy as np 21import os 22import os.path 23import sys 24from tempfile import mkdtemp 25 26from collections.abc import MutableMapping 27 28from .common import ut, TestCase 29import h5py 30from h5py import File, Group, SoftLink, HardLink, ExternalLink 31from h5py import Dataset, Datatype 32from h5py import h5t 33from h5py._hl.compat import filename_encode 34 35# If we can't encode unicode filenames, there's not much point failing tests 36# which must fail 37try: 38 filename_encode(u"α") 39except UnicodeEncodeError: 40 NO_FS_UNICODE = True 41else: 42 NO_FS_UNICODE = False 43 44 45class BaseGroup(TestCase): 46 47 def setUp(self): 48 self.f = File(self.mktemp(), 'w') 49 50 def tearDown(self): 51 if self.f: 52 self.f.close() 53 54class TestCreate(BaseGroup): 55 56 """ 57 Feature: New groups can be created via .create_group method 58 """ 59 60 def test_create(self): 61 """ Simple .create_group call """ 62 grp = self.f.create_group('foo') 63 self.assertIsInstance(grp, Group) 64 65 grp2 = self.f.create_group(b'bar') 66 self.assertIsInstance(grp, Group) 67 68 def test_create_intermediate(self): 69 """ Intermediate groups can be created automatically """ 70 grp = self.f.create_group('foo/bar/baz') 71 self.assertEqual(grp.name, '/foo/bar/baz') 72 73 grp2 = self.f.create_group(b'boo/bar/baz') 74 self.assertEqual(grp2.name, '/boo/bar/baz') 75 76 def test_create_exception(self): 77 """ Name conflict causes group creation to fail with ValueError """ 78 self.f.create_group('foo') 79 with self.assertRaises(ValueError): 80 self.f.create_group('foo') 81 82 def test_unicode(self): 83 """ Unicode names are correctly stored """ 84 name = u"/Name" + chr(0x4500) 85 group = self.f.create_group(name) 86 self.assertEqual(group.name, name) 87 self.assertEqual(group.id.links.get_info(name.encode('utf8')).cset, h5t.CSET_UTF8) 88 89 def test_unicode_default(self): 90 """ Unicode names convertible to ASCII are stored as ASCII (issue 239) 91 """ 92 name = u"/Hello, this is a name" 93 group = self.f.create_group(name) 94 self.assertEqual(group.name, name) 95 self.assertEqual(group.id.links.get_info(name.encode('utf8')).cset, h5t.CSET_ASCII) 96 97 def test_appropriate_low_level_id(self): 98 " Binding a group to a non-group identifier fails with ValueError " 99 dset = self.f.create_dataset('foo', [1]) 100 with self.assertRaises(ValueError): 101 Group(dset.id) 102 103class TestDatasetAssignment(BaseGroup): 104 105 """ 106 Feature: Datasets can be created by direct assignment of data 107 """ 108 109 def test_ndarray(self): 110 """ Dataset auto-creation by direct assignment """ 111 data = np.ones((4,4),dtype='f') 112 self.f['a'] = data 113 self.assertIsInstance(self.f['a'], Dataset) 114 self.assertArrayEqual(self.f['a'][...], data) 115 116 def test_name_bytes(self): 117 data = np.ones((4, 4), dtype='f') 118 self.f[b'b'] = data 119 self.assertIsInstance(self.f[b'b'], Dataset) 120 121class TestDtypeAssignment(BaseGroup): 122 123 """ 124 Feature: Named types can be created by direct assignment of dtypes 125 """ 126 127 def test_dtype(self): 128 """ Named type creation """ 129 dtype = np.dtype('|S10') 130 self.f['a'] = dtype 131 self.assertIsInstance(self.f['a'], Datatype) 132 self.assertEqual(self.f['a'].dtype, dtype) 133 134 def test_name_bytes(self): 135 """ Named type creation """ 136 dtype = np.dtype('|S10') 137 self.f[b'b'] = dtype 138 self.assertIsInstance(self.f[b'b'], Datatype) 139 140 141class TestRequire(BaseGroup): 142 143 """ 144 Feature: Groups can be auto-created, or opened via .require_group 145 """ 146 147 def test_open_existing(self): 148 """ Existing group is opened and returned """ 149 grp = self.f.create_group('foo') 150 grp2 = self.f.require_group('foo') 151 self.assertEqual(grp2, grp) 152 153 grp3 = self.f.require_group(b'foo') 154 self.assertEqual(grp3, grp) 155 156 def test_create(self): 157 """ Group is created if it doesn't exist """ 158 grp = self.f.require_group('foo') 159 self.assertIsInstance(grp, Group) 160 self.assertEqual(grp.name, '/foo') 161 162 def test_require_exception(self): 163 """ Opening conflicting object results in TypeError """ 164 self.f.create_dataset('foo', (1,), 'f') 165 with self.assertRaises(TypeError): 166 self.f.require_group('foo') 167 168 def test_intermediate_create_dataset(self): 169 """ Intermediate is created if it doesn't exist """ 170 dt = h5py.string_dtype() 171 self.f.require_dataset("foo/bar/baz", (1,), dtype=dt) 172 group = self.f.get('foo') 173 assert isinstance(group, Group) 174 group = self.f.get('foo/bar') 175 assert isinstance(group, Group) 176 177 def test_intermediate_create_group(self): 178 dt = h5py.string_dtype() 179 self.f.require_group("foo/bar/baz") 180 group = self.f.get('foo') 181 assert isinstance(group, Group) 182 group = self.f.get('foo/bar') 183 assert isinstance(group, Group) 184 group = self.f.get('foo/bar/baz') 185 assert isinstance(group, Group) 186 187class TestDelete(BaseGroup): 188 189 """ 190 Feature: Objects can be unlinked via "del" operator 191 """ 192 193 def test_delete(self): 194 """ Object deletion via "del" """ 195 self.f.create_group('foo') 196 self.assertIn('foo', self.f) 197 del self.f['foo'] 198 self.assertNotIn('foo', self.f) 199 200 def test_nonexisting(self): 201 """ Deleting non-existent object raises KeyError """ 202 with self.assertRaises(KeyError): 203 del self.f['foo'] 204 205 def test_readonly_delete_exception(self): 206 """ Deleting object in readonly file raises KeyError """ 207 # Note: it is impossible to restore the old behavior (ValueError) 208 # without breaking the above test (non-existing objects) 209 fname = self.mktemp() 210 hfile = File(fname, 'w') 211 try: 212 hfile.create_group('foo') 213 finally: 214 hfile.close() 215 216 hfile = File(fname, 'r') 217 try: 218 with self.assertRaises(KeyError): 219 del hfile['foo'] 220 finally: 221 hfile.close() 222 223class TestOpen(BaseGroup): 224 225 """ 226 Feature: Objects can be opened via indexing syntax obj[name] 227 """ 228 229 def test_open(self): 230 """ Simple obj[name] opening """ 231 grp = self.f.create_group('foo') 232 grp2 = self.f['foo'] 233 grp3 = self.f['/foo'] 234 self.assertEqual(grp, grp2) 235 self.assertEqual(grp, grp3) 236 237 def test_nonexistent(self): 238 """ Opening missing objects raises KeyError """ 239 with self.assertRaises(KeyError): 240 self.f['foo'] 241 242 def test_reference(self): 243 """ Objects can be opened by HDF5 object reference """ 244 grp = self.f.create_group('foo') 245 grp2 = self.f[grp.ref] 246 self.assertEqual(grp2, grp) 247 248 def test_reference_numpyobj(self): 249 """ Object can be opened by numpy.object_ containing object ref 250 251 Test for issue 181, issue 202. 252 """ 253 g = self.f.create_group('test') 254 255 dt = np.dtype([('a', 'i'),('b', h5py.ref_dtype)]) 256 dset = self.f.create_dataset('test_dset', (1,), dt) 257 258 dset[0] =(42,g.ref) 259 data = dset[0] 260 self.assertEqual(self.f[data[1]], g) 261 262 def test_invalid_ref(self): 263 """ Invalid region references should raise an exception """ 264 265 ref = h5py.h5r.Reference() 266 267 with self.assertRaises(ValueError): 268 self.f[ref] 269 270 self.f.create_group('x') 271 ref = self.f['x'].ref 272 del self.f['x'] 273 274 with self.assertRaises(Exception): 275 self.f[ref] 276 277 def test_path_type_validation(self): 278 """ Access with non bytes or str types should raise an exception """ 279 self.f.create_group('group') 280 281 with self.assertRaises(TypeError): 282 self.f[0] 283 284 with self.assertRaises(TypeError): 285 self.f[...] 286 287 # TODO: check that regionrefs also work with __getitem__ 288 289class TestRepr(BaseGroup): 290 """Opened and closed groups provide a useful __repr__ string""" 291 292 def test_repr(self): 293 """ Opened and closed groups provide a useful __repr__ string """ 294 g = self.f.create_group('foo') 295 self.assertIsInstance(repr(g), str) 296 g.id._close() 297 self.assertIsInstance(repr(g), str) 298 g = self.f['foo'] 299 # Closing the file shouldn't break it 300 self.f.close() 301 self.assertIsInstance(repr(g), str) 302 303class BaseMapping(BaseGroup): 304 305 """ 306 Base class for mapping tests 307 """ 308 def setUp(self): 309 self.f = File(self.mktemp(), 'w') 310 self.groups = ('a', 'b', 'c', 'd') 311 for x in self.groups: 312 self.f.create_group(x) 313 self.f['x'] = h5py.SoftLink('/mongoose') 314 self.groups = self.groups + ('x',) 315 316 def tearDown(self): 317 if self.f: 318 self.f.close() 319 320class TestLen(BaseMapping): 321 322 """ 323 Feature: The Python len() function returns the number of groups 324 """ 325 326 def test_len(self): 327 """ len() returns number of group members """ 328 self.assertEqual(len(self.f), len(self.groups)) 329 self.f.create_group('e') 330 self.assertEqual(len(self.f), len(self.groups)+1) 331 332 333class TestContains(BaseGroup): 334 335 """ 336 Feature: The Python "in" builtin tests for membership 337 """ 338 339 def test_contains(self): 340 """ "in" builtin works for membership (byte and Unicode) """ 341 self.f.create_group('a') 342 self.assertIn(b'a', self.f) 343 self.assertIn('a', self.f) 344 self.assertIn(b'/a', self.f) 345 self.assertIn('/a', self.f) 346 self.assertNotIn(b'mongoose', self.f) 347 self.assertNotIn('mongoose', self.f) 348 349 def test_exc(self): 350 """ "in" on closed group returns False (see also issue 174) """ 351 self.f.create_group('a') 352 self.f.close() 353 self.assertFalse(b'a' in self.f) 354 self.assertFalse('a' in self.f) 355 356 def test_empty(self): 357 """ Empty strings work properly and aren't contained """ 358 self.assertNotIn('', self.f) 359 self.assertNotIn(b'', self.f) 360 361 def test_dot(self): 362 """ Current group "." is always contained """ 363 self.assertIn(b'.', self.f) 364 self.assertIn('.', self.f) 365 366 def test_root(self): 367 """ Root group (by itself) is contained """ 368 self.assertIn(b'/', self.f) 369 self.assertIn('/', self.f) 370 371 def test_trailing_slash(self): 372 """ Trailing slashes are unconditionally ignored """ 373 self.f.create_group('group') 374 self.f['dataset'] = 42 375 self.assertIn('/group/', self.f) 376 self.assertIn('group/', self.f) 377 self.assertIn('/dataset/', self.f) 378 self.assertIn('dataset/', self.f) 379 380 def test_softlinks(self): 381 """ Broken softlinks are contained, but their members are not """ 382 self.f.create_group('grp') 383 self.f['/grp/soft'] = h5py.SoftLink('/mongoose') 384 self.f['/grp/external'] = h5py.ExternalLink('mongoose.hdf5', '/mongoose') 385 self.assertIn('/grp/soft', self.f) 386 self.assertNotIn('/grp/soft/something', self.f) 387 self.assertIn('/grp/external', self.f) 388 self.assertNotIn('/grp/external/something', self.f) 389 390 def test_oddball_paths(self): 391 """ Technically legitimate (but odd-looking) paths """ 392 self.f.create_group('x/y/z') 393 self.f['dset'] = 42 394 self.assertIn('/', self.f) 395 self.assertIn('//', self.f) 396 self.assertIn('///', self.f) 397 self.assertIn('.///', self.f) 398 self.assertIn('././/', self.f) 399 grp = self.f['x'] 400 self.assertIn('.//x/y/z', self.f) 401 self.assertNotIn('.//x/y/z', grp) 402 self.assertIn('x///', self.f) 403 self.assertIn('./x///', self.f) 404 self.assertIn('dset///', self.f) 405 self.assertIn('/dset//', self.f) 406 407class TestIter(BaseMapping): 408 409 """ 410 Feature: You can iterate over group members via "for x in y", etc. 411 """ 412 413 def test_iter(self): 414 """ "for x in y" iteration """ 415 lst = [x for x in self.f] 416 self.assertSameElements(lst, self.groups) 417 418 def test_iter_zero(self): 419 """ Iteration works properly for the case with no group members """ 420 hfile = File(self.mktemp(), 'w') 421 try: 422 lst = [x for x in hfile] 423 self.assertEqual(lst, []) 424 finally: 425 hfile.close() 426 427class TestTrackOrder(BaseGroup): 428 def populate(self, g): 429 for i in range(100): 430 # Mix group and dataset creation. 431 if i % 10 == 0: 432 g.create_group(str(i)) 433 else: 434 g[str(i)] = [i] 435 436 def test_track_order(self): 437 g = self.f.create_group('order', track_order=True) # creation order 438 self.populate(g) 439 440 ref = [str(i) for i in range(100)] 441 self.assertEqual(list(g), ref) 442 self.assertEqual(list(reversed(g)), list(reversed(ref))) 443 444 def test_no_track_order(self): 445 g = self.f.create_group('order', track_order=False) # name alphanumeric 446 self.populate(g) 447 448 ref = sorted([str(i) for i in range(100)]) 449 self.assertEqual(list(g), ref) 450 self.assertEqual(list(reversed(g)), list(reversed(ref))) 451 452class TestPy3Dict(BaseMapping): 453 454 def test_keys(self): 455 """ .keys provides a key view """ 456 kv = getattr(self.f, 'keys')() 457 ref = self.groups 458 self.assertSameElements(list(kv), ref) 459 self.assertSameElements(list(reversed(kv)), list(reversed(ref))) 460 461 for x in self.groups: 462 self.assertIn(x, kv) 463 self.assertEqual(len(kv), len(self.groups)) 464 465 def test_values(self): 466 """ .values provides a value view """ 467 vv = getattr(self.f, 'values')() 468 ref = [self.f.get(x) for x in self.groups] 469 self.assertSameElements(list(vv), ref) 470 self.assertSameElements(list(reversed(vv)), list(reversed(ref))) 471 472 self.assertEqual(len(vv), len(self.groups)) 473 for x in self.groups: 474 self.assertIn(self.f.get(x), vv) 475 476 def test_items(self): 477 """ .items provides an item view """ 478 iv = getattr(self.f, 'items')() 479 ref = [(x,self.f.get(x)) for x in self.groups] 480 self.assertSameElements(list(iv), ref) 481 self.assertSameElements(list(reversed(iv)), list(reversed(ref))) 482 483 self.assertEqual(len(iv), len(self.groups)) 484 for x in self.groups: 485 self.assertIn((x, self.f.get(x)), iv) 486 487class TestAdditionalMappingFuncs(BaseMapping): 488 """ 489 Feature: Other dict methods (pop, pop_item, clear, update, setdefault) are 490 available. 491 """ 492 def setUp(self): 493 self.f = File(self.mktemp(), 'w') 494 for x in ('/test/a', '/test/b', '/test/c', '/test/d'): 495 self.f.create_group(x) 496 self.group = self.f['test'] 497 498 def tearDown(self): 499 if self.f: 500 self.f.close() 501 502 def test_pop_item(self): 503 """.pop_item exists and removes item""" 504 key, val = self.group.popitem() 505 self.assertNotIn(key, self.group) 506 507 def test_pop(self): 508 """.pop exists and removes specified item""" 509 self.group.pop('a') 510 self.assertNotIn('a', self.group) 511 512 def test_pop_default(self): 513 """.pop falls back to default""" 514 # e shouldn't exist as a group 515 value = self.group.pop('e', None) 516 self.assertEqual(value, None) 517 518 def test_pop_raises(self): 519 """.pop raises KeyError for non-existence""" 520 # e shouldn't exist as a group 521 with self.assertRaises(KeyError): 522 key = self.group.pop('e') 523 524 def test_clear(self): 525 """.clear removes groups""" 526 self.group.clear() 527 self.assertEqual(len(self.group), 0) 528 529 def test_update_dict(self): 530 """.update works with dict""" 531 new_items = {'e': np.array([42])} 532 self.group.update(new_items) 533 self.assertIn('e', self.group) 534 535 def test_update_iter(self): 536 """.update works with list""" 537 new_items = [ 538 ('e', np.array([42])), 539 ('f', np.array([42])) 540 ] 541 self.group.update(new_items) 542 self.assertIn('e', self.group) 543 544 def test_update_kwargs(self): 545 """.update works with kwargs""" 546 new_items = {'e': np.array([42])} 547 self.group.update(**new_items) 548 self.assertIn('e', self.group) 549 550 def test_setdefault(self): 551 """.setdefault gets group if it exists""" 552 value = self.group.setdefault('a') 553 self.assertEqual(value, self.group.get('a')) 554 555 def test_setdefault_with_default(self): 556 """.setdefault gets default if group doesn't exist""" 557 # e shouldn't exist as a group 558 # 42 used as groups should be strings 559 value = self.group.setdefault('e', np.array([42])) 560 self.assertEqual(value, 42) 561 562 def test_setdefault_no_default(self): 563 """ 564 .setdefault gets None if group doesn't exist, but as None isn't defined 565 as data for a dataset, this should raise a TypeError. 566 """ 567 # e shouldn't exist as a group 568 with self.assertRaises(TypeError): 569 self.group.setdefault('e') 570 571 572class TestGet(BaseGroup): 573 574 """ 575 Feature: The .get method allows access to objects and metadata 576 """ 577 578 def test_get_default(self): 579 """ Object is returned, or default if it doesn't exist """ 580 default = object() 581 out = self.f.get('mongoose', default) 582 self.assertIs(out, default) 583 584 grp = self.f.create_group('a') 585 out = self.f.get(b'a') 586 self.assertEqual(out, grp) 587 588 def test_get_class(self): 589 """ Object class is returned with getclass option """ 590 self.f.create_group('foo') 591 out = self.f.get('foo', getclass=True) 592 self.assertEqual(out, Group) 593 594 self.f.create_dataset('bar', (4,)) 595 out = self.f.get('bar', getclass=True) 596 self.assertEqual(out, Dataset) 597 598 self.f['baz'] = np.dtype('|S10') 599 out = self.f.get('baz', getclass=True) 600 self.assertEqual(out, Datatype) 601 602 def test_get_link_class(self): 603 """ Get link classes """ 604 default = object() 605 606 sl = SoftLink('/mongoose') 607 el = ExternalLink('somewhere.hdf5', 'mongoose') 608 609 self.f.create_group('hard') 610 self.f['soft'] = sl 611 self.f['external'] = el 612 613 out_hl = self.f.get('hard', default, getlink=True, getclass=True) 614 out_sl = self.f.get('soft', default, getlink=True, getclass=True) 615 out_el = self.f.get('external', default, getlink=True, getclass=True) 616 617 self.assertEqual(out_hl, HardLink) 618 self.assertEqual(out_sl, SoftLink) 619 self.assertEqual(out_el, ExternalLink) 620 621 def test_get_link(self): 622 """ Get link values """ 623 sl = SoftLink('/mongoose') 624 el = ExternalLink('somewhere.hdf5', 'mongoose') 625 626 self.f.create_group('hard') 627 self.f['soft'] = sl 628 self.f['external'] = el 629 630 out_hl = self.f.get('hard', getlink=True) 631 out_sl = self.f.get('soft', getlink=True) 632 out_el = self.f.get('external', getlink=True) 633 634 #TODO: redo with SoftLink/ExternalLink built-in equality 635 self.assertIsInstance(out_hl, HardLink) 636 self.assertIsInstance(out_sl, SoftLink) 637 self.assertEqual(out_sl._path, sl._path) 638 self.assertIsInstance(out_el, ExternalLink) 639 self.assertEqual(out_el._path, el._path) 640 self.assertEqual(out_el._filename, el._filename) 641 642class TestVisit(TestCase): 643 644 """ 645 Feature: The .visit and .visititems methods allow iterative access to 646 group and subgroup members 647 """ 648 649 def setUp(self): 650 self.f = File(self.mktemp(), 'w') 651 self.groups = [ 652 'grp1', 'grp1/sg1', 'grp1/sg2', 'grp2', 'grp2/sg1', 'grp2/sg1/ssg1' 653 ] 654 for x in self.groups: 655 self.f.create_group(x) 656 657 def tearDown(self): 658 self.f.close() 659 660 def test_visit(self): 661 """ All subgroups are visited """ 662 l = [] 663 self.f.visit(l.append) 664 self.assertSameElements(l, self.groups) 665 666 def test_visititems(self): 667 """ All subgroups and contents are visited """ 668 l = [] 669 comp = [(x, self.f[x]) for x in self.groups] 670 self.f.visititems(lambda x, y: l.append((x,y))) 671 self.assertSameElements(comp, l) 672 673 def test_bailout(self): 674 """ Returning a non-None value immediately aborts iteration """ 675 x = self.f.visit(lambda x: x) 676 self.assertEqual(x, self.groups[0]) 677 x = self.f.visititems(lambda x, y: (x,y)) 678 self.assertEqual(x, (self.groups[0], self.f[self.groups[0]])) 679 680class TestSoftLinks(BaseGroup): 681 682 """ 683 Feature: Create and manage soft links with the high-level interface 684 """ 685 686 def test_spath(self): 687 """ SoftLink path attribute """ 688 sl = SoftLink('/foo') 689 self.assertEqual(sl.path, '/foo') 690 691 def test_srepr(self): 692 """ SoftLink path repr """ 693 sl = SoftLink('/foo') 694 self.assertIsInstance(repr(sl), str) 695 696 def test_create(self): 697 """ Create new soft link by assignment """ 698 g = self.f.create_group('new') 699 sl = SoftLink('/new') 700 self.f['alias'] = sl 701 g2 = self.f['alias'] 702 self.assertEqual(g, g2) 703 704 def test_exc(self): 705 """ Opening dangling soft link results in KeyError """ 706 self.f['alias'] = SoftLink('new') 707 with self.assertRaises(KeyError): 708 self.f['alias'] 709 710class TestExternalLinks(TestCase): 711 712 """ 713 Feature: Create and manage external links 714 """ 715 716 def setUp(self): 717 self.f = File(self.mktemp(), 'w') 718 self.ename = self.mktemp() 719 self.ef = File(self.ename, 'w') 720 self.ef.create_group('external') 721 self.ef.close() 722 723 def tearDown(self): 724 if self.f: 725 self.f.close() 726 if self.ef: 727 self.ef.close() 728 729 def test_epath(self): 730 """ External link paths attributes """ 731 el = ExternalLink('foo.hdf5', '/foo') 732 self.assertEqual(el.filename, 'foo.hdf5') 733 self.assertEqual(el.path, '/foo') 734 735 def test_erepr(self): 736 """ External link repr """ 737 el = ExternalLink('foo.hdf5','/foo') 738 self.assertIsInstance(repr(el), str) 739 740 def test_create(self): 741 """ Creating external links """ 742 self.f['ext'] = ExternalLink(self.ename, '/external') 743 grp = self.f['ext'] 744 self.ef = grp.file 745 self.assertNotEqual(self.ef, self.f) 746 self.assertEqual(grp.name, '/external') 747 748 def test_exc(self): 749 """ KeyError raised when attempting to open broken link """ 750 self.f['ext'] = ExternalLink(self.ename, '/missing') 751 with self.assertRaises(KeyError): 752 self.f['ext'] 753 754 # I would prefer IOError but there's no way to fix this as the exception 755 # class is determined by HDF5. 756 def test_exc_missingfile(self): 757 """ KeyError raised when attempting to open missing file """ 758 self.f['ext'] = ExternalLink('mongoose.hdf5','/foo') 759 with self.assertRaises(KeyError): 760 self.f['ext'] 761 762 def test_close_file(self): 763 """ Files opened by accessing external links can be closed 764 765 Issue 189. 766 """ 767 self.f['ext'] = ExternalLink(self.ename, '/') 768 grp = self.f['ext'] 769 f2 = grp.file 770 f2.close() 771 self.assertFalse(f2) 772 773 @ut.skipIf(NO_FS_UNICODE, "No unicode filename support") 774 def test_unicode_encode(self): 775 """ 776 Check that external links encode unicode filenames properly 777 Testing issue #732 778 """ 779 ext_filename = os.path.join(mkdtemp(), u"α.hdf5") 780 with File(ext_filename, "w") as ext_file: 781 ext_file.create_group('external') 782 self.f['ext'] = ExternalLink(ext_filename, '/external') 783 784 @ut.skipIf(NO_FS_UNICODE, "No unicode filename support") 785 def test_unicode_decode(self): 786 """ 787 Check that external links decode unicode filenames properly 788 Testing issue #732 789 """ 790 ext_filename = os.path.join(mkdtemp(), u"α.hdf5") 791 with File(ext_filename, "w") as ext_file: 792 ext_file.create_group('external') 793 ext_file["external"].attrs["ext_attr"] = "test" 794 self.f['ext'] = ExternalLink(ext_filename, '/external') 795 self.assertEqual(self.f["ext"].attrs["ext_attr"], "test") 796 797 def test_unicode_hdf5_path(self): 798 """ 799 Check that external links handle unicode hdf5 paths properly 800 Testing issue #333 801 """ 802 ext_filename = os.path.join(mkdtemp(), "external.hdf5") 803 with File(ext_filename, "w") as ext_file: 804 ext_file.create_group('α') 805 ext_file["α"].attrs["ext_attr"] = "test" 806 self.f['ext'] = ExternalLink(ext_filename, '/α') 807 self.assertEqual(self.f["ext"].attrs["ext_attr"], "test") 808 809class TestExtLinkBugs(TestCase): 810 811 """ 812 Bugs: Specific regressions for external links 813 """ 814 815 def test_issue_212(self): 816 """ Issue 212 817 818 Fails with: 819 820 AttributeError: 'SharedConfig' object has no attribute 'lapl' 821 """ 822 def closer(x): 823 def w(): 824 try: 825 if x: 826 x.close() 827 except IOError: 828 pass 829 return w 830 orig_name = self.mktemp() 831 new_name = self.mktemp() 832 f = File(orig_name, 'w') 833 self.addCleanup(closer(f)) 834 f.create_group('a') 835 f.close() 836 837 g = File(new_name, 'w') 838 self.addCleanup(closer(g)) 839 g['link'] = ExternalLink(orig_name, '/') # note root group 840 g.close() 841 842 h = File(new_name, 'r') 843 self.addCleanup(closer(h)) 844 self.assertIsInstance(h['link']['a'], Group) 845 846 847class TestCopy(TestCase): 848 849 def setUp(self): 850 self.f1 = File(self.mktemp(), 'w') 851 self.f2 = File(self.mktemp(), 'w') 852 853 def tearDown(self): 854 if self.f1: 855 self.f1.close() 856 if self.f2: 857 self.f2.close() 858 859 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 860 "Bug in HDF5<1.8.8 prevents copying open dataset") 861 def test_copy_path_to_path(self): 862 foo = self.f1.create_group('foo') 863 foo['bar'] = [1,2,3] 864 865 self.f1.copy('foo', 'baz') 866 baz = self.f1['baz'] 867 self.assertIsInstance(baz, Group) 868 self.assertArrayEqual(baz['bar'], np.array([1,2,3])) 869 870 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 871 "Bug in HDF5<1.8.8 prevents copying open dataset") 872 def test_copy_path_to_group(self): 873 foo = self.f1.create_group('foo') 874 foo['bar'] = [1,2,3] 875 baz = self.f1.create_group('baz') 876 877 self.f1.copy('foo', baz) 878 baz = self.f1['baz'] 879 self.assertIsInstance(baz, Group) 880 self.assertArrayEqual(baz['foo/bar'], np.array([1,2,3])) 881 882 self.f1.copy('foo', self.f2['/']) 883 self.assertIsInstance(self.f2['/foo'], Group) 884 self.assertArrayEqual(self.f2['foo/bar'], np.array([1,2,3])) 885 886 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 887 "Bug in HDF5<1.8.8 prevents copying open dataset") 888 def test_copy_group_to_path(self): 889 890 foo = self.f1.create_group('foo') 891 foo['bar'] = [1,2,3] 892 893 self.f1.copy(foo, 'baz') 894 baz = self.f1['baz'] 895 self.assertIsInstance(baz, Group) 896 self.assertArrayEqual(baz['bar'], np.array([1,2,3])) 897 898 self.f2.copy(foo, 'foo') 899 self.assertIsInstance(self.f2['/foo'], Group) 900 self.assertArrayEqual(self.f2['foo/bar'], np.array([1,2,3])) 901 902 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 903 "Bug in HDF5<1.8.8 prevents copying open dataset") 904 def test_copy_group_to_group(self): 905 906 foo = self.f1.create_group('foo') 907 foo['bar'] = [1,2,3] 908 baz = self.f1.create_group('baz') 909 910 self.f1.copy(foo, baz) 911 baz = self.f1['baz'] 912 self.assertIsInstance(baz, Group) 913 self.assertArrayEqual(baz['foo/bar'], np.array([1,2,3])) 914 915 self.f1.copy(foo, self.f2['/']) 916 self.assertIsInstance(self.f2['/foo'], Group) 917 self.assertArrayEqual(self.f2['foo/bar'], np.array([1,2,3])) 918 919 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 920 "Bug in HDF5<1.8.8 prevents copying open dataset") 921 def test_copy_dataset(self): 922 self.f1['foo'] = [1,2,3] 923 foo = self.f1['foo'] 924 grp = self.f1.create_group("grp") 925 926 self.f1.copy(foo, 'bar') 927 self.assertArrayEqual(self.f1['bar'], np.array([1,2,3])) 928 929 self.f1.copy('foo', 'baz') 930 self.assertArrayEqual(self.f1['baz'], np.array([1,2,3])) 931 932 self.f1.copy(foo, grp) 933 self.assertArrayEqual(self.f1['/grp/foo'], np.array([1,2,3])) 934 935 self.f1.copy('foo', self.f2) 936 self.assertArrayEqual(self.f2['foo'], np.array([1,2,3])) 937 938 self.f2.copy(self.f1['foo'], self.f2, 'bar') 939 self.assertArrayEqual(self.f2['bar'], np.array([1,2,3])) 940 941 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 942 "Bug in HDF5<1.8.8 prevents copying open dataset") 943 def test_copy_shallow(self): 944 945 foo = self.f1.create_group('foo') 946 bar = foo.create_group('bar') 947 foo['qux'] = [1,2,3] 948 bar['quux'] = [4,5,6] 949 950 self.f1.copy(foo, 'baz', shallow=True) 951 baz = self.f1['baz'] 952 self.assertIsInstance(baz, Group) 953 self.assertIsInstance(baz['bar'], Group) 954 self.assertEqual(len(baz['bar']), 0) 955 self.assertArrayEqual(baz['qux'], np.array([1,2,3])) 956 957 self.f2.copy(foo, 'foo', shallow=True) 958 self.assertIsInstance(self.f2['/foo'], Group) 959 self.assertIsInstance(self.f2['foo/bar'], Group) 960 self.assertEqual(len(self.f2['foo/bar']), 0) 961 self.assertArrayEqual(self.f2['foo/qux'], np.array([1,2,3])) 962 963 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 964 "Bug in HDF5<1.8.8 prevents copying open dataset") 965 def test_copy_without_attributes(self): 966 967 self.f1['foo'] = [1,2,3] 968 foo = self.f1['foo'] 969 foo.attrs['bar'] = [4,5,6] 970 971 self.f1.copy(foo, 'baz', without_attrs=True) 972 self.assertArrayEqual(self.f1['baz'], np.array([1,2,3])) 973 assert 'bar' not in self.f1['baz'].attrs 974 975 self.f2.copy(foo, 'baz', without_attrs=True) 976 self.assertArrayEqual(self.f2['baz'], np.array([1,2,3])) 977 assert 'bar' not in self.f2['baz'].attrs 978 979 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 980 "Bug in HDF5<1.8.8 prevents copying open dataset") 981 def test_copy_soft_links(self): 982 983 self.f1['bar'] = [1, 2, 3] 984 foo = self.f1.create_group('foo') 985 foo['baz'] = SoftLink('/bar') 986 987 self.f1.copy(foo, 'qux', expand_soft=True) 988 self.f2.copy(foo, 'foo', expand_soft=True) 989 del self.f1['bar'] 990 991 self.assertIsInstance(self.f1['qux'], Group) 992 self.assertArrayEqual(self.f1['qux/baz'], np.array([1, 2, 3])) 993 994 self.assertIsInstance(self.f2['/foo'], Group) 995 self.assertArrayEqual(self.f2['foo/baz'], np.array([1, 2, 3])) 996 997 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 998 "Bug in HDF5<1.8.8 prevents copying open dataset") 999 def test_copy_external_links(self): 1000 1001 filename = self.f1.filename 1002 self.f1['foo'] = [1,2,3] 1003 self.f2['bar'] = ExternalLink(filename, 'foo') 1004 self.f1.close() 1005 self.f1 = None 1006 1007 self.assertArrayEqual(self.f2['bar'], np.array([1,2,3])) 1008 1009 self.f2.copy('bar', 'baz', expand_external=True) 1010 os.unlink(filename) 1011 self.assertArrayEqual(self.f2['baz'], np.array([1,2,3])) 1012 1013 @ut.skipIf(h5py.version.hdf5_version_tuple < (1,8,9), 1014 "Bug in HDF5<1.8.8 prevents copying open dataset") 1015 def test_copy_refs(self): 1016 1017 self.f1['foo'] = [1,2,3] 1018 self.f1['bar'] = [4,5,6] 1019 foo = self.f1['foo'] 1020 bar = self.f1['bar'] 1021 foo.attrs['bar'] = bar.ref 1022 1023 self.f1.copy(foo, 'baz', expand_refs=True) 1024 self.assertArrayEqual(self.f1['baz'], np.array([1,2,3])) 1025 baz_bar = self.f1['baz'].attrs['bar'] 1026 self.assertArrayEqual(self.f1[baz_bar], np.array([4,5,6])) 1027 # The reference points to a copy of bar, not to bar itself. 1028 self.assertNotEqual(self.f1[baz_bar].name, bar.name) 1029 1030 self.f1.copy('foo', self.f2, 'baz', expand_refs=True) 1031 self.assertArrayEqual(self.f2['baz'], np.array([1,2,3])) 1032 baz_bar = self.f2['baz'].attrs['bar'] 1033 self.assertArrayEqual(self.f2[baz_bar], np.array([4,5,6])) 1034 1035 self.f1.copy('/', self.f2, 'root', expand_refs=True) 1036 self.assertArrayEqual(self.f2['root/foo'], np.array([1,2,3])) 1037 self.assertArrayEqual(self.f2['root/bar'], np.array([4,5,6])) 1038 foo_bar = self.f2['root/foo'].attrs['bar'] 1039 self.assertArrayEqual(self.f2[foo_bar], np.array([4,5,6])) 1040 # There's only one copy of bar, which the reference points to. 1041 self.assertEqual(self.f2[foo_bar], self.f2['root/bar']) 1042 1043 1044class TestMove(BaseGroup): 1045 1046 """ 1047 Feature: Group.move moves links in a file 1048 """ 1049 1050 def test_move_hardlink(self): 1051 """ Moving an object """ 1052 grp = self.f.create_group("X") 1053 self.f.move("X", "Y") 1054 self.assertEqual(self.f["Y"], grp) 1055 self.f.move("Y", "new/nested/path") 1056 self.assertEqual(self.f['new/nested/path'], grp) 1057 1058 def test_move_softlink(self): 1059 """ Moving a soft link """ 1060 self.f['soft'] = h5py.SoftLink("relative/path") 1061 self.f.move('soft', 'new_soft') 1062 lnk = self.f.get('new_soft', getlink=True) 1063 self.assertEqual(lnk.path, "relative/path") 1064 1065 def test_move_conflict(self): 1066 """ Move conflict raises ValueError """ 1067 self.f.create_group("X") 1068 self.f.create_group("Y") 1069 with self.assertRaises(ValueError): 1070 self.f.move("X", "Y") 1071 1072 def test_short_circuit(self): 1073 ''' Test that a null-move works ''' 1074 self.f.create_group("X") 1075 self.f.move("X", "X") 1076 1077 1078class TestMutableMapping(BaseGroup): 1079 '''Tests if the registration of Group as a MutableMapping 1080 behaves as expected 1081 ''' 1082 def test_resolution(self): 1083 assert issubclass(Group, MutableMapping) 1084 grp = self.f.create_group("K") 1085 assert isinstance(grp, MutableMapping) 1086 1087 def test_validity(self): 1088 ''' 1089 Test that the required functions are implemented. 1090 ''' 1091 Group.__getitem__ 1092 Group.__setitem__ 1093 Group.__delitem__ 1094 Group.__iter__ 1095 Group.__len__ 1096