if self.prompt_on_exit:<EOL><INDENT>retval = confirm(parent  = info.ui.control,<EOL>message = "<STR_LIT>",<EOL>title   = "<STR_LIT>",<EOL>default = YES)<EOL>if retval == YES:<EOL><INDENT>self._on_close( info )<EOL><DEDENT><DEDENT>else:<EOL><INDENT>self._on_close( info )<EOL><DEDENT>

Handles the user attempting to exit Godot.


label = self.label<EOL>if label[:<NUM_LIT:1>] == '<STR_LIT:=>':<EOL><INDENT>return label[<NUM_LIT:1>:]<EOL><DEDENT>label = xgetattr( object, label, '<STR_LIT>' )<EOL>if self.formatter is None:<EOL><INDENT>return label<EOL><DEDENT>return self.formatter( object, label )<EOL>

Gets the label to display for a specified object.


label_name = self.label<EOL>if label_name[:<NUM_LIT:1>] != '<STR_LIT:=>':<EOL><INDENT>xsetattr( object, label_name, label )<EOL><DEDENT>


def when_label_changed ( self, object, listener, remove ):

label = self.label<EOL>if label[:<NUM_LIT:1>] != '<STR_LIT:=>':<EOL><INDENT>object.on_trait_change( listener, label, remove = remove,<EOL>dispatch = '<STR_LIT>' )<EOL><DEDENT>

Sets up or removes a listener for the label being changed on a
            specified object.


self._graph = graph = Graph()<EOL>ui = graph.edit_traits(parent=parent, kind="<STR_LIT>")<EOL>self.control = ui.control<EOL>

Finishes initialising the editor by creating the underlying toolkit
            widget.


super(SimpleGraphEditor, self).dispose()<EOL>


pass<EOL>

Handles an error that occurs while setting the object's trait
            value.


object = self.value<EOL>canvas = self.factory.canvas<EOL>if canvas is not None:<EOL><INDENT>for nodes_name in canvas.node_children:<EOL><INDENT>node_children = getattr(object, nodes_name)<EOL>self._add_nodes(node_children)<EOL><DEDENT>for edges_name in canvas.edge_children:<EOL><INDENT>edge_children = getattr(object, edges_name)<EOL>self._add_edges(edge_children)<EOL><DEDENT><DEDENT>self._add_listeners()<EOL>

Updates the editor when the object trait changes externally to the
            editor.


object = self.value<EOL>canvas = self.factory.canvas<EOL>if canvas is not None:<EOL><INDENT>for name in canvas.node_children:<EOL><INDENT>object.on_trait_change(self._nodes_replaced, name)<EOL>object.on_trait_change(self._nodes_changed, name + "<STR_LIT>")<EOL><DEDENT>for name in canvas.edge_children:<EOL><INDENT>object.on_trait_change(self._edges_replaced, name)<EOL>object.on_trait_change(self._edges_changed, name + "<STR_LIT>")<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>

Adds the event listeners for a specified object.


def _nodes_replaced(self, object, name, old, new):

self._delete_nodes(old)<EOL>self._add_nodes(new)<EOL>


def _nodes_changed(self, object, name, undefined, event):

self._delete_nodes(event.removed)<EOL>self._add_nodes(event.added)<EOL>


graph = self._graph<EOL>if graph is not None:<EOL><INDENT>for feature in features:<EOL><INDENT>for graph_node in self.factory.nodes:<EOL><INDENT>if feature.__class__ in graph_node.node_for:<EOL><INDENT>graph.add_node( id(feature), **graph_node.dot_attr )<EOL>break<EOL><DEDENT><DEDENT><DEDENT><DEDENT>graph.arrange_all()<EOL>

Adds a node to the graph for each item in 'features' using
            the GraphNodes from the editor factory.


graph = self._graph<EOL>if graph is not None:<EOL><INDENT>for feature in features:<EOL><INDENT>graph.delete_node( id(feature) )<EOL><DEDENT><DEDENT>graph.arrange_all()<EOL>

Removes the node corresponding to each item in 'features'.


def _edges_replaced(self, object, name, old, new):

self._delete_edges(old)<EOL>self._add_edges(new)<EOL>


def _edges_changed(self, object, name, undefined, event):

self._delete_edges(event.removed)<EOL>self._add_edges(event.added)<EOL>


graph = self._graph<EOL>if graph is not None:<EOL><INDENT>for feature in features:<EOL><INDENT>for graph_edge in self.factory.edges:<EOL><INDENT>if feature.__class__ in graph_edge.edge_for:<EOL><INDENT>tail_feature = getattr(feature, graph_edge.tail_name)<EOL>head_feature = getattr(feature, graph_edge.head_name)<EOL>graph.add_edge( id(tail_feature), id(head_feature),<EOL>**graph_edge.dot_attr )<EOL>break<EOL><DEDENT><DEDENT><DEDENT><DEDENT>graph.arrange_all()<EOL>

Adds an edge to the graph for each item in 'features' using
            the GraphEdges from the editor factory.


graph = self._graph<EOL>if graph is not None:<EOL><INDENT>for feature in features:<EOL><INDENT>for graph_edge in self.factory.edges:<EOL><INDENT>if feature.__class__ in graph_edge.edge_for:<EOL><INDENT>tail_feature = getattr(feature, graph_edge.tail_name)<EOL>head_feature = getattr(feature, graph_edge.head_name)<EOL>graph.delete_edge( id(tail_feature), id(head_feature) )<EOL><DEDENT><DEDENT><DEDENT><DEDENT>graph.arrange_all()<EOL>


return SimpleGraphEditor<EOL>

Returns the editor class to use for "simple" style views.


return True<EOL>

Returns whether this object can have children.


children = []<EOL>children.extend( object.subgraphs )<EOL>children.extend( object.clusters )<EOL>children.extend( object.nodes )<EOL>children.extend( object.edges )<EOL>return children<EOL>


def append_child ( self, object, child ):

if isinstance( child, Subgraph ):<EOL><INDENT>object.subgraphs.append( child )<EOL><DEDENT>elif isinstance( child, Cluster ):<EOL><INDENT>object.clusters.append( child )<EOL><DEDENT>elif isinstance( child, Node ):<EOL><INDENT>object.nodes.append( child )<EOL><DEDENT>elif isinstance( child, Edge ):<EOL><INDENT>object.edges.append( child )<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>

Appends a child to the object's children.


def insert_child ( self, object, index, child ):

if isinstance( child, Subgraph ):<EOL><INDENT>object.subgraphs.insert( index, child )<EOL><DEDENT>elif isinstance( child, Cluster ):<EOL><INDENT>object.clusters.insert( index, child )<EOL><DEDENT>elif isinstance( child, Node ):<EOL><INDENT>object.nodes.insert( index, child )<EOL><DEDENT>elif isinstance( child, Edge ):<EOL><INDENT>object.edges.insert( index, child )<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>

Inserts a child into the object's children.


def delete_child ( self, object, index ):

if isinstance( child, Subgraph ):<EOL><INDENT>object.subgraphs.pop(index)<EOL><DEDENT>elif isinstance( child, Cluster ):<EOL><INDENT>object.clusters.pop( index )<EOL><DEDENT>elif isinstance( child, Node ):<EOL><INDENT>object.nodes.pop( index )<EOL><DEDENT>elif isinstance( child, Edge ):<EOL><INDENT>object.edges.pop( index )<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>

Deletes a child at a specified index from the object's children.


def when_children_replaced ( self, object, listener, remove ):

object.on_trait_change( listener, "<STR_LIT>", remove = remove,<EOL>dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>", remove = remove,<EOL>dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>", remove = remove,<EOL>dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>", remove = remove,<EOL>dispatch = "<STR_LIT>" )<EOL>

Sets up or removes a listener for children being replaced on a
            specified object.


def when_children_changed ( self, object, listener, remove ):

object.on_trait_change( listener, "<STR_LIT>",<EOL>remove = remove, dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>",<EOL>remove = remove, dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>",<EOL>remove = remove, dispatch = "<STR_LIT>" )<EOL>object.on_trait_change( listener, "<STR_LIT>",<EOL>remove = remove, dispatch = "<STR_LIT>" )<EOL>

Sets up or removes a listener for children being changed on a
            specified object.


if object is not None:<EOL><INDENT>object.edit_traits(kind="<STR_LIT>")<EOL>return None<EOL><DEDENT>return True<EOL>


super(Graph, self).__init__(*args, **kw_args)<EOL>self.on_trait_change(self._on_nodes, "<STR_LIT>")<EOL>self.on_trait_change(self._on_nodes, "<STR_LIT>")<EOL>self.on_trait_change(self._on_edges, "<STR_LIT>")<EOL>self.on_trait_change(self._on_edges, "<STR_LIT>")<EOL>


s = "<STR_LIT>"<EOL>if self.strict:<EOL><INDENT>s += "<STR_LIT>"<EOL><DEDENT>if self.directed:<EOL><INDENT>s += "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>s += "<STR_LIT>"<EOL><DEDENT>return "<STR_LIT>" % ( s, super(Graph, self).__str__() )<EOL>

Returns a string representation of the graph in dot language. It
            will return the graph and all its subelements in string form.


import godot.dot_data_parser<EOL>parser = godot.dot_data_parser.GodotDataParser()<EOL>xdot_data = self.create( format = "<STR_LIT>" )<EOL><INDENT>print "<STR_LIT>", str( self )<EOL>print "<STR_LIT>", xdot_data<EOL><DEDENT>parser.dotparser.parseWithTabs()<EOL>ndata = xdot_data.replace( "<STR_LIT>", "<STR_LIT>" )<EOL>tokens = parser.dotparser.parseString( ndata )[<NUM_LIT:0>]<EOL>parser.build_graph( graph=self, tokens=tokens[<NUM_LIT:3>] )<EOL>self.redraw_canvas()<EOL>

Sets for the _draw_ and _ldraw_ attributes for each of the graph
            sub-elements by processing the xdot format of the graph.


from xdot_parser import XdotAttrParser<EOL>xdot_parser = XdotAttrParser()<EOL>canvas = self._component_default()<EOL>for node in self.nodes:<EOL><INDENT>components = xdot_parser.parse_xdot_data( node._draw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( node._ldraw_ )<EOL>canvas.add( *components )<EOL><DEDENT>for edge in self.edges:<EOL><INDENT>components = xdot_parser.parse_xdot_data( edge._draw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( edge._ldraw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( edge._hdraw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( edge._tdraw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( edge._hldraw_ )<EOL>canvas.add( *components )<EOL>components = xdot_parser.parse_xdot_data( edge._tldraw_ )<EOL>canvas.add( *components )<EOL><DEDENT>self.component = canvas<EOL>self.vp.request_redraw()<EOL>

Parses the Xdot attributes of all graph components and adds
            the components to a new canvas.


node = super(Graph, self).get_node(ID)<EOL>if node is not None:<EOL><INDENT>return node<EOL><DEDENT>for graph in self.all_graphs:<EOL><INDENT>for each_node in graph.nodes:<EOL><INDENT>if each_node.ID == ID:<EOL><INDENT>return each_node<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>return None<EOL><DEDENT>

Returns a node given an ID or None if no such node exists.


return Canvas( draw_axes=True, bgcolor="<STR_LIT>")<EOL>

Trait initialiser.  Overrides the base class to use a Canvas
            for the root Graph.


if self.mode == "<STR_LIT>":<EOL><INDENT>return <NUM_LIT> * len(self.nodes)<EOL><DEDENT>else:<EOL><INDENT>return <NUM_LIT><EOL><DEDENT>


mode = self.mode<EOL>if mode == "<STR_LIT>":<EOL><INDENT>return <NUM_LIT:100> * len(self.nodes)<EOL><DEDENT>elif mode == "<STR_LIT>":<EOL><INDENT>return <NUM_LIT:200><EOL><DEDENT>else:<EOL><INDENT>return <NUM_LIT><EOL><DEDENT>


top_graph = self<EOL>def get_subgraphs(graph):<EOL><INDENT>assert isinstance(graph, BaseGraph)<EOL>subgraphs = graph.subgraphs[:]<EOL>for subgraph in graph.subgraphs:<EOL><INDENT>subsubgraphs = get_subgraphs(subgraph)<EOL>subgraphs.extend(subsubgraphs)<EOL><DEDENT>return subgraphs<EOL><DEDENT>subgraphs = get_subgraphs(top_graph)<EOL>return [top_graph] + subgraphs<EOL>


if new:<EOL><INDENT>conn = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>conn = "<STR_LIT>"<EOL><DEDENT>for edge in [e for g in self.all_graphs for e in g.edges]:<EOL><INDENT>edge.conn = conn<EOL><DEDENT>

Sets the connection string for all edges.


all_graphs = self.all_graphs<EOL>all_nodes = [n for g in all_graphs for n in g.nodes]<EOL>for graph in all_graphs:<EOL><INDENT>for edge in graph.edges:<EOL><INDENT>edge._nodes = all_nodes<EOL><DEDENT><DEDENT>

Maintains each branch's list of available nodes in order that they
            may move themselves (InstanceEditor values).


def _on_edges(self, object, name, old, new):

if name == "<STR_LIT>":<EOL><INDENT>edges = new.added<EOL><DEDENT>elif name == "<STR_LIT>":<EOL><INDENT>edges = new<EOL><DEDENT>else:<EOL><INDENT>edges = []<EOL><DEDENT>all_nodes = [n for g in self.all_graphs for n in g.nodes]<EOL>for each_edge in edges:<EOL><INDENT>if each_edge.tail_node not in all_nodes:<EOL><INDENT>object.nodes.append( each_edge.tail_node )<EOL><DEDENT>if each_edge.head_node not in all_nodes:<EOL><INDENT>object.nodes.append( each_edge.head_node )<EOL><DEDENT>each_edge._nodes = all_nodes<EOL><DEDENT>

Handles the list of edges for any graph changing.


self.parser = self.define_parser()<EOL>self.pen = Pen()<EOL>


parser = self.parser<EOL><INDENT>if pyparsing_version >= "<STR_LIT>":<EOL><INDENT>parser.parseWithTabs()<EOL><DEDENT><DEDENT>if data:<EOL><INDENT>return parser.parseString(data)<EOL><DEDENT>else:<EOL><INDENT>return []<EOL><DEDENT>

Parses xdot data and returns the associated components.


<EOL>point = Group(integer.setResultsName("<STR_LIT:x>") +<EOL>integer.setResultsName("<STR_LIT:y>"))<EOL>n_points = (integer.setResultsName("<STR_LIT:n>") +<EOL>OneOrMore(point).setResultsName("<STR_LIT>"))<EOL>n_bytes = Suppress(integer) + Suppress(minus) +Word(printables).setResultsName("<STR_LIT:b>")<EOL>justify = ToInteger(<EOL>Literal("<STR_LIT>") | Literal("<STR_LIT:0>") | Literal("<STR_LIT:1>")<EOL>).setResultsName("<STR_LIT>")<EOL>fill = (Literal("<STR_LIT:C>").suppress() + Suppress(integer) + Suppress(minus) +<EOL>colour.setResultsName("<STR_LIT>")).setResultsName("<STR_LIT>")<EOL>stroke = (Literal("<STR_LIT:c>").suppress() + Suppress(integer) +<EOL>Suppress(minus) + colour.setResultsName("<STR_LIT>")<EOL>).setResultsName("<STR_LIT>")<EOL>font = (Literal("<STR_LIT:F>").suppress() + real.setResultsName("<STR_LIT:s>") +<EOL>n_bytes).setResultsName("<STR_LIT>")<EOL>style = (Literal("<STR_LIT:S>").suppress() + n_bytes).setResultsName("<STR_LIT>")<EOL>filled_ellipse = (Literal("<STR_LIT:E>").suppress() +<EOL>integer.setResultsName("<STR_LIT>") + integer.setResultsName("<STR_LIT>") +<EOL>integer.setResultsName("<STR_LIT:w>") + integer.setResultsName("<STR_LIT:h>")<EOL>).setResultsName("<STR_LIT>")<EOL>ellipse = (Literal("<STR_LIT:e>").suppress() +<EOL>integer.setResultsName("<STR_LIT>") + integer.setResultsName("<STR_LIT>") +<EOL>integer.setResultsName("<STR_LIT:w>") + integer.setResultsName("<STR_LIT:h>")<EOL>).setResultsName("<STR_LIT>")<EOL>filled_polygon = (Literal("<STR_LIT:P>").suppress() +<EOL>n_points).setResultsName("<STR_LIT>")<EOL>polygon = (Literal("<STR_LIT:p>").suppress() +<EOL>n_points).setResultsName("<STR_LIT>")<EOL>polyline = (Literal("<STR_LIT:L>").suppress() +<EOL>n_points).setResultsName("<STR_LIT>")<EOL>bspline = (Literal("<STR_LIT:B>").suppress() +<EOL>n_points).setResultsName("<STR_LIT>")<EOL>filled_bspline = (Literal("<STR_LIT:b>").suppress() +<EOL>n_points).setResultsName("<STR_LIT>")<EOL>text = (Literal("<STR_LIT:T>").suppress() + integer.setResultsName("<STR_LIT:x>") +<EOL>integer.setResultsName("<STR_LIT:y>") + justify +<EOL>integer.setResultsName("<STR_LIT:w>") + n_bytes).setResultsName("<STR_LIT:text>")<EOL>image = (Literal("<STR_LIT:I>").suppress() + integer.setResultsName("<STR_LIT:x>") +<EOL>integer.setResultsName("<STR_LIT:y>") + integer.setResultsName("<STR_LIT:w>") +<EOL>integer.setResultsName("<STR_LIT:h>") + n_bytes).setResultsName("<STR_LIT:image>")<EOL>value = (Optional(quote).suppress() + OneOrMore(filled_ellipse |<EOL>ellipse | filled_polygon | polygon | polyline | bspline |<EOL>filled_bspline | text | fill | stroke | font | style | image) +<EOL>Optional(quote).suppress()).setResultsName("<STR_LIT:value>")<EOL><INDENT>draw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>ldraw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>hdraw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>tdraw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>hldraw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>tldraw_ = Literal("<STR_LIT>") + Suppress(equals) + value<EOL>n_points.setParseAction(self.proc_points)<EOL><DEDENT>fill.setParseAction(self.proc_fill_color)<EOL>stroke.setParseAction(self.proc_stroke_color)<EOL>font.setParseAction(self.proc_font)<EOL>style.setParseAction(self.proc_style)<EOL>filled_ellipse.setParseAction(self.proc_filled_ellipse)<EOL>ellipse.setParseAction(self.proc_unfilled_ellipse)<EOL>filled_polygon.setParseAction(self.proc_filled_polygon)<EOL>polygon.setParseAction(self.proc_unfilled_polygon)<EOL>polyline.setParseAction(self.proc_polyline)<EOL>bspline.setParseAction(self.proc_unfilled_bspline)<EOL>filled_bspline.setParseAction(self.proc_filled_bspline)<EOL>text.setParseAction(self.proc_text)<EOL>image.setParseAction(self.proc_image)<EOL>return value<EOL>

Defines xdot grammar.

        @see: http://graphviz.org/doc/info/output.html#d:xdot


self.pen.fill_color = self._proc_color(tokens)<EOL>return []<EOL>


self.pen.color = self._proc_color(tokens)<EOL>return []<EOL>


keys = list(tokens.keys())<EOL>if "<STR_LIT>" in keys: <EOL><INDENT>rr, gg, bb = tokens["<STR_LIT>"], tokens["<STR_LIT>"], tokens["<STR_LIT>"]<EOL>hex2int = lambda h: int(h, <NUM_LIT:16>)<EOL>if "<STR_LIT>" in keys:<EOL><INDENT>a = tokens["<STR_LIT>"]<EOL>c = str((hex2int(rr), hex2int(gg), hex2int(bb), hex2int(a)))<EOL><DEDENT>else:<EOL><INDENT>c = str((hex2int(rr), hex2int(gg), hex2int(bb)))<EOL><DEDENT><DEDENT>elif "<STR_LIT>" in keys: <EOL><INDENT>r, g, b = hsv_to_rgb(tokens["<STR_LIT>"],<EOL>tokens["<STR_LIT>"],<EOL>tokens["<STR_LIT:value>"])<EOL>c = str((int(r*<NUM_LIT:255>), int(g*<NUM_LIT:255>), int(b*<NUM_LIT:255>)))<EOL><DEDENT>else:<EOL><INDENT>c = tokens["<STR_LIT>"]<EOL><DEDENT>return c<EOL>

The color traits of a Pen instance must be a string of the form
        (r,g,b) or (r,g,b,a) where r, g, b, and a are integers from 0 to 255,
        a wx.Colour instance, an integer which in hex is of the form 0xRRGGBB,
        where RR is red, GG is green, and BB is blue or a valid color name.


size = int(tokens["<STR_LIT:s>"])<EOL>self.pen.font = "<STR_LIT>" % (tokens["<STR_LIT:b>"], size)<EOL>return []<EOL>


<EOL>return []<EOL>

Sets the style.  At present, the recognized style names are
        "dashed", "dotted", "solid", "invis" and "bold" for nodes and edges,
        and "filled", "diagonals" and "rounded" for nodes only. The styles
        "filled" and "rounded" are recognized for clusters. Additional styles
        are available in device-dependent form.


return self._proc_ellipse(tokens, filled=True)<EOL>

Returns the components of a filled ellipse.


return self._proc_ellipse(tokens, filled=False)<EOL>

Returns the components of an unfilled ellipse.


def _proc_ellipse(self, tokens, filled):


return self._proc_polygon(tokens, filled=True)<EOL>

Returns the components of a filled polygon.


return self._proc_polygon(tokens, filled=False)<EOL>

Returns the components of an unfilled polygon.


def _proc_polygon(self, tokens, filled):

pts = [(p["<STR_LIT:x>"], p["<STR_LIT:y>"]) for p in tokens["<STR_LIT>"]]<EOL>component = Polygon(pen=self.pen, points=pts, filled=filled)<EOL>return component<EOL>


pts = [(p["<STR_LIT:x>"], p["<STR_LIT:y>"]) for p in tokens["<STR_LIT>"]]<EOL>component = Polyline(pen=self.pen, points=pts)<EOL>return component<EOL>


return self._proc_bspline(tokens, filled=True)<EOL>

Returns the components of a filled B-spline.


return self._proc_bspline(tokens, filled=False)<EOL>

Returns the components of an unfilled B-spline.


def _proc_bspline(self, tokens, filled):

pts = [(p["<STR_LIT:x>"], p["<STR_LIT:y>"]) for p in tokens["<STR_LIT>"]]<EOL>component = BSpline(pen=self.pen, points=pts, filled=filled)<EOL>return component<EOL>

Returns the components of a B-spline (Bezier curve).


print("<STR_LIT>", tokens, tokens.asList(), list(tokens.keys()))<EOL>raise NotImplementedError<EOL>


self.ID = ID<EOL>super(Node, self).__init__(**traits)<EOL>


attrs = []<EOL>for trait_name, trait in self.traits(graphviz=True).items():<EOL><INDENT>value = getattr(self, trait_name)<EOL>if value != trait.default:<EOL><INDENT>trait = self.trait( trait_name )<EOL>if trait.is_trait_type( Tuple ):<EOL><INDENT>valstr  = '<STR_LIT>' % "<STR_LIT:U+002C>".join( [str(d) for d in value] )<EOL><DEDENT>elif isinstance( value, str ):<EOL><INDENT>valstr = '<STR_LIT>' % value<EOL><DEDENT>else:<EOL><INDENT>valstr = str( value )<EOL><DEDENT>attrs.append('<STR_LIT>' % (trait_name, valstr))<EOL><DEDENT><DEDENT>if attrs:<EOL><INDENT>attrstr = "<STR_LIT>" % "<STR_LIT:U+002CU+0020>".join(attrs)<EOL>return "<STR_LIT>" % (self.ID, attrstr)<EOL><DEDENT>else:<EOL><INDENT>return "<STR_LIT:%s>" % self.ID<EOL><DEDENT>

Returns a string representation of the node.


return hash(self.ID)<EOL>

objects which compare equal have the same hash value.


try:<EOL><INDENT>if isinstance(other, Node) and (self.ID == other.ID):<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>elif self.ID == other:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT>return -<NUM_LIT:1><EOL>

Called by comparison operations if rich comparison
            (__eq__, __lt__, __gt__) is not defined.


component = Container(fit_window=False, auto_size=True,<EOL>bgcolor="<STR_LIT>")<EOL>component.tools.append( MoveTool(component) )<EOL><INDENT>component.tools.append( TraitsTool(component) )<EOL><DEDENT>return component<EOL>


vp = Viewport(component=self.component)<EOL>vp.enable_zoom=True<EOL><INDENT>vp.view_position = [-<NUM_LIT:10>, -<NUM_LIT:10>]<EOL><DEDENT>vp.tools.append(ViewportPanTool(vp))<EOL>return vp<EOL>


<EOL>import godot.dot_data_parser<EOL>import godot.graph<EOL>graph = godot.graph.Graph(ID="<STR_LIT:g>")<EOL>graph.add_node(self)<EOL>print("<STR_LIT>", str(graph))<EOL>xdot_data = graph.create( format = "<STR_LIT>" )<EOL>print("<STR_LIT>", xdot_data)<EOL>parser = godot.dot_data_parser.GodotDataParser()<EOL><INDENT>parser.parse_dot_data(xdot_data)<EOL><DEDENT>flat_data = xdot_data.replace('<STR_LIT>','<STR_LIT>')<EOL>tokens = parser.dotparser.parseString(flat_data)[<NUM_LIT:0>]<EOL>for element in tokens[<NUM_LIT:3>]:<EOL><INDENT>print("<STR_LIT>", element)<EOL>cmd = element[<NUM_LIT:0>]<EOL>if cmd == '<STR_LIT>':<EOL><INDENT>cmd, nodename, opts = element<EOL>assert nodename == self.ID<EOL>print("<STR_LIT>", opts)<EOL>self.set( **opts )<EOL><DEDENT><DEDENT>

Arrange the components of the node using Graphviz.


components = XdotAttrParser().parse_xdot_data(new)<EOL>max_x = max( [c.bounds[<NUM_LIT:0>] for c in components] + [<NUM_LIT:1>] )<EOL>max_y = max( [c.bounds[<NUM_LIT:1>] for c in components] + [<NUM_LIT:1>] )<EOL>pos_x = min( [c.x for c in components] )<EOL>pos_y = min( [c.y for c in components] )<EOL>move_to_origin(components)<EOL>container = Container(auto_size=True,<EOL>position=[pos_x-self.pos[<NUM_LIT:0>], pos_y-self.pos[<NUM_LIT:1>]],<EOL>bgcolor="<STR_LIT>")<EOL><INDENT>self.bounds = bounds=[max_x, max_y]<EOL>container = Container(fit_window=False, auto_size=True, bgcolor="<STR_LIT>")<EOL><DEDENT>container.add( *components )<EOL>self.drawing = container<EOL>

Parses the drawing directive, updating the node components.


components = XdotAttrParser().parse_xdot_data(new)<EOL>pos_x = min( [c.x for c in components] )<EOL>pos_y = min( [c.y for c in components] )<EOL>move_to_origin(components)<EOL>container = Container(auto_size=True,<EOL>position=[pos_x-self.pos[<NUM_LIT:0>], pos_y-self.pos[<NUM_LIT:1>]],<EOL>bgcolor="<STR_LIT>")<EOL>container.add( *components )<EOL>self.label_drawing = container<EOL>

Parses the label drawing directive, updating the label
            components.


if old is not None:<EOL><INDENT>self.component.remove( old )<EOL><DEDENT>if new is not None:<EOL><INDENT>new.bgcolor="<STR_LIT>"<EOL>

Handles the container of drawing components changing.


def _label_drawing_changed(self, old, new):

if old is not None:<EOL><INDENT>self.component.remove(old)<EOL><DEDENT>if new is not None:<EOL><INDENT>self.component.add(new)<EOL><DEDENT>w, h = self.component.bounds<EOL>self.component.position = [ self.pos[<NUM_LIT:0>] - (w/<NUM_LIT:2>), self.pos[<NUM_LIT:1>] - (h/<NUM_LIT:2>) ]<EOL><INDENT>self.component.position = list( self.pos )<EOL><DEDENT>self.component.request_redraw()<EOL>

Handles the container of label components changing.


w, h = self.component.bounds<EOL>self.pos = tuple([ new[<NUM_LIT:0>] + (w/<NUM_LIT:2>), new[<NUM_LIT:1>] + (h/<NUM_LIT:2>) ])<EOL>

Handles the poition of the component changing.


w, h = self.component.bounds<EOL>self.component.position = [ new[<NUM_LIT:0>] - (w/<NUM_LIT:2>), new[<NUM_LIT:1>] - (h/<NUM_LIT:2>) ]<EOL><INDENT>self.component.position = list( new )<EOL><DEDENT>self.component.request_redraw()<EOL>

Handles the Graphviz position attribute changing.


canvas = Canvas()<EOL>for tool in self.tools:<EOL><INDENT>canvas.tools.append(tool(canvas))<EOL><DEDENT>return canvas<EOL>


vp = Viewport(component=self.diagram_canvas, enable_zoom=True)<EOL>vp.view_position = [<NUM_LIT:0>,<NUM_LIT:0>]<EOL>vp.tools.append(ViewportPanTool(vp))<EOL>return vp<EOL>


logger.debug("<STR_LIT>")<EOL>canvas = self.diagram_canvas<EOL>for tool in self.tools:<EOL><INDENT>if canvas is not None:<EOL><INDENT>print("<STR_LIT>" % tool)<EOL>canvas.tools.append(tool(canvas))<EOL><DEDENT><DEDENT>


logger.debug("<STR_LIT>")<EOL>old_canvas = self.diagram_canvas<EOL><INDENT>logger.debug("<STR_LIT>" % canvas.components)<EOL>for component in canvas.components:<EOL><INDENT>canvas.remove(component)<EOL><DEDENT>logger.debug("<STR_LIT>" % canvas.components)<EOL>for component in canvas.components:<EOL><INDENT>canvas.remove(component)<EOL><DEDENT>logger.debug("<STR_LIT>" % canvas.components)<EOL>canvas.request_redraw()<EOL><DEDENT>new_canvas = Canvas()<EOL>new_canvas.copy_traits(old_canvas, ["<STR_LIT>", "<STR_LIT>"])<EOL>self.diagram_canvas = new_canvas<EOL>self.viewport.component=new_canvas<EOL>self.viewport.request_redraw()<EOL>return<EOL>


self.map_model(self.diagram.domain_model)<EOL>


def _domain_model_changed_for_diagram(self, obj, name, old, new):

if old is not None:<EOL><INDENT>self.unmap_model(old)<EOL><DEDENT>if new is not None:<EOL><INDENT>self.map_model(new)<EOL><DEDENT>


logger.debug("<STR_LIT>")<EOL>dot = Dot()<EOL>self.diagram.clear_canvas()<EOL>for node_mapping in self.nodes:<EOL><INDENT>ct = node_mapping.containment_trait<EOL>logger.debug("<STR_LIT>" % ct)<EOL>if hasattr(new, ct):<EOL><INDENT>elements = getattr(new, ct)<EOL>logger.debug("<STR_LIT>" % len(elements))<EOL>for element in elements:<EOL><INDENT>pydot_node = Node(str(id(element)))<EOL>dot_attrs = node_mapping.dot_node<EOL>if dot_attrs is not None:<EOL><INDENT>self._style_node(pydot_node, dot_attrs)<EOL><DEDENT>dot.add_node(pydot_node)<EOL>new.on_trait_change(self.map_element, ct+"<STR_LIT>")<EOL><DEDENT><DEDENT><DEDENT>logger.debug("<STR_LIT>")<EOL>xdot = graph_from_dot_data(dot.create(self.program, "<STR_LIT>"))<EOL>parser = XDotParser()<EOL>for node in xdot.get_node_list():<EOL><INDENT>diagram_node = parser.parse_node(node)<EOL>logger.debug(<EOL>"<STR_LIT>" %<EOL>(node, diagram_node)<EOL>)<EOL>if diagram_node is not None:<EOL><INDENT>for node_mapping in self.nodes: <EOL><INDENT>ct = node_mapping.containment_trait<EOL>for element in getattr(new, ct):<EOL><INDENT>if str(id(element)) == diagram_node.dot_node.get_name():<EOL><INDENT>logger.debug(<EOL>"<STR_LIT>" %<EOL>(element, diagram_node)<EOL>)<EOL>diagram_node.element = element<EOL>break<EOL><DEDENT><DEDENT>if isinstance(diagram_node.element, node_mapping.element):<EOL><INDENT>for tool in node_mapping.tools:<EOL><INDENT>logger.debug(<EOL>"<STR_LIT>" %<EOL>(tool, diagram_node)<EOL>)<EOL>diagram_node.tools.append(tool(diagram_node))<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>if diagram_node.element is None:<EOL><INDENT>logger.warning("<STR_LIT>")<EOL><DEDENT><DEDENT>self.diagram.diagram_canvas.add(diagram_node)<EOL><DEDENT><DEDENT>del parser<EOL>


for node_mapping in self.nodes:<EOL><INDENT>ct = node_mapping.containment_trait<EOL>if hasattr(old, ct):<EOL><INDENT>old_elements = getattr(old, ct)<EOL>for old_element in old_elements:<EOL><INDENT>old.on_trait_change(<EOL>self.map_element, ct+"<STR_LIT>", remove=True<EOL>)<EOL><DEDENT><DEDENT><DEDENT>


def map_element(self, obj, name, event):

canvas = self.diagram.diagram_canvas<EOL>parser = XDotParser()<EOL>for element in event.added:<EOL><INDENT>logger.debug("<STR_LIT>" % element)<EOL>for node_mapping in self.nodes:<EOL><INDENT>ct = name[:-<NUM_LIT:6>] <EOL>if node_mapping.containment_trait == ct:<EOL><INDENT>dot_attrs = node_mapping.dot_node<EOL>dot = Dot()<EOL>graph_node = Node(str(id(element)))<EOL>self._style_node(graph_node, dot_attrs)<EOL>dot.add_node(graph_node)<EOL>xdot = graph_from_dot_data(dot.create(self.program,"<STR_LIT>"))<EOL>diagram_nodes = parser.parse_nodes(xdot)<EOL>for dn in diagram_nodes:<EOL><INDENT>if dn is not None:<EOL><INDENT>dn.element = element<EOL>for tool in node_mapping.tools:<EOL><INDENT>dn.tools.append(tool(dn))<EOL><DEDENT>canvas.add(dn)<EOL>canvas.request_redraw()<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>for element in event.removed:<EOL><INDENT>logger.debug("<STR_LIT>" % element)<EOL>for component in canvas.components:<EOL><INDENT>if element == component.element:<EOL><INDENT>canvas.remove(component)<EOL>canvas.request_redraw()<EOL>break<EOL><DEDENT><DEDENT><DEDENT>

Handles mapping elements to diagram components


def _style_node(self, pydot_node, dot_attrs):


s = "<STR_LIT>"<EOL>return "<STR_LIT>" % ( s, super(Cluster, self).__str__() )<EOL>

Returns a string representation of the cluster in dot language.


return "<STR_LIT>"<EOL>


if new[:<NUM_LIT:7>].lower() != "<STR_LIT>":<EOL><INDENT>self.ID = "<STR_LIT>" % new<EOL><DEDENT>

Handles the ID changing by ensuring that it has a 'cluster' prefix.


x = event.x<EOL>y = event.y<EOL><INDENT>candidates = []<EOL><DEDENT>component = self.component<EOL><INDENT>if isinstance(component, Container):<EOL><INDENT>candidates = get_nested_components(self.component)<EOL><DEDENT>else:<EOL><INDENT>return<EOL><DEDENT>item = None<EOL>for candidate, offset in candidates:<EOL><INDENT>if candidate.is_in(x-offset[<NUM_LIT:0>], y-offset[<NUM_LIT:1>]):<EOL><INDENT>item = candidate<EOL>break<EOL><DEDENT><DEDENT><DEDENT>if hasattr(component, "<STR_LIT>"):<EOL><INDENT>if component.element is not None:<EOL><INDENT>component.active_tool = self<EOL>component.element.edit_traits(kind="<STR_LIT>")<EOL>event.handled = True<EOL>component.active_tool = None<EOL>component.request_redraw()<EOL><DEDENT><DEDENT>return<EOL>

Handles the left mouse button being double-clicked when the tool
        is in the 'normal' state.

        If the event occurred on this tool's component (or any contained
        component of that component), the method opens a Traits UI view on the
        object referenced by the 'element' trait of the component that was
        double-clicked, setting the tool as the active tool for the duration
        of the view.


x = event.x<EOL>y = event.y<EOL><INDENT>candidates = []<EOL><DEDENT>component = self.component<EOL><INDENT>if isinstance(component, Container):<EOL><INDENT>candidates = get_nested_components(self.component)<EOL><DEDENT>else:<EOL><INDENT>return<EOL><DEDENT>item = None<EOL>for candidate, offset in candidates:<EOL><INDENT>if candidate.is_in(x-offset[<NUM_LIT:0>], y-offset[<NUM_LIT:1>]):<EOL><INDENT>item = candidate<EOL>break<EOL><DEDENT><DEDENT><DEDENT>for tool in component.tools:<EOL><INDENT>component.active_tool = self<EOL>event.handled = True<EOL>component.active_tool = None<EOL>component.request_redraw()<EOL><DEDENT>return<EOL>

Handles the right mouse button being clicked when the tool is in
        the 'normal' state.

        If the event occurred on this tool's component (or any contained
        component of that component), the method opens a context menu with
        menu items from any tool of the parent component that implements
        MenuItemTool interface i.e. has a get_item() method.


self.subcommand = Status()<EOL>self.subcommand_str = "<STR_LIT:status>"<EOL>super(TestSubcommandStatus, self).setUp()<EOL>config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": "<STR_LIT>",<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": "<STR_LIT>"<EOL>}<EOL>}<EOL>}<EOL>}<EOL>self.config.update(config_data)<EOL>


self.subcommand = Update()<EOL>self.subcommand_str = "<STR_LIT>"<EOL>super(TestSubcommandUpdate, self).setUp()<EOL>config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": "<STR_LIT>",<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": "<STR_LIT>"<EOL>}<EOL>}<EOL>}<EOL>}<EOL>self.config.update(config_data)<EOL>


self.subcommand = CfgSubcommand()<EOL>self.subcommand_str = "<STR_LIT>"<EOL>super(TestSubcommandConfig, self).setUp()<EOL>


self.subcommand = Workspace()<EOL>self.subcommand_str = "<STR_LIT>"<EOL>super(TestSubcommandWorkspace, self).setUp()<EOL>config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": "<STR_LIT>",<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": "<STR_LIT>"<EOL>}<EOL>}<EOL>}<EOL>}<EOL>self.config.update(config_data)<EOL>


super(TestWorkspacesSubcommands, self).setUp()<EOL>self.subcommand = WorkspaceSubcommands(<EOL>"<STR_LIT>", self.subparser, self.config<EOL>)<EOL>self.config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": self.sandbox.path<EOL>}<EOL>}<EOL>}<EOL>self.config.update(self.config_data)<EOL>


self.subcommand = Show()<EOL>self.subcommand_str = "<STR_LIT>"<EOL>super(TestSubcommandShow, self).setUp()<EOL>config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": "<STR_LIT>",<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": self.sandbox.path + "<STR_LIT>"<EOL>}<EOL>},<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": "<STR_LIT>",<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": self.sandbox.path + "<STR_LIT>"<EOL>}<EOL>}<EOL>}<EOL>}<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.config.update(config_data)<EOL>


self.subcommand = Jump()<EOL>self.subcommand_str = "<STR_LIT>"<EOL>super(TestSubcommandJump, self).setUp()<EOL>


super(TestConfig, self).setUp()<EOL>self.filename = self.sandbox.path + "<STR_LIT>"<EOL>f = open(self.filename, "<STR_LIT:w>")<EOL>f.write("<STR_LIT>")<EOL>f.close()<EOL>


super(TestAdapterAbstract, self).setUp()<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.adapter = TestAdapter(os.path.join(self.sandbox.path, "<STR_LIT>"))<EOL>self.adapter.executable = "<STR_LIT>"<EOL>


super(TestAdapterAbstract, self).tearDown()<EOL>self.adapter = None<EOL>


super(TestGit, self).setUp(Git)<EOL>


super(TestHg, self).setUp(Hg)<EOL>


super(TestBzr, self).setUp(Bzr)<EOL>


super(TestSvn, self).setUp(Svn)<EOL>


super(TestFindPathFunction, self).setUp()<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>config_data = {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": os.path.join(self.sandbox.path, "<STR_LIT>"),<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": os.path.join(self.sandbox.path, "<STR_LIT>"),<EOL>"<STR_LIT>": os.path.join(self.sandbox.path, "<STR_LIT>"),<EOL>"<STR_LIT>": os.path.join(self.sandbox.path, "<STR_LIT>")<EOL>}<EOL>},<EOL>"<STR_LIT>": {<EOL>"<STR_LIT:path>": os.path.join(self.sandbox.path, "<STR_LIT>"),<EOL>"<STR_LIT>": {<EOL>"<STR_LIT>": os.path.join(self.sandbox.path, "<STR_LIT>"),<EOL>"<STR_LIT>": os.path.join(self.sandbox.path, "<STR_LIT>")<EOL>}<EOL>}<EOL>}<EOL>}<EOL>self.config = Config(self.sandbox.path + "<STR_LIT>")<EOL>self.config.update(config_data)<EOL>


if path is None:<EOL><INDENT>path = os.path.dirname(os.path.realpath(__file__)) + "<STR_LIT>"<EOL><DEDENT>self.path = path<EOL>if os.path.exists(path):<EOL><INDENT>self.destroy()<EOL><DEDENT>os.mkdir(path)<EOL>


os.mkdir(os.path.join(self.path, directory))<EOL>


full_path = os.path.join(self.path, file)<EOL>with open(full_path, '<STR_LIT:w>'):<EOL><INDENT>os.utime(full_path, None)<EOL><DEDENT>


if os.path.exists(self.path):<EOL><INDENT>shutil.rmtree(self.path)<EOL><DEDENT>


self.sandbox = Sandbox()<EOL>


self.sandbox.destroy()<EOL>


def assert_config_file_contains(self, config_file, expected):

file = open(config_file)<EOL>config = yaml.safe_load(file.read())<EOL>file.close()<EOL>self.assertEqual(config, expected)<EOL>

Custom assert to check content of config_file.


super(SubcommandTestHelper, self).setUp()<EOL>self.parser = argparse.ArgumentParser(prog="<STR_LIT>")<EOL>self.subparser = self.parser.add_subparsers(dest="<STR_LIT>")<EOL>self.config = Config(self.sandbox.path + "<STR_LIT>")<EOL>if self.subcommand is not None and self.subcommand_str is not None:<EOL><INDENT>self.subcommand.setup(<EOL>self.subcommand_str, self.config, self.subparser)<EOL><DEDENT>


super(SubcommandTestHelper, self).tearDown()<EOL>self.parser = None<EOL>self.subcommand = None<EOL>


def assert_subcommand_parsing(self, commands, expected):

self.subcommand.parse()<EOL>args = self.parser.parse_args(commands)<EOL>for k, v in expected.items():<EOL><INDENT>self.assertEqual(v, getattr(args, k))<EOL><DEDENT>

This method provides a way to assert subcommand parsing.


def assert_subcommand_parsing_raises_error(self, commands, error_expected):

self.subcommand.parse()<EOL>self.assertRaises(<EOL>error_expected,<EOL>self.parser.parse_args, commands<EOL>)<EOL>


def assert_subcommand_exec_raises_error(self, args, error_expected):

self.subcommand.parse()<EOL>self.assertRaises(error_expected, self.subcommand.execute, args)<EOL>

Asserts an error when execute subcommand with passed args.


super(AdapterTestHelper, self).setUp()<EOL>self.sandbox.mkdir("<STR_LIT>")<EOL>self.adapter = adapter(<EOL>os.path.join(self.sandbox.path, "<STR_LIT>"))<EOL>self.adapter.execute = Mock(return_value=None)<EOL>


super(AdapterTestHelper, self).tearDown()<EOL>self.adapter = None<EOL>


def assert_executed_command(self, expected_cmd, with_path=True):

Assert that adapter has executed expected command.


self.mocks["<STR_LIT>"] = Mock()<EOL>self.mocks["<STR_LIT>"].setup.return_value = None<EOL>self.mocks["<STR_LIT>"].parse.return_value = None<EOL>self.mocks["<STR_LIT>"].execute.return_value = None<EOL>self.mocks["<STR_LIT>"] = Mock()<EOL>self.subcmds = Subcommands(self.mocks["<STR_LIT>"])<EOL>


self.mocks = None<EOL>self.subcmds = None<EOL>


def __init__(self, config_file, *args, **kwargs):

self.config_file = config_file<EOL>super(Config, self).__init__(*args, **kwargs)<EOL>if not os.path.exists(config_file) or config_file is None:<EOL><INDENT>self.update({"<STR_LIT>": {}})<EOL>self.write()<EOL><DEDENT>else:<EOL><INDENT>file = open(self.config_file)<EOL>config = yaml.safe_load(file.read())<EOL>file.close()<EOL>if config is not None:<EOL><INDENT>self.update(config)<EOL><DEDENT><DEDENT>


file = open(self.config_file, "<STR_LIT>")<EOL>file.write(yaml.dump(dict(self), default_flow_style=False))<EOL>file.close()<EOL>

Write config in configuration file.
Data must me a dict.


parser = self.subparser.add_parser(<EOL>"<STR_LIT>",<EOL>help="<STR_LIT>",<EOL>description="<STR_LIT>")<EOL>group = parser.add_mutually_exclusive_group(required=True)<EOL>group.add_argument('<STR_LIT>', action='<STR_LIT:store_true>', help="<STR_LIT>")<EOL>group.add_argument('<STR_LIT:name>', type=str, help="<STR_LIT>", nargs='<STR_LIT:?>')<EOL>


if args.name is not None:<EOL><INDENT>self.print_workspace(args.name)<EOL><DEDENT>elif args.all is not None:<EOL><INDENT>self.print_all()<EOL><DEDENT>


for ws_name in self.config['<STR_LIT>']:<EOL><INDENT>self.print_workspace(ws_name)<EOL><DEDENT>


path_list = find_path(name, self.config)<EOL>if len(path_list) == <NUM_LIT:0>:<EOL><INDENT>self.logger.error("<STR_LIT>" % name)<EOL>return False<EOL><DEDENT>for name, path in path_list.items():<EOL><INDENT>self.print_update(name, path)<EOL><DEDENT>


def print_update(self, repo_name, repo_path):

color = Color()<EOL>self.logger.info(color.colored(<EOL>"<STR_LIT>" % (repo_name, repo_path), "<STR_LIT>"))<EOL>try:<EOL><INDENT>repo = Repository(repo_path)<EOL>repo.update()<EOL><DEDENT>except RepositoryError as e:<EOL><INDENT>self.logger.error(e)<EOL>pass<EOL><DEDENT>print("<STR_LIT:\n>")<EOL>


def __init__(self, name, subparser, config):

self.name = name<EOL>self.parser = subparser.add_parser(<EOL>name,<EOL>description="<STR_LIT>" % name)<EOL>self.config = config<EOL>


if args.name is not None:<EOL><INDENT>self.show_workspace(slashes2dash(args.name))<EOL><DEDENT>elif args.all is not None:<EOL><INDENT>self.show_all()<EOL><DEDENT>


if not self.workspace.exists(name):<EOL><INDENT>raise ValueError("<STR_LIT>" % name)<EOL><DEDENT>color = Color()<EOL>workspaces = self.workspace.list()<EOL>self.logger.info("<STR_LIT>" % color.colored(name, "<STR_LIT>"))<EOL>self.logger.info("<STR_LIT>" % workspaces[name]["<STR_LIT:path>"])<EOL>self.logger.info("<STR_LIT>"<EOL>% color.colored(<EOL>len(workspaces[name]["<STR_LIT>"]),<EOL>"<STR_LIT>"))<EOL>repo_colored = color.colored("<STR_LIT>", "<STR_LIT>")<EOL>path_colored = color.colored("<STR_LIT>", "<STR_LIT>")<EOL>trepositories = PrettyTable(<EOL>[repo_colored, path_colored, color.colored("<STR_LIT:+>", "<STR_LIT>")])<EOL>trepositories.align[repo_colored] = "<STR_LIT:l>"<EOL>trepositories.align[path_colored] = "<STR_LIT:l>"<EOL>for repo_name in workspaces[name]["<STR_LIT>"]:<EOL><INDENT>fullname = "<STR_LIT>" % (name, repo_name)<EOL>fullpath = find_path(fullname, self.config)[fullname]<EOL>try:<EOL><INDENT>repo = Repository(fullpath)<EOL>repo_scm = repo.get_scm()<EOL><DEDENT>except RepositoryAdapterNotFound:<EOL><INDENT>repo_scm = None<EOL><DEDENT>trepositories.add_row(<EOL>[color.colored(repo_name, "<STR_LIT>"), fullpath, repo_scm])<EOL><DEDENT>self.logger.info(trepositories)<EOL>


for ws in self.workspace.list().keys():<EOL><INDENT>self.show_workspace(ws)<EOL>print("<STR_LIT>")<EOL><DEDENT>


parser = self.subparser.add_parser(<EOL>"<STR_LIT:status>",<EOL>help="<STR_LIT>",<EOL>description="<STR_LIT>")<EOL>group = parser.add_mutually_exclusive_group(required=True)<EOL>group.add_argument('<STR_LIT>', action='<STR_LIT:store_true>', help="<STR_LIT>")<EOL>group.add_argument('<STR_LIT:name>', type=str, help="<STR_LIT>", nargs='<STR_LIT:?>')<EOL>


path_list = find_path(name, self.config)<EOL>if len(path_list) == <NUM_LIT:0>:<EOL><INDENT>self.logger.error("<STR_LIT>" % name)<EOL>return False<EOL><DEDENT>for name, path in path_list.items():<EOL><INDENT>self.print_status(name, path)<EOL><DEDENT>


def print_status(self, repo_name, repo_path):

color = Color()<EOL>self.logger.info(color.colored(<EOL>"<STR_LIT>" % (repo_name, repo_path), "<STR_LIT>"))<EOL>try:<EOL><INDENT>repo = Repository(repo_path)<EOL>repo.status()<EOL><DEDENT>except RepositoryError as e:<EOL><INDENT>self.logger.error(e)<EOL>pass<EOL><DEDENT>print("<STR_LIT:\n>")<EOL>


requirement = pkg_resources.Requirement.parse("<STR_LIT>")<EOL>provider = pkg_resources.get_provider(requirement)<EOL>return provider.version<EOL>


return self.exec_on_path("<STR_LIT>" % self.executable)<EOL>


return self.execute("<STR_LIT>" % (self.executable,<EOL>url, self.path))<EOL>


raise NotImplemented("<STR_LIT>")<EOL>


return self.exec_on_path("<STR_LIT>")<EOL>


return self.execute("<STR_LIT>" % (url, self.path))<EOL>


logger = logging.getLogger(__name__)<EOL>self.check_executable()<EOL>logger.debug("<STR_LIT>" % (command, path))<EOL>process = subprocess.Popen(<EOL>command,<EOL>shell=True,<EOL>cwd=path,<EOL>stdout=subprocess.PIPE,<EOL>stderr=subprocess.PIPE<EOL>)<EOL>stdout, stderr = process.communicate()<EOL>exit_code = process.wait()<EOL>if stdout:<EOL><INDENT>logger.info(stdout.decode("<STR_LIT:utf-8>"))<EOL><DEDENT>if stderr:<EOL><INDENT>if exit_code != <NUM_LIT:0>:<EOL><INDENT>logger.error(stderr.decode("<STR_LIT:utf-8>"))<EOL><DEDENT>else:<EOL><INDENT>logger.info(stderr.decode("<STR_LIT:utf-8>"))<EOL><DEDENT><DEDENT>return process<EOL>

Execute command with os.popen and return output.


self.execute("<STR_LIT:%s>" % command, self.path)<EOL>


if not find_executable(self.executable):<EOL><INDENT>raise ExecutableNotFoundException(<EOL>"<STR_LIT>" % self.executable<EOL>)<EOL><DEDENT>


def find_path(name, config, wsonly=False):

workspace = Workspace(config)<EOL>config = config["<STR_LIT>"]<EOL>path_list = {}<EOL>if name.find('<STR_LIT:/>') != -<NUM_LIT:1>:<EOL><INDENT>wsonly = False<EOL>try:<EOL><INDENT>ws, repo = name.split('<STR_LIT:/>')<EOL><DEDENT>except ValueError:<EOL><INDENT>raise ValueError("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>if (workspace.exists(ws)):<EOL><INDENT>if (repo in config[ws]["<STR_LIT>"]):<EOL><INDENT>path_name = "<STR_LIT>" % (ws, repo)<EOL>path_list[path_name] = config[ws]["<STR_LIT>"][repo]<EOL><DEDENT><DEDENT><DEDENT>for ws_name, ws in sorted(config.items()):<EOL><INDENT>if (name == ws_name):<EOL><INDENT>if wsonly is True:<EOL><INDENT>return {ws_name: ws["<STR_LIT:path>"]}<EOL><DEDENT>repositories = sorted(config[ws_name]["<STR_LIT>"].items())<EOL>for name, path in repositories:<EOL><INDENT>path_list["<STR_LIT>" % (ws_name, name)] = path<EOL><DEDENT>break<EOL><DEDENT>for repo_name, repo_path in sorted(ws["<STR_LIT>"].items()):<EOL><INDENT>if (repo_name == name):<EOL><INDENT>path_list["<STR_LIT>" % (ws_name, repo_name)] = repo_path<EOL><DEDENT><DEDENT><DEDENT>return path_list<EOL>

Find path for given workspace and|or repository.


return string.replace("<STR_LIT:/>", "<STR_LIT:->")<EOL>

Replace slashes in given string to dash.


adapter = None<EOL>if url[:<NUM_LIT:4>] == "<STR_LIT>" or url[-<NUM_LIT:4>:] == "<STR_LIT>":<EOL><INDENT>adapter = Git(path)<EOL><DEDENT>if url[:<NUM_LIT:6>] == "<STR_LIT>":<EOL><INDENT>adapter = Svn(path)<EOL><DEDENT>if url[:<NUM_LIT:6>] == "<STR_LIT>":<EOL><INDENT>adapter = Bzr(path)<EOL><DEDENT>if url[:<NUM_LIT:9>] == "<STR_LIT>":<EOL><INDENT>adapter = Hg(path)<EOL><DEDENT>if adapter is None:<EOL><INDENT>raise RepositoryAdapterNotFound(<EOL>"<STR_LIT>" % url)<EOL><DEDENT>return adapter.clone(url)<EOL>


if self.adapter is None:<EOL><INDENT>return None<EOL><DEDENT>return self.adapter.__class__.__name__<EOL>


if not isinstance(config, Config):<EOL><INDENT>raise TypeError("<STR_LIT>")<EOL><DEDENT>self.config = config<EOL>


if not (os.path.exists(path)):<EOL><INDENT>raise ValueError("<STR_LIT>" % path)<EOL><DEDENT>if (self.exists(name)):<EOL><INDENT>raise ValueError("<STR_LIT>" % name)<EOL><DEDENT>self.config["<STR_LIT>"][name] = {"<STR_LIT:path>": path, "<STR_LIT>": {}}<EOL>self.config.write()<EOL>

Add a workspace entry in user config file.


if not (self.exists(name)):<EOL><INDENT>raise ValueError("<STR_LIT>" % name)<EOL><DEDENT>self.config["<STR_LIT>"].pop(name, <NUM_LIT:0>)<EOL>self.config.write()<EOL>


ws_list = {}<EOL>for key, value in self.config["<STR_LIT>"].items():<EOL><INDENT>ws_list[key] = dict({"<STR_LIT:name>": key}, **value)<EOL><DEDENT>return ws_list<EOL>


ws_list = self.list()<EOL>return ws_list[name] if name in ws_list else None<EOL>

Get workspace infos from name.
Return None if workspace doesn't exists.


return name in self.list().keys()<EOL>


def repository_exists(self, workspace, repo):

if not self.exists(workspace):<EOL><INDENT>return False<EOL><DEDENT>workspaces = self.list()<EOL>return repo in workspaces[workspace]["<STR_LIT>"]<EOL>

Return True if workspace contains repository name.


path = self.config["<STR_LIT>"][ws_name]["<STR_LIT:path>"]<EOL>repositories = self.config["<STR_LIT>"][ws_name]["<STR_LIT>"]<EOL>logger = logging.getLogger(__name__)<EOL>color = Color()<EOL>for r in os.listdir(path):<EOL><INDENT>try:<EOL><INDENT>repo = Repository(os.path.join(path, r))<EOL><DEDENT>except RepositoryError:<EOL><INDENT>continue<EOL><DEDENT>else:<EOL><INDENT>repositories[r] = repo.path<EOL><DEDENT><DEDENT>for repo_name, path in repositories.items():<EOL><INDENT>logger.info(color.colored(<EOL>"<STR_LIT>" % repo_name, "<STR_LIT>"))<EOL><DEDENT>self.config["<STR_LIT>"][ws_name]["<STR_LIT>"]<EOL>self.config.write()<EOL>


handler = logging.FileHandler(logfile)<EOL>handler.setLevel(logging.NOTSET)<EOL>handler.setFormatter(Formatter(FORMAT))<EOL>self.addHandler(handler)<EOL>


console = logging.StreamHandler()<EOL>console.setFormatter(Formatter(LFORMAT))<EOL>if not debug:<EOL><INDENT>console.setLevel(logging.INFO)<EOL><DEDENT>self.addHandler(console)<EOL>


pass<EOL>


pass<EOL>

>>> # xdoctest: +REQUIRES(--demo-failure)
>>> assert False, 'this test should fail'


pass<EOL>

>>> if True:
>>>     print('doctest requires a special ... prefix')
doctest requires a special ... prefix


pass<EOL>

>>> x = '''
    xdoctest is good at dealing with triple quoted strings
    you don't even need to have the >>> prefix, because the
    AST knows you are in a string context
    '''
>>> print(x)
xdoctest is good at dealing with triple quoted strings
you don't even need to have the >>> prefix, because the
AST knows you are in a string context


pass<EOL>

>>> print('In builtin doctest you have to handle stdout on EVERY line')
>>> print('But in xdoctest its no problem')
In builtin doctest you have to handle stdout on EVERY line
But in xdoctest its no problem


pass<EOL>

>>> print('but sometimes repl is good')
but sometimes repl is good
>>> print('thats ok, we support it')
thats ok, we support it


This is one corner case, where doctest can do something xdoctest cannot.

>>> for i in range(2):
...     '%s' % i
...
'0'
'1'


However, xdoctest can handle this as long as you print to stdout

>>> for i in range(2):
...     print('%s' % i)
...
0
1


pass<EOL>

CommandLine:
    xdoctest -m ~/code/xdoctest/dev/demo_errors.py demo1

Example:
    >>> raise Exception('demo1')


pass<EOL>

CommandLine:
    xdoctest -m ~/code/xdoctest/dev/demo_errors.py demo2

Example:
    >>> print('error on different line')
    >>> raise Exception('demo2')


pass<EOL>

CommandLine:
    xdoctest -m ~/code/xdoctest/dev/demo_errors.py demo3

Example:
    >>> print('demo5')
    demo3


pass<EOL>

CommandLine:
    xdoctest -m ~/code/xdoctest/dev/demo_errors.py Demo5.demo5

Example:
    >>> raise Exception


import redbaron<EOL>import ubelt as ub<EOL>conf_path = '<STR_LIT>'<EOL>source = ub.readfrom(conf_path)<EOL>red = redbaron.RedBaron(source)<EOL>extra_extensions = [<EOL>'<STR_LIT>'<EOL>]<EOL>ext_node = red.find('<STR_LIT:name>', value='<STR_LIT>').parent<EOL>ext_node.value.value.extend(extra_extensions)<EOL>theme_node = red.find('<STR_LIT:name>', value='<STR_LIT>').parent<EOL>theme_node.value.value = '<STR_LIT>'<EOL>ub.writeto(conf_path, red.dumps())<EOL>


from xdoctest import static_analysis as static<EOL>from xdoctest import utils  <EOL>module = utils.import_module_from_path(modpath)<EOL>calldefs = {}<EOL>if getattr(module, '<STR_LIT>'):<EOL><INDENT>calldefs['<STR_LIT>'] = static.CallDefNode(<EOL>callname='<STR_LIT>',<EOL>docstr=module.__doc__,<EOL>lineno=<NUM_LIT:0>,<EOL>doclineno=<NUM_LIT:1>,<EOL>doclineno_end=<NUM_LIT:1>,<EOL>args=None<EOL>)<EOL><DEDENT>for key, val in iter_module_doctestables(module):<EOL><INDENT>if hasattr(val, '<STR_LIT>') and hasattr(val, '<STR_LIT>'):<EOL><INDENT>calldefs[key] = static.CallDefNode(<EOL>callname=val.__name__,<EOL>docstr=val.__doc__,<EOL>lineno=<NUM_LIT:0>,<EOL>doclineno=<NUM_LIT:1>,<EOL>doclineno_end=<NUM_LIT:1>,<EOL>args=None<EOL>)<EOL><DEDENT><DEDENT>return calldefs<EOL>

Dynamic parsing of module doctestable items.

While this does execute module code it is needed for testing extension
libraries.

CommandLine:
    python -m xdoctest.dynamic_analysis parse_dynamic_calldefs

Example:
    >>> from xdoctest import dynamic_analysis
    >>> module = dynamic_analysis
    >>> calldefs = parse_dynamic_calldefs(module.__file__)
    >>> for key, calldef in sorted(calldefs.items()):
    ...     print('key = {!r}'.format(key))
    ...     print(' * calldef.callname = {}'.format(calldef.callname))
    ...     if calldef.docstr is None:
    ...         print(' * len(calldef.docstr) = {}'.format(calldef.docstr))
    ...     else:
    ...         print(' * len(calldef.docstr) = {}'.format(len(calldef.docstr)))


frame_cur = inspect.currentframe()<EOL>for ix in range(n + <NUM_LIT:1>):<EOL><INDENT>frame_next = frame_cur.f_back<EOL>if frame_next is None:  <EOL><INDENT>if strict:<EOL><INDENT>raise AssertionError('<STR_LIT>' % ix)<EOL><DEDENT>else:<EOL><INDENT>break<EOL><DEDENT><DEDENT>frame_cur = frame_next<EOL><DEDENT>return frame_cur<EOL>

Gets the current stack frame or any of its ancestors dynamically

Args:
    n (int): n=0 means the frame you called this function in.
             n=1 is the parent frame.
    strict (bool): (default = True)

Returns:
    frame: frame_cur

Example:
    >>> frame_cur = get_stack_frame(n=0)
    >>> print('frame_cur = %r' % (frame_cur,))
    >>> assert frame_cur.f_globals['frame_cur'] is frame_cur


parent_frame = get_stack_frame(n=n + <NUM_LIT:2>)<EOL>return parent_frame<EOL>

Returns the frame of that called you.
This is equivalent to `get_stack_frame(n=1)`

Args:
    n (int): n=0 means the frame you called this function in.
             n=1 is the parent frame.

Returns:
    frame: parent_frame

Example:
    >>> root0 = get_stack_frame(n=0)
    >>> def foo():
    >>>     child = get_stack_frame(n=0)
    >>>     root1 = get_parent_frame(n=0)
    >>>     root2 = get_stack_frame(n=1)
    >>>     return child, root1, root2
    >>> # Note this wont work in IPython because several
    >>> # frames will be inserted between here and foo
    >>> child, root1, root2 = foo()
    >>> print('root0 = %r' % (root0,))
    >>> print('root1 = %r' % (root1,))
    >>> print('root2 = %r' % (root2,))
    >>> print('child = %r' % (child,))
    >>> assert root0 == root1
    >>> assert root1 == root2
    >>> assert child != root1


from xdoctest import static_analysis as static<EOL>target_modname = module.__name__<EOL>flag = False<EOL>if isinstance(item, types.ModuleType):<EOL><INDENT>if not hasattr(item, '<STR_LIT>'):<EOL><INDENT>try:<EOL><INDENT>name = static.modpath_to_modname(module.__file__)<EOL>flag = name in str(item)<EOL><DEDENT>except:<EOL><INDENT>flag = False<EOL><DEDENT><DEDENT>else:<EOL><INDENT>item_modpath = os.path.realpath(os.path.dirname(item.__file__))<EOL>mod_fpath = module.__file__.replace('<STR_LIT>', '<STR_LIT>')<EOL>if not mod_fpath.endswith('<STR_LIT>'):<EOL><INDENT>flag = False<EOL><DEDENT>else:<EOL><INDENT>modpath = os.path.realpath(os.path.dirname(mod_fpath))<EOL>modpath = modpath.replace('<STR_LIT>', '<STR_LIT>')<EOL>flag = item_modpath.startswith(modpath)<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>if isinstance(item, property):<EOL><INDENT>item = item.fget<EOL><DEDENT>if isinstance(item, staticmethod):<EOL><INDENT>item = item.__func__<EOL><DEDENT>if isinstance(item, classmethod):<EOL><INDENT>item = item.__func__<EOL><DEDENT>if getattr(item, '<STR_LIT>', None) == target_modname:<EOL><INDENT>flag = True<EOL><DEDENT>elif hasattr(item, '<STR_LIT>'):<EOL><INDENT>parent = item.__objclass__<EOL>if getattr(parent, '<STR_LIT>', None) == target_modname:<EOL><INDENT>flag = True<EOL><DEDENT><DEDENT>if not flag:<EOL><INDENT>try:<EOL><INDENT>item_modname = _func_globals(item)['<STR_LIT>']<EOL>if item_modname == target_modname:<EOL><INDENT>flag = True<EOL><DEDENT><DEDENT>except  AttributeError:<EOL><INDENT>pass<EOL><DEDENT><DEDENT><DEDENT>return flag<EOL>

Check if item is directly defined by a module.

This check may not always work, especially for decorated functions.

Example:
    >>> from xdoctest import dynamic_analysis
    >>> item = dynamic_analysis.is_defined_by_module
    >>> module = dynamic_analysis
    >>> assert is_defined_by_module(item, module)
    >>> item = dynamic_analysis.six
    >>> assert not is_defined_by_module(item, module)
    >>> item = dynamic_analysis.six.print_
    >>> assert not is_defined_by_module(item, module)
    >>> assert not is_defined_by_module(print, module)
    >>> import _ctypes
    >>> item = _ctypes.Array
    >>> module = _ctypes
    >>> assert is_defined_by_module(item, module)
    >>> item = _ctypes.CFuncPtr.restype
    >>> module = _ctypes
    >>> assert is_defined_by_module(item, module)


def check_got_vs_want(want, got_stdout, got_eval=constants.NOT_EVALED,<EOL>runstate=None):

<EOL>if got_eval is constants.NOT_EVALED:<EOL><INDENT>got = got_stdout<EOL>flag = check_output(got, want, runstate)<EOL><DEDENT>else:<EOL><INDENT>if not got_stdout:<EOL><INDENT>got = repr(got_eval)<EOL>flag = check_output(got, want, runstate)<EOL><DEDENT>else:<EOL><INDENT>got = got_stdout<EOL>flag = check_output(got, want, runstate)<EOL>if not flag:<EOL><INDENT>got = repr(got_eval)<EOL>flag = check_output(got, want, runstate)<EOL>if not flag:<EOL><INDENT>got = got_stdout<EOL><DEDENT><DEDENT><DEDENT><DEDENT>if not flag:<EOL><INDENT>msg = '<STR_LIT>'<EOL>ex = GotWantException(msg, got, want)<EOL>raise ex<EOL><DEDENT>return flag<EOL>

Determines to check against either got_stdout or got_eval, and then does
the comparison.

If both stdout and eval "got" outputs are specified, then the "want"
target may match either value.

Args:
    want (str): target to match against
    got_stdout (str): output from stdout
    got_eval (str): output from an eval statement.

Raises:
    GotWantException - If the "got" differs from this parts want.


def check_exception(exc_got, want, runstate=None):

exc_want = extract_exc_want(want)<EOL>if exc_want is None:<EOL><INDENT>raise<EOL><DEDENT>flag = check_output(exc_got, exc_want, runstate)<EOL>if not flag and runstate['<STR_LIT>']:<EOL><INDENT>exc_got1 = _strip_exception_details(exc_got)<EOL>exc_want1 = _strip_exception_details(exc_want)<EOL>flag = check_output(exc_got1, exc_want1, runstate)<EOL>if flag:<EOL><INDENT>exc_got = exc_got1<EOL>exc_want = exc_want1<EOL><DEDENT><DEDENT>if not flag:<EOL><INDENT>msg = '<STR_LIT>'<EOL>ex = GotWantException(msg, exc_got, exc_want)<EOL>raise ex<EOL><DEDENT>return flag<EOL>

Checks want against an exception

Raises:
    GotWantException - If the "got" differs from this parts want.


def check_output(got, want, runstate=None):

if not want:  <EOL><INDENT>return True<EOL><DEDENT>if want:<EOL><INDENT>if got == want:<EOL><INDENT>return True<EOL><DEDENT>if runstate is None:<EOL><INDENT>runstate = directive.RuntimeState()<EOL><DEDENT>got, want = normalize(got, want, runstate)<EOL>return _check_match(got, want, runstate)<EOL><DEDENT>return False<EOL>

Does the actual comparison between `got` and `want`


if ELLIPSIS_MARKER not in want:<EOL><INDENT>return want == got<EOL><DEDENT>ws = re.split(r'<STR_LIT>'.format(re.escape(ELLIPSIS_MARKER)), want,<EOL>flags=re.MULTILINE)<EOL>assert len(ws) >= <NUM_LIT:2><EOL>startpos, endpos = <NUM_LIT:0>, len(got)<EOL>w = ws[<NUM_LIT:0>]<EOL>if w:   <EOL><INDENT>if got.startswith(w):<EOL><INDENT>startpos = len(w)<EOL>del ws[<NUM_LIT:0>]<EOL><DEDENT>else:<EOL><INDENT>return False<EOL><DEDENT><DEDENT>w = ws[-<NUM_LIT:1>]<EOL>if w:   <EOL><INDENT>if got.endswith(w):<EOL><INDENT>endpos -= len(w)<EOL>del ws[-<NUM_LIT:1>]<EOL><DEDENT>else:<EOL><INDENT>return False<EOL><DEDENT><DEDENT>if startpos > endpos:<EOL><INDENT>return False<EOL><DEDENT>for w in ws:<EOL><INDENT>startpos = got.find(w, startpos, endpos)<EOL>if startpos < <NUM_LIT:0>:<EOL><INDENT>return False<EOL><DEDENT>startpos += len(w)<EOL><DEDENT>return True<EOL>

r"""
    The ellipsis matching algorithm taken directly from standard doctest.

    Worst-case linear-time ellipsis matching.

    CommandLine:
        python -m xdoctest.checker _ellipsis_match

    Example:
        >>> _ellipsis_match('aaa', 'aa...aa')
        False
        >>> _ellipsis_match('anything', '...')
        True
        >>> _ellipsis_match('prefix-anything', 'prefix-...')
        True
        >>> _ellipsis_match('suffix-anything', 'prefix-...')
        False
        >>> _ellipsis_match('foo', '... foo')
        True
        >>> _ellipsis_match('took=3.4s', 'took=...s')
        True
        >>> _ellipsis_match('best=3.4s ave=4.5s', 'best=...s ave=...s')
        True
        >>> _ellipsis_match('took: 1.16e-05 s\nbest=9.63e-07 s ave=1.002e-06 ± 3e-08 s\n',
        >>>                 'took: ...s\nbest=...s ave=...s\n')
        True


def normalize(got, want, runstate=None):

if runstate is None:<EOL><INDENT>runstate = directive.RuntimeState()<EOL><DEDENT>def remove_prefixes(regex, text):<EOL><INDENT>return re.sub(regex, r'<STR_LIT>', text)<EOL><DEDENT>def visible_text(lines):<EOL><INDENT>return [line for line in lines if not line.endswith('<STR_LIT:\r>')]<EOL><DEDENT>if True:<EOL><INDENT>got = utils.strip_ansi(got)<EOL>want = utils.strip_ansi(want)<EOL><DEDENT>if True:<EOL><INDENT>got = remove_prefixes(unicode_literal_re, got)<EOL>want = remove_prefixes(unicode_literal_re, want)<EOL>got = remove_prefixes(bytes_literal_re, got)<EOL>want = remove_prefixes(bytes_literal_re, want)<EOL><DEDENT>if not runstate['<STR_LIT>']:<EOL><INDENT>want = remove_blankline_marker(want)<EOL><DEDENT>got = re.sub(TRAILING_WS, '<STR_LIT>', got)<EOL>want = re.sub(TRAILING_WS, '<STR_LIT>', want)<EOL>want = want.rstrip()<EOL>got = got.rstrip()<EOL>got_lines = got.splitlines(True)<EOL>want_lines = want.splitlines(True)<EOL>got_lines = visible_text(got_lines)<EOL>want_lines = visible_text(want_lines)<EOL>want = '<STR_LIT>'.join(want_lines)<EOL>got = '<STR_LIT>'.join(got_lines)<EOL>if runstate['<STR_LIT>'] or runstate['<STR_LIT>']:<EOL><INDENT>got = '<STR_LIT:U+0020>'.join(got.split())<EOL>want = '<STR_LIT:U+0020>'.join(want.split())<EOL><DEDENT>if runstate['<STR_LIT>']:<EOL><INDENT>got = re.sub(r'<STR_LIT>', '<STR_LIT>', got, flags=re.MULTILINE)<EOL>want = re.sub(r'<STR_LIT>', '<STR_LIT>', want, flags=re.MULTILINE)<EOL><DEDENT>if runstate['<STR_LIT>']:<EOL><INDENT>def norm_repr(a, b):<EOL><INDENT>if not _check_match(a, b, runstate):<EOL><INDENT>for q in ['<STR_LIT:">', "<STR_LIT:'>"]:<EOL><INDENT>if a.startswith(q) and a.endswith(q):<EOL><INDENT>if _check_match(a[<NUM_LIT:1>:-<NUM_LIT:1>], b, runstate):<EOL><INDENT>return a[<NUM_LIT:1>:-<NUM_LIT:1>]<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return a<EOL><DEDENT>got = norm_repr(got, want)<EOL>want = norm_repr(want, got)<EOL><DEDENT>return got, want<EOL>

r"""
    Adapated from doctest_nose_plugin.py from the nltk project:
        https://github.com/nltk/nltk

    Further extended to also support byte literals.

    Example:
        >>> want = "...\n(0, 2, {'weight': 1})\n(0, 3, {'weight': 2})"
        >>> got = "(0, 2, {'weight': 1})\n(0, 3, {'weight': 2})"


pos_lb = '<STR_LIT>'  <EOL>blankline_pattern = '<STR_LIT:|>'.join([<EOL>'<STR_LIT>', '<STR_LIT>',<EOL>'<STR_LIT>', '<STR_LIT>']).format(<EOL>marker=BLANKLINE_MARKER, pos_lb=pos_lb)<EOL>new_text = re.sub(blankline_pattern, '<STR_LIT:\n>', text, re.MULTILINE)<EOL>return new_text<EOL>

r"""
    Example:
        >>> text1 = 'foo\n{}\nbar'.format(BLANKLINE_MARKER)
        >>> text2 = '{}\nbar'.format(BLANKLINE_MARKER)
        >>> text4 = 'foo\n{}'.format(BLANKLINE_MARKER)
        >>> text3 = '{}'.format(BLANKLINE_MARKER)
        >>> text5 = text1 + text1 + text1
        >>> assert BLANKLINE_MARKER not in remove_blankline_marker(text1)
        >>> assert BLANKLINE_MARKER not in remove_blankline_marker(text2)
        >>> assert BLANKLINE_MARKER not in remove_blankline_marker(text3)
        >>> assert BLANKLINE_MARKER not in remove_blankline_marker(text4)
        >>> assert BLANKLINE_MARKER not in remove_blankline_marker(text5)


def output_difference(self, runstate=None, colored=True):

got = self.got<EOL>want = self.want<EOL>if runstate is None:<EOL><INDENT>runstate = directive.RuntimeState()<EOL><DEDENT>runstate_ = runstate.to_dict()<EOL>runstate_['<STR_LIT>'] = False<EOL>runstate_['<STR_LIT>'] = False<EOL>got, want = normalize(got, want, runstate_)<EOL>got = utils.ensure_unicode(got)<EOL>if self._do_a_fancy_diff(runstate):<EOL><INDENT>want_lines = want.splitlines(True)<EOL>got_lines = got.splitlines(True)<EOL>if runstate['<STR_LIT>']:<EOL><INDENT>diff = difflib.unified_diff(want_lines, got_lines, n=<NUM_LIT:2>)<EOL>diff = list(diff)[<NUM_LIT:2>:]  <EOL>kind = '<STR_LIT>'<EOL><DEDENT>elif runstate['<STR_LIT>']:<EOL><INDENT>diff = difflib.context_diff(want_lines, got_lines, n=<NUM_LIT:2>)<EOL>diff = list(diff)[<NUM_LIT:2>:]  <EOL>kind = '<STR_LIT>'<EOL><DEDENT>elif runstate['<STR_LIT>']:<EOL><INDENT>engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK)<EOL>diff = list(engine.compare(want_lines, got_lines))<EOL>kind = '<STR_LIT>'<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>diff = [line.rstrip() + '<STR_LIT:\n>' for line in diff]<EOL>diff_text = '<STR_LIT>'.join(diff)<EOL>if colored:<EOL><INDENT>diff_text = utils.highlight_code(diff_text, lexer_name='<STR_LIT>')<EOL><DEDENT>text = '<STR_LIT>' % kind + utils.indent(diff_text)<EOL><DEDENT>else:<EOL><INDENT>if want and got:<EOL><INDENT>if colored:<EOL><INDENT>got = utils.color_text(got, '<STR_LIT>')<EOL>want = utils.color_text(want, '<STR_LIT>')<EOL><DEDENT>text = '<STR_LIT>' % (<EOL>utils.indent(want), utils.indent(got))<EOL><DEDENT>elif want:<EOL><INDENT>if colored:<EOL><INDENT>got = utils.color_text(got, '<STR_LIT>')<EOL>want = utils.color_text(want, '<STR_LIT>')<EOL><DEDENT>text = '<STR_LIT>' % utils.indent(want)<EOL><DEDENT>elif got:  <EOL><INDENT>raise AssertionError('<STR_LIT>')<EOL>text = '<STR_LIT>' % utils.indent(got)<EOL><DEDENT>else:  <EOL><INDENT>raise AssertionError('<STR_LIT>')<EOL>text = '<STR_LIT>'<EOL><DEDENT><DEDENT>return text<EOL>

Return a string describing the differences between the expected output
for a given example (`example`) and the actual output (`got`).
The `runstate` contains option flags used to compare `want` and `got`.

Notes:
    This does not check if got matches want, it only outputs the raw
    differences. Got/Want normalization may make the differences appear
    more exagerated than they are.


minimal_got = self.got.rstrip()<EOL>minimal_want = self.want.rstrip()<EOL>if runstate is None:<EOL><INDENT>runstate = directive.RuntimeState()<EOL><DEDENT>runstate_ = runstate.to_dict()<EOL>if not runstate_['<STR_LIT>']:<EOL><INDENT>minimal_want = remove_blankline_marker(minimal_want)<EOL><DEDENT>lines = [<EOL>('<STR_LIT>'),<EOL>('<STR_LIT>'.format(minimal_got)),<EOL>('<STR_LIT>'.format(minimal_want)),<EOL>]<EOL>return '<STR_LIT:\n>'.join(lines)<EOL>

Constructs a repr difference with minimal normalization.


indents = [len(indent) for indent in INDENT_RE.findall(s)]<EOL>if len(indents) > <NUM_LIT:0>:<EOL><INDENT>return min(indents)<EOL><DEDENT>else:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>

Return the minimum indentation of any non-blank line in `s`


if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>if sys.version_info.major == <NUM_LIT:2>:  <EOL><INDENT>string = utils.ensure_unicode(string)<EOL><DEDENT>if not isinstance(string, six.string_types):<EOL><INDENT>raise TypeError('<STR_LIT>'.format(string))<EOL><DEDENT>string = string.expandtabs()<EOL>min_indent = min_indentation(string)<EOL>if min_indent > <NUM_LIT:0>:<EOL><INDENT>string = '<STR_LIT:\n>'.join([l[min_indent:] for l in string.splitlines()])<EOL><DEDENT>labeled_lines = None<EOL>grouped_lines = None<EOL>all_parts = None<EOL>try:<EOL><INDENT>labeled_lines = self._label_docsrc_lines(string)<EOL>grouped_lines = self._group_labeled_lines(labeled_lines)<EOL>all_parts = list(self._package_groups(grouped_lines))<EOL><DEDENT>except Exception as orig_ex:<EOL><INDENT>if labeled_lines is None:<EOL><INDENT>failpoint = '<STR_LIT>'<EOL><DEDENT>elif grouped_lines is None:<EOL><INDENT>failpoint = '<STR_LIT>'<EOL><DEDENT>elif all_parts is None:<EOL><INDENT>failpoint = '<STR_LIT>'<EOL><DEDENT>if DEBUG:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(failpoint))<EOL>import ubelt as ub<EOL>import traceback<EOL>tb_text = traceback.format_exc()<EOL>tb_text = ub.highlight_code(tb_text)<EOL>tb_text = ub.indent(tb_text)<EOL>print(tb_text)<EOL>print('<STR_LIT>')<EOL>print(string)<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(ub.repr2(info)))<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(orig_ex))<EOL>print('<STR_LIT>'.format(ub.repr2(labeled_lines)))<EOL>print('<STR_LIT>'.format(ub.repr2(grouped_lines, nl=<NUM_LIT:3>)))<EOL>print('<STR_LIT>'.format(ub.repr2(all_parts)))<EOL>print('<STR_LIT>')<EOL><DEDENT>raise exceptions.DoctestParseError(<EOL>'<STR_LIT>'.format(failpoint),<EOL>string=string, info=info, orig_ex=orig_ex)<EOL><DEDENT>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>return all_parts<EOL>

Divide the given string into examples and interleaving text.

Args:
    string (str): string representing the doctest
    info (dict): info about where the string came from in case of an
        error

Returns:
    list : a list of `DoctestPart` objects

CommandLine:
    python -m xdoctest.parser DoctestParser.parse

Example:
    >>> s = 'I am a dummy example with two parts'
    >>> x = 10
    >>> print(s)
    I am a dummy example with two parts
    >>> s = 'My purpose it so demonstrate how wants work here'
    >>> print('The new want applies ONLY to stdout')
    >>> print('given before the last want')
    >>> '''
        this wont hurt the test at all
        even though its multiline '''
    >>> y = 20
    The new want applies ONLY to stdout
    given before the last want
    >>> # Parts from previous examples are executed in the same context
    >>> print(x + y)
    30

    this is simply text, and doesnt apply to the previous doctest the
    <BLANKLINE> directive is still in effect.

Example:
    >>> from xdoctest import parser
    >>> from xdoctest.docstr import docscrape_google
    >>> from xdoctest import core
    >>> self = parser.DoctestParser()
    >>> docstr = self.parse.__doc__
    >>> blocks = docscrape_google.split_google_docblocks(docstr)
    >>> doclineno = self.parse.__func__.__code__.co_firstlineno
    >>> key, (string, offset) = blocks[-2]
    >>> self._label_docsrc_lines(string)
    >>> doctest_parts = self.parse(string)
    >>> # each part with a want-string needs to be broken in two
    >>> assert len(doctest_parts) == 6


def _package_chunk(self, raw_source_lines, raw_want_lines, lineno=<NUM_LIT:0>):

if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>match = INDENT_RE.search(raw_source_lines[<NUM_LIT:0>])<EOL>line_indent = <NUM_LIT:0> if match is None else (match.end() - match.start())<EOL>source_lines = [p[line_indent:] for p in raw_source_lines]<EOL>want_lines = [p[line_indent:] for p in raw_want_lines]<EOL>exec_source_lines = [p[<NUM_LIT:4>:] for p in source_lines]<EOL>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>ps1_linenos, eval_final = self._locate_ps1_linenos(source_lines)<EOL>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>break_linenos = []<EOL>ps1_to_directive = {}<EOL>for s1, s2 in zip(ps1_linenos, ps1_linenos[<NUM_LIT:1>:] + [None]):<EOL><INDENT>lines = exec_source_lines[s1:s2]<EOL>directives = list(directive.Directive.extract('<STR_LIT:\n>'.join(lines)))<EOL>if directives:<EOL><INDENT>ps1_to_directive[s1] = directives<EOL>break_linenos.append(s1)<EOL>if directives[<NUM_LIT:0>].inline:<EOL><INDENT>if s2 is not None:<EOL><INDENT>break_linenos.append(s2)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>def slice_example(s1, s2, want_lines=None):<EOL><INDENT>exec_lines = exec_source_lines[s1:s2]<EOL>orig_lines = source_lines[s1:s2]<EOL>directives = ps1_to_directive.get(s1, None)<EOL>example = doctest_part.DoctestPart(exec_lines,<EOL>want_lines=want_lines,<EOL>orig_lines=orig_lines,<EOL>line_offset=lineno + s1,<EOL>directives=directives)<EOL>return example<EOL><DEDENT>s1 = <NUM_LIT:0><EOL>s2 = <NUM_LIT:0><EOL>if self.simulate_repl:<EOL><INDENT>for s1, s2 in zip(ps1_linenos, ps1_linenos[<NUM_LIT:1>:]):<EOL><INDENT>example = slice_example(s1, s2)<EOL>yield example<EOL><DEDENT>s1 = s2<EOL><DEDENT>else:<EOL><INDENT>if break_linenos:<EOL><INDENT>break_linenos = sorted(set([<NUM_LIT:0>] + break_linenos))<EOL>for s1, s2 in zip(break_linenos, break_linenos[<NUM_LIT:1>:]):<EOL><INDENT>example = slice_example(s1, s2)<EOL>yield example<EOL><DEDENT>s1 = s2<EOL><DEDENT>if want_lines and eval_final:<EOL><INDENT>s2 = ps1_linenos[-<NUM_LIT:1>]<EOL>if s2 != s1:  <EOL><INDENT>example = slice_example(s1, s2)<EOL>yield example<EOL>s1 = s2<EOL><DEDENT><DEDENT><DEDENT>s2 = None<EOL>example = slice_example(s1, s2, want_lines)<EOL>example.use_eval = bool(want_lines) and eval_final<EOL>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>yield example<EOL>

if `self.simulate_repl` is True, then each statement is broken into its
own part.  Otherwise, statements are grouped by the closest `want`
statement.

Example:
    >>> from xdoctest.parser import *
    >>> raw_source_lines = ['>>> "string"']
    >>> raw_want_lines = ['string']
    >>> self = DoctestParser()
    >>> part, = self._package_chunk(raw_source_lines, raw_want_lines)
    >>> part.source
    '"string"'
    >>> part.want
    'string'


<EOL>exec_source_lines = [p[<NUM_LIT:4>:] for p in source_lines]<EOL>def _hack_comment_statements(lines):<EOL><INDENT>def balanced_intervals(lines):<EOL><INDENT>"""<STR_LIT>"""<EOL>intervals = []<EOL>a = len(lines) - <NUM_LIT:1><EOL>b = len(lines)<EOL>while b > <NUM_LIT:0>:<EOL><INDENT>while not static.is_balanced_statement(lines[a:b], only_tokens=True):<EOL><INDENT>a -= <NUM_LIT:1><EOL><DEDENT>intervals.append((a, b))<EOL>b = a<EOL>a = a - <NUM_LIT:1><EOL><DEDENT>intervals = intervals[::-<NUM_LIT:1>]<EOL>return intervals<EOL><DEDENT>intervals = balanced_intervals(lines)<EOL>interval_starts = {t[<NUM_LIT:0>] for t in intervals}<EOL>for i, line in enumerate(lines):<EOL><INDENT>if i in interval_starts and line.startswith('<STR_LIT:#>'):<EOL><INDENT>yield '<STR_LIT>'<EOL><DEDENT>else:<EOL><INDENT>yield line<EOL><DEDENT><DEDENT><DEDENT>exec_source_lines = list(_hack_comment_statements(exec_source_lines))<EOL>source_block = '<STR_LIT:\n>'.join(exec_source_lines)<EOL>try:<EOL><INDENT>pt = static.six_axt_parse(source_block)<EOL><DEDENT>except SyntaxError as syn_ex:<EOL><INDENT>if syn_ex.text is None:<EOL><INDENT>if syn_ex.lineno is not None:<EOL><INDENT>line = source_block.split('<STR_LIT:\n>')[syn_ex.lineno - <NUM_LIT:1>]<EOL>syn_ex.text = line  + '<STR_LIT:\n>'<EOL><DEDENT><DEDENT>raise syn_ex<EOL><DEDENT>statement_nodes = pt.body<EOL>ps1_linenos = [node.lineno - <NUM_LIT:1> for node in statement_nodes]<EOL>NEED_16806_WORKAROUND = True<EOL>if NEED_16806_WORKAROUND:  <EOL><INDENT>ps1_linenos = self._workaround_16806(<EOL>ps1_linenos, exec_source_lines)<EOL><DEDENT>ps2_linenos = {<EOL>x for x, p in enumerate(source_lines) if p[:<NUM_LIT:4>] != '<STR_LIT>'<EOL>}<EOL>ps1_linenos = sorted(set(ps1_linenos).difference(ps2_linenos))<EOL>if len(statement_nodes) == <NUM_LIT:0>:<EOL><INDENT>eval_final = False<EOL><DEDENT>else:<EOL><INDENT>if sys.version_info.major == <NUM_LIT:2>:  <EOL><INDENT>eval_final = isinstance(statement_nodes[-<NUM_LIT:1>], (<EOL>ast.Expr, ast.Print))<EOL><DEDENT>else:<EOL><INDENT>eval_final = isinstance(statement_nodes[-<NUM_LIT:1>], ast.Expr)<EOL><DEDENT><DEDENT>return ps1_linenos, eval_final<EOL>

Determines which lines in the source begin a "logical block" of code.

Args:
    source_lines (list): lines belonging only to the doctest src
        these will be unindented, prefixed, and without any want.

Returns:
    (list, bool): a list of indices indicating which lines
       are considered "PS1" and a flag indicating if the final line
       should be considered for a got/want assertion.

Example:
    >>> self = DoctestParser()
    >>> source_lines = ['>>> def foo():', '>>>     return 0', '>>> 3']
    >>> linenos, eval_final = self._locate_ps1_linenos(source_lines)
    >>> assert linenos == [0, 2]
    >>> assert eval_final is True

Example:
    >>> self = DoctestParser()
    >>> source_lines = ['>>> x = [1, 2, ', '>>> 3, 4]', '>>> print(len(x))']
    >>> linenos, eval_final = self._locate_ps1_linenos(source_lines)
    >>> assert linenos == [0, 2]
    >>> assert eval_final is True


new_ps1_lines = []<EOL>b = len(exec_source_lines)<EOL>for a in ps1_linenos[::-<NUM_LIT:1>]:<EOL><INDENT>while not static.is_balanced_statement(exec_source_lines[a:b], only_tokens=True):<EOL><INDENT>a -= <NUM_LIT:1><EOL><DEDENT>new_ps1_lines.append(a)<EOL>b = a<EOL><DEDENT>return new_ps1_lines[::-<NUM_LIT:1>]<EOL>

workaround for python issue 16806 (https://bugs.python.org/issue16806)

This issue causes the AST to report line numbers for multiline strings
as the line they end on. The correct behavior is to report the line
they start on. Given a list of line numbers and the original source
code, this workaround fixes any line number that points from the end of
a multiline string to point to the start of it instead.

Args:
    ps1_linenos (List[int]): AST provided line numbers that begin
        statements and may be Python Issue #16806.
    exec_source_lines (List[str]): code referenced by ps1_linenos

Returns:
    List[int]: new_ps1_lines: Fixed `ps1_linenos` where multiline
        strings now point to the line where they begin.

Notes:
    A patch for this issue exists
    `https://github.com/python/cpython/pull/1800`. This workaround is a
    no-op when the line numbers are correct, so nothing should break
    when this bug is fixed.

    Starting from the end look at consecutive pairs of indices to
    inspect the statement it corresponds to.  (the first statement goes
    from ps1_linenos[-1] to the end of the line list.

Example:
    >>> ps1_linenos = [0, 2, 3]
    >>> exec_source_lines = ["x = 1", "y = '''foo", " bar'''", "pass"]
    >>> DoctestParser._workaround_16806(ps1_linenos, exec_source_lines)
    [0, 1, 3]


def _complete_source(line, state_indent, line_iter):<EOL><INDENT>"""<STR_LIT>"""<EOL>norm_line = line[state_indent:]  <EOL>prefix = norm_line[:<NUM_LIT:4>]<EOL>suffix = norm_line[<NUM_LIT:4>:]<EOL>assert prefix.strip() in {'<STR_LIT>', '<STR_LIT>'}, '<STR_LIT:{}>'.format(prefix)<EOL>yield line<EOL>source_parts = [suffix]<EOL>HACK_TRIPLE_QUOTE_FIX = True<EOL>try:<EOL><INDENT>while not static.is_balanced_statement(source_parts, only_tokens=True):<EOL><INDENT>line_idx, next_line = next(line_iter)<EOL>norm_line = next_line[state_indent:]<EOL>prefix = norm_line[:<NUM_LIT:4>]<EOL>suffix = norm_line[<NUM_LIT:4>:]<EOL>if prefix.strip() not in {'<STR_LIT>', '<STR_LIT>', '<STR_LIT>'}:  <EOL><INDENT>error = True<EOL>if HACK_TRIPLE_QUOTE_FIX:<EOL><INDENT>if any("<STR_LIT>" in s or '<STR_LIT>' in s for s in source_parts):<EOL><INDENT>next_line = next_line[:state_indent] + '<STR_LIT>' + norm_line<EOL>norm_line = '<STR_LIT>' + norm_line<EOL>prefix = '<STR_LIT>'<EOL>suffix = norm_line<EOL>error = False<EOL><DEDENT><DEDENT>if error:<EOL><INDENT>if DEBUG:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(source_parts))<EOL>print('<STR_LIT>'.format(prefix))<EOL>print('<STR_LIT>'.format(norm_line))<EOL>print('<STR_LIT>')<EOL><DEDENT>raise SyntaxError(<EOL>'<STR_LIT>'.format(<EOL>line_idx, next_line))<EOL><DEDENT><DEDENT>source_parts.append(suffix)<EOL>yield next_line<EOL><DEDENT><DEDENT>except StopIteration:<EOL><INDENT>if DEBUG:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>import traceback<EOL>tb_text = traceback.format_exc()<EOL>tb_text = ub.highlight_code(tb_text)<EOL>tb_text = ub.indent(tb_text)<EOL>print(tb_text)<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(state_indent))<EOL>print('<STR_LIT>'.format(line))<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(ub.repr2(source_parts, nl=<NUM_LIT:2>)))<EOL>print(ub.codeblock(<EOL>r'''<STR_LIT>'''))<EOL>print('<STR_LIT>')<EOL><DEDENT>raise IncompleteParseError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(ub.repr2(source_parts, nl=<NUM_LIT:2>)))<EOL>print('<STR_LIT>')<EOL><DEDENT><DEDENT><DEDENT>labeled_lines = []<EOL>state_indent = <NUM_LIT:0><EOL>TEXT = '<STR_LIT:text>'<EOL>DSRC = '<STR_LIT>'<EOL>WANT = '<STR_LIT>'<EOL>prev_state = TEXT<EOL>curr_state = None<EOL>line_iter = enumerate(string.splitlines())<EOL>def hasprefix(line, prefixes):<EOL><INDENT>if not isinstance(prefixes, tuple):<EOL><INDENT>prefixes = [prefixes]<EOL><DEDENT>return any(line == p or line.startswith(p + '<STR_LIT:U+0020>') for p in prefixes)<EOL><DEDENT>for line_idx, line in line_iter:<EOL><INDENT>match = INDENT_RE.search(line)<EOL>line_indent = <NUM_LIT:0> if match is None else (match.end() - match.start())<EOL>norm_line = line[state_indent:]  <EOL>strip_line = line.strip()<EOL>if prev_state == TEXT:<EOL><INDENT>if hasprefix(strip_line, '<STR_LIT>'):<EOL><INDENT>curr_state = DSRC<EOL><DEDENT>else:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT><DEDENT>elif prev_state == WANT:<EOL><INDENT>if len(strip_line) == <NUM_LIT:0>:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>elif hasprefix(line.strip(), '<STR_LIT>'):<EOL><INDENT>curr_state = DSRC<EOL><DEDENT>elif line_indent < state_indent:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>else:<EOL><INDENT>curr_state = WANT<EOL><DEDENT><DEDENT>elif prev_state == DSRC:  <EOL><INDENT>if len(strip_line) == <NUM_LIT:0> or line_indent < state_indent:<EOL><INDENT>curr_state = TEXT<EOL><DEDENT>elif hasprefix(norm_line, ('<STR_LIT>', '<STR_LIT>')):<EOL><INDENT>if strip_line == '<STR_LIT>':<EOL><INDENT>curr_state = WANT<EOL><DEDENT>else:<EOL><INDENT>curr_state = DSRC<EOL><DEDENT><DEDENT>else:<EOL><INDENT>curr_state = WANT<EOL><DEDENT><DEDENT>else:  <EOL><INDENT>raise AssertionError('<STR_LIT>'.format(<EOL>prev_state))<EOL><DEDENT>if prev_state != curr_state:<EOL><INDENT>if curr_state == TEXT:<EOL><INDENT>state_indent = <NUM_LIT:0><EOL><DEDENT>if curr_state == DSRC:<EOL><INDENT>state_indent = line_indent<EOL>norm_line = line[state_indent:]<EOL><DEDENT><DEDENT>if curr_state == DSRC:<EOL><INDENT>try:<EOL><INDENT>for part in _complete_source(line, state_indent, line_iter):<EOL><INDENT>labeled_lines.append((DSRC, part))<EOL><DEDENT><DEDENT>except IncompleteParseError as orig_ex:<EOL><INDENT>raise<EOL><DEDENT>except SyntaxError as orig_ex:<EOL><INDENT>if DEBUG:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(line_iter))<EOL>print('<STR_LIT>'.format(state_indent))<EOL>print('<STR_LIT>'.format(line))<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>for line in labeled_lines:<EOL><INDENT>print(line)<EOL><DEDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL><DEDENT>raise<EOL><DEDENT><DEDENT>elif curr_state == WANT:<EOL><INDENT>labeled_lines.append((WANT, line))<EOL><DEDENT>elif curr_state == TEXT:<EOL><INDENT>labeled_lines.append((TEXT, line))<EOL><DEDENT>prev_state = curr_state<EOL><DEDENT>if DEBUG > <NUM_LIT:1>:<EOL><INDENT>import ubelt as ub<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>'.format(ub.repr2(labeled_lines, nl=<NUM_LIT:1>)))<EOL>print('<STR_LIT>')<EOL><DEDENT>return labeled_lines<EOL>

Example:
    >>> from xdoctest.parser import *
    >>> # Having multiline strings in doctests can be nice
    >>> string = utils.codeblock(
            '''
            text
            >>> items = ['also', 'nice', 'to', 'not', 'worry',
            >>>          'about', '...', 'vs', '>>>']
            ... print('but its still allowed')
            but its still allowed

            more text
            ''')
    >>> self = DoctestParser()
    >>> labeled = self._label_docsrc_lines(string)
    >>> expected = [
    >>>     ('text', 'text'),
    >>>     ('dsrc', ">>> items = ['also', 'nice', 'to', 'not', 'worry',"),
    >>>     ('dsrc', ">>>          'about', '...', 'vs', '>>>']"),
    >>>     ('dsrc', "... print('but its still allowed')"),
    >>>     ('want', 'but its still allowed'),
    >>>     ('text', ''),
    >>>     ('text', 'more text')
    >>> ]
    >>> assert labeled == expected


blocks = split_google_docblocks(docstr)<EOL>for key, block in blocks:<EOL><INDENT>lines = block[<NUM_LIT:0>]<EOL>if key == '<STR_LIT>':<EOL><INDENT>for argdict in parse_google_argblock(lines):<EOL><INDENT>yield argdict<EOL><DEDENT><DEDENT><DEDENT>

r"""
    Generates dictionaries of argument hints based on a google docstring

    Args:
        docstr (str): a google-style docstring

    Yields:
        Dict[str, str]: dictionaries of parameter hints

    Example:
        >>> docstr = parse_google_args.__doc__
        >>> argdict_list = list(parse_google_args(docstr))
        >>> print([sorted(d.items()) for d in argdict_list])
        [[('desc', 'a google-style docstring'), ('name', 'docstr'), ('type', 'str')]]


blocks = split_google_docblocks(docstr)<EOL>for key, block in blocks:<EOL><INDENT>lines = block[<NUM_LIT:0>]<EOL>if key == '<STR_LIT>':<EOL><INDENT>for retdict in parse_google_retblock(lines, return_annot):<EOL><INDENT>yield retdict<EOL><DEDENT><DEDENT>if key == '<STR_LIT>':<EOL><INDENT>for retdict in parse_google_retblock(lines, return_annot):<EOL><INDENT>yield retdict<EOL><DEDENT><DEDENT><DEDENT>

r"""
    Generates dictionaries of possible return hints based on a google docstring

    Args:
        docstr (str): a google-style docstring
        return_annot (str): the return type annotation (if one exists)

    Yields:
        Dict[str, str]: dictionaries of return value hints

    Example:
        >>> docstr = parse_google_returns.__doc__
        >>> retdict_list = list(parse_google_returns(docstr))
        >>> print([sorted(d.items()) for d in retdict_list])
        [[('desc', 'dictionaries of return value hints'), ('type', 'Dict[str, str]')]]

    Example:
        >>> docstr = split_google_docblocks.__doc__
        >>> retdict_list = list(parse_google_returns(docstr))
        >>> print([sorted(d.items())[1] for d in retdict_list])
        [('type', 'List[Tuple]')]


if return_annot is not None:<EOL><INDENT>retdict = {'<STR_LIT:type>': return_annot, '<STR_LIT>': lines}<EOL>yield retdict<EOL><DEDENT>else:<EOL><INDENT>def finalize(retdict):<EOL><INDENT>final_desc = '<STR_LIT:U+0020>'.join([p for p in retdict['<STR_LIT>'] if p])<EOL>retdict['<STR_LIT>'] = final_desc<EOL>return retdict<EOL><DEDENT>retdict = None<EOL>noindent_pat = re.compile(r'<STR_LIT>')<EOL>for line in lines.split('<STR_LIT:\n>'):<EOL><INDENT>if noindent_pat.match(line):<EOL><INDENT>if retdict is not None:<EOL><INDENT>yield finalize(retdict)<EOL>retdict = None<EOL><DEDENT>if '<STR_LIT::>' in line:<EOL><INDENT>parts = line.split('<STR_LIT::>')<EOL>retdict = {<EOL>'<STR_LIT:type>': parts[<NUM_LIT:0>].strip(),<EOL>'<STR_LIT>': ['<STR_LIT::>'.join(parts[<NUM_LIT:1>:]).strip()],<EOL>}<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if retdict is not None:<EOL><INDENT>retdict['<STR_LIT>'].append(line.strip())<EOL><DEDENT><DEDENT><DEDENT>if retdict is not None:<EOL><INDENT>yield finalize(retdict)<EOL><DEDENT><DEDENT>

r"""
    Args:
        lines (str): unindented lines from a Returns or Yields section
        return_annot (str): the return type annotation (if one exists)

    Yeilds:
        Dict[str, str]: each dict specifies the return type and its description

    Example:
        >>> # Test various ways that retlines can be written
        >>> assert len(list(parse_google_retblock('list: a desc'))) == 1
        >>> assert len(list(parse_google_retblock('no type, just desc'))) == 0
        >>> # ---
        >>> hints = list(parse_google_retblock('\n'.join([
        ...     'entire line can be desc',
        ...     ' ',
        ...     ' if a return type annotation is given',
        ... ]), return_annot='int'))
        >>> assert len(hints) == 1
        >>> # ---
        >>> hints = list(parse_google_retblock('\n'.join([
        ...     'bool: a description',
        ...     ' with a newline',
        ... ])))
        >>> assert len(hints) == 1
        >>> # ---
        >>> hints = list(parse_google_retblock('\n'.join([
        ...     'int or bool: a description',
        ...     ' ',
        ...     ' with a separated newline',
        ...     ' ',
        ... ])))
        >>> assert len(hints) == 1
        >>> # ---
        >>> hints = list(parse_google_retblock('\n'.join([
        ...     # Multiple types can be specified
        ...     'threading.Thread: a description',
        ...     '(int, str): a tuple of int and str',
        ...     'tuple: a tuple of int and str',
        ...     'Tuple[int, str]: a tuple of int and str',
        ... ])))
        >>> assert len(hints) == 4
        >>> # ---
        >>> hints = list(parse_google_retblock('\n'.join([
        ...     # If the colon is not specified nothing will be parsed
        ...     'list',
        ...     'Tuple[int, str]',
        ... ])))
        >>> assert len(hints) == 0
        ...


def named(key, pattern):<EOL><INDENT>return '<STR_LIT>'.format(key, pattern)<EOL><DEDENT>def optional(pattern):<EOL><INDENT>return '<STR_LIT>'.format(pattern)<EOL><DEDENT>def positive_lookahead(pattern):<EOL><INDENT>return '<STR_LIT>'.format(pattern)<EOL><DEDENT>def regex_or(patterns):<EOL><INDENT>return '<STR_LIT>'.format('<STR_LIT:|>'.join(patterns))<EOL><DEDENT>whitespace = r'<STR_LIT>'<EOL>endofstr = r'<STR_LIT>'<EOL>varname = named('<STR_LIT:name>', '<STR_LIT>')<EOL>typename = named('<STR_LIT:type>', '<STR_LIT>')<EOL>argdesc = named('<STR_LIT>', '<STR_LIT>')<EOL>optional_type = optional(whitespace.join([r'<STR_LIT>', typename, r'<STR_LIT>']))<EOL>argdef = whitespace.join([varname, optional_type, '<STR_LIT::>'])<EOL>end_desc = regex_or(['<STR_LIT>' + positive_lookahead(r'<STR_LIT>'), endofstr])<EOL>flags = re.MULTILINE | re.DOTALL<EOL>argline_pat = re.compile('<STR_LIT>' + argdef + argdesc + end_desc, flags=flags)<EOL>for match in argline_pat.finditer(lines):<EOL><INDENT>argdict = match.groupdict()<EOL>desc_lines = [p.strip() for p in argdict['<STR_LIT>'].split('<STR_LIT:\n>')]<EOL>argdict['<STR_LIT>'] = '<STR_LIT:U+0020>'.join([p for p in desc_lines if p])<EOL>yield argdict<EOL><DEDENT>

r"""
    Args:
        lines (str): the unindented lines from an Args docstring section

    References:
        # It is not clear which of these is *the* standard or if there is one
        https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html#example-google
        http://www.sphinx-doc.org/en/stable/ext/example_google.html#example-google

    Example:
        >>> # Test various ways that arglines can be written
        >>> line_list = [
        ...     '',
        ...     'foo1 (int): a description',
        ...     'foo2: a description\n    with a newline',
        ...     'foo3 (int or str): a description',
        ...     'foo4 (int or threading.Thread): a description',
        ...     #
        ...     # this is sphynx-like typing style
        ...     'param1 (:obj:`str`, optional): ',
        ...     'param2 (:obj:`list` of :obj:`str`):',
        ...     #
        ...     # the Type[type] syntax is defined by the python typeing module
        ...     'attr1 (Optional[int]): Description of `attr1`.',
        ...     'attr2 (List[str]): Description of `attr2`.',
        ...     'attr3 (Dict[str, str]): Description of `attr3`.',
        ... ]
        >>> lines = '\n'.join(line_list)
        >>> argdict_list = list(parse_google_argblock(lines))
        >>> # All lines except the first should be accepted
        >>> assert len(argdict_list) == len(line_list) - 1
        >>> assert argdict_list[1]['desc'] == 'a description with a newline'


if not isinstance(docstr, six.string_types):<EOL><INDENT>raise TypeError('<STR_LIT>'.format(<EOL>type(docstr)))<EOL><DEDENT>def get_indentation(line_):<EOL><INDENT>"""<STR_LIT>"""<EOL>return len(line_) - len(line_.lstrip())<EOL><DEDENT>docstr = ensure_unicode(docstr)<EOL>docstr = textwrap.dedent(docstr)<EOL>docstr_lines = docstr.split('<STR_LIT:\n>')<EOL>line_indent = [get_indentation(line) for line in docstr_lines]<EOL>line_len = [len(line) for line in docstr_lines]<EOL>adjusted = False<EOL>is_nonzero = [len_ > <NUM_LIT:0> for len_ in line_len]<EOL>if len(line_indent) >= <NUM_LIT:2>:<EOL><INDENT>if line_len[<NUM_LIT:0>] != <NUM_LIT:0>:<EOL><INDENT>indents = [x for x, f in zip(line_indent, is_nonzero) if f]<EOL>if len(indents) >= <NUM_LIT:2>:<EOL><INDENT>indent_adjust = min(indents[<NUM_LIT:1>:])<EOL>line_indent[<NUM_LIT:0>] += indent_adjust<EOL>line_len[<NUM_LIT:0>] += indent_adjust<EOL>docstr_lines[<NUM_LIT:0>] = ('<STR_LIT:U+0020>' * indent_adjust) + docstr_lines[<NUM_LIT:0>]<EOL>adjusted = True<EOL><DEDENT><DEDENT><DEDENT>if adjusted:<EOL><INDENT>docstr = textwrap.dedent('<STR_LIT:\n>'.join(docstr_lines))<EOL>docstr_lines = docstr.split('<STR_LIT:\n>')<EOL>line_indent = [get_indentation(line) for line in docstr_lines]<EOL>line_len = [len(line) for line in docstr_lines]<EOL><DEDENT>indents = [x for x, f in zip(line_indent, is_nonzero) if f]<EOL>if len(indents) >= <NUM_LIT:1>:<EOL><INDENT>if indents[<NUM_LIT:0>] != <NUM_LIT:0>:<EOL><INDENT>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>')<EOL>print(docstr)<EOL>print('<STR_LIT>')<EOL>raise exceptions.MalformedDocstr('<STR_LIT>')<EOL><DEDENT><DEDENT>base_indent = <NUM_LIT:0><EOL>true_indent = []<EOL>prev_indent = None<EOL>for indent_, len_ in zip(line_indent, line_len):<EOL><INDENT>if len_ == <NUM_LIT:0>:<EOL><INDENT>indent_ = prev_indent<EOL><DEDENT>true_indent.append(indent_)<EOL>prev_indent = indent_<EOL><DEDENT>tag_groups = [<EOL>['<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>', '<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>['<STR_LIT>'],<EOL>]<EOL>tag_aliases = dict([(item, group[<NUM_LIT:0>]) for group in tag_groups for item in group])<EOL>tag_pattern = '<STR_LIT>' + '<STR_LIT:(>' + '<STR_LIT:|>'.join(tag_aliases.keys()) + '<STR_LIT>'<EOL>group_id = <NUM_LIT:0><EOL>prev_indent = <NUM_LIT:0><EOL>group_list = []<EOL>in_tag = False<EOL>for line_num, (line, indent_) in enumerate(zip(docstr_lines, true_indent)):<EOL><INDENT>if re.match(tag_pattern, line):<EOL><INDENT>if line_num + <NUM_LIT:1> < len(docstr_lines):<EOL><INDENT>indent_increase = true_indent[line_num + <NUM_LIT:1>] > base_indent<EOL>indent_zero = line_len[line_num + <NUM_LIT:1>] == <NUM_LIT:0><EOL>matches_tag = re.match(tag_pattern, docstr_lines[line_num + <NUM_LIT:1>])<EOL>if (indent_increase or indent_zero or matches_tag):<EOL><INDENT>group_id += <NUM_LIT:1><EOL>in_tag = True<EOL><DEDENT><DEDENT>else:<EOL><INDENT>group_id += <NUM_LIT:1><EOL>in_tag = True<EOL><DEDENT><DEDENT>elif in_tag and indent_ != prev_indent and indent_ == base_indent:<EOL><INDENT>group_id += <NUM_LIT:1><EOL>in_tag = False<EOL><DEDENT>group_list.append(group_id)<EOL>prev_indent = indent_<EOL><DEDENT>assert len(docstr_lines) == len(group_list)<EOL>groups_ = collections.defaultdict(list)<EOL>for groupid, line in zip(group_list, docstr_lines):<EOL><INDENT>groups_[groupid].append(line)<EOL><DEDENT>groups = []<EOL>line_offset = <NUM_LIT:0><EOL>for k, lines in groups_.items():<EOL><INDENT>if len(lines) == <NUM_LIT:0> or (len(lines) == <NUM_LIT:1> and len(lines[<NUM_LIT:0>]) == <NUM_LIT:0>):<EOL><INDENT>line_offset += len(lines)<EOL>continue<EOL><DEDENT>elif len(lines) >= <NUM_LIT:1> and re.match(tag_pattern, lines[<NUM_LIT:0>]):<EOL><INDENT>key = lines[<NUM_LIT:0>].strip().rstrip('<STR_LIT::>')<EOL>val = lines[<NUM_LIT:1>:]<EOL>subblock = textwrap.dedent('<STR_LIT:\n>'.join(val))<EOL><DEDENT>else:<EOL><INDENT>key = '<STR_LIT>'<EOL>val = lines[:]<EOL>subblock = '<STR_LIT:\n>'.join(val)<EOL><DEDENT>key = tag_aliases.get(key, key)<EOL>block = (subblock, line_offset)<EOL>groups.append((key, block))<EOL>line_offset += len(lines)<EOL><DEDENT>return groups<EOL>

Breaks a docstring into parts defined by google style

    Args:
        docstr (str): a docstring

    Returns:
        List[Tuple]: list of 2-tuples where the first item is a google style
            docstring tag and the second item is the bock corresponding to that
            tag.

    CommandLine:
        xdoctest xdoctest.docstr.docscrape_google split_google_docblocks:2

    Example:
        >>> from xdoctest.docstr.docscrape_google import *  # NOQA
        >>> from xdoctest import utils
        >>> docstr = utils.codeblock(
        ...     '''
        ...     one line description
        ...
        ...     multiline
        ...     description
        ...
        ...     Args:
        ...         foo: bar
        ...
        ...     Returns:
        ...         None
        ...
        ...     Example:
        ...         >>> print('eg1')
        ...         eg1
        ...
        ...     Example:
        ...         >>> print('eg2')
        ...         eg2
        ...     ''')
        >>> groups = split_google_docblocks(docstr)
        >>> assert len(groups) == 5
        >>> [g[0] for g in groups]
        ['__DOC__', 'Args', 'Returns', 'Example', 'Example']

    Example:
        >>> from xdoctest.docstr.docscrape_google import *  # NOQA
        >>> docstr = split_google_docblocks.__doc__
        >>> groups = split_google_docblocks(docstr)

    Example:
        >>> from xdoctest.docstr.docscrape_google import *  # NOQA
        >>> from xdoctest import utils
        >>> docstr = utils.codeblock(
        ...     '''
        ...     Example:
        ...         >>> foobar
        ...     ''')
        >>> # Check that line offsets are valid if the first line is not blank
        >>> groups = split_google_docblocks(docstr)
        >>> offset = groups[0][1][1]
        >>> print('offset = {!r}'.format(offset))
        >>> assert offset == 0
        >>> # Check that line offsets are valid if the first line is blank
        >>> groups = split_google_docblocks(chr(10) + docstr)
        >>> offset = groups[0][1][1]
        >>> print('offset = {!r}'.format(offset))
        >>> assert offset == 1


def add_line_numbers(source, start=<NUM_LIT:1>, n_digits=None):

import math<EOL>import six<EOL>was_string = isinstance(source, six.string_types)<EOL>part_lines = source.splitlines() if was_string else source<EOL>if n_digits is None:<EOL><INDENT>endline = start + len(part_lines)<EOL>n_digits = math.log(max(<NUM_LIT:1>, endline), <NUM_LIT:10>)<EOL>n_digits = int(math.ceil(n_digits))<EOL><DEDENT>src_fmt = '<STR_LIT>'<EOL>part_lines = [<EOL>src_fmt.format(n_digits=n_digits, count=count, line=line)<EOL>for count, line in enumerate(part_lines, start=start)<EOL>]<EOL>if was_string:<EOL><INDENT>return '<STR_LIT:\n>'.join(part_lines)<EOL><DEDENT>else:<EOL><INDENT>return part_lines<EOL><DEDENT>

HACKED IN, IDK WHY I NEEDED TO DO THIS HERE
Prefixes code with line numbers

Example:
    >>> from xdoctest.utils.util_str import *
    >>> print(chr(10).join(add_line_numbers(['a', 'b', 'c'])))
    1 a
    2 b
    3 c
    >>> print(add_line_numbers(chr(10).join(['a', 'b', 'c'])))
    1 a
    2 b
    3 c


if self._directives is None:<EOL><INDENT>self._directives = list(directive.Directive.extract(self.source))<EOL><DEDENT>return self._directives<EOL>

CommandLine:
    python -m xdoctest.parser DoctestPart.directives

Example:
    >>> self = DoctestPart(['# doctest: +SKIP'], None, 0)
    >>> print(', '.join(list(map(str, self.directives))))
    <Directive(+SKIP)>