Make a bed file with promoters for transcripts or genes from the Gencode GTF
    file.

    Parameters
    ----------
    gtf : str
        Filename of the Gencode gtf file.

    up : int
        Number of bases to add upstream of the transcription start site.

    down : int
        Number of bases to add downstream of the transcription start site.

    feature : str
        Either transcript or gene. If transcript, promoters, for each transcript
        will be included. If gene, a promoter for each gene entry in the GTF
        will be included.

    use_gene_id : bool
        If True and feature='transcript', the gene ID will be used to name each
        promoter rather than the transcript ID.

    merge_by_gene : bool
        If True and feature='transcript', promoters from all of the transcripts
        of the same gene will be merged and named using the gene ID. This is
        useful for knowing what sequences are promoters for a given gene.

    tss : bool
        Output the transcription start site for each transcript. The up and down
        options will be ignored and the single base that is the TSS will be
        written.

    out : str
        If provided, the bed file will be written to a file with this name.

    unique_name : bool
        If True, ensure that the name column is unique. If the column isn't
        unique, the coordinates are added to the column to make it unique.

    Returns
    -------
    bed : pybedtools.BedTool
        A sorted pybedtools BedTool object.

def make_promoter_bed(
    gtf, 
    up=2000, 
    down=200, 
    feature='transcript',
    use_gene_id=False, 
    merge_by_gene=False, 
    tss=False,
    out=None, 
    unique_name=True,
):
    """
    Make a bed file with promoters for transcripts or genes from the Gencode GTF
    file.

    Parameters
    ----------
    gtf : str
        Filename of the Gencode gtf file.

    up : int
        Number of bases to add upstream of the transcription start site.

    down : int
        Number of bases to add downstream of the transcription start site.

    feature : str
        Either transcript or gene. If transcript, promoters, for each transcript
        will be included. If gene, a promoter for each gene entry in the GTF
        will be included.

    use_gene_id : bool
        If True and feature='transcript', the gene ID will be used to name each
        promoter rather than the transcript ID.

    merge_by_gene : bool
        If True and feature='transcript', promoters from all of the transcripts
        of the same gene will be merged and named using the gene ID. This is
        useful for knowing what sequences are promoters for a given gene.

    tss : bool
        Output the transcription start site for each transcript. The up and down
        options will be ignored and the single base that is the TSS will be
        written.

    out : str
        If provided, the bed file will be written to a file with this name.

    unique_name : bool
        If True, ensure that the name column is unique. If the column isn't
        unique, the coordinates are added to the column to make it unique.

    Returns
    -------
    bed : pybedtools.BedTool
        A sorted pybedtools BedTool object. 

    """
    import HTSeq
    import itertools as it

    if merge_by_gene:
        use_gene_id = True

    plus_feats = []
    minus_feats = []
    if feature == 'gene' or use_gene_id or merge_by_gene:
        name_id = 'gene_id'
    elif feature == 'transcript':
        name_id = 'transcript_id'

    if tss:
        ftype = 'tss'
    else:
        ftype = 'promoter'

    gtf = it.islice(HTSeq.GFF_Reader(gtf), None)
    line = gtf.next()
    while line != '':
        if line.type == feature:
            if line.iv.strand == '+':
                plus_feats.append(
                    ('\t'.join([line.iv.chrom, str(line.iv.start),
                                str(line.iv.start),
                                '{}_{}'.format(line.attr[name_id], ftype), '.',
                                line.iv.strand])))
            elif line.iv.strand == '-':
                minus_feats.append(
                    ('\t'.join([line.iv.chrom, str(line.iv.end),
                                str(line.iv.end),
                                '{}_{}'.format(line.attr[name_id], ftype), '.',
                                line.iv.strand])))
        try:
            line = gtf.next()
        except StopIteration:
            line = ''

    plus = pbt.BedTool('\n'.join(plus_feats) + '\n', from_string=True)
    minus = pbt.BedTool('\n'.join(minus_feats) + '\n', from_string=True)

    if tss:
        plus = plus.slop(l=0, r=1, g=pbt.chromsizes('hg19'))
        minus = minus.slop(l=1, r=0, g=pbt.chromsizes('hg19'))
    else:
        plus = plus.slop(l=up, r=down, g=pbt.chromsizes('hg19'))
        minus = minus.slop(l=down, r=up, g=pbt.chromsizes('hg19'))

    if merge_by_gene:
        plus = merge_bed_by_name(plus)
        minus = merge_bed_by_name(minus)

    bt  = plus.cat(minus, postmerge=False)
    # We'll sort so bedtools operations can be done faster.
    bt = bt.sort()

    if unique_name:
        df = bt.to_dataframe()
        if len(set(df['name'])) != df.shape[0]:
            df['name'] = (df.name + '_' + df.chrom.astype(str) + ':' +
                          df.start.astype(str) + '-' + df.end.astype(str))
            bt = pbt.BedTool('\n'.join(['\t'.join([str(x) for x in y]) for y in
                                        df.values]) + '\n', from_string=True)

    if out:
        bt.saveas(out)
    return bt