Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tables/group.py: 19%

412 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-10 06:15 +0000

1"""Here is defined the Group class.""" 

2 

3import os 

4import weakref 

5import warnings 

6 

7from .misc.proxydict import ProxyDict 

8from . import hdf5extension 

9from . import utilsextension 

10from .registry import class_id_dict 

11from .exceptions import (NodeError, NoSuchNodeError, NaturalNameWarning, 

12 PerformanceWarning) 

13from .filters import Filters 

14from .registry import get_class_by_name 

15from .path import check_name_validity, join_path, isvisiblename 

16from .node import Node, NotLoggedMixin 

17from .leaf import Leaf 

18from .unimplemented import UnImplemented, Unknown 

19 

20from .link import Link, SoftLink, ExternalLink 

21 

22 

23obversion = "1.0" 

24 

25 

26class _ChildrenDict(ProxyDict): 

27 def _get_value_from_container(self, container, key): 

28 return container._f_get_child(key) 

29 

30 

31class Group(hdf5extension.Group, Node): 

32 """Basic PyTables grouping structure. 

33 

34 Instances of this class are grouping structures containing *child* 

35 instances of zero or more groups or leaves, together with 

36 supporting metadata. Each group has exactly one *parent* group. 

37 

38 Working with groups and leaves is similar in many ways to working 

39 with directories and files, respectively, in a Unix filesystem. 

40 As with Unix directories and files, objects in the object tree are 

41 often described by giving their full (or absolute) path names. 

42 This full path can be specified either as a string (like in 

43 '/group1/group2') or as a complete object path written in *natural 

44 naming* schema (like in file.root.group1.group2). 

45 

46 A collateral effect of the *natural naming* schema is that the 

47 names of members in the Group class and its instances must be 

48 carefully chosen to avoid colliding with existing children node 

49 names. For this reason and to avoid polluting the children 

50 namespace all members in a Group start with some reserved prefix, 

51 like _f_ (for public methods), _g_ (for private ones), _v_ (for 

52 instance variables) or _c_ (for class variables). Any attempt to 

53 create a new child node whose name starts with one of these 

54 prefixes will raise a ValueError exception. 

55 

56 Another effect of natural naming is that children named after 

57 Python keywords or having names not valid as Python identifiers 

58 (e.g. class, $a or 44) can not be accessed using the node.child 

59 syntax. You will be forced to use node._f_get_child(child) to 

60 access them (which is recommended for programmatic accesses). 

61 

62 You will also need to use _f_get_child() to access an existing 

63 child node if you set a Python attribute in the Group with the 

64 same name as that node (you will get a NaturalNameWarning when 

65 doing this). 

66 

67 Parameters 

68 ---------- 

69 parentnode 

70 The parent :class:`Group` object. 

71 name : str 

72 The name of this node in its parent group. 

73 title 

74 The title for this group 

75 new 

76 If this group is new or has to be read from disk 

77 filters : Filters 

78 A Filters instance 

79 

80 

81 .. versionchanged:: 3.0 

82 *parentNode* renamed into *parentnode* 

83 

84 Notes 

85 ----- 

86 The following documentation includes methods that are automatically 

87 called when a Group instance is accessed in a special way. 

88 

89 For instance, this class defines the __setattr__, __getattr__, 

90 __delattr__ and __dir__ methods, and they set, get and delete 

91 *ordinary Python attributes* as normally intended. In addition to that, 

92 __getattr__ allows getting *child nodes* by their name for the sake of 

93 easy interaction on the command line, as long as there is no Python 

94 attribute with the same name. Groups also allow the interactive 

95 completion (when using readline) of the names of child nodes. 

96 For instance:: 

97 

98 # get a Python attribute 

99 nchild = group._v_nchildren 

100 

101 # Add a Table child called 'table' under 'group'. 

102 h5file.create_table(group, 'table', myDescription) 

103 table = group.table # get the table child instance 

104 group.table = 'foo' # set a Python attribute 

105 

106 # (PyTables warns you here about using the name of a child node.) 

107 foo = group.table # get a Python attribute 

108 del group.table # delete a Python attribute 

109 table = group.table # get the table child instance again 

110 

111 Additionally, on interactive python sessions you may get autocompletions 

112 of children named as *valid python identifiers* by pressing the `[Tab]` 

113 key, or to use the dir() global function. 

114 

115 .. rubric:: Group attributes 

116 

117 The following instance variables are provided in addition to those 

118 in Node (see :ref:`NodeClassDescr`): 

119 

120 .. attribute:: _v_children 

121 

122 Dictionary with all nodes hanging from this group. 

123 

124 .. attribute:: _v_groups 

125 

126 Dictionary with all groups hanging from this group. 

127 

128 .. attribute:: _v_hidden 

129 

130 Dictionary with all hidden nodes hanging from this group. 

131 

132 .. attribute:: _v_leaves 

133 

134 Dictionary with all leaves hanging from this group. 

135 

136 .. attribute:: _v_links 

137 

138 Dictionary with all links hanging from this group. 

139 

140 .. attribute:: _v_unknown 

141 

142 Dictionary with all unknown nodes hanging from this group. 

143 

144 """ 

145 

146 # Class identifier. 

147 _c_classid = 'GROUP' 

148 

149 # Children containers that should be loaded only in a lazy way. 

150 # These are documented in the ``Group._g_add_children_names`` method. 

151 _c_lazy_children_attrs = ( 

152 '__members__', '_v_children', '_v_groups', '_v_leaves', 

153 '_v_links', '_v_unknown', '_v_hidden') 

154 

155 # `_v_nchildren` is a direct read-only shorthand 

156 # for the number of *visible* children in a group. 

157 def _g_getnchildren(self): 

158 """The number of children hanging from this group.""" 

159 return len(self._v_children) 

160 

161 _v_nchildren = property(_g_getnchildren) 

162 

163 # `_v_filters` is a direct read-write shorthand for the ``FILTERS`` 

164 # attribute with the default `Filters` instance as a default value. 

165 def _g_getfilters(self): 

166 filters = getattr(self._v_attrs, 'FILTERS', None) 

167 if filters is None: 

168 filters = Filters() 

169 return filters 

170 

171 def _g_setfilters(self, value): 

172 if not isinstance(value, Filters): 

173 raise TypeError( 

174 f"value is not an instance of `Filters`: {value!r}") 

175 self._v_attrs.FILTERS = value 

176 

177 def _g_delfilters(self): 

178 del self._v_attrs.FILTERS 

179 

180 _v_filters = property( 

181 _g_getfilters, _g_setfilters, _g_delfilters, 

182 """Default filter properties for child nodes. 

183 

184 You can (and are encouraged to) use this property to get, set and 

185 delete the FILTERS HDF5 attribute of the group, which stores a Filters 

186 instance (see :ref:`FiltersClassDescr`). When the group has no such 

187 attribute, a default Filters instance is used. 

188 """) 

189 

190 def __init__(self, parentnode, name, 

191 title="", new=False, filters=None, 

192 _log=True): 

193 

194 # Remember to assign these values in the root group constructor 

195 # if it does not use this one! 

196 

197 # First, set attributes belonging to group objects. 

198 

199 self._v_version = obversion 

200 """The object version of this group.""" 

201 

202 self._v_new = new 

203 """Is this the first time the node has been created?""" 

204 

205 self._v_new_title = title 

206 """New title for this node.""" 

207 

208 self._v_new_filters = filters 

209 """New default filter properties for child nodes.""" 

210 

211 self._v_max_group_width = parentnode._v_file.params['MAX_GROUP_WIDTH'] 

212 """Maximum number of children on each group before warning the user. 

213 

214 .. versionchanged:: 3.0 

215 The *_v_maxGroupWidth* attribute has been renamed into 

216 *_v_max_group_width*. 

217 

218 """ 

219 

220 # Finally, set up this object as a node. 

221 super().__init__(parentnode, name, _log) 

222 

223 def _g_post_init_hook(self): 

224 if self._v_new: 

225 if self._v_file.params['PYTABLES_SYS_ATTRS']: 

226 # Save some attributes for the new group on disk. 

227 set_attr = self._v_attrs._g__setattr 

228 # Set the title, class and version attributes. 

229 set_attr('TITLE', self._v_new_title) 

230 set_attr('CLASS', self._c_classid) 

231 set_attr('VERSION', self._v_version) 

232 

233 # Set the default filter properties. 

234 newfilters = self._v_new_filters 

235 if newfilters is None: 

236 # If no filters have been passed in the constructor, 

237 # inherit them from the parent group, but only if they 

238 # have been inherited or explicitly set. 

239 newfilters = getattr( 

240 self._v_parent._v_attrs, 'FILTERS', None) 

241 if newfilters is not None: 

242 set_attr('FILTERS', newfilters) 

243 else: 

244 # If the file has PyTables format, get the VERSION attr 

245 if 'VERSION' in self._v_attrs._v_attrnamessys: 

246 self._v_version = self._v_attrs.VERSION 

247 else: 

248 self._v_version = "0.0 (unknown)" 

249 # We don't need to get more attributes from disk, 

250 # since the most important ones are defined as properties. 

251 

252 def __del__(self): 

253 if (self._v_isopen and 

254 self._v_pathname in self._v_file._node_manager.registry and 

255 '_v_children' in self.__dict__): 

256 # The group is going to be killed. Rebuild weak references 

257 # (that Python cancelled just before calling this method) so 

258 # that they are still usable if the object is revived later. 

259 selfref = weakref.ref(self) 

260 self._v_children.containerref = selfref 

261 self._v_groups.containerref = selfref 

262 self._v_leaves.containerref = selfref 

263 self._v_links.containerref = selfref 

264 self._v_unknown.containerref = selfref 

265 self._v_hidden.containerref = selfref 

266 

267 super().__del__() 

268 

269 def _g_get_child_group_class(self, childname): 

270 """Get the class of a not-yet-loaded group child. 

271 

272 `childname` must be the name of a *group* child. 

273 

274 """ 

275 

276 childCID = self._g_get_gchild_attr(childname, 'CLASS') 

277 if childCID is not None and not isinstance(childCID, str): 

278 childCID = childCID.decode('utf-8') 

279 

280 if childCID in class_id_dict: 

281 return class_id_dict[childCID] # look up group class 

282 else: 

283 return Group # default group class 

284 

285 def _g_get_child_leaf_class(self, childname, warn=True): 

286 """Get the class of a not-yet-loaded leaf child. 

287 

288 `childname` must be the name of a *leaf* child. If the child 

289 belongs to an unknown kind of leaf, or if its kind can not be 

290 guessed, `UnImplemented` will be returned and a warning will be 

291 issued if `warn` is true. 

292 

293 """ 

294 

295 if self._v_file.params['PYTABLES_SYS_ATTRS']: 

296 childCID = self._g_get_lchild_attr(childname, 'CLASS') 

297 if childCID is not None and not isinstance(childCID, str): 

298 childCID = childCID.decode('utf-8') 

299 else: 

300 childCID = None 

301 

302 if childCID in class_id_dict: 

303 return class_id_dict[childCID] # look up leaf class 

304 else: 

305 # Unknown or no ``CLASS`` attribute, try a guess. 

306 childCID2 = utilsextension.which_class(self._v_objectid, childname) 

307 if childCID2 == 'UNSUPPORTED': 

308 if warn: 

309 if childCID is None: 

310 warnings.warn( 

311 "leaf ``%s`` is of an unsupported type; " 

312 "it will become an ``UnImplemented`` node" 

313 % self._g_join(childname)) 

314 else: 

315 warnings.warn( 

316 ("leaf ``%s`` has an unknown class ID ``%s``; " 

317 "it will become an ``UnImplemented`` node") 

318 % (self._g_join(childname), childCID)) 

319 return UnImplemented 

320 assert childCID2 in class_id_dict 

321 return class_id_dict[childCID2] # look up leaf class 

322 

323 def _g_add_children_names(self): 

324 """Add children names to this group taking into account their 

325 visibility and kind.""" 

326 

327 mydict = self.__dict__ 

328 

329 # The names of the lazy attributes 

330 mydict['__members__'] = members = [] 

331 """The names of visible children nodes for readline-style completion. 

332 """ 

333 mydict['_v_children'] = children = _ChildrenDict(self) 

334 """The number of children hanging from this group.""" 

335 mydict['_v_groups'] = groups = _ChildrenDict(self) 

336 """Dictionary with all groups hanging from this group.""" 

337 mydict['_v_leaves'] = leaves = _ChildrenDict(self) 

338 """Dictionary with all leaves hanging from this group.""" 

339 mydict['_v_links'] = links = _ChildrenDict(self) 

340 """Dictionary with all links hanging from this group.""" 

341 mydict['_v_unknown'] = unknown = _ChildrenDict(self) 

342 """Dictionary with all unknown nodes hanging from this group.""" 

343 mydict['_v_hidden'] = hidden = _ChildrenDict(self) 

344 """Dictionary with all hidden nodes hanging from this group.""" 

345 

346 # Get the names of *all* child groups and leaves. 

347 (group_names, leaf_names, link_names, unknown_names) = \ 

348 self._g_list_group(self._v_parent) 

349 

350 # Separate groups into visible groups and hidden nodes, 

351 # and leaves into visible leaves and hidden nodes. 

352 for (childnames, childdict) in ((group_names, groups), 

353 (leaf_names, leaves), 

354 (link_names, links), 

355 (unknown_names, unknown)): 

356 

357 for childname in childnames: 

358 # See whether the name implies that the node is hidden. 

359 # (Assigned values are entirely irrelevant.) 

360 if isvisiblename(childname): 

361 # Visible node. 

362 members.insert(0, childname) 

363 children[childname] = None 

364 childdict[childname] = None 

365 else: 

366 # Hidden node. 

367 hidden[childname] = None 

368 

369 def _g_check_has_child(self, name): 

370 """Check whether 'name' is a children of 'self' and return its type.""" 

371 

372 # Get the HDF5 name matching the PyTables name. 

373 node_type = self._g_get_objinfo(name) 

374 if node_type == "NoSuchNode": 

375 raise NoSuchNodeError( 

376 "group ``%s`` does not have a child named ``%s``" 

377 % (self._v_pathname, name)) 

378 return node_type 

379 

380 def __iter__(self): 

381 """Iterate over the child nodes hanging directly from the group. 

382 

383 This iterator is *not* recursive. 

384 

385 Examples 

386 -------- 

387 

388 :: 

389 

390 # Non-recursively list all the nodes hanging from '/detector' 

391 print("Nodes in '/detector' group:") 

392 for node in h5file.root.detector: 

393 print(node) 

394 

395 """ 

396 

397 return self._f_iter_nodes() 

398 

399 def __contains__(self, name): 

400 """Is there a child with that `name`? 

401 

402 Returns a true value if the group has a child node (visible or 

403 hidden) with the given `name` (a string), false otherwise. 

404 

405 """ 

406 

407 self._g_check_open() 

408 try: 

409 self._g_check_has_child(name) 

410 except NoSuchNodeError: 

411 return False 

412 return True 

413 

414 def __getitem__(self, childname): 

415 """Return the (visible or hidden) child with that `name` ( a string). 

416 

417 Raise IndexError if child not exist. 

418 """ 

419 try: 

420 return self._f_get_child(childname) 

421 except NoSuchNodeError: 

422 raise IndexError(childname) 

423 

424 def _f_walknodes(self, classname=None): 

425 """Iterate over descendant nodes. 

426 

427 This method recursively walks *self* top to bottom (preorder), 

428 iterating over child groups in alphanumerical order, and yielding 

429 nodes. If classname is supplied, only instances of the named class are 

430 yielded. 

431 

432 If *classname* is Group, it behaves like :meth:`Group._f_walk_groups`, 

433 yielding only groups. If you don't want a recursive behavior, 

434 use :meth:`Group._f_iter_nodes` instead. 

435 

436 Examples 

437 -------- 

438 

439 :: 

440 

441 # Recursively print all the arrays hanging from '/' 

442 print("Arrays in the object tree '/':") 

443 for array in h5file.root._f_walknodes('Array', recursive=True): 

444 print(array) 

445 

446 """ 

447 

448 self._g_check_open() 

449 

450 # For compatibility with old default arguments. 

451 if classname == '': 

452 classname = None 

453 

454 if classname == "Group": 

455 # Recursive algorithm 

456 yield from self._f_walk_groups() 

457 else: 

458 for group in self._f_walk_groups(): 

459 yield from group._f_iter_nodes(classname) 

460 

461 def _g_join(self, name): 

462 """Helper method to correctly concatenate a name child object with the 

463 pathname of this group.""" 

464 

465 if name == "/": 

466 # This case can happen when doing copies 

467 return self._v_pathname 

468 return join_path(self._v_pathname, name) 

469 

470 def _g_width_warning(self): 

471 """Issue a :exc:`PerformanceWarning` on too many children.""" 

472 

473 warnings.warn("""\ 

474group ``%s`` is exceeding the recommended maximum number of children (%d); \ 

475be ready to see PyTables asking for *lots* of memory and possibly slow I/O.""" 

476 % (self._v_pathname, self._v_max_group_width), 

477 PerformanceWarning) 

478 

479 def _g_refnode(self, childnode, childname, validate=True): 

480 """Insert references to a `childnode` via a `childname`. 

481 

482 Checks that the `childname` is valid and does not exist, then 

483 creates references to the given `childnode` by that `childname`. 

484 The validation of the name can be omitted by setting `validate` 

485 to a false value (this may be useful for adding already existing 

486 nodes to the tree). 

487 

488 """ 

489 

490 # Check for name validity. 

491 if validate: 

492 check_name_validity(childname) 

493 childnode._g_check_name(childname) 

494 

495 # Check if there is already a child with the same name. 

496 # This can be triggered because of the user 

497 # (via node construction or renaming/movement). 

498 # Links are not checked here because they are copied and referenced 

499 # using ``File.get_node`` so they already exist in `self`. 

500 if (not isinstance(childnode, Link)) and childname in self: 

501 raise NodeError( 

502 "group ``%s`` already has a child node named ``%s``" 

503 % (self._v_pathname, childname)) 

504 

505 # Show a warning if there is an object attribute with that name. 

506 if childname in self.__dict__: 

507 warnings.warn( 

508 "group ``%s`` already has an attribute named ``%s``; " 

509 "you will not be able to use natural naming " 

510 "to access the child node" 

511 % (self._v_pathname, childname), NaturalNameWarning) 

512 

513 # Check group width limits. 

514 if (len(self._v_children) + len(self._v_hidden) >= 

515 self._v_max_group_width): 

516 self._g_width_warning() 

517 

518 # Update members information. 

519 # Insert references to the new child. 

520 # (Assigned values are entirely irrelevant.) 

521 if isvisiblename(childname): 

522 # Visible node. 

523 self.__members__.insert(0, childname) # enable completion 

524 self._v_children[childname] = None # insert node 

525 if isinstance(childnode, Unknown): 

526 self._v_unknown[childname] = None 

527 elif isinstance(childnode, Link): 

528 self._v_links[childname] = None 

529 elif isinstance(childnode, Leaf): 

530 self._v_leaves[childname] = None 

531 elif isinstance(childnode, Group): 

532 self._v_groups[childname] = None 

533 else: 

534 # Hidden node. 

535 self._v_hidden[childname] = None # insert node 

536 

537 def _g_unrefnode(self, childname): 

538 """Remove references to a node. 

539 

540 Removes all references to the named node. 

541 

542 """ 

543 

544 # This can *not* be triggered because of the user. 

545 assert childname in self, \ 

546 ("group ``%s`` does not have a child node named ``%s``" 

547 % (self._v_pathname, childname)) 

548 

549 # Update members information, if needed 

550 if '_v_children' in self.__dict__: 

551 if childname in self._v_children: 

552 # Visible node. 

553 members = self.__members__ 

554 member_index = members.index(childname) 

555 del members[member_index] # disables completion 

556 

557 del self._v_children[childname] # remove node 

558 self._v_unknown.pop(childname, None) 

559 self._v_links.pop(childname, None) 

560 self._v_leaves.pop(childname, None) 

561 self._v_groups.pop(childname, None) 

562 else: 

563 # Hidden node. 

564 del self._v_hidden[childname] # remove node 

565 

566 def _g_move(self, newparent, newname): 

567 # Move the node to the new location. 

568 oldpath = self._v_pathname 

569 super()._g_move(newparent, newname) 

570 newpath = self._v_pathname 

571 

572 # Update location information in children. This node shouldn't 

573 # be affected since it has already been relocated. 

574 self._v_file._update_node_locations(oldpath, newpath) 

575 

576 def _g_copy(self, newparent, newname, recursive, _log=True, **kwargs): 

577 # Compute default arguments. 

578 title = kwargs.get('title', self._v_title) 

579 filters = kwargs.get('filters', None) 

580 stats = kwargs.get('stats', None) 

581 

582 # Fix arguments with explicit None values for backwards compatibility. 

583 if title is None: 

584 title = self._v_title 

585 # If no filters have been passed to the call, copy them from the 

586 # source group, but only if inherited or explicitly set. 

587 if filters is None: 

588 filters = getattr(self._v_attrs, 'FILTERS', None) 

589 

590 # Create a copy of the object. 

591 new_node = Group(newparent, newname, 

592 title, new=True, filters=filters, _log=_log) 

593 

594 # Copy user attributes if needed. 

595 if kwargs.get('copyuserattrs', True): 

596 self._v_attrs._g_copy(new_node._v_attrs, copyclass=True) 

597 

598 # Update statistics if needed. 

599 if stats is not None: 

600 stats['groups'] += 1 

601 

602 if recursive: 

603 # Copy child nodes if a recursive copy was requested. 

604 # Some arguments should *not* be passed to children copy ops. 

605 kwargs = kwargs.copy() 

606 kwargs.pop('title', None) 

607 self._g_copy_children(new_node, **kwargs) 

608 

609 return new_node 

610 

611 def _g_copy_children(self, newparent, **kwargs): 

612 """Copy child nodes. 

613 

614 Copies all nodes descending from this one into the specified 

615 `newparent`. If the new parent has a child node with the same 

616 name as one of the nodes in this group, the copy fails with a 

617 `NodeError`, maybe resulting in a partial copy. Nothing is 

618 logged. 

619 

620 """ 

621 

622 # Recursive version of children copy. 

623 # for srcchild in self._v_children.itervalues(): 

624 # srcchild._g_copy_as_child(newparent, **kwargs) 

625 

626 # Non-recursive version of children copy. 

627 use_hardlinks = kwargs.get('use_hardlinks', False) 

628 if use_hardlinks: 

629 address_map = kwargs.setdefault('address_map', {}) 

630 

631 parentstack = [(self, newparent)] # [(source, destination), ...] 

632 while parentstack: 

633 (srcparent, dstparent) = parentstack.pop() 

634 

635 if use_hardlinks: 

636 for srcchild in srcparent._v_children.values(): 

637 addr, rc = srcchild._get_obj_info() 

638 if rc > 1 and addr in address_map: 

639 where, name = address_map[addr][0] 

640 localsrc = os.path.join(where, name) 

641 dstparent._v_file.create_hard_link(dstparent, 

642 srcchild.name, 

643 localsrc) 

644 address_map[addr].append( 

645 (dstparent._v_pathname, srcchild.name) 

646 ) 

647 

648 # Update statistics if needed. 

649 stats = kwargs.pop('stats', None) 

650 if stats is not None: 

651 stats['hardlinks'] += 1 

652 else: 

653 dstchild = srcchild._g_copy_as_child(dstparent, 

654 **kwargs) 

655 if isinstance(srcchild, Group): 

656 parentstack.append((srcchild, dstchild)) 

657 

658 if rc > 1: 

659 address_map[addr] = [ 

660 (dstparent._v_pathname, srcchild.name) 

661 ] 

662 else: 

663 for srcchild in srcparent._v_children.values(): 

664 dstchild = srcchild._g_copy_as_child(dstparent, **kwargs) 

665 if isinstance(srcchild, Group): 

666 parentstack.append((srcchild, dstchild)) 

667 

668 def _f_get_child(self, childname): 

669 """Get the child called childname of this group. 

670 

671 If the child exists (be it visible or not), it is returned. Else, a 

672 NoSuchNodeError is raised. 

673 

674 Using this method is recommended over getattr() when doing programmatic 

675 accesses to children if childname is unknown beforehand or when its 

676 name is not a valid Python identifier. 

677 

678 """ 

679 

680 self._g_check_open() 

681 

682 self._g_check_has_child(childname) 

683 

684 childpath = join_path(self._v_pathname, childname) 

685 return self._v_file._get_node(childpath) 

686 

687 def _f_list_nodes(self, classname=None): 

688 """Return a *list* with children nodes. 

689 

690 This is a list-returning version of :meth:`Group._f_iter_nodes()`. 

691 

692 """ 

693 

694 return list(self._f_iter_nodes(classname)) 

695 

696 def _f_iter_nodes(self, classname=None): 

697 """Iterate over children nodes. 

698 

699 Child nodes are yielded alphanumerically sorted by node name. If the 

700 name of a class derived from Node (see :ref:`NodeClassDescr`) is 

701 supplied in the classname parameter, only instances of that class (or 

702 subclasses of it) will be returned. 

703 

704 This is an iterator version of :meth:`Group._f_list_nodes`. 

705 

706 """ 

707 

708 self._g_check_open() 

709 

710 if not classname: 

711 # Returns all the children alphanumerically sorted 

712 for name in sorted(self._v_children): 

713 yield self._v_children[name] 

714 elif classname == 'Group': 

715 # Returns all the groups alphanumerically sorted 

716 for name in sorted(self._v_groups): 

717 yield self._v_groups[name] 

718 elif classname == 'Leaf': 

719 # Returns all the leaves alphanumerically sorted 

720 for name in sorted(self._v_leaves): 

721 yield self._v_leaves[name] 

722 elif classname == 'Link': 

723 # Returns all the links alphanumerically sorted 

724 for name in sorted(self._v_links): 

725 yield self._v_links[name] 

726 elif classname == 'IndexArray': 

727 raise TypeError( 

728 "listing ``IndexArray`` nodes is not allowed") 

729 else: 

730 class_ = get_class_by_name(classname) 

731 for childname, childnode in sorted(self._v_children.items()): 

732 if isinstance(childnode, class_): 

733 yield childnode 

734 

735 def _f_walk_groups(self): 

736 """Recursively iterate over descendent groups (not leaves). 

737 

738 This method starts by yielding *self*, and then it goes on to 

739 recursively iterate over all child groups in alphanumerical order, top 

740 to bottom (preorder), following the same procedure. 

741 

742 """ 

743 

744 self._g_check_open() 

745 

746 stack = [self] 

747 yield self 

748 # Iterate over the descendants 

749 while stack: 

750 objgroup = stack.pop() 

751 groupnames = sorted(objgroup._v_groups) 

752 # Sort the groups before delivering. This uses the groups names 

753 # for groups in tree (in order to sort() can classify them). 

754 for groupname in groupnames: 

755 # TODO: check recursion 

756 stack.append(objgroup._v_groups[groupname]) 

757 yield objgroup._v_groups[groupname] 

758 

759 def __delattr__(self, name): 

760 """Delete a Python attribute called name. 

761 

762 This method only provides a extra warning in case the user 

763 tries to delete a children node using __delattr__. 

764 

765 To remove a children node from this group use 

766 :meth:`File.remove_node` or :meth:`Node._f_remove`. To delete 

767 a PyTables node attribute use :meth:`File.del_node_attr`, 

768 :meth:`Node._f_delattr` or :attr:`Node._v_attrs``. 

769 

770 If there is an attribute and a child node with the same name, 

771 the child node will be made accessible again via natural naming. 

772 

773 """ 

774 

775 try: 

776 super().__delattr__(name) # nothing particular 

777 except AttributeError as ae: 

778 hint = " (use ``node._f_remove()`` if you want to remove a node)" 

779 raise ae.__class__(str(ae) + hint) 

780 

781 def __dir__(self): 

782 """Autocomplete only children named as valid python identifiers. 

783 

784 Only PY3 supports this special method. 

785 """ 

786 subnods = [c for c in self._v_children if c.isidentifier()] 

787 return super().__dir__() + subnods 

788 

789 def __getattr__(self, name): 

790 """Get a Python attribute or child node called name. 

791 If the node has a child node called name it is returned, 

792 else an AttributeError is raised. 

793 """ 

794 

795 if name in self._c_lazy_children_attrs: 

796 self._g_add_children_names() 

797 return self.__dict__[name] 

798 return self._f_get_child(name) 

799 

800 def __setattr__(self, name, value): 

801 """Set a Python attribute called name with the given value. 

802 

803 This method stores an *ordinary Python attribute* in the object. It 

804 does *not* store new children nodes under this group; for that, use the 

805 File.create*() methods (see the File class 

806 in :ref:`FileClassDescr`). It does *neither* store a PyTables node 

807 attribute; for that, 

808 use :meth:`File.set_node_attr`, :meth`:Node._f_setattr` 

809 or :attr:`Node._v_attrs`. 

810 

811 If there is already a child node with the same name, a 

812 NaturalNameWarning will be issued and the child node will not be 

813 accessible via natural naming nor getattr(). It will still be available 

814 via :meth:`File.get_node`, :meth:`Group._f_get_child` and children 

815 dictionaries in the group (if visible). 

816 

817 """ 

818 

819 # Show a warning if there is an child node with that name. 

820 # 

821 # ..note:: 

822 # 

823 # Using ``if name in self:`` is not right since that would 

824 # require ``_v_children`` and ``_v_hidden`` to be already set 

825 # when the very first attribute assignments are made. 

826 # Moreover, this warning is only concerned about clashes with 

827 # names used in natural naming, i.e. those in ``__members__``. 

828 # 

829 # ..note:: 

830 # 

831 # The check ``'__members__' in myDict`` allows attribute 

832 # assignment to happen before calling `Group.__init__()`, by 

833 # avoiding to look into the still not assigned ``__members__`` 

834 # attribute. This allows subclasses to set up some attributes 

835 # and then call the constructor of the superclass. If the 

836 # check above is disabled, that results in Python entering an 

837 # endless loop on exit! 

838 

839 mydict = self.__dict__ 

840 if '__members__' in mydict and name in self.__members__: 

841 warnings.warn( 

842 "group ``%s`` already has a child node named ``%s``; " 

843 "you will not be able to use natural naming " 

844 "to access the child node" 

845 % (self._v_pathname, name), NaturalNameWarning) 

846 

847 super().__setattr__(name, value) 

848 

849 def _f_flush(self): 

850 """Flush this Group.""" 

851 

852 self._g_check_open() 

853 self._g_flush_group() 

854 

855 def _g_close_descendents(self): 

856 """Close all the *loaded* descendent nodes of this group.""" 

857 

858 node_manager = self._v_file._node_manager 

859 node_manager.close_subtree(self._v_pathname) 

860 

861 def _g_close(self): 

862 """Close this (open) group.""" 

863 

864 if self._v_isopen: 

865 # hdf5extension operations: 

866 # Close HDF5 group. 

867 self._g_close_group() 

868 

869 # Close myself as a node. 

870 super()._f_close() 

871 

872 def _f_close(self): 

873 """Close this group and all its descendents. 

874 

875 This method has the behavior described in :meth:`Node._f_close`. 

876 It should be noted that this operation closes all the nodes 

877 descending from this group. 

878 

879 You should not need to close nodes manually because they are 

880 automatically opened/closed when they are loaded/evicted from 

881 the integrated LRU cache. 

882 

883 """ 

884 

885 # If the group is already closed, return immediately 

886 if not self._v_isopen: 

887 return 

888 

889 # First, close all the descendents of this group, unless a) the 

890 # group is being deleted (evicted from LRU cache) or b) the node 

891 # is being closed during an aborted creation, in which cases 

892 # this is not an explicit close issued by the user. 

893 if not (self._v__deleting or self._v_objectid is None): 

894 self._g_close_descendents() 

895 

896 # When all the descendents have been closed, close this group. 

897 # This is done at the end because some nodes may still need to 

898 # be loaded during the closing process; thus this node must be 

899 # open until the very end. 

900 self._g_close() 

901 

902 def _g_remove(self, recursive=False, force=False): 

903 """Remove (recursively if needed) the Group. 

904 

905 This version correctly handles both visible and hidden nodes. 

906 

907 """ 

908 

909 if self._v_nchildren > 0: 

910 if not (recursive or force): 

911 raise NodeError("group ``%s`` has child nodes; " 

912 "please set `recursive` or `force` to true " 

913 "to remove it" 

914 % (self._v_pathname,)) 

915 

916 # First close all the descendents hanging from this group, 

917 # so that it is not possible to use a node that no longer exists. 

918 self._g_close_descendents() 

919 

920 # Remove the node itself from the hierarchy. 

921 super()._g_remove(recursive, force) 

922 

923 def _f_copy(self, newparent=None, newname=None, 

924 overwrite=False, recursive=False, createparents=False, 

925 **kwargs): 

926 """Copy this node and return the new one. 

927 

928 This method has the behavior described in :meth:`Node._f_copy`. 

929 In addition, it recognizes the following keyword arguments: 

930 

931 Parameters 

932 ---------- 

933 title 

934 The new title for the destination. If omitted or None, the 

935 original title is used. This only applies to the topmost 

936 node in recursive copies. 

937 filters : Filters 

938 Specifying this parameter overrides the original filter 

939 properties in the source node. If specified, it must be an 

940 instance of the Filters class (see :ref:`FiltersClassDescr`). 

941 The default is to copy the filter properties from the source 

942 node. 

943 copyuserattrs 

944 You can prevent the user attributes from being copied by setting 

945 thisparameter to False. The default is to copy them. 

946 stats 

947 This argument may be used to collect statistics on the copy 

948 process. When used, it should be a dictionary with keys 'groups', 

949 'leaves', 'links' and 'bytes' having a numeric value. Their values 

950 willbe incremented to reflect the number of groups, leaves and 

951 bytes, respectively, that have been copied during the operation. 

952 

953 """ 

954 

955 return super()._f_copy( 

956 newparent, newname, 

957 overwrite, recursive, createparents, **kwargs) 

958 

959 def _f_copy_children(self, dstgroup, overwrite=False, recursive=False, 

960 createparents=False, **kwargs): 

961 """Copy the children of this group into another group. 

962 

963 Children hanging directly from this group are copied into dstgroup, 

964 which can be a Group (see :ref:`GroupClassDescr`) object or its 

965 pathname in string form. If createparents is true, the needed groups 

966 for the given destination group path to exist will be created. 

967 

968 The operation will fail with a NodeError if there is a child node 

969 in the destination group with the same name as one of the copied 

970 children from this one, unless overwrite is true; in this case, 

971 the former child node is recursively removed before copying the 

972 later. 

973 

974 By default, nodes descending from children groups of this node 

975 are not copied. If the recursive argument is true, all descendant 

976 nodes of this node are recursively copied. 

977 

978 Additional keyword arguments may be passed to customize the 

979 copying process. For instance, title and filters may be changed, 

980 user attributes may be or may not be copied, data may be sub-sampled, 

981 stats may be collected, etc. Arguments unknown to nodes are simply 

982 ignored. Check the documentation for copying operations of nodes to 

983 see which options they support. 

984 

985 """ 

986 

987 self._g_check_open() 

988 

989 # `dstgroup` is used instead of its path to avoid accepting 

990 # `Node` objects when `createparents` is true. Also, note that 

991 # there is no risk of creating parent nodes and failing later 

992 # because of destination nodes already existing. 

993 dstparent = self._v_file._get_or_create_path(dstgroup, createparents) 

994 self._g_check_group(dstparent) # Is it a group? 

995 

996 if not overwrite: 

997 # Abort as early as possible when destination nodes exist 

998 # and overwriting is not enabled. 

999 for childname in self._v_children: 

1000 if childname in dstparent: 

1001 raise NodeError( 

1002 "destination group ``%s`` already has " 

1003 "a node named ``%s``; " 

1004 "you may want to use the ``overwrite`` argument" 

1005 % (dstparent._v_pathname, childname)) 

1006 

1007 use_hardlinks = kwargs.get('use_hardlinks', False) 

1008 if use_hardlinks: 

1009 address_map = kwargs.setdefault('address_map', {}) 

1010 

1011 for child in self._v_children.values(): 

1012 addr, rc = child._get_obj_info() 

1013 if rc > 1 and addr in address_map: 

1014 where, name = address_map[addr][0] 

1015 localsrc = os.path.join(where, name) 

1016 dstparent._v_file.create_hard_link(dstparent, child.name, 

1017 localsrc) 

1018 address_map[addr].append( 

1019 (dstparent._v_pathname, child.name) 

1020 ) 

1021 

1022 # Update statistics if needed. 

1023 stats = kwargs.pop('stats', None) 

1024 if stats is not None: 

1025 stats['hardlinks'] += 1 

1026 else: 

1027 child._f_copy(dstparent, None, overwrite, recursive, 

1028 **kwargs) 

1029 if rc > 1: 

1030 address_map[addr] = [ 

1031 (dstparent._v_pathname, child.name) 

1032 ] 

1033 else: 

1034 for child in self._v_children.values(): 

1035 child._f_copy(dstparent, None, overwrite, recursive, **kwargs) 

1036 

1037 def __str__(self): 

1038 """Return a short string representation of the group. 

1039 

1040 Examples 

1041 -------- 

1042 

1043 :: 

1044 

1045 >>> import tables 

1046 >>> f = tables.open_file('tables/tests/Tables_lzo2.h5') 

1047 >>> print(f.root.group0) 

1048 /group0 (Group) '' 

1049 >>> f.close() 

1050 

1051 """ 

1052 

1053 return (f"{self._v_pathname} ({self.__class__.__name__}) " 

1054 f"{self._v_title!r}") 

1055 

1056 def __repr__(self): 

1057 """Return a detailed string representation of the group. 

1058 

1059 Examples 

1060 -------- 

1061 

1062 :: 

1063 

1064 >>> import tables 

1065 >>> f = tables.open_file('tables/tests/Tables_lzo2.h5') 

1066 >>> f.root.group0 

1067 /group0 (Group) '' 

1068 children := ['group1' (Group), 'tuple1' (Table)] 

1069 >>> f.close() 

1070 

1071 """ 

1072 

1073 rep = [ 

1074 f'{childname!r} ({child.__class__.__name__})' 

1075 for (childname, child) in self._v_children.items() 

1076 ] 

1077 return f'{self!s}\n children := [{", ".join(rep)}]' 

1078 

1079 

1080# Special definition for group root 

1081class RootGroup(Group): 

1082 

1083 def __init__(self, ptfile, name, title, new, filters): 

1084 mydict = self.__dict__ 

1085 

1086 # Set group attributes. 

1087 self._v_version = obversion 

1088 self._v_new = new 

1089 if new: 

1090 self._v_new_title = title 

1091 self._v_new_filters = filters 

1092 else: 

1093 self._v_new_title = None 

1094 self._v_new_filters = None 

1095 

1096 # Set node attributes. 

1097 self._v_file = ptfile 

1098 self._v_isopen = True # root is always open 

1099 self._v_pathname = '/' 

1100 self._v_name = '/' 

1101 self._v_depth = 0 

1102 self._v_max_group_width = ptfile.params['MAX_GROUP_WIDTH'] 

1103 self._v__deleting = False 

1104 self._v_objectid = None # later 

1105 

1106 # Only the root node has the file as a parent. 

1107 # Bypass __setattr__ to avoid the ``Node._v_parent`` property. 

1108 mydict['_v_parent'] = ptfile 

1109 ptfile._node_manager.register_node(self, '/') 

1110 

1111 # hdf5extension operations (do before setting an AttributeSet): 

1112 # Update node attributes. 

1113 self._g_new(ptfile, name, init=True) 

1114 # Open the node and get its object ID. 

1115 self._v_objectid = self._g_open() 

1116 

1117 # Set disk attributes and read children names. 

1118 # 

1119 # This *must* be postponed because this method needs the root node 

1120 # to be created and bound to ``File.root``. 

1121 # This is an exception to the rule, handled by ``File.__init()__``. 

1122 # 

1123 # self._g_post_init_hook() 

1124 

1125 def _g_load_child(self, childname): 

1126 """Load a child node from disk. 

1127 

1128 The child node `childname` is loaded from disk and an adequate 

1129 `Node` object is created and returned. If there is no such 

1130 child, a `NoSuchNodeError` is raised. 

1131 

1132 """ 

1133 

1134 if self._v_file.root_uep != "/": 

1135 childname = join_path(self._v_file.root_uep, childname) 

1136 # Is the node a group or a leaf? 

1137 node_type = self._g_check_has_child(childname) 

1138 

1139 # Nodes that HDF5 report as H5G_UNKNOWN 

1140 if node_type == 'Unknown': 

1141 return Unknown(self, childname) 

1142 

1143 # Guess the PyTables class suited to the node, 

1144 # build a PyTables node and return it. 

1145 if node_type == "Group": 

1146 if self._v_file.params['PYTABLES_SYS_ATTRS']: 

1147 ChildClass = self._g_get_child_group_class(childname) 

1148 else: 

1149 # Default is a Group class 

1150 ChildClass = Group 

1151 return ChildClass(self, childname, new=False) 

1152 elif node_type == "Leaf": 

1153 ChildClass = self._g_get_child_leaf_class(childname, warn=True) 

1154 # Building a leaf may still fail because of unsupported types 

1155 # and other causes. 

1156 # return ChildClass(self, childname) # uncomment for debugging 

1157 try: 

1158 return ChildClass(self, childname) 

1159 except Exception as exc: # XXX 

1160 warnings.warn( 

1161 "problems loading leaf ``%s``::\n\n" 

1162 " %s\n\n" 

1163 "The leaf will become an ``UnImplemented`` node." 

1164 % (self._g_join(childname), exc)) 

1165 # If not, associate an UnImplemented object to it 

1166 return UnImplemented(self, childname) 

1167 elif node_type == "SoftLink": 

1168 return SoftLink(self, childname) 

1169 elif node_type == "ExternalLink": 

1170 return ExternalLink(self, childname) 

1171 else: 

1172 return UnImplemented(self, childname) 

1173 

1174 def _f_rename(self, newname): 

1175 raise NodeError("the root node can not be renamed") 

1176 

1177 def _f_move(self, newparent=None, newname=None, createparents=False): 

1178 raise NodeError("the root node can not be moved") 

1179 

1180 def _f_remove(self, recursive=False): 

1181 raise NodeError("the root node can not be removed") 

1182 

1183 

1184class TransactionGroupG(NotLoggedMixin, Group): 

1185 _c_classid = 'TRANSGROUP' 

1186 

1187 def _g_width_warning(self): 

1188 warnings.warn("""\ 

1189the number of transactions is exceeding the recommended maximum (%d);\ 

1190be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" 

1191 % (self._v_max_group_width,), PerformanceWarning) 

1192 

1193 

1194class TransactionG(NotLoggedMixin, Group): 

1195 _c_classid = 'TRANSG' 

1196 

1197 def _g_width_warning(self): 

1198 warnings.warn("""\ 

1199transaction ``%s`` is exceeding the recommended maximum number of marks (%d);\ 

1200be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" 

1201 % (self._v_pathname, self._v_max_group_width), 

1202 PerformanceWarning) 

1203 

1204 

1205class MarkG(NotLoggedMixin, Group): 

1206 # Class identifier. 

1207 _c_classid = 'MARKG' 

1208 

1209 import re 

1210 _c_shadow_name_re = re.compile(r'^a[0-9]+$') 

1211 

1212 def _g_width_warning(self): 

1213 warnings.warn("""\ 

1214mark ``%s`` is exceeding the recommended maximum action storage (%d nodes);\ 

1215be ready to see PyTables asking for *lots* of memory and possibly slow I/O""" 

1216 % (self._v_pathname, self._v_max_group_width), 

1217 PerformanceWarning) 

1218 

1219 def _g_reset(self): 

1220 """Empty action storage (nodes and attributes). 

1221 

1222 This method empties all action storage kept in this node: nodes 

1223 and attributes. 

1224 

1225 """ 

1226 

1227 # Remove action storage nodes. 

1228 for child in list(self._v_children.values()): 

1229 child._g_remove(True, True) 

1230 

1231 # Remove action storage attributes. 

1232 attrs = self._v_attrs 

1233 shname = self._c_shadow_name_re 

1234 for attrname in attrs._v_attrnamesuser[:]: 

1235 if shname.match(attrname): 

1236 attrs._g__delattr(attrname)