예제 #1
0
    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)
예제 #2
0
 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)
예제 #3
0
    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
예제 #4
0
    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)
예제 #5
0
    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)
예제 #6
0
    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)
예제 #7
0
    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)
예제 #8
0
 def _f_remove(self, recursive = False):
     raise NodeError("the root node can not be removed")
예제 #9
0
 def _f_move(self, newparent=None, newname=None, createparents=False):
     raise NodeError("the root node can not be moved")
예제 #10
0
 def _f_rename(self, newname):
     raise NodeError("the root node can not be renamed")