def graph_symmetric_difference(graph1,
                               graph2,
                               edge_diff=False,
                               return_copy=False):
    """
    Return a new graph with the symmetric difference in nodes and edges of two
    graphs.

    The symmetric difference represents the nodes and edges connecting them
    that are present in `graph1` but not in `graph2` and vice versa.
    It is thus the opposite of the `graph_intersection`.
    The difference is node driven resulting in edges being removed when the
    nodes on either end are removed. Use the `edge_diff` argument to switch
    to an edge driven difference calculation,

    If the graphs share a common origin graph and `return_copy` is False
    the returned intersection graph will be a view on the origin, else it is
    a new graph.

    :param graph1:      first graph
    :type graph1:       :graphit:Graph
    :param graph2:      second graph
    :type graph2:       :graphit:Graph
    :param edge_diff:   switch from node to edge driven difference calculation
    :type edge_diff:    :py:bool
    :param return_copy: force return a new grpah as deep copy based on graph1
    :type return_copy:  :py:bool

    :return:            symmetric difference graph
    :rtype:             :graphit:Graph
    :raises:            AttributeError, if arguments no instance of Graph class
    """

    # Validate if all arguments are Graphs
    check_graphbase_instance(graph1, graph2)

    # Compute node or edge symmetric difference.
    if edge_diff:
        symdiff_edges = graph1.edges.symmetric_difference(graph2.edges)
    else:
        symdiff_nodes = graph1.nodes.symmetric_difference(graph2.nodes)

    if share_common_origin(graph1, graph2) and not return_copy:
        if edge_diff:
            return graph1.origin.getedges(symdiff_edges)
        return graph1.origin.getnodes(symdiff_nodes)
    else:
        # Get node or edge difference for both and join them in a new graph
        if edge_diff:
            result = graph1.getedges(symdiff_edges.difference(
                graph2.edges)).copy(deep=True, copy_view=False)
            graph_join(result,
                       graph2.getedges(symdiff_edges.difference(graph1.edges)))
        else:
            result = graph1.getnodes(symdiff_nodes.difference(
                graph2.nodes)).copy(deep=True, copy_view=False)
            graph_join(result,
                       graph2.getnodes(symdiff_nodes.difference(graph1.nodes)))

        return result
Exemple #2
0
    def new(self, **kwargs):
        """
        Implements 'new' abstract base class method to create new
        task node tree.

        Load task from JSON Schema workflow_wamp_task.v1.json in package
        /schemas/endpoints folder.
        """

        # Do not initiate twice in case method gets called more then once.
        if not len(self.children()):

            logging.info('Init task {0} ({1}) from schema: {2}'.format(self.nid, self.key, TASK_SCHEMA))
            graph_join(self.origin, TASK.descendants(),
                       links=[(self.nid, i) for i in TASK.children(return_nids=True)])

            # Set unique task uuid
            self.task_metadata.task_id.set(self.data.value_tag, self.task_metadata.task_id.create())
def read_yaml(yaml_file, graph=None, **kwargs):
    """
    Parse (hierarchical) YAML data structure to a graph

    Additional keyword arguments (kwargs) are passed to `read_pydata`
    
    :param yaml_file:      yaml data to parse
    :type yaml_file:       File, string, stream or URL
    :param graph:          Graph object to import dictionary data in
    :type graph:           :graphit:Graph
    
    :return:               GraphAxis object
    :rtype:                :graphit:GraphAxis
    """

    # Try parsing the string using default Python yaml parser
    yaml_file = open_anything(yaml_file)
    try:
        yaml_file = yaml.safe_load(yaml_file)
    except IOError:
        logger.error('Unable to decode YAML string')
        return

    if not isinstance(yaml_file, list):
        yaml_file = [yaml_file]

    base_graph = read_pydata(yaml_file[0], graph=graph, **kwargs)

    for yaml_object in yaml_file[1:]:
        sub_graph = read_pydata(yaml_object)

        # If sub-graph root is of type 'root', connect children to base_graph
        root = sub_graph.getnodes(sub_graph.root)
        if root[sub_graph.key_tag] == 'root':
            links = [(base_graph.root, child)
                     for child in root.children(return_nids=True)]
        else:
            links = [(base_graph.root, sub_graph.root)]

        graph_join(base_graph, sub_graph, links=links)

    return base_graph
def resolve_json_ref(graph, **kwargs):
    """
    Resolve JSON Schema $ref pointers

    :param graph:   Graph to resolve $ref for
    """

    # Get path to current document for resolving relative document $ref
    path = graph.get_root().document_path

    for nid, ref in [(k, v['$ref']) for k, v in graph.nodes.items()
                     if '$ref' in v]:

        # Parse JSON $ref
        parsed = uritools.urisplit(ref)

        # Internal ref to definition
        def_graph = None
        if parsed.fragment and parsed.fragment.startswith(
                '/definitions') and not len(parsed.path):
            result = graph.xpath(parsed.fragment.replace('/definitions', '/'))
            if not result.empty():
                def_graph = result.descendants(include_self=True).copy()

        # Include ref from another JSON Schema
        elif len(parsed.path) and os.path.isfile(
                os.path.abspath(
                    os.path.join(os.path.dirname(path), parsed.path))):
            external = read_json_schema(
                os.path.abspath(
                    os.path.join(os.path.dirname(path), parsed.path)),
                **kwargs)
            fragment = parsed.fragment or 'root'
            result = external.xpath('{0}'.format(
                fragment.replace('/definitions', '/')))
            if not result.empty():
                def_graph = result.descendants(include_self=True).copy()

        else:
            logging.warn(
                'Unresolvable JSON schema $ref pointer: {0}'.format(ref))

        # Merge definitions with target
        # TODO: check for property overloading at merge level
        if def_graph:
            def_root = def_graph.get_root()
            def_target = graph.getnodes(nid)

            # If def_root is not the JSON Schema document root then
            # update target node dictionary
            if not def_root.get('document_path'):
                for k, v in def_root.nodes[def_root.nid].items():
                    if k not in def_target:
                        def_target.set(k, v)

            if len(def_graph) > 1:
                links = [(nid, child)
                         for child in def_root.children(return_nids=True)]
                def_graph.remove_node(def_root.nid)

                graph_join(graph, def_graph, links=links)

    # Remove 'definitions' from graph
    for nodes in graph.query_nodes(schema_label='definitions'):
        graph.remove_nodes(
            nodes.descendants(include_self=True, return_nids=True))