U
    _                     @   s   d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dlmZ d dlm	Z	 d dlm
Z d dlmZ d dlmZ dd	 Ze Zd
d ZG dd deZdS )    N)bins)helpers)	constantsmerge_criteria)Feature)FeatureNotFoundErrorc                 C   s   | d |j d< |S )a  
    Helper for add_relation()
    Sets 'Parent' attribute to parent['ID']

    Parameters
    ----------

    parent : Feature
        Parent Feature

    :param parent: parent Feature

    child : Feature
        Child Feature

    Returns
    -------

    Child Feature
    IDZParent)
attributes)parentchild r   1lib/python3.8/site-packages/gffutils/interface.pyassign_child   s    r   c                 C   s8   t |dkr.dtdd |D | _|| _nt| _| S )a  
    Helper for FeatureDB.merge() to update source and assign children property

    Parameters
    ----------
    feature : Feature
        feature to finalise
    
    feature_children
        list of children to assign
    
    Returns
    -------
    feature, modified
       ,c                 s   s   | ]}|j V  qd S N)source).0r   r   r   r   	<genexpr><   s     z"_finalize_merge.<locals>.<genexpr>)lenjoinsetr   childrenno_childrenfeaturefeature_childrenr   r   r   _finalize_merge+   s
    r   c                   @   s  e Zd ZdZddejdejfddZdd Z	dd	 Z
d
d Zdd Zdd ZdKddZdLddZdMddZdNddZdd ZdOddZdPddZdQd d!Zd"d# Zd$d% Zd&d' ZdRd(d)ZdSd+d,ZdTd-d.ZdUd/d0ZdVd1d2Zd3d4 Zd5d6 ZdWd9d:Z dXd;d<Z!e"j#e"j$e"j%e"j&fdfd=d>Z'd?e"j#e"j$e"j%e"j&fd@dfdAdBZ(dYdCdDZ)d7gdEgddFdfdGdHZ*ej+j,ej+dIe_+ej+j,ej+dIe_+eeeefD ]Z-e-j+j,edJe-_+qdS )Z	FeatureDBa5  
        limit : string or tuple
            Limit the results to a genomic region.  If string, then of the form
            "seqid:start-end"; if tuple, then (seqid, start, end).

        strand : "-" | "+" | "."
            Limit the results to one strand

        featuretype : string or tuple
            Limit the results to one or several featuretypes.

        order_by : string or tuple
            Order results by one or many fields; the string or tuple items must
            be in: 'seqid', 'source', 'featuretype', 'start', 'end', 'score',
            'strand', 'frame', 'attributes', 'extra'.

        reverse : bool
            Change sort order; only relevant if `order_by` is not None.  By
            default, results will be in ascending order, so use `reverse=True`
            for descending.

        completely_within : bool
            If False (default), a single bp overlap with `limit` is sufficient
            to return a feature; if True, then the feature must be completely
            within `limit`. Only relevant when `limit` is not None.
    zutf-8Fc                 C   s:  ddl m} t||jr*|j| _|j| _nXt|tjrD|| _|| _n>|dkrVtdn,t	j
|sntd| || _t| j| _|dk	r|| j_tj| j_|| _|| _|| _| j }|d | \}	}
|	| _t|
| _|d d	d
 |D | _|d tt|| _| | |   s6t!"d| j  dS )aH  
        Connect to a database created by :func:`gffutils.create_db`.

        Parameters
        ----------

        dbfn : str

            Path to a database created by :func:`gffutils.create_db`.

        text_factory : callable

            Optionally set the way sqlite3 handles strings.  Default is
            sqlite3.OptimizedUnicode, which returns ascii when possible,
            unicode otherwise

        encoding : str

            When non-ASCII characters are encountered, assume they are in this
            encoding.

        keep_order : bool

            If True, all features returned from this instance will have the
            order of their attributes maintained.  This can be turned on or off
            database-wide by setting the `keep_order` attribute or with this
            kwarg, or on a feature-by-feature basis by setting the `keep_order`
            attribute of an individual feature.

            Default is False, since this includes a sorting step that can get
            time-consuming for many features.

        pragmas : dict
            Dictionary of pragmas to use when connecting to the database.  See
            http://www.sqlite.org/pragma.html for the full list of
            possibilities, and constants.default_pragmas for the defaults.
            These can be changed later using the :meth:`FeatureDB.set_pragmas`
            method.

        Notes
        -----

            `dbfn` can also be a subclass of :class:`_DBCreator`, useful for
            when :func:`gffutils.create_db` is provided the ``dbfn=":memory:"``
            kwarg.

        r   createz:memory:z:cannot connect to memory db; please provide the connectionzDatabase file %s does not existNz;
            SELECT version, dialect FROM meta
            z:
            SELECT directive FROM directives
            c                 S   s   g | ]}|r|d  qS )r   r   )r   Z	directiver   r   r   
<listcomp>   s      z&FeatureDB.__init__.<locals>.<listcomp>z<
            SELECT base, n FROM autoincrements
            a"  It appears that this database has not had the ANALYZE sqlite3 command run on it. Doing so can dramatically speed up queries, and is done by default for databases created with gffutils >0.8.7.1 (this database was created with version %s) Consider calling the analyze() method of this object.)#gffutilsr!   
isinstanceZ
_DBCreatorconndbfnsqlite3Z
Connection
ValueErrorospathexistsZconnecttext_factoryZRowZrow_factorydefault_encoding
keep_ordersort_attribute_valuescursorexecutefetchoneversionr   Z
_unjsonifydialectZ
directivescollectionsdefaultdictint_autoincrementsset_pragmas	_analyzedwarningswarn)selfr&   r-   r.   pragmasr/   r,   r!   cr3   r4   r   r   r   __init__`   sV    7




zFeatureDB.__init__c                 C   s>   || _ | j }|ddd | j  D  | j  dS )a  
        Set pragmas for the current database connection.

        Parameters
        ----------
        pragmas : dict
            Dictionary of pragmas; see constants.default_pragmas for a template
            and http://www.sqlite.org/pragma.html for a full list.
        z;
c                 S   s   g | ]}d | qS )zPRAGMA %s=%sr   r   ir   r   r   r"      s     z)FeatureDB.set_pragmas.<locals>.<listcomp>N)r>   r%   r0   Zexecutescriptr   itemscommit)r=   r>   r?   r   r   r   r9      s    

zFeatureDB.set_pragmasc                 K   s4   | d| j | d| j | d| j tf |S )zQ
        Returns a feature, adding additional database-specific defaults
        r4   r.   r/   )
setdefaultr4   r.   r/   r   )r=   kwargsr   r   r   _feature_returner   s    zFeatureDB._feature_returnerc                 C   s   |  d}tt|dkS )Nzp
            SELECT name FROM sqlite_master WHERE type='table'
            AND name='sqlite_stat1';
            r   )r1   r   list)r=   resr   r   r   r:      s    zFeatureDB._analyzedc                 C   s@   | j  }|d g }|D ]\}|dk	r|| qd|S )z:
        Returns the database schema as a string.
        z7
            SELECT sql FROM sqlite_master
            N
)r%   r0   r1   appendr   )r=   r?   resultsrB   r   r   r   schema   s    

zFeatureDB.schemac              	   C   s   t |tr|j}| j }z|tjd |f W n2 tj	k
rd   |tjd |
| jf Y nX | }|d kr~t|| jf |S )Nz WHERE id = ?)r$   r   idr%   r0   r1   r   _SELECTr'   ProgrammingErrordecoder-   r2   r   rG   )r=   keyr?   rL   r   r   r   __getitem__  s    


zFeatureDB.__getitem__Nc                 C   sH   | j  }|dk	r"|d|f n
|d | }|dk	rD|d }|S )a  
        Simple count of features.

        Can be faster than "grep", and is faster than checking the length of
        results from :meth:`gffutils.FeatureDB.features_of_type`.

        Parameters
        ----------

        featuretype : string

            Feature type (e.g., "gene") to count.  If None, then count *all*
            features in the database.

        Returns
        -------
        The number of features of this type, as an integer

        Nzd
                SELECT count() FROM features
                WHERE featuretype = ?
                z>
                SELECT count() FROM features
                r   )r%   r0   r1   r2   )r=   featuretyper?   rL   r   r   r   count_features_of_type  s    
z FeatureDB.count_features_of_typec           
   	   c   s@   t jg ||||||d\}}| ||D ]}	| jf |	V  q(dS )z
        Returns an iterator of :class:`gffutils.Feature` objects.

        Parameters
        ----------
        {_method_doc}
        )argslimitrT   order_byreversestrandcompletely_withinNr   
make_query_executerG   )
r=   rT   rW   rZ   rX   rY   r[   queryrV   rB   r   r   r   features_of_typeA  s    


zFeatureDB.features_of_typegenec           	      c   s6   | j |d}|D ] }|gt| |j }|V  qdS )a  
        For each parent of type `featuretype`, yield a list L of that parent
        and all of its children (`[parent] + list(children)`). The parent will
        always be L[0].

        This is useful for "sanitizing" a GFF file for downstream tools.

        Additional kwargs are passed to :meth:`FeatureDB.children`, and will
        therefore only affect items L[1:] in each yielded list.
        )rT   N)all_featuresrH   r   rN   )	r=   rT   levelrX   rY   r[   Zparent_recsZ
parent_recZunit_recordsr   r   r   iter_by_parent_childsY  s
    zFeatureDB.iter_by_parent_childsc           
   	   c   s@   t jg ||||||d\}}| ||D ]}	| jf |	V  q(dS )z
        Iterate through the entire database.

        Returns
        -------
        A generator object that yields :class:`Feature` objects.

        Parameters
        ----------
        {_method_doc}
        )rV   rW   rZ   rT   rX   rY   r[   Nr\   )
r=   rW   rZ   rT   rX   rY   r[   r_   rV   rB   r   r   r   rb   o  s    
	zFeatureDB.all_featuresc                 c   s*   | j  }|d |D ]\}|V  qdS )z
        Iterate over feature types found in the database.

        Returns
        -------
        A generator object that yields featuretypes (as strings)
        zC
            SELECT DISTINCT featuretype from features
            Nr%   r0   r1   )r=   r?   rB   r   r   r   featuretypes  s    

zFeatureDB.featuretypesc
              
   c   s   t |tr|j}djf t }
|g}d}|dk	r>d}|| tj||
|||||	|d\}}|dd}| 	||D ]}| j
f |V  qtdS )a  
        Parameters
        ----------

        id : string or a Feature object

        level : None or int

            If `level=None` (default), then return all children regardless
            of level.  If `level` is an integer, then constrain to just that
            level.
        {_method_doc}

        Returns
        -------
        A generator object that yields :class:`Feature` objects.
        zs
        JOIN relations
        ON relations.{join_on} = features.id
        WHERE relations.{join_to} = ?
         Nzrelations.level = ?)rV   otherextrarT   rX   rY   rW   r[   ZSELECTzSELECT DISTINCT)r$   r   rN   formatlocalsrK   r   r]   replacer^   rG   )r=   rN   join_onjoin_torc   rT   rX   rY   r[   rW   rh   rV   Zlevel_clauser_   rB   r   r   r   	_relation  s.    


zFeatureDB._relationc                 C   s   | j |dd||||||d	S )zP
        Return children of feature `id`.
        {_relation_docstring}
        r   r   rm   rn   rc   rT   rX   rY   rW   r[   ro   )r=   rN   rc   rT   rX   rY   rW   r[   r   r   r   r     s          zFeatureDB.childrenc                 C   s   | j |dd||||||d	S )zO
        Return parents of feature `id`.
        {_relation_docstring}
        r   r   rp   rq   )r=   rN   rc   rT   rX   rY   r[   rW   r   r   r   parents  s          zFeatureDB.parentsc                 C   s*   || _ || _| j }||t| |S r   )_last_query
_last_argsr%   r0   r1   tuple)r=   r_   rV   r?   r   r   r   r^     s
    
zFeatureDB._executec                 C   s   | j  }||S )a  
        Execute arbitrary queries on the db.

        .. seealso::

                :class:`FeatureDB.schema` may be helpful when writing your own
                queries.

        Parameters
        ----------

        query : str

            Query to execute -- trailing ";" optional.

        Returns
        -------
        A sqlite3.Cursor object that can be iterated over.
        re   )r=   r_   r?   r   r   r   r1     s    
zFeatureDB.executec                 C   s   |  d | j  dS )zg
        Runs the sqlite ANALYZE command to potentially speed up queries
        dramatically.
        zANALYZE featuresN)r1   r%   rD   )r=   r   r   r   analyze  s    
zFeatureDB.analyzec                 c   s  |dk	r|dk	s |dk	s |dk	r(t dt|tjr|d}t|dkr\|d }d\}}q|dd \}}	t|dkr|d }|	d	\}}n6t|tr|j}|j}|j	}|j
}n|dd \}}}|rd
}
d}nd}
d}|| }}g }g }|dk	r|d || |dk	r6t|}|d|
  || |dk	r`t|}|d|  || d|}d}|dk	r|dk	r|r|tjkr|tjkrttj||dd}t|dk rddd |D }d| }||7 }dtjd||g}|dk	rBt|tjr|g}ddd |D }|d| 7 }|| |dk	rbd}||7 }|| | j }|| _|| _||||d| _||t| |D ]}| jf |V  qdS ) a  
        Return features within specified genomic coordinates.

        Specifying genomic coordinates can be done in a flexible manner

        Parameters
        ----------
        region : string, tuple, or Feature instance
            If string, then of the form "seqid:start-end".  If tuple, then
            (seqid, start, end).  If :class:`Feature`, then use the features
            seqid, start, and end values.

            This argument is mutually exclusive with start/end/seqid.

            *Note*: By design, even if a feature is provided, its strand will
            be ignored.  If you want to restrict the output by strand, use the
            separate `strand` kwarg.

        strand : + | - | . | None
            If `strand` is provided, then only those features exactly matching
            `strand` will be returned. So `strand='.'` will only return
            unstranded features. Default is `strand=None` which does not
            restrict by strand.

        seqid, start, end, strand
            Mutually exclusive with `region`.  These kwargs can be used to
            approximate slice notation; see "Details" section below.

        featuretype : None, string, or iterable
            If not None, then restrict output.  If string, then only report
            that feature type.  If iterable, then report all featuretypes in
            the iterable.

        completely_within : bool
            By default (`completely_within=False`), returns features that
            partially or completely overlap `region`.  If
            `completely_within=True`, features that are completely within
            `region` will be returned.

        Notes
        -------

        The meaning of `seqid`, `start`, and `end` is interpreted as follows:

        ====== ====== ===== ======================================
        seqid  start  end   meaning
        ====== ====== ===== ======================================
        str    int    int   equivalent to `region` kwarg
        None   int    int   features from all chroms within coords
        str    None   int   equivalent to [:end] slice notation
        str    int    None  equivalent to [start:] slice notation
        None   None   None  equivalent to FeatureDB.all_features()
        ====== ====== ===== ======================================

        If performance is a concern, use `completely_within=True`. This allows
        the query to be optimized by only looking for features that fall in the
        precise genomic bin (same strategy as UCSC Genome Browser and
        BEDTools). Otherwise all features' start/stop coords need to be
        searched to see if they partially overlap the region of interest.

        Examples
        --------

        - `region(seqid="chr1", start=1000)` returns all features on chr1 that
          start or extend past position 1000

        - `region(seqid="chr1", start=1000, completely_within=True)` returns
          all features on chr1 that start past position 1000.

        - `region("chr1:1-100", strand="+", completely_within=True)` returns
          only plus-strand features that completely fall within positions 1 to
          100 on chr1.

        Returns
        -------
        A generator object that yields :class:`Feature` objects.
        NzLIf region is supplied, do not supply seqid, start, or end as separate kwargs:r   r   )NN      -z>=z<=<>z	seqid = ?z
start %s ?zend %s ?z AND rg   FZonei  z or c                 S   s   g | ]}d qS )zbin = ?r   r   _r   r   r   r"     s     z$FeatureDB.region.<locals>.<listcomp>z
AND ( %s ) zWHERE c                 S   s   g | ]}d qS )zfeaturetype = ?r   r~   r   r   r   r"     s     z
 AND (%s) z and strand = ? )startendseqidregion)r(   r$   sixstring_typessplitr   r   r   r   r   rZ   rK   r7   r   r   ZMAX_CHROM_SIZErH   r   rO   extendr%   r0   rs   rt   Z_contextr1   ru   rG   )r=   r   r   r   r   rZ   rT   r[   ZtoksZcoordsZstart_opZend_oprV   Zposition_clauseZ_bin_clauseZ_binsr_   Zfeature_clauseZstrand_clauser?   rB   r   r   r   r     s    P
















zFeatureDB.regionTc                 c   s.  t |D ]\}}|dkr&|j}	|}
q|j}|dkrDd|
j|jf }|
j|jkrVd}n|j}|
j|jkrn|}
q|}|
j}|	d7 }	|d8 }|rt|
j|j}ni }|r|	| t
j
|	|dd}d}t|d||	|d|d||d	
}|dkrz
| j}W n tk
r   d}Y nX | jf |V  |j}	|}
qdS )
a  
        Construct new features representing the space between features.

        For example, if `features` is a list of exons, then this method will
        return the introns.  If `features` is a list of genes, then this method
        will return the intergenic regions.

        Providing N features will return N - 1 new features.

        This method purposefully does *not* do any merging or sorting of
        coordinates, so you may want to use :meth:`FeatureDB.merge` first, or
        when selecting features use the `order_by` kwarg, e.g.,
        `db.features_of_type('gene', order_by=('seqid', 'start'))`.

        Parameters
        ----------
        features : iterable of :class:`feature.Feature` instances
            Sorted, merged iterable

        new_featuretype : string or None
            The new features will all be of this type, or, if None (default)
            then the featuretypes will be constructed from the neighboring
            features, e.g., `inter_exon_exon`.

        merge_attributes : bool
            If True, new features' attributes will be a merge of the neighboring
            features' attributes.  This is useful if you have provided a list of
            exons; the introns will then retain the transcript and/or gene
            parents as a single item. Otherwise, if False, the attribute will
            be a comma-separated list of values, potentially listing the same
            gene ID twice.

        attribute_func : callable or None
            If None, then nothing special is done to the attributes.  If
            callable, then the callable accepts two attribute dictionaries and
            returns a single attribute dictionary.  If `merge_attributes` is
            True, then `attribute_func` is called before `merge_attributes`.
            This could be useful for manually managing IDs for the new
            features.

        update_attributes : dict
            After attributes have been modified and merged, this dictionary can
            be used to replace parts of the attributes dictionary.

        Returns
        -------
        A generator that yields :class:`Feature` objects
        r   Nzinter_%s_%s.r   Tr}   Zgffutils_derived)
r   r   rT   r   r   scorerZ   framer
   bin)	enumeratestopr   rT   rZ   chromr   merge_attributesr
   updater   dictr4   AttributeErrorrG   )r=   featuresnew_featuretyper   r4   Zattribute_funcZupdate_attributesrB   fZinterfeature_startZlast_featureZinterfeature_stopZ
new_strandrZ   r   Znew_attributesZnew_bin_idfieldsr   r   r   interfeatures  sn    3  
  


zFeatureDB.interfeaturesc           	      K   s   |r&t | jtjr&t| j| jd  | j }d}d}t |trJ|	 }t |tjr\|g}t |t
rl|g}|D ]:}t |tjr|}n|j}|||f ||||f qp| j  | S )a  
        Delete features from database.

        features : str, iterable, FeatureDB instance
            If FeatureDB, all features will be used. If string, assume it's the
            ID of the feature to remove. Otherwise, assume it's an iterable of
            Feature objects. The classes in gffutils.iterators may be helpful
            in this case.

        make_backup : bool
            If True, and the database you're about to update is a file on disk,
            makes a copy of the existing database and saves it with a .bak
            extension.

        Returns
        -------
        FeatureDB object, with features deleted.
        .bakz3
        DELETE FROM features WHERE id = ?
        zE
        DELETE FROM relations WHERE parent = ? OR child = ?
        )r$   r&   r   r   shutilcopy2r%   r0   r   rb   r   rN   r1   rD   )	r=   r   make_backuprF   r?   Zquery1Zquery2r   r   r   r   r   delete6  s(    



zFeatureDB.deletec                 K   sR  ddl m} ddl m} |r>t| jtjr>t| j| jd  i }|	 D ]\}}|t
jkrJ|||< qJ|j|f|}| j|d< | jd dkrd|krd	d
d|d< |jf || j| jd|}	n@| jd dkrd|krd|d< |jf || j| jd|}	nt||jd\}
|_t|
dkr&|	S |	| |	  |	  | j|	j | S )a  
        Update the on-disk database with features in `data`.

        WARNING: If you used any non-default kwargs for gffutils.create_db when
        creating the database in the first place (especially
        `disable_infer_transcripts` or `disable_infer_genes`) then you should
        use those same arguments here.

        The returned object is the same FeatureDB, but since it is pointing to
        the same database and that has been just updated, the new features can
        now be accessed.

        Parameters
        ----------

        data : str, iterable, FeatureDB instance
            If FeatureDB, all data will be used. If string, assume it's
            a filename of a GFF or GTF file.  Otherwise, assume it's an
            iterable of Feature objects.  The classes in gffutils.iterators may
            be helpful in this case.

        make_backup : bool
            If True, and the database you're about to update is a file on disk,
            makes a copy of the existing database and saves it with a .bak
            extension.

        kwargs :
            Additional kwargs are passed to gffutils.create_db; see the help
            for that function for details.

        Returns
        -------
        Returns self but with the underlying database updated.
        r   r    )	iteratorsr   r8   ZfmtZgtfZid_specZgene_idZtranscript_id)ra   Z
transcript)datar&   r4   Zgff3r	   r   )r#   r!   r   r$   r&   r   r   r   r   rC   r   _iterator_kwargsZDataIteratorr8   r4   Z_GTFDBCreatorZ_GFFDBCreatorr(   peekZ_iterr   Z_populate_from_linesZ_update_relationsZ	_finalizer   )r=   r   r   rF   r!   r   r   kvZdbr   r   r   r   r   d  sT    $


 
     
zFeatureDB.updatec                 C   s   t |tjr| | }t |tjr(| | }| j }|d|j|j|f |dk	rf|||}| || |dk	r|||}| || | j  | S )a  
        Manually add relations to the database.

        Parameters
        ----------
        parent : str or Feature instance
             Parent feature to add.

        child : str or Feature instance
            Child feature to add

        level : int
            Level of the relation.  For example, if parent is a gene and child
            is an mRNA, then you might want level to be 1.  But if child is an
            exon, then level would be 2.

        parent_func, child_func : callable
            These optional functions control how attributes are updated in the
            database.  They both have the signature `func(parent, child)` and
            must return a [possibly modified] Feature instance.  For example,
            we could add the child's database id as the "child" attribute in
            the parent::

                def parent_func(parent, child):
                    parent.attributes['child'] = child.id

            and add the parent's "gene_id" as the child's "Parent" attribute::

                def child_func(parent, child):
                    child.attributes['Parent'] = parent['gene_id']

        Returns
        -------
        FeatureDB object with new relations added.
        zb
                  INSERT INTO relations (parent, child, level)
                  VALUES (?, ?, ?)N)	r$   r   r   r%   r0   r1   rN   _updaterD   )r=   r   r   rc   Zparent_func
child_funcr?   r   r   r   add_relation  s     %



zFeatureDB.add_relationc                 C   s0   t | |jg g}|jtjft|  d S r   )rH   astuplerN   r1   r   Z_UPDATEru   )r=   r   r0   valuesr   r   r   r     s    zFeatureDB._updatec              	   C   sH   z| tj|  W n, tjk
rB   | tj|| j Y nX dS )z5
        Insert a feature into the database.
        N)r1   r   Z_INSERTr   r'   rP   r-   )r=   r   r0   r   r   r   _insert  s     
zFeatureDB._insertexonintronc           
      #   s    rs dkr dkr t d r4 fdd}nrFfdd}| D ]6}j|d|dd}j|||jd	D ]
}	|	V  qvqLdS )
a  
        Create introns from existing annotations.


        Parameters
        ----------
        exon_featuretype : string
            Feature type to use in order to infer introns.  Typically `"exon"`.

        grandparent_featuretype : string
            If `grandparent_featuretype` is not None, then group exons by
            children of this featuretype.  If `granparent_featuretype` is
            "gene" (default), then introns will be created for all first-level
            children of genes.  This may include mRNA, rRNA, ncRNA, etc.  If
            you only want to infer introns from one of these featuretypes
            (e.g., mRNA), then use the `parent_featuretype` kwarg which is
            mutually exclusive with `grandparent_featuretype`.

        parent_featuretype : string
            If `parent_featuretype` is not None, then only use this featuretype
            to infer introns.  Use this if you only want a subset of
            featuretypes to have introns (e.g., "mRNA" only, and not ncRNA or
            rRNA). Mutually exclusive with `grandparent_featuretype`.

        new_featuretype : string
            Feature type to use for the inferred introns; default is
            `"intron"`.

        merge_attributes : bool
            Whether or not to merge attributes from all exons. If False then no
            attributes will be created for the introns.

        Returns
        -------
        A generator object that yields :class:`Feature` objects representing
        new introns

        Notes
        -----
        The returned generator can be passed directly to the
        :meth:`FeatureDB.update` method to permanently add them to the
        database, e.g., ::

            db.update(db.create_introns())

        NzSexactly one of `grandparent_featuretype` or `parent_featuretype` should be providedc                  3   s.     D ]} j| ddD ]
}|V  qq
d S )Nr   )rc   )r`   r   )ra   r   )grandparent_featuretyper=   r   r   	child_gen4  s    z+FeatureDB.create_introns.<locals>.child_genc                  3   s     D ]
} | V  q
d S r   )r`   )r   )parent_featuretyper=   r   r   r   9  s    r   r   )rc   rT   rX   )r   r   r4   )r(   r   r   r4   )
r=   Zexon_featuretyper   r   r   r   r   r   exonsr   r   )r   r   r=   r   create_introns  s*    1

  
zFeatureDB.create_intronsc                 c   sF  t |}t|dkrt|r"d}n.dd |D }tt|dkrHtd|d }dd |D }tt|dkrvtd|d }|d j}|d j}|dd	 D ]`}	|	j|d kr|	j|kr|	j}qqqt|	j	d|	j
||d|dd
d	}
| jf |
V  |	j}|	j}qt|dkr|d }	t|	j	d|	j
||d|dd
d	}
| jf |
V  d	S )ay  
        DEPRECATED, only retained here for backwards compatibility. Please use
        merge().

        Merge overlapping features together.

        Parameters
        ----------

        features : iterator of Feature instances

        ignore_strand : bool
            If True, features on multiple strands will be merged, and the final
            strand will be set to '.'.  Otherwise, ValueError will be raised if
            trying to merge features on differnt strands.

        Returns
        -------
        A generator object that yields :class:`Feature` objects representing
        the newly merged features.
        r   r   c                 S   s   g | ]
}|j qS r   )rZ   rA   r   r   r   r"   g  s     z(FeatureDB._old_merge.<locals>.<listcomp>r   z?Specify ignore_strand=True to force merging of multiple strandsc                 S   s   g | ]
}|j qS r   )r   rA   r   r   r   r"   n  s     z,Merging multiple chromosomes not implementedNrg   )	r   r   rT   r   r   r   rZ   r   r
   )rH   r   StopIterationr   r(   NotImplementedErrorr   r   r   r   rT   rG   )r=   r   ignore_strandrZ   ZstrandsZchromsr   Zcurrent_merged_startZcurrent_merged_stopr   Zmerged_featurer   r   r   
_old_mergeF  s`    




zFeatureDB._old_mergec                 #   sB  t |ts2zt|}W n tk
r0   |g}Y nX t|}d}d g |D ]މ dkrtfdd|D r| gqJttV  d}qJtdkrt fdd|D r  nt tV   d}qJt fdd|D rtdkrvt	 
   d=  d	=  d
=  d=  d= | jf   |sh| j j  d7  <  jd t| j j  }| d< | _ j jdkr  jdj 7  _j jkrd _j jkrd _j jkrd _j jk rj _j jkr*j _qJt V   g d}qJ r>t V  dS )a  
        Merge features matching criteria together


        Returned Features have a special property called 'children' that is
        a list of the component features.  This only exists for the lifetime of
        the Feature instance.

        Parameters
        ----------
        features : iterable
            Iterable of Feature instances to merge

        merge_criteria : list
            List of merge criteria callbacks. All must evaluate to True in
            order for a feature to be merged. See notes below on callback
            signature.

        multiline : bool
            True to emit multiple features with the same ID attribute, False
            otherwise.

        Returns
        -------
        Generator yielding merged Features

        Notes
        -----

        See the `gffutils.merge_criteria` module (imported here as `mc`) for
        existing callback functions. For writing custom callbacks, functions
        must have the following signature::

            callback(
                acc: gffutils.Feature,
                cur: gffutils.Feature,
                components: [gffutils.Feature]
            ) -> bool

        Where:

            - `acc`: current accumulated feature
            - `cur`: candidate feature to merge
            - `components`: list of features that compose acc

        The function should return True to merge `cur` into `acc`, False to set
        `cur` to `acc` (that is, start a new merged feature).


        If merge criteria allows different feature types then the merged
        features' feature types should have their featuretype property
        reassigned to a more specific ontology value.
        Nc                 3   s   | ]}|  V  qd S r   r   r   Zcriteriar   r   r   r     s     z"FeatureDB.merge.<locals>.<genexpr>r   c                 3   s   | ]}|  V  qd S r   r   r   )current_mergedr   r   r   r     s     c                 3   s   | ]}| V  qd S r   r   r   r   r   r   r   r   r      s     r   r
   ri   r4   r.   r/   r   r	   r   r   Zsequence_feature)r$   rH   	TypeErroriterallr   r   r   rK   varscopyrG   r8   rT   strrN   r   r   rZ   r   r   r   )r=   r   r   Z	multilineZlast_idr   r   r   merge  sv    8

	


    
zFeatureDB.merge)r   rT   rZ   r   r   c           	   	   C   s   t |sd}g }|D ]v}| j| j||d|dD ]X}|jr0| || j  |r^| |j n|jD ]}| j||dt	d qd|
| q0q0q|S )a  
        Merge all features in database according to criteria.
        Merged features will be assigned as children of the merged record.
        The resulting records are added to the database.

        Parameters
        ----------
        merge_order : list
            Ordered list of columns with which to group features before evaluating criteria
        merge_criteria : list
            List of merge criteria callbacks. See merge().
        featuretypes_groups : list
            iterable of sets of featuretypes to merge together
        exclude_components : bool
            True: child features will be discarded. False to keep them.

        Returns
        -------
            list of merge features
        r   rT   rX   r   r   )r   )r   r   rb   r   r   r%   r0   r   r   r   rK   )	r=   Zmerge_orderr   Zfeaturetypes_groupsZexclude_componentsZresult_featuresZfeaturegroupZmergedr   r   r   r   	merge_all3  s     

zFeatureDB.merge_allc                 C   s@   | j ||dd}|r"| j||d}d}|D ]}|t|7 }q*|S )a  
        Total bp of all children of a featuretype.

        Useful for getting the exonic bp of an mRNA.

        Parameters
        ----------

        feature : str or Feature instance

        child_featuretype : str
            Which featuretype to consider.  For example, to get exonic bp of an
            mRNA, use `child_featuretype='exon'`.

        merge : bool
            Whether or not to merge child features together before summing
            them.

        ignore_strand : bool
            If True, then overlapping features on different strands will be
            merged together; otherwise, merging features with different strands
            will result in a ValueError.

        Returns
        -------
        Integer representing the total number of bp.
        r   r   )r   r   )r   r   r   )r=   r   Zchild_featuretyper   r   r   totalr   r   r   r   children_bpg  s    zFeatureDB.children_bpZCDSr	   c                    sL  |r|rt dt| j||dd}t|dkr6|g}| | }|d j}|d j}	||jkrnt d||jf |	|jkrt d|	|jf |dkrd	}|d
d }|j}
|jd  |j}t	j
}dt	_
z|| d }W n tk
r   d}Y nX |t	_
|j}|dkrd}|j}|}t|}dd |D } fdd|D }|rt| j||dd}t|dkrt|j}|j}n|d jd }|d j}|rt| j||dd}t|dkr|j}|j}n|d j}|d jd } |d  |d  }||kstd||f |
 ||||||||dtt|dtt|g}dtt|S )a;
  
        Converts `feature` into a BED12 format.

        GFF and GTF files do not necessarily define genes consistently, so this
        method provides flexiblity in specifying what to call a "transcript".

        Parameters
        ----------
        feature : str or Feature instance
            In most cases, this feature should be a transcript rather than
            a gene.

        block_featuretype : str or list
            Which featuretype to use as the exons. These are represented as
            blocks in the BED12 format.  Typically 'exon'.

            Use the `thick_featuretype` and `thin_featuretype` arguments to
            control the display of CDS as thicker blocks and UTRs as thinner
            blocks.

            Note that the features for `thick` or `thin` are *not*
            automatically included in the blocks; if you do want them included,
            then those featuretypes should be added to this `block_features`
            list.

            If no child features of type `block_featuretype` are found, then
            the full `feature` is returned in BED12 format as if it had
            a single exon.

        thick_featuretype : str or list
            Child featuretype(s) to use in order to determine the boundaries of
            the "thick" blocks. In BED12 format, these represent coding
            sequences; typically this would be set to "CDS".  This argument is
            mutually exclusive with `thin_featuretype`.

            Specifically, the BED12 thickStart will be the start coord of the
            first `thick` item and the thickEnd will be the stop coord of the
            last `thick` item.

        thin_featuretype : str or list
            Child featuretype(s) to use in order to determine the boundaries of
            the "thin" blocks.  In BED12 format, these represent untranslated
            regions.  Typically "utr" or ['three_prime_UTR', 'five_prime_UTR'].
            Mutually exclusive with `thick_featuretype`.

            Specifically, the BED12 thickStart field will be the stop coord of
            the first `thin` item and the thickEnd field will be the start
            coord of the last `thin` item.

        name_field : str
            Which attribute of `feature` to use as the feature's name.  If this
            field is not present, a "." placeholder will be used instead.

        color : None or str
            If None, then use black (0,0,0) as the RGB color; otherwise this
            should be a comma-separated string of R,G,B values each of which
            are integers in the range 0-255.
        zACan only specify one of `thick_featuertype` or `thin_featuretype`r   r   r   z=Start of first exon (%s) does not match start of feature (%s)z8End of last exon (%s) does not match end of feature (%s)Nz0,0,0r   rg   r   Tr   0c                 S   s   g | ]}t |qS r   )r   rA   r   r   r   r"     s     z#FeatureDB.bed12.<locals>.<listcomp>c                    s   g | ]}|j d    qS )r   )r   rA   Z
chromStartr   r   r"     s     ztst=%s; chromEnd=%sr   	)r(   rH   r   r   r   r   rl   stripr   r   Zalways_return_listKeyErrorr   rZ   AssertionErrorr   mapr   )r=   r   Zblock_featuretypeZthick_featuretypeZthin_featuretypeZ
name_fieldZcolorr   firstZlastr   ZchromEndZorignamer   rZ   ZitemRgbZ
blockCountZ
blockSizesZblockStartsZthickZ
thickStartZthickEndZthinZtstr   r   r   r   bed12  s    =











zFeatureDB.bed12)Z_relation_docstring)_method_doc)N)NNNFF)ra   NNFF)NNNNFF)NNNFFN)NNNFNF)NNNFFN)NNNNNNF)NTNNN)T)T)NN)r   ra   Nr   T)F)r   FF).__name__
__module____qualname__r   r   Zdefault_pragmasr'   ZOptimizedUnicoder@   r9   rG   r:   rM   rS   rU   r`   rd   rb   rf   ro   r   rr   r^   r1   rv   r   r   r   r   r   r   r   r   r   mcr   Zoverlap_end_inclusiverZ   Zfeature_typer   r   r   r   __doc__rj   methodr   r   r   r   r   B   s   
x	
&     
     
      
       
8      
      
      
 4       
w
.
P  
9
       
J
d
 
4  
(  
 r   )r5   r)   r   r'   r   r;   r#   r   r   r   r   r   Zgffutils.featurer   Zgffutils.exceptionsr   r   ru   r   r   objectr   r   r   r   r   <module>   s   