def _g_maybe_remove(self, parent, name, overwrite): if name in parent: if not overwrite: raise NodeError("""\ destination group ``%s`` already has a node named ``%s``; \ you may want to use the ``overwrite`` argument""" % (parent._v_pathname, name)) parent._f_get_child(name)._f_remove(True)
def _g_check_not_contains(self, pathname): # The not-a-TARDIS test. ;) mypathname = self._v_pathname if (mypathname == '/' # all nodes fall below the root group or pathname == mypathname or pathname.startswith(mypathname + '/')): raise NodeError("can not move or recursively copy node ``%s`` " "into itself" % mypathname)
def _g_refnode(self, childnode, childname, validate=True): """Insert references to a `childnode` via a `childname`. Checks that the `childname` is valid and does not exist, then creates references to the given `childnode` by that `childname`. The validation of the name can be omitted by setting `validate` to a false value (this may be useful for adding already existing nodes to the tree). """ # Check for name validity. if validate: check_name_validity(childname) childnode._g_check_name(childname) # Check if there is already a child with the same name. # This can be triggered because of the user # (via node construction or renaming/movement). # Links are not checked here because they are copied and referenced # using ``File.get_node`` so they already exist in `self`. if (not isinstance(childnode, Link)) and childname in self: raise NodeError( "group ``%s`` already has a child node named ``%s``" % (self._v_pathname, childname)) # Show a warning if there is an object attribute with that name. if childname in self.__dict__: warnings.warn( "group ``%s`` already has an attribute named ``%s``; " "you will not be able to use natural naming " "to access the child node" % (self._v_pathname, childname), NaturalNameWarning) # Check group width limits. if (len(self._v_children) + len(self._v_hidden) >= self._v_max_group_width): self._g_width_warning() # Update members information. # Insert references to the new child. # (Assigned values are entirely irrelevant.) if isvisiblename(childname): # Visible node. self.__members__.insert(0, childname) # enable completion self._v_children[childname] = None # insert node if isinstance(childnode, Unknown): self._v_unknown[childname] = None elif isinstance(childnode, Link): self._v_links[childname] = None elif isinstance(childnode, Leaf): self._v_leaves[childname] = None elif isinstance(childnode, Group): self._v_groups[childname] = None else: # Hidden node. self._v_hidden[childname] = None # insert node
def _f_copy_children(self, dstgroup, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy the children of this group into another group. Children hanging directly from this group are copied into dstgroup, which can be a Group (see :ref:`GroupClassDescr`) object or its pathname in string form. If createparents is true, the needed groups for the given destination group path to exist will be created. The operation will fail with a NodeError if there is a child node in the destination group with the same name as one of the copied children from this one, unless overwrite is true; in this case, the former child node is recursively removed before copying the later. By default, nodes descending from children groups of this node are not copied. If the recursive argument is true, all descendant nodes of this node are recursively copied. Additional keyword arguments may be passed to customize the copying process. For instance, title and filters may be changed, user attributes may be or may not be copied, data may be sub-sampled, stats may be collected, etc. Arguments unknown to nodes are simply ignored. Check the documentation for copying operations of nodes to see which options they support. """ self._g_check_open() # `dstgroup` is used instead of its path to avoid accepting # `Node` objects when `createparents` is true. Also, note that # there is no risk of creating parent nodes and failing later # because of destination nodes already existing. dstparent = self._v_file._get_or_create_path(dstgroup, createparents) self._g_check_group(dstparent) # Is it a group? if not overwrite: # Abort as early as possible when destination nodes exist # and overwriting is not enabled. for childname in self._v_children: if childname in dstparent: raise NodeError( "destination group ``%s`` already has " "a node named ``%s``; " "you may want to use the ``overwrite`` argument" % (dstparent._v_pathname, childname)) for child in self._v_children.itervalues(): child._f_copy(dstparent, None, overwrite, recursive, **kwargs)
def _g_remove(self, recursive=False): """Remove (recursively if needed) the Group. This version correctly handles both visible and hidden nodes. """ if self._v_nchildren > 0: if not recursive: raise NodeError("group ``%s`` has child nodes; " "please state recursive removal to remove it" % (self._v_pathname,)) # First close all the descendents hanging from this group, # so that it is not possible to use a node that no longer exists. self._g_closeDescendents() # Remove the node itself from the hierarchy. super(Group, self)._g_remove(recursive)
def _f_copy(self, newparent=None, newname=None, overwrite=False, recursive=False, createparents=False, **kwargs): """Copy this node and return the new node. Creates and returns a copy of the node, maybe in a different place in the hierarchy. newparent can be a Group object (see :ref:`GroupClassDescr`) or a pathname in string form. If it is not specified or None, the current parent group is chosen as the new parent. newname must be a string with a new name. If it is not specified or None, the current name is chosen as the new name. If recursive copy is stated, all descendants are copied as well. If createparents is true, the needed groups for the given new parent group path to exist will be created. Copying a node across databases is supported but can not be undone. Copying a node over itself is not allowed, nor it is recursively copying a node into itself. These result in a NodeError. Copying over another existing node is similarly not allowed, unless the optional overwrite argument is true, in which case that node is recursively removed before copying. Additional keyword arguments may be passed to customize the copying process. For instance, title and filters may be changed, user attributes may be or may not be copied, data may be sub-sampled, stats may be collected, etc. See the documentation for the particular node type. Using only the first argument is equivalent to copying the node to a new location without changing its name. Using only the second argument is equivalent to making a copy of the node in the same group. """ self._g_check_open() srcfile = self._v_file srcparent = self._v_parent srcname = self._v_name dstparent = newparent dstname = newname # Set default arguments. if dstparent is None and dstname is None: raise NodeError("you should specify at least " "a ``newparent`` or a ``newname`` parameter") if dstparent is None: dstparent = srcparent if dstname is None: dstname = srcname # Get destination location. if hasattr(dstparent, '_v_file'): # from node dstfile = dstparent._v_file dstpath = dstparent._v_pathname elif hasattr(dstparent, 'startswith'): # from path dstfile = srcfile dstpath = dstparent else: raise TypeError("new parent is not a node nor a path: %r" % (dstparent, )) # Validity checks on arguments. if dstfile is srcfile: # Copying over itself? srcpath = srcparent._v_pathname if dstpath == srcpath and dstname == srcname: raise NodeError( "source and destination nodes are the same node: ``%s``" % self._v_pathname) # Recursively copying into itself? if recursive: self._g_check_not_contains(dstpath) # Note that the previous checks allow us to go ahead and create # the parent groups if `createparents` is true. `dstParent` is # used instead of `dstPath` because it may be in other file, and # to avoid accepting `Node` objects when `createparents` is # true. dstparent = srcfile._get_or_create_path(dstparent, createparents) self._g_check_group(dstparent) # Is it a group? # Copying to another file with undo enabled? if dstfile is not srcfile and srcfile.is_undo_enabled(): warnings.warn( "copying across databases can not be undone " "nor redone from this database", UndoRedoWarning) # Copying over an existing node? self._g_maybe_remove(dstparent, dstname, overwrite) # Copy the node. # The constructor of the new node takes care of logging. return self._g_copy(dstparent, dstname, recursive, **kwargs)
def _f_move(self, newparent=None, newname=None, overwrite=False, createparents=False): """Move or rename this node. Moves a node into a new parent group, or changes the name of the node. newparent can be a Group object (see :ref:`GroupClassDescr`) or a pathname in string form. If it is not specified or None, the current parent group is chosen as the new parent. newname must be a string with a new name. If it is not specified or None, the current name is chosen as the new name. If createparents is true, the needed groups for the given new parent group path to exist will be created. Moving a node across databases is not allowed, nor it is moving a node *into* itself. These result in a NodeError. However, moving a node *over* itself is allowed and simply does nothing. Moving over another existing node is similarly not allowed, unless the optional overwrite argument is true, in which case that node is recursively removed before moving. Usually, only the first argument will be used, effectively moving the node to a new location without changing its name. Using only the second argument is equivalent to renaming the node in place. """ self._g_check_open() file_ = self._v_file oldparent = self._v_parent oldname = self._v_name # Set default arguments. if newparent is None and newname is None: raise NodeError("you should specify at least " "a ``newparent`` or a ``newname`` parameter") if newparent is None: newparent = oldparent if newname is None: newname = oldname # Get destination location. if hasattr(newparent, '_v_file'): # from node newfile = newparent._v_file newpath = newparent._v_pathname elif hasattr(newparent, 'startswith'): # from path newfile = file_ newpath = newparent else: raise TypeError("new parent is not a node nor a path: %r" % (newparent, )) # Validity checks on arguments. # Is it in the same file? if newfile is not file_: raise NodeError("nodes can not be moved across databases; " "please make a copy of the node") # The movement always fails if the hosting file can not be modified. file_._check_writable() # Moving over itself? oldpath = oldparent._v_pathname if newpath == oldpath and newname == oldname: # This is equivalent to renaming the node to its current name, # and it does not change the referenced object, # so it is an allowed no-op. return # Moving into itself? self._g_check_not_contains(newpath) # Note that the previous checks allow us to go ahead and create # the parent groups if `createparents` is true. `newparent` is # used instead of `newpath` to avoid accepting `Node` objects # when `createparents` is true. newparent = file_._get_or_create_path(newparent, createparents) self._g_check_group(newparent) # Is it a group? # Moving over an existing node? self._g_maybe_remove(newparent, newname, overwrite) # Move the node. oldpathname = self._v_pathname self._g_move(newparent, newname) # Log the change. if file_.is_undo_enabled(): self._g_log_move(oldpathname)
def _f_remove(self, recursive = False): raise NodeError("the root node can not be removed")
def _f_move(self, newparent=None, newname=None, createparents=False): raise NodeError("the root node can not be moved")
def _f_rename(self, newname): raise NodeError("the root node can not be renamed")