diff env/lib/python3.7/site-packages/networkx/readwrite/graphml.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400 (2020-06-01)
parents 79f47841a781
children
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/networkx/readwrite/graphml.py	Thu May 14 16:47:39 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,918 +0,0 @@
-#    Copyright (C) 2008-2019 by
-#    Aric Hagberg <hagberg@lanl.gov>
-#    Dan Schult <dschult@colgate.edu>
-#    Pieter Swart <swart@lanl.gov>
-#    All rights reserved.
-#    BSD license.
-#
-# Authors: Salim Fadhley
-#          Aric Hagberg (hagberg@lanl.gov)
-"""
-*******
-GraphML
-*******
-Read and write graphs in GraphML format.
-
-This implementation does not support mixed graphs (directed and unidirected
-edges together), hyperedges, nested graphs, or ports.
-
-"GraphML is a comprehensive and easy-to-use file format for graphs. It
-consists of a language core to describe the structural properties of a
-graph and a flexible extension mechanism to add application-specific
-data. Its main features include support of
-
-    * directed, undirected, and mixed graphs,
-    * hypergraphs,
-    * hierarchical graphs,
-    * graphical representations,
-    * references to external data,
-    * application-specific attribute data, and
-    * light-weight parsers.
-
-Unlike many other file formats for graphs, GraphML does not use a
-custom syntax. Instead, it is based on XML and hence ideally suited as
-a common denominator for all kinds of services generating, archiving,
-or processing graphs."
-
-http://graphml.graphdrawing.org/
-
-Format
-------
-GraphML is an XML format.  See
-http://graphml.graphdrawing.org/specification.html for the specification and
-http://graphml.graphdrawing.org/primer/graphml-primer.html
-for examples.
-"""
-import warnings
-from collections import defaultdict
-
-try:
-    from xml.etree.cElementTree import Element, ElementTree
-    from xml.etree.cElementTree import tostring, fromstring
-except ImportError:
-    try:
-        from xml.etree.ElementTree import Element, ElementTree
-        from xml.etree.ElementTree import tostring, fromstring
-    except ImportError:
-        pass
-
-try:
-    import lxml.etree as lxmletree
-except ImportError:
-    lxmletree = None
-
-import networkx as nx
-from networkx.utils import open_file, make_str
-
-__all__ = ['write_graphml', 'read_graphml', 'generate_graphml',
-           'write_graphml_xml', 'write_graphml_lxml',
-           'parse_graphml', 'GraphMLWriter', 'GraphMLReader']
-
-
-@open_file(1, mode='wb')
-def write_graphml_xml(G, path, encoding='utf-8', prettyprint=True,
-                      infer_numeric_types=False):
-    """Write G in GraphML XML format to path
-
-    Parameters
-    ----------
-    G : graph
-       A networkx graph
-    path : file or string
-       File or filename to write.
-       Filenames ending in .gz or .bz2 will be compressed.
-    encoding : string (optional)
-       Encoding for text data.
-    prettyprint : bool (optional)
-       If True use line breaks and indenting in output XML.
-    infer_numeric_types : boolean
-       Determine if numeric types should be generalized.
-       For example, if edges have both int and float 'weight' attributes,
-       we infer in GraphML that both are floats.
-
-    Examples
-    --------
-    >>> G = nx.path_graph(4)
-    >>> nx.write_graphml(G, "test.graphml")
-
-    Notes
-    -----
-    It may be a good idea in Python2 to convert strings to unicode
-    before giving the graph to write_gml. At least the strings with
-    either many characters to escape.
-
-    This implementation does not support mixed graphs (directed
-    and unidirected edges together) hyperedges, nested graphs, or ports.
-    """
-    writer = GraphMLWriter(encoding=encoding, prettyprint=prettyprint,
-                           infer_numeric_types=infer_numeric_types)
-    writer.add_graph_element(G)
-    writer.dump(path)
-
-
-@open_file(1, mode='wb')
-def write_graphml_lxml(G, path, encoding='utf-8', prettyprint=True,
-                       infer_numeric_types=False):
-    """Write G in GraphML XML format to path
-
-    This function uses the LXML framework and should be faster than
-    the version using the xml library.
-
-    Parameters
-    ----------
-    G : graph
-       A networkx graph
-    path : file or string
-       File or filename to write.
-       Filenames ending in .gz or .bz2 will be compressed.
-    encoding : string (optional)
-       Encoding for text data.
-    prettyprint : bool (optional)
-       If True use line breaks and indenting in output XML.
-    infer_numeric_types : boolean
-       Determine if numeric types should be generalized.
-       For example, if edges have both int and float 'weight' attributes,
-       we infer in GraphML that both are floats.
-
-    Examples
-    --------
-    >>> G = nx.path_graph(4)
-    >>> nx.write_graphml_lxml(G, "fourpath.graphml")  # doctest: +SKIP
-
-    Notes
-    -----
-    This implementation does not support mixed graphs (directed
-    and unidirected edges together) hyperedges, nested graphs, or ports.
-    """
-    writer = GraphMLWriterLxml(path, graph=G, encoding=encoding,
-                               prettyprint=prettyprint,
-                               infer_numeric_types=infer_numeric_types)
-    writer.dump()
-
-
-def generate_graphml(G, encoding='utf-8', prettyprint=True):
-    """Generate GraphML lines for G
-
-    Parameters
-    ----------
-    G : graph
-       A networkx graph
-    encoding : string (optional)
-       Encoding for text data.
-    prettyprint : bool (optional)
-       If True use line breaks and indenting in output XML.
-
-    Examples
-    --------
-    >>> G = nx.path_graph(4)
-    >>> linefeed = chr(10)  # linefeed = \n
-    >>> s = linefeed.join(nx.generate_graphml(G))  # doctest: +SKIP
-    >>> for line in nx.generate_graphml(G):  # doctest: +SKIP
-    ...    print(line)
-
-    Notes
-    -----
-    This implementation does not support mixed graphs (directed and unidirected
-    edges together) hyperedges, nested graphs, or ports.
-    """
-    writer = GraphMLWriter(encoding=encoding, prettyprint=prettyprint)
-    writer.add_graph_element(G)
-    for line in str(writer).splitlines():
-        yield line
-
-
-@open_file(0, mode='rb')
-def read_graphml(path, node_type=str, edge_key_type=int):
-    """Read graph in GraphML format from path.
-
-    Parameters
-    ----------
-    path : file or string
-       File or filename to write.
-       Filenames ending in .gz or .bz2 will be compressed.
-
-    node_type: Python type (default: str)
-       Convert node ids to this type
-
-    edge_key_type: Python type (default: int)
-       Convert graphml edge ids to this type as key of multi-edges
-
-
-    Returns
-    -------
-    graph: NetworkX graph
-        If no parallel edges are found a Graph or DiGraph is returned.
-        Otherwise a MultiGraph or MultiDiGraph is returned.
-
-    Notes
-    -----
-    Default node and edge attributes are not propagated to each node and edge.
-    They can be obtained from `G.graph` and applied to node and edge attributes
-    if desired using something like this:
-
-    >>> default_color = G.graph['node_default']['color']  # doctest: +SKIP
-    >>> for node, data in G.nodes(data=True):  # doctest: +SKIP
-    ...     if 'color' not in data:
-    ...         data['color']=default_color
-    >>> default_color = G.graph['edge_default']['color']  # doctest: +SKIP
-    >>> for u, v, data in G.edges(data=True):  # doctest: +SKIP
-    ...     if 'color' not in data:
-    ...         data['color']=default_color
-
-    This implementation does not support mixed graphs (directed and unidirected
-    edges together), hypergraphs, nested graphs, or ports.
-
-    For multigraphs the GraphML edge "id" will be used as the edge
-    key.  If not specified then they "key" attribute will be used.  If
-    there is no "key" attribute a default NetworkX multigraph edge key
-    will be provided.
-
-    Files with the yEd "yfiles" extension will can be read but the graphics
-    information is discarded.
-
-    yEd compressed files ("file.graphmlz" extension) can be read by renaming
-    the file to "file.graphml.gz".
-
-    """
-    reader = GraphMLReader(node_type=node_type, edge_key_type=edge_key_type)
-    # need to check for multiple graphs
-    glist = list(reader(path=path))
-    if len(glist) == 0:
-        # If no graph comes back, try looking for an incomplete header
-        header = b'<graphml xmlns="http://graphml.graphdrawing.org/xmlns">'
-        path.seek(0)
-        old_bytes = path.read()
-        new_bytes = old_bytes.replace(b'<graphml>', header)
-        glist = list(reader(string=new_bytes))
-        if len(glist) == 0:
-            raise nx.NetworkXError('file not successfully read as graphml')
-    return glist[0]
-
-
-def parse_graphml(graphml_string, node_type=str):
-    """Read graph in GraphML format from string.
-
-    Parameters
-    ----------
-    graphml_string : string
-       String containing graphml information
-       (e.g., contents of a graphml file).
-
-    node_type: Python type (default: str)
-       Convert node ids to this type
-
-    Returns
-    -------
-    graph: NetworkX graph
-        If no parallel edges are found a Graph or DiGraph is returned.
-        Otherwise a MultiGraph or MultiDiGraph is returned.
-
-    Examples
-    --------
-    >>> G = nx.path_graph(4)
-    >>> linefeed = chr(10)  # linefeed = \n
-    >>> s = linefeed.join(nx.generate_graphml(G))
-    >>> H = nx.parse_graphml(s)
-
-    Notes
-    -----
-    Default node and edge attributes are not propagated to each node and edge.
-    They can be obtained from `G.graph` and applied to node and edge attributes
-    if desired using something like this:
-
-    >>> default_color = G.graph['node_default']['color']  # doctest: +SKIP
-    >>> for node, data in G.nodes(data=True):  # doctest: +SKIP
-    ...    if 'color' not in data:
-    ...        data['color']=default_color
-    >>> default_color = G.graph['edge_default']['color']  # doctest: +SKIP
-    >>> for u, v, data in G.edges(data=True):  # doctest: +SKIP
-    ...    if 'color' not in data:
-    ...        data['color']=default_color
-
-    This implementation does not support mixed graphs (directed and unidirected
-    edges together), hypergraphs, nested graphs, or ports.
-
-    For multigraphs the GraphML edge "id" will be used as the edge
-    key.  If not specified then they "key" attribute will be used.  If
-    there is no "key" attribute a default NetworkX multigraph edge key
-    will be provided.
-
-    """
-    reader = GraphMLReader(node_type=node_type)
-    # need to check for multiple graphs
-    glist = list(reader(string=graphml_string))
-    if len(glist) == 0:
-        # If no graph comes back, try looking for an incomplete header
-        header = '<graphml xmlns="http://graphml.graphdrawing.org/xmlns">'
-        new_string = graphml_string.replace('<graphml>', header)
-        glist = list(reader(string=new_string))
-        if len(glist) == 0:
-            raise nx.NetworkXError('file not successfully read as graphml')
-    return glist[0]
-
-
-class GraphML(object):
-    NS_GRAPHML = "http://graphml.graphdrawing.org/xmlns"
-    NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
-    # xmlns:y="http://www.yworks.com/xml/graphml"
-    NS_Y = "http://www.yworks.com/xml/graphml"
-    SCHEMALOCATION = \
-        ' '.join(['http://graphml.graphdrawing.org/xmlns',
-                  'http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd'])
-
-    try:
-        chr(12345)     # Fails on Py!=3.
-        unicode = str  # Py3k's str is our unicode type
-        long = int     # Py3K's int is our long type
-    except ValueError:
-        # Python 2.x
-        pass
-
-    types = [(int, "integer"),  # for Gephi GraphML bug
-             (str, "yfiles"), (str, "string"), (unicode, "string"),
-             (int, "int"), (long, "long"),
-             (float, "float"), (float, "double"),
-             (bool, "boolean")]
-
-    # These additions to types allow writing numpy types
-    try:
-        import numpy as np
-    except:
-        pass
-    else:
-        # prepend so that python types are created upon read (last entry wins)
-        types = [(np.float64, "float"), (np.float32, "float"),
-                 (np.float16, "float"), (np.float_, "float"),
-                 (np.int, "int"), (np.int8, "int"),
-                 (np.int16, "int"), (np.int32, "int"),
-                 (np.int64, "int"), (np.uint8, "int"),
-                 (np.uint16, "int"), (np.uint32, "int"),
-                 (np.uint64, "int"), (np.int_, "int"),
-                 (np.intc, "int"), (np.intp, "int"),
-                ] + types
-
-    xml_type = dict(types)
-    python_type = dict(reversed(a) for a in types)
-
-    # This page says that data types in GraphML follow Java(TM).
-    #  http://graphml.graphdrawing.org/primer/graphml-primer.html#AttributesDefinition
-    # true and false are the only boolean literals:
-    #  http://en.wikibooks.org/wiki/Java_Programming/Literals#Boolean_Literals
-    convert_bool = {
-        # We use data.lower() in actual use.
-        'true': True, 'false': False,
-        # Include integer strings for convenience.
-        '0': False, 0: False,
-        '1': True, 1: True
-    }
-
-
-class GraphMLWriter(GraphML):
-    def __init__(self, graph=None, encoding="utf-8", prettyprint=True,
-                 infer_numeric_types=False):
-        try:
-            import xml.etree.ElementTree
-        except ImportError:
-            msg = 'GraphML writer requires xml.elementtree.ElementTree'
-            raise ImportError(msg)
-        self.myElement = Element
-
-        self.infer_numeric_types = infer_numeric_types
-        self.prettyprint = prettyprint
-        self.encoding = encoding
-        self.xml = self.myElement("graphml",
-                                  {'xmlns': self.NS_GRAPHML,
-                                   'xmlns:xsi': self.NS_XSI,
-                                   'xsi:schemaLocation': self.SCHEMALOCATION})
-        self.keys = {}
-        self.attributes = defaultdict(list)
-        self.attribute_types = defaultdict(set)
-
-        if graph is not None:
-            self.add_graph_element(graph)
-
-    def __str__(self):
-        if self.prettyprint:
-            self.indent(self.xml)
-        s = tostring(self.xml).decode(self.encoding)
-        return s
-
-    def attr_type(self, name, scope, value):
-        """Infer the attribute type of data named name. Currently this only
-        supports inference of numeric types.
-
-        If self.infer_numeric_types is false, type is used. Otherwise, pick the
-        most general of types found across all values with name and scope. This
-        means edges with data named 'weight' are treated separately from nodes
-        with data named 'weight'.
-        """
-        if self.infer_numeric_types:
-            types = self.attribute_types[(name, scope)]
-
-            try:
-                chr(12345)     # Fails on Py<3.
-                local_long = int     # Py3's int is Py2's long type
-                local_unicode = str  # Py3's str is Py2's unicode type
-            except ValueError:
-                # Python 2.x
-                local_long = long
-                local_unicode = unicode
-
-            if len(types) > 1:
-                if str in types:
-                    return str
-                elif local_unicode in types:
-                    return local_unicode
-                elif float in types:
-                    return float
-                elif local_long in types:
-                    return local_long
-                else:
-                    return int
-            else:
-                return list(types)[0]
-        else:
-            return type(value)
-
-    def get_key(self, name, attr_type, scope, default):
-        keys_key = (name, attr_type, scope)
-        try:
-            return self.keys[keys_key]
-        except KeyError:
-            new_id = "d%i" % len(list(self.keys))
-            self.keys[keys_key] = new_id
-            key_kwargs = {"id": new_id,
-                          "for": scope,
-                          "attr.name": name,
-                          "attr.type": attr_type}
-            key_element = self.myElement("key", **key_kwargs)
-            # add subelement for data default value if present
-            if default is not None:
-                default_element = self.myElement("default")
-                default_element.text = make_str(default)
-                key_element.append(default_element)
-            self.xml.insert(0, key_element)
-        return new_id
-
-    def add_data(self, name, element_type, value,
-                 scope="all",
-                 default=None):
-        """
-        Make a data element for an edge or a node. Keep a log of the
-        type in the keys table.
-        """
-        if element_type not in self.xml_type:
-            msg = 'GraphML writer does not support %s as data values.'
-            raise nx.NetworkXError(msg % element_type)
-        keyid = self.get_key(name, self.xml_type[element_type], scope, default)
-        data_element = self.myElement("data", key=keyid)
-        data_element.text = make_str(value)
-        return data_element
-
-    def add_attributes(self, scope, xml_obj, data, default):
-        """Appends attribute data to edges or nodes, and stores type information
-        to be added later. See add_graph_element.
-        """
-        for k, v in data.items():
-            self.attribute_types[(make_str(k), scope)].add(type(v))
-            self.attributes[xml_obj].append([k, v, scope, default.get(k)])
-
-    def add_nodes(self, G, graph_element):
-        default = G.graph.get('node_default', {})
-        for node, data in G.nodes(data=True):
-            node_element = self.myElement("node", id=make_str(node))
-            self.add_attributes("node", node_element, data, default)
-            graph_element.append(node_element)
-
-    def add_edges(self, G, graph_element):
-        if G.is_multigraph():
-            for u, v, key, data in G.edges(data=True, keys=True):
-                edge_element = self.myElement("edge", source=make_str(u),
-                                              target=make_str(v),
-                                              id=make_str(key))
-                default = G.graph.get('edge_default', {})
-                self.add_attributes("edge", edge_element, data, default)
-                graph_element.append(edge_element)
-        else:
-            for u, v, data in G.edges(data=True):
-                edge_element = self.myElement("edge", source=make_str(u),
-                                              target=make_str(v))
-                default = G.graph.get('edge_default', {})
-                self.add_attributes("edge", edge_element, data, default)
-                graph_element.append(edge_element)
-
-    def add_graph_element(self, G):
-        """
-        Serialize graph G in GraphML to the stream.
-        """
-        if G.is_directed():
-            default_edge_type = 'directed'
-        else:
-            default_edge_type = 'undirected'
-
-        graphid = G.graph.pop('id', None)
-        if graphid is None:
-            graph_element = self.myElement("graph",
-                                           edgedefault=default_edge_type)
-        else:
-            graph_element = self.myElement("graph",
-                                           edgedefault=default_edge_type,
-                                           id=graphid)
-        default = {}
-        data = {k: v for (k, v) in G.graph.items()
-                if k not in ['node_default', 'edge_default']}
-        self.add_attributes("graph", graph_element, data, default)
-        self.add_nodes(G, graph_element)
-        self.add_edges(G, graph_element)
-
-        # self.attributes contains a mapping from XML Objects to a list of
-        # data that needs to be added to them.
-        # We postpone processing in order to do type inference/generalization.
-        # See self.attr_type
-        for (xml_obj, data) in self.attributes.items():
-            for (k, v, scope, default) in data:
-                xml_obj.append(self.add_data(make_str(k),
-                                             self.attr_type(k, scope, v),
-                                             make_str(v), scope, default))
-        self.xml.append(graph_element)
-
-    def add_graphs(self, graph_list):
-        """ Add many graphs to this GraphML document. """
-        for G in graph_list:
-            self.add_graph_element(G)
-
-    def dump(self, stream):
-        if self.prettyprint:
-            self.indent(self.xml)
-        document = ElementTree(self.xml)
-        document.write(stream, encoding=self.encoding, xml_declaration=True)
-
-    def indent(self, elem, level=0):
-        # in-place prettyprint formatter
-        i = "\n" + level * "  "
-        if len(elem):
-            if not elem.text or not elem.text.strip():
-                elem.text = i + "  "
-            if not elem.tail or not elem.tail.strip():
-                elem.tail = i
-            for elem in elem:
-                self.indent(elem, level + 1)
-            if not elem.tail or not elem.tail.strip():
-                elem.tail = i
-        else:
-            if level and (not elem.tail or not elem.tail.strip()):
-                elem.tail = i
-
-
-class IncrementalElement(object):
-    """Wrapper for _IncrementalWriter providing an Element like interface.
-
-    This wrapper does not intend to be a complete implementation but rather to
-    deal with those calls used in GraphMLWriter.
-    """
-
-    def __init__(self, xml, prettyprint):
-        self.xml = xml
-        self.prettyprint = prettyprint
-
-    def append(self, element):
-        self.xml.write(element, pretty_print=self.prettyprint)
-
-
-class GraphMLWriterLxml(GraphMLWriter):
-    def __init__(self, path, graph=None, encoding='utf-8', prettyprint=True,
-                 infer_numeric_types=False):
-        self.myElement = lxmletree.Element
-
-        self._encoding = encoding
-        self._prettyprint = prettyprint
-        self.infer_numeric_types = infer_numeric_types
-
-        self._xml_base = lxmletree.xmlfile(path, encoding=encoding)
-        self._xml = self._xml_base.__enter__()
-        self._xml.write_declaration()
-
-        # We need to have a xml variable that support insertion. This call is
-        # used for adding the keys to the document.
-        # We will store those keys in a plain list, and then after the graph
-        # element is closed we will add them to the main graphml element.
-        self.xml = []
-        self._keys = self.xml
-        self._graphml = self._xml.element(
-            'graphml',
-            {
-                'xmlns': self.NS_GRAPHML,
-                'xmlns:xsi': self.NS_XSI,
-                'xsi:schemaLocation': self.SCHEMALOCATION
-            })
-        self._graphml.__enter__()
-        self.keys = {}
-        self.attribute_types = defaultdict(set)
-
-        if graph is not None:
-            self.add_graph_element(graph)
-
-    def add_graph_element(self, G):
-        """
-        Serialize graph G in GraphML to the stream.
-        """
-        if G.is_directed():
-            default_edge_type = 'directed'
-        else:
-            default_edge_type = 'undirected'
-
-        graphid = G.graph.pop('id', None)
-        if graphid is None:
-            graph_element = self._xml.element('graph',
-                                              edgedefault=default_edge_type)
-        else:
-            graph_element = self._xml.element('graph',
-                                              edgedefault=default_edge_type,
-                                              id=graphid)
-
-        # gather attributes types for the whole graph
-        # to find the most general numeric format needed.
-        # Then pass through attributes to create key_id for each.
-        graphdata = {k: v for k, v in G.graph.items()
-                     if k not in ('node_default', 'edge_default')}
-        node_default = G.graph.get('node_default', {})
-        edge_default = G.graph.get('edge_default', {})
-        # Graph attributes
-        for k, v in graphdata.items():
-            self.attribute_types[(make_str(k), "graph")].add(type(v))
-        for k, v in graphdata.items():
-            element_type = self.xml_type[self.attr_type(k, "graph", v)]
-            self.get_key(make_str(k), element_type, "graph", None)
-        # Nodes and data
-        for node, d in G.nodes(data=True):
-            for k, v in d.items():
-                self.attribute_types[(make_str(k), "node")].add(type(v))
-        for node, d in G.nodes(data=True):
-            for k, v in d.items():
-                T = self.xml_type[self.attr_type(k, "node", v)]
-                self.get_key(make_str(k), T, "node", node_default.get(k))
-        # Edges and data
-        if G.is_multigraph():
-            for u, v, ekey, d in G.edges(keys=True, data=True):
-                for k, v in d.items():
-                    self.attribute_types[(make_str(k), "edge")].add(type(v))
-            for u, v, ekey, d in G.edges(keys=True, data=True):
-                for k, v in d.items():
-                    T = self.xml_type[self.attr_type(k, "edge", v)]
-                    self.get_key(make_str(k), T, "edge", edge_default.get(k))
-        else:
-            for u, v, d in G.edges(data=True):
-                for k, v in d.items():
-                    self.attribute_types[(make_str(k), "edge")].add(type(v))
-            for u, v, d in G.edges(data=True):
-                for k, v in d.items():
-                    T = self.xml_type[self.attr_type(k, "edge", v)]
-                    self.get_key(make_str(k), T, "edge", edge_default.get(k))
-
-        # Now add attribute keys to the xml file
-        for key in self.xml:
-            self._xml.write(key, pretty_print=self._prettyprint)
-
-        # The incremental_writer writes each node/edge as it is created
-        incremental_writer = IncrementalElement(self._xml, self._prettyprint)
-        with graph_element:
-            self.add_attributes('graph', incremental_writer, graphdata, {})
-            self.add_nodes(G, incremental_writer)  # adds attributes too
-            self.add_edges(G, incremental_writer)  # adds attributes too
-
-    def add_attributes(self, scope, xml_obj, data, default):
-        """Appends attribute data."""
-        for k, v in data.items():
-            data_element = self.add_data(make_str(k),
-                                         self.attr_type(make_str(k), scope, v),
-                                         make_str(v), scope, default.get(k))
-            xml_obj.append(data_element)
-
-    def __str__(self):
-        return object.__str__(self)
-
-    def dump(self):
-        self._graphml.__exit__(None, None, None)
-        self._xml_base.__exit__(None, None, None)
-
-
-# Choose a writer function for default
-if lxmletree is None:
-    write_graphml = write_graphml_xml
-else:
-    write_graphml = write_graphml_lxml
-
-
-class GraphMLReader(GraphML):
-    """Read a GraphML document.  Produces NetworkX graph objects."""
-
-    def __init__(self, node_type=str, edge_key_type=int):
-        try:
-            import xml.etree.ElementTree
-        except ImportError:
-            msg = 'GraphML reader requires xml.elementtree.ElementTree'
-            raise ImportError(msg)
-        self.node_type = node_type
-        self.edge_key_type = edge_key_type
-        self.multigraph = False  # assume multigraph and test for multiedges
-        self.edge_ids = {}  # dict mapping (u,v) tuples to id edge attributes
-
-    def __call__(self, path=None, string=None):
-        if path is not None:
-            self.xml = ElementTree(file=path)
-        elif string is not None:
-            self.xml = fromstring(string)
-        else:
-            raise ValueError("Must specify either 'path' or 'string' as kwarg")
-        (keys, defaults) = self.find_graphml_keys(self.xml)
-        for g in self.xml.findall("{%s}graph" % self.NS_GRAPHML):
-            yield self.make_graph(g, keys, defaults)
-
-    def make_graph(self, graph_xml, graphml_keys, defaults, G=None):
-        # set default graph type
-        edgedefault = graph_xml.get("edgedefault", None)
-        if G is None:
-            if edgedefault == 'directed':
-                G = nx.MultiDiGraph()
-            else:
-                G = nx.MultiGraph()
-        # set defaults for graph attributes
-        G.graph['node_default'] = {}
-        G.graph['edge_default'] = {}
-        for key_id, value in defaults.items():
-            key_for = graphml_keys[key_id]['for']
-            name = graphml_keys[key_id]['name']
-            python_type = graphml_keys[key_id]['type']
-            if key_for == 'node':
-                G.graph['node_default'].update({name: python_type(value)})
-            if key_for == 'edge':
-                G.graph['edge_default'].update({name: python_type(value)})
-        # hyperedges are not supported
-        hyperedge = graph_xml.find("{%s}hyperedge" % self.NS_GRAPHML)
-        if hyperedge is not None:
-            raise nx.NetworkXError("GraphML reader doesn't support hyperedges")
-        # add nodes
-        for node_xml in graph_xml.findall("{%s}node" % self.NS_GRAPHML):
-            self.add_node(G, node_xml, graphml_keys, defaults)
-        # add edges
-        for edge_xml in graph_xml.findall("{%s}edge" % self.NS_GRAPHML):
-            self.add_edge(G, edge_xml, graphml_keys)
-        # add graph data
-        data = self.decode_data_elements(graphml_keys, graph_xml)
-        G.graph.update(data)
-
-        # switch to Graph or DiGraph if no parallel edges were found.
-        if not self.multigraph:
-            if G.is_directed():
-                G = nx.DiGraph(G)
-            else:
-                G = nx.Graph(G)
-            nx.set_edge_attributes(G, values=self.edge_ids, name='id')
-
-        return G
-
-    def add_node(self, G, node_xml, graphml_keys, defaults):
-        """Add a node to the graph.
-        """
-        # warn on finding unsupported ports tag
-        ports = node_xml.find("{%s}port" % self.NS_GRAPHML)
-        if ports is not None:
-            warnings.warn("GraphML port tag not supported.")
-        # find the node by id and cast it to the appropriate type
-        node_id = self.node_type(node_xml.get("id"))
-        # get data/attributes for node
-        data = self.decode_data_elements(graphml_keys, node_xml)
-        G.add_node(node_id, **data)
-        # get child nodes
-        if node_xml.attrib.get('yfiles.foldertype') == 'group':
-            graph_xml = node_xml.find("{%s}graph" % self.NS_GRAPHML)
-            self.make_graph(graph_xml, graphml_keys, defaults, G)
-
-    def add_edge(self, G, edge_element, graphml_keys):
-        """Add an edge to the graph.
-        """
-        # warn on finding unsupported ports tag
-        ports = edge_element.find("{%s}port" % self.NS_GRAPHML)
-        if ports is not None:
-            warnings.warn("GraphML port tag not supported.")
-
-        # raise error if we find mixed directed and undirected edges
-        directed = edge_element.get("directed")
-        if G.is_directed() and directed == 'false':
-            msg = "directed=false edge found in directed graph."
-            raise nx.NetworkXError(msg)
-        if (not G.is_directed()) and directed == 'true':
-            msg = "directed=true edge found in undirected graph."
-            raise nx.NetworkXError(msg)
-
-        source = self.node_type(edge_element.get("source"))
-        target = self.node_type(edge_element.get("target"))
-        data = self.decode_data_elements(graphml_keys, edge_element)
-        # GraphML stores edge ids as an attribute
-        # NetworkX uses them as keys in multigraphs too if no key
-        # attribute is specified
-        edge_id = edge_element.get("id")
-        if edge_id:
-            # self.edge_ids is used by `make_graph` method for non-multigraphs
-            self.edge_ids[source, target] = edge_id
-            try:
-                edge_id = self.edge_key_type(edge_id)
-            except ValueError:  # Could not convert.
-                pass
-        else:
-            edge_id = data.get('key')
-
-        if G.has_edge(source, target):
-            # mark this as a multigraph
-            self.multigraph = True
-
-        # Use add_edges_from to avoid error with add_edge when `'key' in data`
-        G.add_edges_from([(source, target, edge_id, data)])
-
-    def decode_data_elements(self, graphml_keys, obj_xml):
-        """Use the key information to decode the data XML if present."""
-        data = {}
-        for data_element in obj_xml.findall("{%s}data" % self.NS_GRAPHML):
-            key = data_element.get("key")
-            try:
-                data_name = graphml_keys[key]['name']
-                data_type = graphml_keys[key]['type']
-            except KeyError:
-                raise nx.NetworkXError("Bad GraphML data: no key %s" % key)
-            text = data_element.text
-            # assume anything with subelements is a yfiles extension
-            if text is not None and len(list(data_element)) == 0:
-                if data_type == bool:
-                    # Ignore cases.
-                    # http://docs.oracle.com/javase/6/docs/api/java/lang/
-                    # Boolean.html#parseBoolean%28java.lang.String%29
-                    data[data_name] = self.convert_bool[text.lower()]
-                else:
-                    data[data_name] = data_type(text)
-            elif len(list(data_element)) > 0:
-                # Assume yfiles as subelements, try to extract node_label
-                node_label = None
-                for node_type in ['ShapeNode', 'SVGNode', 'ImageNode']:
-                    pref = "{%s}%s/{%s}" % (self.NS_Y, node_type, self.NS_Y)
-                    geometry = data_element.find("%sGeometry" % pref)
-                    if geometry is not None:
-                        data['x'] = geometry.get('x')
-                        data['y'] = geometry.get('y')
-                    if node_label is None:
-                        node_label = data_element.find("%sNodeLabel" % pref)
-                if node_label is not None:
-                    data['label'] = node_label.text
-
-                # check all the different types of edges avaivable in yEd.
-                for e in ['PolyLineEdge', 'SplineEdge', 'QuadCurveEdge',
-                          'BezierEdge', 'ArcEdge']:
-                    pref = "{%s}%s/{%s}" % (self.NS_Y, e, self.NS_Y)
-                    edge_label = data_element.find("%sEdgeLabel" % pref)
-                    if edge_label is not None:
-                        break
-
-                if edge_label is not None:
-                    data['label'] = edge_label.text
-        return data
-
-    def find_graphml_keys(self, graph_element):
-        """Extracts all the keys and key defaults from the xml.
-        """
-        graphml_keys = {}
-        graphml_key_defaults = {}
-        for k in graph_element.findall("{%s}key" % self.NS_GRAPHML):
-            attr_id = k.get("id")
-            attr_type = k.get('attr.type')
-            attr_name = k.get("attr.name")
-            yfiles_type = k.get("yfiles.type")
-            if yfiles_type is not None:
-                attr_name = yfiles_type
-                attr_type = 'yfiles'
-            if attr_type is None:
-                attr_type = "string"
-                warnings.warn("No key type for id %s. Using string" % attr_id)
-            if attr_name is None:
-                raise nx.NetworkXError("Unknown key for id %s." % attr_id)
-            graphml_keys[attr_id] = {"name": attr_name,
-                                     "type": self.python_type[attr_type],
-                                     "for": k.get("for")}
-            # check for "default" subelement of key element
-            default = k.find("{%s}default" % self.NS_GRAPHML)
-            if default is not None:
-                graphml_key_defaults[attr_id] = default.text
-        return graphml_keys, graphml_key_defaults
-
-
-# fixture for pytest
-def setup_module(module):
-    import pytest
-    xml.etree.ElementTree = pytest.importorskip('xml.etree.ElementTree')
-
-
-# fixture for pytest
-def teardown_module(module):
-    import os
-    try:
-        os.unlink('test.graphml')
-    except:
-        pass