GRAPH_LOGGER = logging.getLogger(__name__)
    NODE = 0
    EDGE = 1
    SUBGRAPH = 2
        H = self._graph.__class__()
        FOLDER, _ = os.path.split(__file__)
class InvalidSearchParameters(Exception):
class InvalidElementError(Exception):
class GFAError(Exception):
class Element:
class GFA(DovetailIterator):
def _index(obj, other):
    def __init__(self, base_graph=None, is_rGFA = None):
    def __contains__(self, id_):
    def clear(self):
    def _get_virtual_id(self, increment=True):
    def _find_max_virtual_id(self):
    def nodes(self, data=False, with_sequence=False, identifier=None):
    def edges(self,identifier = None, adj_dict = False, **kwargs):
    def subgraphs(self, identifier=None):
    def _search_edge_by_key(self, edge_key):
    def _search_edge_by_nodes(self, nodes):
    def _get_edge_end_nodes(self, edge_key):
    def get(self, key):
    def as_graph_element(self, key):
    def add_graph_element(self, element):
    def add_node(self, new_node, safe=False):
    def remove_node(self, nid):
    def nodes_iter(self, data=False, with_sequence=False):
    def nbunch_iter(self, nbunch=None):
    def add_edge(self, new_edge, safe=False):
    def max_SR(self, from_node, to_node):
    def check_rGFA(self, force = False):
    def check_rGFA_nodes(self, nodes):
    def check_rGFA_node(self,node):
    def check_rGFA_edges(self, edges):
    def check_rGFA_edge(self, edge):
    def remove_edge(self, identifier):
    def remove_edges(self, from_node, to_node):
    def edges_iter(self, nbunch=None, data=False, keys=False, default=None):
    def add_subgraph(self, subgraph, safe=False):
    def remove_subgraph(self, subgraph_id):
    def subgraphs_iter(self, data=False):
    def get_subgraph(self, sub_key):
    def subgraph(self, nbunch, copy=True):
    def dovetails_subgraph(self, nbunch=None, copy=True):
    def neighbors(self, nid):
    def search(self, \
               comparator, \
               limit_type=None):
    def search_on_nodes(self, comparator):
    def search_on_edges(self, comparator):
    def search_on_subgraph(self, comparator):
    def from_string(self, string):
    def from_file(cls, filepath, is_rGFA = None): # pragma: no cover
    def pprint(self): # pragma: no cover
    def dump(self, gfa_version=1, out=None):
    def _make_edge_lut(self):
    def _make_edge_table(self):
    def _look_for_edge(self, key, edge_table):
    def __eq__(self, other):
    def __neq__(self, other):
    def compression(self, type_compression='by_nodes'):
    def overlap_consistency(self, external_file=None):
    def subgraphs_extractor(self, n_source, distance): class Header(line.Line):
    PREDEFINED_OPTFIELDS = \
      { \
        'VN' : fv.TYPE_Z, \
        'TS' : fv.TYPE_i \
      }
    def __init__(self):
    def from_string(cls, string): def is_segmentv1(line_repr):
def is_segmentv2(line_repr):
    def __init__(self):
    def from_string(cls, string):
    def __init__(self):
    def from_string(cls, string):
class SegmentV1(line.Line):
class SegmentV2(line.Line):
    REQUIRED_FIELDS = { \
    'name' : fv.GFA1_NAME, \
    'sequence' : fv.GFA1_SEQUENCE \
    }
    PREDEFINED_OPTFIELDS = { \
    'LN' : fv.TYPE_i, \
    'RC' : fv.TYPE_i, \
    'FC' : fv.TYPE_i, \
    'KC' : fv.TYPE_i, \
    'SH' : fv.HEX_BYTE_ARRAY, \
    'UR' : fv.TYPE_Z \
    }
    REQUIRED_FIELDS = { \
    'sid' : fv.GFA2_ID, \
    'slen' : fv.GFA2_INT, \
    'sequence' : fv.GFA2_SEQUENCE \
    } class Link(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'from' : fv.GFA1_NAME, \
    'from_orn' : fv.GFA1_ORIENTATION, \
    'to': fv.GFA1_NAME, \
    'to_orn' : fv.GFA1_ORIENTATION, \
    'overlap' : fv.GFA1_CIGAR \
    }
    PREDEFINED_OPTFIELDS = { \
    'MQ' : fv.TYPE_i, \
    'NM' : fv.TYPE_i, \
    'RC' : fv.TYPE_i, \
    'FC' : fv.TYPE_i, \
    'KC' : fv.TYPE_i, \
    'ID' : fv.TYPE_Z \
    } class Path(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'path_name' : fv.GFA1_NAME, \
    'seqs_names' : fv.GFA1_NAMES, \
    'overlaps': fv.GFA1_CIGARS \
    }
    PREDEFINED_OPTFIELDS = {} class Containment(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'from' : fv.GFA1_NAME, \
    'from_orn' : fv.GFA1_ORIENTATION, \
    'to': fv.GFA1_NAME, \
    'to_orn' : fv.GFA1_ORIENTATION, \
    'pos' : fv.GFA1_INT, \
    'overlap' : fv.GFA1_CIGAR \
    }
    PREDEFINED_OPTFIELDS = { \
    'NM' : fv.TYPE_i, \
    'RC' : fv.TYPE_i, \
    'ID' : fv.TYPE_Z \
    } class Fragment(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'sid' : fv.GFA2_ID, \
    'external' : fv.GFA2_REFERENCE, \
    'sbeg' : fv.GFA2_POSITION, \
    'send' : fv.GFA2_POSITION, \
    'fbeg' : fv.GFA2_POSITION, \
    'fend' : fv.GFA2_POSITION, \
    'alignment' : fv.GFA2_ALIGNMENT \
    } class Edge(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'eid' : fv.GFA2_OPTIONAL_ID, \
    'sid1' : fv.GFA2_REFERENCE, \
    'sid2' : fv.GFA2_REFERENCE, \
    'beg1' : fv.GFA2_POSITION, \
    'end1' : fv.GFA2_POSITION, \
    'beg2' : fv.GFA2_POSITION, \
    'end2' : fv.GFA2_POSITION, \
    'alignment' : fv.GFA2_ALIGNMENT \
    } class Gap(line.Line):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'gid' : fv.GFA2_OPTIONAL_ID, \
    'sid1' : fv.GFA2_REFERENCE, \
    'sid2' : fv.GFA2_REFERENCE, \
    'distance' : fv.GFA2_INT, \
    'variance' : fv.GFA2_OPTIONAL_INT \
    } class OGroup(line.Line):
class UGroup(line.Line):
    def __init__(self):
    def from_string(cls, string):
    def __init__(self):
    def from_string(cls, string):
    REQUIRED_FIELDS = { \
    'oid' : fv.GFA2_OPTIONAL_ID, \
    'references' : fv.GFA2_REFERENCES \
    }
    REQUIRED_FIELDS = { \
    'uid' : fv.GFA2_OPTIONAL_ID, \
    'ids' : fv.GFA2_IDS \
    } class InvalidLineError(Exception):
class Line:
class Field:
class OptField(Field):
def is_field(field):
def is_optfield(field):
    def __init__(self, line_type=None):
    def is_valid(cls, line_):
    def get_static_fields(cls):
    def type(self):
    def fields(self):
    def add_field(self, field):
    def remove_field(self, field):
    def from_string(cls, string): # pragma: no cover
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover
    def __init__(self, name, value):
    def name(self):
    def value(self):
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover
    def __init__(self, name, value, field_type):
    def type(self):
    def from_string(cls, string):
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover
    REQUIRED_FIELDS = {}
    PREDEFINED_OPTFIELDS = {} class InvalidFieldError(Exception):
class UnknownDataTypeError(Exception):
class FormatError(Exception):
TYPE_A = 'A'
TYPE_Z = 'Z'
JSON = 'J'
HEX_BYTE_ARRAY = 'H'
DEC_ARRAY = 'B'
GFA1_NAME = 'lbl'
GFA1_NAMES = 'lbs'
GFA1_ORIENTATION = 'orn'
GFA1_SEQUENCE = 'seq'
GFA1_CIGAR = 'cig'
GFA1_CIGARS = 'cgs'
GFA1_INT = 'pos'
GFA2_ID = 'id'
GFA2_IDS = 'ids'
GFA2_REFERENCE = 'ref'
GFA2_REFERENCES = 'rfs'
GFA2_INT = 'int'
GFA2_TRACE = 'trc'
GFA2_ALIGNMENT = 'aln'
GFA2_POSITION = 'pos2'
GFA2_CIGAR = 'cig2'
GFA2_SEQUENCE = 'seq2'
GFA2_OPTIONAL_INT = 'oint'
GFA2_OPTIONAL_ID = 'oid'
DATASTRING_VALIDATION_REGEXP = \
  {\
  TYPE_A : "^[!-~]", \
  # any printable character
  #
  TYPE_i : "^[-+]?[0-9]+$", \
  # Signed integer
  #
  TYPE_f : "^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$", \
  # Single-precision floating number
  #
  TYPE_Z : "^[ !-~]+$", \
  # Printable string, including space
  #
  JSON : "^[ !-~]+$",  \
  # JSON, excluding new-line and tab characters
  #
  HEX_BYTE_ARRAY : "^[0-9A-F]+$", \
  # Byte array in the Hex format
  #
  DEC_ARRAY : "^[cCsSiIf](,[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)+$", \
  # Integer or numeric array
  #
  GFA1_NAME : "^[!-)+-<>-~][!-~]*$", \
  # segment/path label(segment name)
  #
  GFA1_ORIENTATION : "^\+|-$", \
  #segment orientation
  #
  ###
  #'lbs' : "^[!-)+-<>-~][!-~]*[+-](,[!-)+-<>-~][!-~]*[+-])+$",
  # multiple labels with orientations, comma-sep
  #
  # Changed according to issue 59, since the comma is accepted by [!-~],
  # it's not possible to make a clear regexp for an array of labels,
  # so the implementation has been modified to reflect
  # this behaviour, splitting the labels and checking them one by one
  # with the new lbs regexp beyond.
  #
  GFA1_NAMES : "^[!-)+-<>-~][!-~]*[+-]$", \
  GFA1_SEQUENCE : "^\*$|^[A-Za-z=.]+$", \
  # nucleotide sequence(segment sequence)
  #
  GFA1_INT : "^[0-9]*$", \
  # positive integer(CLAIM ISSUE HERE, MOVE TO -> int)
  #
  GFA1_CIGAR : "^(\*|(([0-9]+[MIDNSHPX=])+))$", # CIGAR string \
  GFA1_CIGARS : "^(\*|(([0-9]+[MIDNSHPX=])+))(,(\*|(([0-9]+[MIDNSHPX=])+)))*$", \
  # multiple CIGARs, comma-sep \
  #
  'cmt' : ".*", \
  # content of comment line, everything is allowed
  #
  GFA2_ID : "^[!-~]+$", \
  # it's the lbl for GFA2
  #
  GFA2_IDS : "^[!-~]+([ ][!-~]+)*$", \
  # it's the lbs for GFA2
  #
  GFA2_REFERENCE : "^[!-~]+[+-]$", \
  GFA2_REFERENCES : "^[!-~]+[+-]([ ][!-~]+[+-])*$", \
  # array of references
  #
  GFA2_INT : "^[0-9]+$", \
  # GFA1 has pos to describe any positive integer,
  # but pos accept the empty string, while 'int' doesn't
  #
  GFA2_TRACE : "^[0-9]+(,[0-9]+)*$", \
  GFA2_ALIGNMENT : "^\*$|^[0-9]+(,[0-9]+)*$|^([0-9]+[MDIP])+$", \
  GFA2_POSITION : "^[0-9]+\$?$", \
  # pos2 represent a position in GFA2, it's similar in NO WAY
  # to pos which represent a positive integer in GFA1
  #
  GFA2_CIGAR : "^([0-9]+[MDIP])+$", \
  # CIGAR string for GFA2
  #
  GFA2_SEQUENCE : "^\*$|^[!-~]+$", # seq2 is a GFA2 sequence,
  # it's more flexible than GFA1 seq
  #
  GFA2_OPTIONAL_ID : "^\*$|^[!-~]+$", \
  # optional id  for GFA2
  #
  GFA2_OPTIONAL_INT : "^\*$|^[0-9]+$" \
  # optional int
  #
  }
def is_valid(string, datatype):
def is_dazzler_trace(string):
def is_gfa1_cigar(string):
def is_gfa2_cigar(string):
def validate(string, datatype): class InvalidNodeError(Exception):
class Node:
def is_node(obj):
    def __init__(self, node_id, sequence, length, opt_fields={}):
    def nid(self):
    def sequence(self):
    def slen(self):
    def opt_fields(self):
    def from_line(cls, segment_line):
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover class InvalidEdgeError(Exception):
class Edge:
def is_edge(obj):
    def __init__(self, \
                  edge_id, \
                  from_node, from_orn, \
                  to_node, to_orn, \
                  from_positions, \
                  to_positions, \
                  alignment, \
                  distance=None, \
                  variance=None, \
                  opt_fields={}, \
                  is_dovetail=False):
    def eid(self):
    def from_node(self):
    def from_orn(self):
    def to_orn(self):
    def to_node(self):
    def from_positions(self):
    def to_positions(self):
    def alignment(self):
    def distance(self):
    def variance(self):
    def opt_fields(self):
    def is_dovetail(self):
    def from_segment_end(self):
    def to_segment_end(self):
    def _set_segments_end(self):
    def _set_segments_end_link(self):
    def _set_segments_end_edge(self):
    def from_line(cls, line_):
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover class InvalidSubgraphError(Exception): pass
class Subgraph:
def is_subgraph(obj):
    def __init__(self, graph_id, elements, opt_fields={}):
    def is_path(self):
    def sub_id(self):
    def elements(self):
    def opt_fields(self):
    def from_line(cls, line_):
    def as_dict(self):
    def __eq__(self, other):
    def __neq__(self, other):
    def __str__(self): # pragma: no cover
import copy
import sys
import networkx as nx
import unittest
import pygfa
from pygfa import gfa
from pygfa.graph_element.parser import header, segment, link, path, containment
from pygfa.graph_element.parser import fragment, edge, gap, group
from pygfa.graph_element.parser import line, field_validator as fv
from pygfa.graph_element import node, edge as ge, subgraph as sg
    def test_add_node (self):
        """Test add_node and remove_node methods."""
        self.graph.clear ()
        seg = segment.SegmentV1.from_string ("S\t3\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test")
        node_ = node.Node.from_line (seg)
        self.graph.add_node (node_)
        seg.fields['name']._value = "node4"
        node_ = node.Node.from_line(seg)
        self.graph.add_node (node_)

        self.graph._graph.add_edge("3", "node4") # create an edge (indirectly)
        self.assertTrue(len(self.graph.edges()) == 1)

        self.assertTrue (len (self.graph.nodes ()) == 2)
        self.assertTrue (self.graph.nodes(identifier = '3')['sequence'] == "TGCAACGTATAGACTTGTCAC")
        self.assertTrue (self.graph.nodes(identifier = '3')['nid'] == "3")
        self.assertTrue (self.graph.nodes(identifier = '3')['ui'].value == "test")
        self.assertTrue (self.graph.nodes(identifier = '3')['ui'].type == "Z")

        self.graph.remove_node("3")
        self.assertTrue(self.graph.nodes(identifier = "3") == None)
        self.assertTrue(len(self.graph.edges()) == 0)
        # the edge between 3 and 4 has been automatically deleted

        self.graph.add_node("S\t3\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test")
        self.assertTrue (self.graph.nodes(identifier = '3')['sequence'] == "TGCAACGTATAGACTTGTCAC")
        self.assertTrue (self.graph.nodes(identifier = '3')['nid'] == "3")
        self.assertTrue (self.graph.nodes(identifier = '3')['ui'].value == "test")
        self.assertTrue (self.graph.nodes(identifier = '3')['ui'].type == "Z")
        self.graph.remove_node("3")

        # test GFA2 segment
        self.graph.add_node("S\t3\t21\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test")
        self.assertTrue (self.graph.nodes(identifier = '3')['sequence'] == "TGCAACGTATAGACTTGTCAC")
        self.assertTrue (self.graph.nodes(identifier = '3')['nid'] == "3")
        self.assertTrue (self.graph.nodes(identifier ='3')['ui'].value == "test")
        self.assertTrue (self.graph.nodes(identifier = '3')['ui'].type == "Z")
        self.assertTrue (self.graph.nodes(identifier = '3')['slen'] == 21)

        with self.assertRaises(gfa.GFAError):
            self.graph.add_node(\
                "S\t3\t21\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test",
                safe=True)

        with self.assertRaises(TypeError):
            self.graph.add_node("21", nid="21", slen="4", sequence="acgt")
        with self.assertRaises(node.InvalidNodeError):
            self.graph.add_node({'nid':"21", 'slen':"4", 'sequence':"acgt"})

        with self.assertRaises(node.InvalidNodeError):
            self.graph.remove_node("42")


    def test_add_edge (self):
        self.graph.clear ()

        self.graph.add_node("S\t3\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test")
        self.graph.add_node("S\t4\tTGCAACGTATAGACTTGTCAC\tRC:i:4\tui:Z:test\tab:Z:another_test")

        line = fragment.Fragment.from_string ("F\t3\t4-\t0\t140$\t0\t140\t11M")
        edg = ge.Edge.from_line (line)
        self.graph.add_edge (edg)

        line = link.Link.from_string ("L\t3\t+\t4\t-\t47M\tui:Z:test\tab:Z:another_test")
        edg = ge.Edge.from_line (line)
        self.graph.add_edge (edg)

        # The F line is added first so it will have id 'virtual_0'
        # This first test get all the edges between node 3 and 4 and
        # the get the edge labelled 'virtual_0'
        self.assertTrue (self.graph.edges(identifier = ('3','4'))['virtual_0']['from_node'] == "3")
        # This test instead get instantly the edge labelled 'virtual_0', that is unique
        # in the graph
        self.assertTrue (self.graph.edges(identifier = 'virtual_0')['from_node'] == "3")
        self.assertTrue (len (self.graph.edges(identifier = ('3', '4'))) == 2)

        self.graph.remove_edge("virtual_0")
        self.assertTrue (self.graph.edges(identifier = 'virtual_0') == None)
        with self.assertRaises(ge.InvalidEdgeError):
            self.graph.remove_edge("virtual_0")

        # remember the virtual id keeps incrementing
        self.graph.add_edge("F\t3\t4-\t0\t140$\t0\t140\t11M")
        self.assertTrue (self.graph.edges(identifier = ('3','4'))['virtual_1']['from_node'] == "3")
        self.graph.remove_edge(('3', '4')) # remove all the edges between 3 and 4
        print(len(self.graph.edges()))
        self.assertTrue(len(self.graph.edges()) == 0)

        # nodes will be automatically created
        self.graph.add_edge("L\t3\t+\t65\t-\t47M\tui:Z:test\tab:Z:another_test")
        self.graph.add_edge("C\ta\t+\tb\t-\t10\t*\tui:Z:test\tab:Z:another_test")
        self.graph.add_edge("E\t*\t23-\t16+\t0\t11\t0\t11\t11M\tui:Z:test\tab:Z:another_test")
        self.graph.add_edge("G\tg\tA+\tB-\t1000\t*\tui:Z:test\tab:Z:another_test")
        self.assertTrue(len(self.graph.edges()) == 4)

        self.graph.remove_edge(("A", "B", "g")) # remove the gap
        self.assertTrue(len(self.graph.edges()) == 3)

        self.graph.add_edge("L\t3\t+\t65\t-\t47M\tui:Z:test\tID:Z:42")
        with self.assertRaises(gfa.GFAError):
            self.graph.add_edge(\
                "L\t3\t+\t65\t-\t47M\tui:Z:test\tID:Z:42", \
                safe=True)
        with self.assertRaises(gfa.GFAError):
            self.graph.add_edge(\
                "L\t3\t+\tnon_exists\t-\t47M\tui:Z:test\tID:Z:47", \
                safe=True)

        line = fragment.Fragment.from_string ("F\t3\t4-\t0\t140$\t0\t140\t11M")
        edg = ge.Edge.from_line (line)
        del(edg._eid)
        with self.assertRaises(ge.InvalidEdgeError):
            self.graph.add_edge (edg)
        with self.assertRaises(ge.InvalidEdgeError):
            self.graph.add_edge ("Z\t3\t4-\t0\t140$\t0\t140\t11M") # invalid line


    def test_add_subgraphs (self):
        self.graph.clear ()


        line = path.Path.from_string("P\t14\t11+,12+\t122M\tui:Z:test\tab:Z:another_test")