def nexson_frag_write_newick(out,
                             edges,
                             nodes,
                             otu_group,
                             label_key,
                             leaf_labels,
                             root_id,
                             needs_quotes_pattern=NEWICK_NEEDING_QUOTING,
                             ingroup_id=None,
                             bracket_ingroup=False,
                             with_edge_lengths=True):
    """`label_key` is a string (a key in the otu object) or a callable that takes two arguments:
        the node, and the otu (which may be None for an internal node)
    If `leaf_labels` is not None, it shoulr be a (list, dict) pair which will be filled. The list will
        hold the order encountered,
        and the dict will map name to index in the list
    """
    unlabeled_counter = 0
    curr_node_id = root_id
    assert curr_node_id
    curr_edge = None
    curr_sib_list = []
    curr_stack = []
    going_tipward = True
    while True:
        if going_tipward:
            outgoing_edges = edges.get(curr_node_id)
            if outgoing_edges is None:
                curr_node = nodes[curr_node_id]
                assert curr_node_id is not None
                assert curr_node_id is not None
                unlabeled_counter = _write_newick_leaf_label(out,
                                                             curr_node_id,
                                                             curr_node,
                                                             otu_group,
                                                             label_key,
                                                             leaf_labels,
                                                             unlabeled_counter,
                                                             needs_quotes_pattern)
                if with_edge_lengths:
                    _write_newick_edge_len(out, curr_edge)
                going_tipward = False
            else:
                te = [(i, e) for i, e in outgoing_edges.items()]
                te.sort()  # produce a consistent rotation... Necessary?
                if bracket_ingroup and (ingroup_id == curr_node_id):
                    out.write('[pre-ingroup-marker]')
                out.write('(')
                next_p = te.pop(0)
                curr_stack.append((curr_edge, curr_node_id, curr_sib_list))
                curr_edge, curr_sib_list = next_p[1], te
                curr_node_id = curr_edge['@target']
        if not going_tipward:
            next_up_edge_id = None
            while True:
                if curr_sib_list:
                    out.write(',')
                    next_up_edge_id, next_up_edge = curr_sib_list.pop(0)
                    break
                if curr_stack:
                    curr_edge, curr_node_id, curr_sib_list = curr_stack.pop(-1)
                    curr_node = nodes[curr_node_id]
                    out.write(')')
                    _write_newick_internal_label(out,
                                                 curr_node_id,
                                                 curr_node,
                                                 otu_group,
                                                 label_key,
                                                 needs_quotes_pattern)
                    if with_edge_lengths:
                        _write_newick_edge_len(out, curr_edge)
                    if bracket_ingroup and (ingroup_id == curr_node_id):
                        out.write('[post-ingroup-marker]')
                else:
                    break
            if next_up_edge_id is None:
                break
            curr_edge = next_up_edge
            curr_node_id = curr_edge['@target']
            going_tipward = True
    out.write(';')