class PairDirectionError(Exception):
    'Error in parser trying to get direction from description'
    pass class InterleaveError(Exception):
    'Error in the alternation of forward and reverse reads'
    pass class ItemsNotSortedError(Exception):
    'A list or generator of items is not sorted as expected'
    pass def write_seqs(seqs, fhand=None, file_format=None):
    'It writes the given sequences'
    if fhand is None:
        fhand = NamedTemporaryFile(suffix='.' + file_format.replace('-', '_'))

    seqs, seqs2 = tee(seqs)
    try:
        seq = seqs2.next()
    except StopIteration:
        # No sequences to write, so we're done
        return fhand
    del seqs2
    seq_class = seq.kind
    if seq_class == SEQITEM:
        _write_seqitems(seqs, fhand, file_format)
    elif seq_class == SEQRECORD:
        seqs = (seq.object for seq in seqs)
        _write_seqrecords(seqs, fhand, file_format)
    else:
        raise ValueError('Unknown class for seq: ' + seq_class)
    return fhand def get_title(seq):
    'Given a seq it returns the title'
    seq_class = seq.kind
    seq = seq.object

    if seq_class == SEQITEM:
        title = seq.lines[0][1:].rstrip()
    elif seq_class == SEQRECORD:
        title = seq.id + ' ' + seq.description
    else:
        msg = 'Do not know how to guess title form this seq class'
        raise NotImplementedError(msg)
    return title def get_name(seq):
    if 'SeqRecord' in seq.__class__.__name__:
        seq_class = SEQRECORD
    else:
        seq_class = seq.kind
        seq = seq.object
    if seq_class == SEQITEM:
        name = seq.name
    elif seq_class == SEQRECORD:
        name = seq.id
    return name FWD = 'fwd' REV = 'rev' def flush_fhand(fhand):
    try:
        fhand.flush()
    except IOError, error:
        # The pipe could be already closed
        if 'Broken pipe' not in str(error):
            raise def sorted_items(items, key=None, max_items_in_memory=None, tempdir=None):
    if max_items_in_memory:
        grouped_items = group_in_packets(items, max_items_in_memory)
    else:
        grouped_items = [items]

    # sort and write to disk all groups but the last one
    write_to_disk = True if max_items_in_memory else False
    sorted_groups = []
    for group in grouped_items:
        sorted_group = sorted(group, key=key)
        del group
        if write_to_disk:
            group_fhand = _pickle_items(sorted_group, tempdir=tempdir)
            sorted_groups.append(_unpickle_items(open(group_fhand.name)))
        else:
            sorted_groups.append(sorted_group)
        del sorted_group
    if len(sorted_groups) > 1:
        if key is None:
            sorted_items = merge_sorted(*sorted_groups)
        else:
            sorted_items = merge_sorted(*sorted_groups, key=key)
    else:
        sorted_items = sorted_groups[0]
    return sorted_items def group_in_packets_fill_last(iterable, packet_size, fillvalue=None):
    'ABCDE -> (A, B), (C, D), (E, None)'
    # It is faster than group_in_packets
    iterables = [iter(iterable)] * packet_size
    kwargs = {'fillvalue': fillvalue}
    return izip_longest(*iterables, **kwargs) class KeyedSet(object):

    def __init__(self, items=None, key=None):
        if items is None:
            items = set()
        elif key:
            items = set(map(key, items))
        else:
            items = set(items)
        self._items = items
        self._len = len(self._items)
        self._key = key

    def check_add(self, item):
        item_key = self._key(item) if self._key else item
        self._items.add(item_key)
        if len(self._items) > self._len:
            self._len = len(self._items)
            return True
        else:
            return False

    def __contains__(self, item):
        item_key = self._key(item) if self._key else item
        return item_key in self._items

    def __len__(self):
        return len(self._items) MSG = 'A python package to run this executable is required,'
BIO = 'biopython'
BIO_BGZF = 'biopython with Bgzf support'
    NCBIXML = create_fake_class(MSG + BIO)
    NCBIWWW = create_fake_class(MSG + BIO)
def create_fake_class(msg):
        def __init__(self, *args, **kwargs):
def create_fake_funct(msg):
    def FakeRequiredfunct(*args, **kwargs):
    class FakePythonRequiredClass(object):
import re
from itertools import izip_longest, chain
from crumbs.exceptions import (PairDirectionError, InterleaveError,
                               ItemsNotSortedError)
from crumbs.seq.seqio import write_seqs
from crumbs.seq.seq import get_title, get_name
from crumbs.utils.tags import FWD, REV
from crumbs.utils.file_utils import flush_fhand
from crumbs.iterutils import sorted_items, group_in_packets_fill_last
from crumbs.collectionz import KeyedSet
from crumbs.utils.optional_modules import first
# Copyright 2012 Jose Blanca, Peio Ziarsolo, COMAV-Univ. Politecnica Valencia
# This file is part of ngs_crumbs.
# ngs_crumbs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# ngs_crumbs is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with ngs_crumbs. If not, see <http://www.gnu.org/licenses/>.





def _parse_pair_direction_and_name(seq):
    'It parses the description field to get the name and the pair direction'
    return _parse_pair_direction_and_name_from_title(get_title(seq))


def _parse_pair_direction_and_name_from_title(title):
    'It guesses the direction from the title line'
    reg_exps = ['(.+)[/|\\\\](\d+)', '(.+)\s(\d+):.+', '(.+)\.(\w)\s?']
    for reg_exp in reg_exps:
        match = re.match(reg_exp, title)
        if match:
            name, direction = match.groups()
            if direction in ('1', 'f'):
                direction = FWD
            elif direction in ('2', 'r'):
                direction = REV
            else:
                raise PairDirectionError('unknown direction descriptor')
            return name, direction
    raise PairDirectionError('Unable to detect the direction of the seq')


def _get_paired_and_orphan(reads, ordered, max_reads_memory, temp_dir):
    if ordered:
        sorted_reads = reads
    else:
        def _key(seq):
            return get_title(seq)
        sorted_reads = sorted_items(reads, _key, max_reads_memory, temp_dir)
    return group_pairs_by_name(sorted_reads)


def match_pairs(reads, out_fhand, orphan_out_fhand, out_format, ordered=True,
                check_order_buffer_size=0, max_reads_memory=None,
                temp_dir=None):
    '''It matches the seq pairs in an iterator and splits the orphan seqs.'''
    counts = 0

    check_order_buffer = KeyedSet()