Example #1
0
    def __init__(self, basenode=None, graph=None, allow_incomplete=False, **kwargs):
        # log.debug('%s.__init__: basenode = %s (id: 0x%x), args = %s' % (self.__class__.__name__, basenode, id(basenode), kwargs))
        if graph is None and basenode is None:
            raise ValueError(
                "You need to specify either a graph or a base node when instantiating a %s" % self.__class__.__name__
            )

        # just to make sure, while developing. This should probably be removed in a stable version
        if basenode is not None and not (  # (graph is not None and not isinstance(graph, ObjectGraph)) or
            isinstance(basenode, AbstractNode) or isinstance(basenode, BaseObject)
        ):
            raise ValueError(
                "Trying to build a BaseObject from a basenode, but you gave a '%s': %s"
                % (type(basenode).__name__, str(basenode))
            )

        created = False
        if basenode is None:
            # if no basenode is given, we need to create a new node
            self.node = graph.create_node(
                reverse_lookup(kwargs, self.__class__), _classes=ontology.parent_classes(self.__class__)
            )
            created = True

        else:
            basenode = get_node(basenode)

            # if basenode is already in this graph, no need to make a copy of it
            # if graph is None, we might just be making a new instance of a node, so it's in the same graph as well
            if graph is None or graph is basenode.graph():
                self.node = basenode
            else:
                if basenode.edge_keys():
                    # we have links, we can't just create the node without adding the dependencies...
                    raise Exception("sorry, can't do that right now...")

                # TODO: we should be able to construct directly from the other node
                self.node = graph.create_node(reverse_lookup(basenode, self.__class__), _classes=basenode._classes)
                created = True

            # optimization: avoid revalidating the classes all the time when creating a BaseObject from a pre-existing node
            if kwargs:
                self.update(kwargs)

        # if we just created a node and the graph is static, we gave it its valid classes without actually checking...
        # if not a valid instance, remove it from the list of valid classes so that the next check will fail
        if created and not self.node.graph()._dynamic:
            if allow_incomplete and not self.node.has_valid_properties(
                self.__class__, set(self.__class__.valid).intersection(set(self.node.keys()))
            ):
                # FIXME: change to log.debug
                log.warning("removing1 class %s", self.__class__)
                self.node.remove_class(self.__class__)
            # FIXME: remove the is_valid_check(), no? nodes should always be instantiated
            #        from their class
            if not allow_incomplete and not self.node.is_valid_instance(self.__class__):
                log.warning("removing2 class %s", self.__class__)
                self.node.remove_class(self.__class__)

        self.init_validate_instance(created)
Example #2
0
    def add_object(self, node, recurse=Equal.OnIdentity, excluded_deps=list()):
        """Add an object and its underlying node and its links recursively into the graph.

        If some dependencies of the node are already in the graph, we should not add
        new instances of them but use the ones already there (ie: merge links).

        This strategy should be configurable, and offer at least the following choices:
          - recurse = OnIdentity   : do not add the dependency only if the exact same node is already there
          - recurse = OnValue      : do not add the dependency only if there is already a node with the exact same properties
          - recurse = OnValidValue : do not add the dependency only if there is already a node with the same valid properties
          - recurse = OnUnique     : do not add the dependency only if there is already a node with the same unique properties
        """
        # FIXME: not necessarily correct, but safer for now to avoid infinite recursion
        #        ie, if we add a node without a class, we won't know its implicit dependencies
        node = node.node.virtual()
        log.debug('Adding to graph: %s - node: %s' % (self, node))
        node, node_class = unwrap_node(node)

        if node_class is None:
            raise TypeError(
                "Can only add BaseObjects to a graph at the moment...")

        # first make sure the node's not already in the graph, using the requested equality comparison
        # TODO: if node is already there, we need to decide what to do with the additional information we have
        #       in the added node dependencies: update missing properties, update all properties (even if already present),
        #       update non-valid properties, ignore new data, etc...
        excluded_properties = node_class.schema._implicit if node_class is not None else []
        log.debug('exclude properties: %s' % excluded_properties)

        gnode = self.find_node(node, recurse, excluded_properties)
        if gnode is not None:
            log.debug('Found already existing node %s' % gnode)
            return wrap_node(gnode, node_class)

        # if node isn't already in graph, we need to make a copy of it that lives in this graph
        log.debug('Creating a new node in this graph')

        # first import any other node this node might depend on
        newprops = []
        for prop, value, reverse_name in reverse_lookup(node, node_class):
            #print '*'*20, prop, value, reverse_name
            #print 'excl props:', excluded_properties, 'excl deps:', excluded_deps
            if len(excluded_deps) > 5:
                raise RuntimeError('Infinite recursion')
            #if (isinstance(value, AbstractNode) or
            #    (isinstance(value, list) and isinstance(value[0], AbstractNode))):
            if isinstance(value, collections.Iterator):
                # use only the explicit properties here
                if prop not in excluded_properties:
                    imported_nodes = []
                    for v in value:
                        if v in excluded_deps:
                            continue
                        log.debug('Importing dependency %s: %s' % (prop, v))
                        cls = node_class.schema.prop_cls(prop)
                        new_node = self.add_object(
                            wrap_node(v, cls),
                            recurse,
                            excluded_deps=excluded_deps + [node]).node
                        imported_nodes.append(new_node)
                    newprops.append((prop, imported_nodes, reverse_name))
            else:
                newprops.append((prop, value, reverse_name))

        # actually create the node
        log.debug('Creating node')
        result = self.create_node(newprops, _classes=node._classes)

        return wrap_node(result, node_class)
Example #3
0
    def __init__(self,
                 basenode=None,
                 graph=None,
                 allow_incomplete=False,
                 **kwargs):
        #log.debug('%s.__init__: basenode = %s (id: 0x%x), args = %s' % (self.__class__.__name__, basenode, id(basenode), kwargs))
        if graph is None and basenode is None:
            raise ValueError(
                'You need to specify either a graph or a base node when instantiating a %s'
                % self.__class__.__name__)

        # just to make sure, while developing. This should probably be removed in a stable version
        if (  #(graph is not None and not isinstance(graph, ObjectGraph)) or
            (basenode is not None
             and not (isinstance(basenode, AbstractNode)
                      or isinstance(basenode, BaseObject)))):
            raise ValueError(
                'Trying to build a BaseObject from a basenode, but you gave a \'%s\': %s'
                % (type(basenode).__name__, str(basenode)))

        created = False
        if basenode is None:
            # if no basenode is given, we need to create a new node
            self.node = graph.create_node(
                reverse_lookup(kwargs, self.__class__),
                _classes=ontology.parent_classes(self.__class__))
            created = True

        else:
            basenode = get_node(basenode)

            # if basenode is already in this graph, no need to make a copy of it
            # if graph is None, we might just be making a new instance of a node, so it's in the same graph as well
            if graph is None or graph is basenode.graph():
                self.node = basenode
            else:
                if basenode.edge_keys():
                    # we have links, we can't just create the node without adding the dependencies...
                    raise Exception("sorry, can't do that right now...")

                # TODO: we should be able to construct directly from the other node
                self.node = graph.create_node(reverse_lookup(
                    basenode, self.__class__),
                                              _classes=basenode._classes)
                created = True

            # optimization: avoid revalidating the classes all the time when creating a BaseObject from a pre-existing node
            if kwargs:
                self.update(kwargs)

        # if we just created a node and the graph is static, we gave it its valid classes without actually checking...
        # if not a valid instance, remove it from the list of valid classes so that the next check will fail
        if created and not self.node.graph()._dynamic:
            if allow_incomplete and not self.node.has_valid_properties(
                    self.__class__,
                    set(self.__class__.valid).intersection(
                        set(self.node.keys()))):
                # FIXME: change to log.debug
                log.warning('removing1 class %s', self.__class__)
                self.node.remove_class(self.__class__)
            # FIXME: remove the is_valid_check(), no? nodes should always be instantiated
            #        from their class
            if not allow_incomplete and not self.node.is_valid_instance(
                    self.__class__):
                log.warning('removing2 class %s', self.__class__)
                self.node.remove_class(self.__class__)

        self.init_validate_instance(created)
Example #4
0
    def add_object(self, node, recurse = Equal.OnIdentity, excluded_deps = list()):
        """Add an object and its underlying node and its links recursively into the graph.

        If some dependencies of the node are already in the graph, we should not add
        new instances of them but use the ones already there (ie: merge links).

        This strategy should be configurable, and offer at least the following choices:
          - recurse = OnIdentity   : do not add the dependency only if the exact same node is already there
          - recurse = OnValue      : do not add the dependency only if there is already a node with the exact same properties
          - recurse = OnValidValue : do not add the dependency only if there is already a node with the same valid properties
          - recurse = OnUnique     : do not add the dependency only if there is already a node with the same unique properties
        """
        # FIXME: not necessarily correct, but safer for now to avoid infinite recursion
        #        ie, if we add a node without a class, we won't know its implicit dependencies
        node = node.node.virtual()
        log.debug('Adding to graph: %s - node: %s' % (self, node))
        node, node_class = unwrap_node(node)

        if node_class is None:
            raise TypeError("Can only add BaseObjects to a graph at the moment...")

        # first make sure the node's not already in the graph, using the requested equality comparison
        # TODO: if node is already there, we need to decide what to do with the additional information we have
        #       in the added node dependencies: update missing properties, update all properties (even if already present),
        #       update non-valid properties, ignore new data, etc...
        excluded_properties = node_class.schema._implicit if node_class is not None else []
        log.debug('exclude properties: %s' % excluded_properties)

        gnode = self.find_node(node, recurse, excluded_properties)
        if gnode is not None:
            log.debug('Found already existing node %s' % gnode)
            return wrap_node(gnode, node_class)

        # if node isn't already in graph, we need to make a copy of it that lives in this graph
        log.debug('Creating a new node in this graph')

        # first import any other node this node might depend on
        newprops = []
        for prop, value, reverse_name in reverse_lookup(node, node_class):
            #print '*'*20, prop, value, reverse_name
            #print 'excl props:', excluded_properties, 'excl deps:', excluded_deps
            if len(excluded_deps) > 5:
                raise RuntimeError('Infinite recursion')
            #if (isinstance(value, AbstractNode) or
            #    (isinstance(value, list) and isinstance(value[0], AbstractNode))):
            if isinstance(value, collections.Iterator):
                # use only the explicit properties here
                if prop not in excluded_properties:
                    imported_nodes = []
                    for v in value:
                        if v in excluded_deps:
                            continue
                        log.debug('Importing dependency %s: %s' % (prop, v))
                        cls = node_class.schema.prop_cls(prop)
                        new_node = self.add_object(wrap_node(v, cls),
                                                   recurse,
                                                   excluded_deps=excluded_deps + [node]).node
                        imported_nodes.append(new_node)
                    newprops.append((prop, imported_nodes, reverse_name))
            else:
                newprops.append((prop, value, reverse_name))

        # actually create the node
        log.debug('Creating node')
        result = self.create_node(newprops, _classes = node._classes)

        return wrap_node(result, node_class)