import gzip
import mmap


def readSeq(fastaFile, seqName):
    
    """Read a single sequence from a fasta file, and return list of sequence.
    
    Args:
        fastaFile (str): Name of fasta file. Accepts uncompressed and gzipped files.
        seqName (str): Name of sequence within fasta file.
    
    Returns:
        list: Sequence split into a list of entirely uppercase letters
            (e.g., 'aCgT' --> ['A', 'C', 'G', 'T']).
    """
    
    assert all([fastaFile.__class__ == str, seqName.__class__ == str]), \
        '`fastaFile` and `seqName` must both be strings.'
    
    if fastaFile.endswith('gz'):
        file = gzip.open(fastaFile, 'r')
    else:
        file = open(fastaFile, 'r')
        
    file_mm = mmap.mmap(file.fileno(), 0, access = mmap.ACCESS_READ)
    
    # Finding the position of the sequence name
    nameInd = file_mm.find(seqName.encode('utf-8'))
    file_mm.seek(nameInd)
    
    # The actual sequence will begin right after the next newline
    start = file_mm.find(b'\n') + 1
    file_mm.seek(start)
    
    # The sequence will end right before the next '>'
    end = file_mm.find(b'>')
    
    # Seek to `start`, read a bytes object of length `end - start`, 
    #   convert to string, remove newlines, and make uppercase
    file_mm.seek(start)
    seqStr = file_mm.read(end - start).decode().replace('\n', '').upper()
    seqList = list(seqStr)
    
    file_mm.close()
    file.close()
    
    assert seqList.__class__ == list, 'Function returned a non-list.'
    assert all([x.__class__ == str for x in seqList]), \
        'Item(s) in return list are non-strings.'

    return seqList