From 2fc00396d3775aae536195d08fffe303ec572457 Mon Sep 17 00:00:00 2001 From: mmestreprofluent Date: Thu, 25 Apr 2024 19:36:23 +0000 Subject: [PATCH 1/2] Fix undesired print with quiet=True --- build/lib/pyblast/__init__.py | 3 + build/lib/pyblast/bloc.py | 438 ++++++++++++++++++++++++++ build/lib/pyblast/qblast.py | 237 ++++++++++++++ build/lib/pyblast/utils.py | 129 ++++++++ dist/pyblast-0.0.0-py3.10.egg | Bin 0 -> 24709 bytes pyblast.egg-info/PKG-INFO | 3 + pyblast.egg-info/SOURCES.txt | 10 + pyblast.egg-info/dependency_links.txt | 1 + pyblast.egg-info/top_level.txt | 1 + pyblast/utils.py | 23 +- 10 files changed, 836 insertions(+), 9 deletions(-) create mode 100644 build/lib/pyblast/__init__.py create mode 100644 build/lib/pyblast/bloc.py create mode 100644 build/lib/pyblast/qblast.py create mode 100644 build/lib/pyblast/utils.py create mode 100644 dist/pyblast-0.0.0-py3.10.egg create mode 100644 pyblast.egg-info/PKG-INFO create mode 100644 pyblast.egg-info/SOURCES.txt create mode 100644 pyblast.egg-info/dependency_links.txt create mode 100644 pyblast.egg-info/top_level.txt diff --git a/build/lib/pyblast/__init__.py b/build/lib/pyblast/__init__.py new file mode 100644 index 0000000..25555c7 --- /dev/null +++ b/build/lib/pyblast/__init__.py @@ -0,0 +1,3 @@ +from pyblast.bloc import * +from pyblast.qblast import QBlastResult, launch_qblasts +from pyblast.utils import TMPFasta \ No newline at end of file diff --git a/build/lib/pyblast/bloc.py b/build/lib/pyblast/bloc.py new file mode 100644 index 0000000..d6e4978 --- /dev/null +++ b/build/lib/pyblast/bloc.py @@ -0,0 +1,438 @@ +import sys +from io import StringIO +from itertools import zip_longest +from copy import deepcopy +from collections import defaultdict +from itertools import product + +import pandas as pd + +from Bio.Seq import Seq +from Bio.Blast import Applications, NCBIXML +from Bio import SeqIO +from Bio import pairwise2 + +from pyblast import utils + +OSP = sys.platform + +class BlocBlastException(Exception) : + pass + +class BCLine() : + + clines = { + "blastp" : Applications.NcbiblastpCommandline, + "blastn" : Applications.NcbiblastnCommandline, + "blastx" : Applications.NcbiblastxCommandline, + "tblastn" : Applications.NcbitblastnCommandline, + "tblastx" : Applications.NcbitblastxCommandline, + "psiblast" : Applications.NcbipsiblastCommandline, + "pstblastn" : Applications.NcbirpstblastnCommandline, + "rpstblastn" : Applications.NcbirpstblastnCommandline, + "deltablast" : Applications.NcbideltablastCommandline + } + + def __init__(self, bkind, ** kwargs) : + + self.query = kwargs.get("query", None) + if not self.query : + raise BlocBlastException("Query not filled") + + if bkind not in BCLine.clines : + raise BlocBlastException("Bkind not found in available blast executable : %s" %(" ".join(BCLine.clines))) + + self.cline = BCLine.clines[bkind](** kwargs) + + def __setitem__(self, name, value) : + for parameter in self.cline.parameters : + if name in parameter.names : + parameter.value = value + return + + raise BlocBlastException("Parameter not found : '%s'" %(name)) + + def __str__(self) : + return str(self.cline) + + @staticmethod + def grouper_fdata(fdata, gsize) : + # based on itertools recipy + # https://docs.python.org/3.6/library/itertools.html#itertools-recipes + + args = [iter(fdata)] * gsize + groups = zip_longest(*args, fillvalue=None) + for group in groups : + yield [seq for seq in group if seq is not None] + + @utils.ftiming + def run_single_core(self, funcname="Blast command line (single)", postfun=None, quiet=False) : + if not quiet : print (self) + res = self.cline() + return postfun(res) if postfun else res + + @staticmethod + def run_single_core_tmp(tmpbcline, postfun=None, quiet=False) : + # since we're not able to pickle a BioPython commande line + # we're juste gonna execute it as a string + + if not quiet : print (str(tmpbcline)) + res = (utils.run_cmdline(str(tmpbcline), funcname="Blast command line (ncore)", quiet=quiet), None) + return postfun(res) if postfun else res + + def run_chunked(self, ncore, chunksize=200, postfun=None, quiet=False) : + fdata = SeqIO.parse(self.query, "fasta") + fdata = BCLine.grouper_fdata(fdata, chunksize) + queries = (utils.TMPFasta(group, quiet=quiet) for group in fdata) + + fun = BCLine.run_single_core_tmp + args = [utils.FunArgs(fun, TMPBCLine(self, query), postfun=postfun, quiet=quiet) for query in queries] + + return utils.mproc(args, ncore, quiet) + + def run(self, ncore=1, chunksize=200, postfun=None, quiet=False) : + if ncore == 1 : + return self.run_single_core(postfun=postfun, quiet=quiet) + else : + return self.run_chunked(ncore, chunksize, postfun=postfun, quiet=quiet) + + @staticmethod + def mbdb(db, ** kwargs) : + # Applications.NcbimakeblastdbCommandline not found + kwargs["in"] = db + cmd = "makeblastdb %s" %(" ".join("-%s %s" %(key, value) for key, value in kwargs.items())) + utils.run_cmdline(cmd) + + @staticmethod + def clean_fasta(fname, astmp=True) : + # Change header to match correct id with _ + # Replace ? to N + # Return a new fasta (tmp) + + def clean_seqrecord(seqrecord) : + name = seqrecord.description.replace(" ", "_") + seqrecord.id = seqrecord.name = seqrecord.description = name + sequence = str(seqrecord.seq).upper().replace("?", "N") + seqrecord.seq = Seq(sequence) + return seqrecord + + fdata = SeqIO.parse(fname, "fasta") + fdata = (clean_seqrecord(sr) for sr in fdata) + return utils.TMPFasta(fdata) if astmp else fdata + +class TMPBCLine() : + + # A single class used when we multiprocess blast + # We cannot pickle BioPython command line + # And we have to keep the TMPFasta instance alive otherwise the file is removed + + def __init__(self, bcline, tmpfasta) : + bcline = deepcopy(bcline) + bcline["query"] = str(tmpfasta) + + self.tmpfasta = tmpfasta + self.cmdline = str(bcline) + + def __str__(self) : + return self.cmdline + +class BCLine6(BCLine) : + + def __init__(self, bkind, ** kwargs) : + + kwargs, self.spec = self.add_specifiers(kwargs) + super(BCLine6, self).__init__(bkind, ** kwargs) + + def add_specifiers(self, kwargs) : + current = kwargs.get("outfmt", "").split(" ") + current = [value for value in current if value] + + toadd = ["qseqid", "sseqid", "pident", "nident", "length", "slen", + "qlen", "qstart", "qend", "sstart", "send", "positive"] + used = list(set(current) | set(toadd)) + + # This is weird, in some mac the usual command line leads + # to an error but not for all of them + # I will set this as an option instead + + if kwargs.pop("macos_formating", False) == True : + kwargs["outfmt"] = "'%s'" %("6 " + " ".join(used)) + else : + kwargs["outfmt"] = "%s" %("6 " + " ".join(used)) + + return kwargs, used + + def treat_res(self, res) : + + if isinstance(res, list) : + # multiprocessing res + return pd.concat([self.treat_res(r) for r in res]) + + out, err = res + if err : print (err) + + out = StringIO(out) + df = pd.read_csv(out, sep="\t", names=self.spec) + + # check if a column is full of na + # may happend if one gave a wrong outfmt + for column in df.columns : + if df[column].isnull().all() and not df.empty : + print ("""WARNING : An output column is full of nan : + This could be a result of a wrong outfmt key, + and may break result outputs\n""") + + df["qlength"] = abs(df["qend"] - df["qstart"]) + 1 + df["slength"] = abs(df["send"] - df["sstart"]) + 1 + + prc = [("qide", "nident", "qlen"), ("qcov", "qlength", "qlen"), ("qpos", "positive", "qlen"), + ("side", "nident", "slen"), ("scov", "slength", "slen"), ("spos", "positive", "slen")] + + for name, divident, divisor in prc : + df[name] = df[divident] * 100 / df[divisor] + + return df + + @staticmethod + def get_best(df, query="qseqid", column="qpos", nmatches=1, use_max=True) : + if use_max : + return BCLine6.get_best_max(df, query, column, nmatches) + else : + return BCLine6.get_best_min(df, query, column, nmatches) + + @staticmethod + def get_best_max(df, query="qseqid", column="qpos", nmatches=1) : + fun = (lambda serie : serie.nlargest(nmatches).min()) if nmatches > 1 else max + mask = df.groupby(query)[column].transform(fun) + return df[df[column] >= mask] + + @staticmethod + def get_best_min(df, query="qseqid", column="qpos", nmatches=1) : + fun = (lambda serie : serie.nsmallest(nmatches).max()) if nmatches > 1 else min + mask = df.groupby(query)[column].transform(fun) + return df[df[column] <= mask] + + @staticmethod + def query_mdbs(bkind, query, dbs, best=False, chunksize=200, ncore=1, + mcolumn="qpos", nmatches=1, ** kwargs) : + + res = [] + for db in dbs : + df = BCLine6(bkind, query=query, subject=db, ** kwargs).run(ncore=ncore, chunksize=chunksize) + if df.empty : continue + df["db"] = db + res.append(df) + + if not res : return pd.DataFrame() + res = pd.concat(res) + + return BCLine6.get_best(res, column=mcolumn, nmatches=nmatches) if best else res + + def run(self, * args, ** kwargs) : + res = super(BCLine6, self).run(* args, ** kwargs) + return self.treat_res(res) + +class BCLineHSPFuse(BCLine) : + + def __init__(self, bkind, ** kwargs) : + + kwargs = self.add_specifiers(kwargs) + super(BCLineHSPFuse, self).__init__(bkind, ** kwargs) + + def add_specifiers(self, kwargs) : + current = kwargs.get("outfmt", "").split(" ") + current = current[1:] if len(current) > 1 else [] + + kwargs["outfmt"] = "'%s'" %("5 " + " ".join(current)) + return kwargs + + @staticmethod + def parse_xml(data) : + f = StringIO(data) + try : return [record for record in NCBIXML.parse(f)] + except ValueError : return [] # empty results + + @staticmethod + def iter_hsp(records) : + for record in records : + qname, qlength = record.query, record.query_length + for aln in record.alignments : + sname, slength = aln.hit_def, aln.length + for hsp in aln.hsps : + yield (qname, qlength), (sname, slength), hsp + + @staticmethod + def get_hsp_ident(hsp, query=True) : + start = hsp.query_start if query else hsp.sbjct_start + end = hsp.query_end if query else hsp.sbjct_end + sequence = hsp.query if query else hsp.sbjct + reverse = start > end + + positives, ident = set(), set() + psequence = -1 + + for idx, letter in enumerate(sequence) : + letter_match = hsp.match[idx] + + if letter_match != ' ' : + position = start - psequence if reverse else psequence + start + positives.add(position) + if letter_match != "+" : ident.add(position) + + psequence = psequence + 1 if letter != "-" else psequence + + return positives, ident + + @staticmethod + def get_sum_sim_all(records) : + qlength, slength = {}, {} + data = defaultdict(lambda : defaultdict(list)) + + for query, subject, aln in BCLineHSPFuse.iter_hsp(records) : + qname, sname = query[0], subject[0] + qlen, slen = query[1], subject[1] + qlength[qname], qlength[sname] = qlen, slen + + qposit, qident = BCLineHSPFuse.get_hsp_ident(aln, query=True) + sposit, sident = BCLineHSPFuse.get_hsp_ident(aln, query=False) + positions = {"qide" : qident, "qpos" : qposit, "side" : sident, "spos" : sposit} + data[qname][sname].append(positions) + + df = [] + kinds = ["qide", "qpos", "side", "spos"] + + for qname, subject in data.items() : + for sname, all_positions in subject.items() : + + hsp_count = len(all_positions) + + if len(all_positions) > 1 : + intersection = {kind + "_inter" : len(set.intersection(* [positions[kind] + for positions in all_positions])) for kind in kinds} + + else : + intersection = {kind + "_inter" : 0 for kind in kinds} + + union = {kind : len(set.union(* [positions[kind] + for positions in all_positions])) for kind in kinds} + + info = {"qseqid" : qname, "sseqid" : sname, "#hsp" : hsp_count, ** union, ** intersection} + df.append(info) + + df = pd.DataFrame(df) + if df.empty : return df + + df["qlen"], df["slen"] = df["qseqid"].map(qlength), df["sseqid"].map(qlength) + df["qide_prc"] = df["qide"] * 100 / df["qlen"] + df["qpos_prc"] = df["qpos"] * 100 / df["qlen"] + df["side_prc"] = df["side"] * 100 / df["slen"] + df["spos_prc"] = df["spos"] * 100 / df["slen"] + + df = df[sorted(df.columns)] + return df + + @staticmethod + def post_treat(res) : + out, err = res + records = BCLineHSPFuse.parse_xml(out) + return BCLineHSPFuse.get_sum_sim_all(records) + + def treat_res(self, res) : + + if isinstance(res, list) : + # multiprocessing res + return pd.concat(res) + + return res + + def run(self, * args, ** kwargs) : + res = super(BCLineHSPFuse, self).run(* args, postfun=BCLineHSPFuse.post_treat, ** kwargs) + return self.treat_res(res) + +class GlobalAlignment() : + + def __init__(self, inputs) : + # inputs are an iterable of pairs of SeqRecords + self.inputs = inputs + + @staticmethod + def get_alignfun(name="globalxx") : + return getattr(pairwise2.align, name) + + @staticmethod + def aln_identity(query, subject, gapless=True) : + if gapless : + return sum(1 if b1 == b2 and b1 != "-" and b2 != "-" else 0 for b1, b2 in zip(query, subject)) + else : + return sum(1 if b1 == b2 else 0 for b1, b2 in zip(query, subject)) + + @staticmethod + def run_pair(query, subject, * args, gfun="globalxx", ** kwargs) : + gfun = GlobalAlignment.get_alignfun(gfun) + alns = gfun(query.seq, subject.seq, * args, ** kwargs) + return query, subject, alns + + @staticmethod + def run_pair_identity(query, subject, * args, gapless=True, addnames=True, ** kwargs) : + query, subject, alns = GlobalAlignment.run_pair(query, subject, * args, ** kwargs) + values = [] + + for aln in alns : + alnID = GlobalAlignment.aln_identity(aln[0], aln[1], gapless=gapless) + values.append({"gID" : alnID, "gPID" : alnID / len(aln[0]), "gQPID" : alnID * 100 / len(query), + "gSPID" : alnID * 100 / len(subject), "gSCORE" : aln[2], "gLEN" : len(aln[0]), + "qseqid" : query.id, "sseqid" : subject.id}) + + return query, subject, pd.DataFrame(values) + + @staticmethod + def run_pair_score(query, subject, * args, ** kwargs) : + kwargs["one_alignment_only"] = True + kwargs["score_only"] = False + return GlobalAlignment.run_pair(query, subject, * args, ** kwargs) + + @staticmethod + def product_fasta(query, subject) : + query = SeqIO.parse(query, "fasta") + subject = SeqIO.parse(query, "fasta") + pairs = product(query, subject) + return GlobalAlignment(pairs) + + def run(self, * args, ncore=1, callback=None, ** kwargs) : + callback = callback or GlobalAlignment.run_pair + args = [utils.FunArgs(callback, query, subject, * args, ** kwargs) for query, subject in self.inputs] + return utils.mproc(args, ncore=ncore) + +class Local2Global() : + + def __init__(self, query, subject) : + # Perform local and global alignment based on the best results + self.query = query + self.subject = subject + + def retrieve_seq(self, query=True, ids=None) : + fname = self.query if query else self.subject + seqs = SeqIO.parse(fname, "fasta") + if ids : return (seq for seq in seqs if seq.id in ids) + return seqs + + def run(self, bkind, gargs=(), gkwargs={}, bkwargs={}, match_fun=BCLine6.get_best, chunksize=200, ncore=1, quiet=False) : + # Blast local alignments + bc = BCLine6(bkind, query=self.query, db=self.subject, ** bkwargs) + bres = bc.run(chunksize=chunksize, ncore=ncore, quiet=quiet) + if match_fun : bres = match_fun(bres) + + # retrieve subject and query sequences + seq_que = {seq.id : seq for seq in self.retrieve_seq(query=True, ids=set(bres["qseqid"].unique()))} + seq_sub = {seq.id : seq for seq in self.retrieve_seq(query=False, ids=set(bres["sseqid"].unique()))} + + # we run global alignments + pairs = bres[["qseqid", "sseqid"]].drop_duplicates(["qseqid", "sseqid"]) + seqs = [(seq_que[query], seq_sub[subject]) for query, subject in zip(pairs["qseqid"], pairs["sseqid"])] + + print ("%i global alignments to process" %(len(seqs))) + call = GlobalAlignment.run_pair_identity + gres = GlobalAlignment(seqs).run(* gargs, ncore=ncore, callback=call, ** gkwargs) + gres = pd.concat([res[2] for res in gres]) + + return bres.merge(gres, on=["qseqid", "sseqid"], how="left") \ No newline at end of file diff --git a/build/lib/pyblast/qblast.py b/build/lib/pyblast/qblast.py new file mode 100644 index 0000000..c279008 --- /dev/null +++ b/build/lib/pyblast/qblast.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +# @Author: jsgounot +# @Date: 2019-04-03 16:04:11 +# @Last modified by: jsgounot +# @Last Modified time: 2019-04-08 09:56:37 + +import os, glob, shutil +import time, datetime + +from itertools import chain +from threading import Thread + +import pandas as pd + +from Bio import SeqIO +from Bio.Blast import NCBIWWW, NCBIXML + +from pyblast import BCLine, BCLineHSPFuse +from pyblast import utils + +""" + +Online blast query + +""" + +current_time = lambda : datetime.datetime.now().strftime("%H:%M:%S") + +class Query(object) : + + def __init__(self, qbr_instance, job_name, program, database, fasta_str, ** kwargs) : + self.qbr_instance = qbr_instance + self.job_name = job_name + + self.program = program + self.database = database + self.fasta_str = fasta_str + self.kwargs = kwargs + + if kwargs.get("format_type", "XML") != "XML" : + raise ValueError("Only XML output are managed") + + def trigger(self) : + print ("%s : Run qblast (%s - %s) - job name : %s" %(current_time(), self.program, self.database, self.job_name)) + res = NCBIWWW.qblast(self.program, self.database, self.fasta_str, ** self.kwargs) + print ("%s : End qblast - job name : %s" %(current_time(), self.job_name)) + + xml_file = utils.TMPFname() + with open(str(xml_file), "w") as f : + f.write(res.read()) + + res.close() + self.qbr_instance.append(xml_file) + +class QueriesManager(object) : + + """ + + Run multiple queries within threads + + """ + + def __init__(self, queries=[]) : + self.queries = queries + + def __add__(self, other) : + if not isinstance(other, QueriesManager) : + raise ValueError("Queries Manager can only be associated with other QM") + + new_queries = self.queries + other.queries + return QueriesManager(new_queries) + + def run(self, time_wait=15) : + # Run parallel qblast queries over ncbi using threads + # https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=DeveloperInfo + # time wait corresponds to the time beetween two parallele requests, if you don't want that + # ncbi kicks your ass, do not lower this value + + threads = [] + fun = lambda query : query.trigger() + + for query in self.queries : + thread = Thread(target=fun, args=(query,)) + thread.start() + threads.append(thread) + time.sleep(time_wait) + + for thread in threads : + thread.join() + + # -------------------------------------------------- + # Create the Queries Manager from data (fasta) + # -------------------------------------------------- + + @staticmethod + def records_to_str(records) : + strs = [] + + for record in records : + identifier, seq = record.id, str(record.seq) + strs.append(">%s" %(identifier)) + + for i in range(0, len(seq), 60) : + strs.append(seq[i:i+60]) + + return "\n".join(strs) + + @staticmethod + def extract_fasta_str(fasta, chunk_size=10) : + # return the fasta sequence as a + # string object + + fdata = list(SeqIO.parse(fasta, "fasta")) + + if chunk_size is None : + return [QueriesManager.records_to_str(fdata)] + + return [QueriesManager.records_to_str(records) + for records in BCLine.grouper_fdata(fdata, chunk_size)] + + @staticmethod + def from_fasta(program, database, fasta, chunk_size=None, ** kwargs) : + # Return the QBlastResult and the Queries manager based on qblast parameter and fasta chunk_size + + qbr = QBlastResult(fasta=fasta) + queries = [] + subsequences = list(QueriesManager.extract_fasta_str(fasta, chunk_size)) + + for idx, subsequence in enumerate(subsequences, start=1) : + job_name = "%s (n = %i)" %(fasta, idx) if len(subsequences) > 1 else fasta + queries.append(Query(qbr, job_name, program, database, subsequence, ** kwargs)) + + return qbr, QueriesManager(queries) + + @staticmethod + def from_multiple_fastas(program, database, fasta_list, chunk_size=None, ** kwargs) : + qbrs, qms = zip(* [QueriesManager.from_fasta(program, database, fasta, chunk_size, ** kwargs) + for fasta in fasta_list]) + + qm = sum(qms, QueriesManager()) + qbrs = qbrs if len(qbrs) > 1 else next(iter(qbrs)) + + return qbrs, qm + +class QBlastResult(object): + + """ + + Wrapper of NCBIWWW Qblast function + http://biopython.org/DIST/docs/api/Bio.Blast.NCBIWWW-module.html + + """ + + def __init__(self, xml_files=None, fasta=None) : + + self.fasta = fasta + + if xml_files is not None : + xml_files = glob.glob(xml_files) if isinstance(xml_files, str) else xml_files + self.xml_files = xml_files + + else : + self.xml_files = [] + + def append(self, xml_file) : + self.xml_files.append(xml_file) + + @staticmethod + def fuse_xml(xml_files) : + raise Exception() + + def save(self, outfile) : + if len(self.xml_files) == 0 : raise ValueError("Empty QBlastResult instance") + xml_file = self.xml_files[0] if len(self.xml_files) == 1 else fuse_xml(self.xml_files) + shutil.copy_file(xml_file, outfile) + + @staticmethod + def parse_xml_file(xml_file) : + with open(str(xml_file)) as f : + try : return [record for record in NCBIXML.parse(f)] + except ValueError : return [] # empty results + + def parse_xml_files(self) : + + if not self.xml_files : + raise ValueError("No xml files produced or provided") + + return chain(* (QBlastResult.parse_xml_file(xml_file) + for xml_file in self.xml_files)) + + def as_table(self) : + records = self.parse_xml_files() + rows = [] + + for query, subject, hsp in BCLineHSPFuse.iter_hsp(records) : + row = {} + + row["qname"], row["sname"] = query[0], subject[0] + row["qlen"], row["slen"] = query[1], subject[1] + row["length"], row["gap"] = hsp.align_length, hsp.gaps + row["hsp_ident"], row["hsp_pos"] = hsp.identities, hsp.positives + row["expect"] = hsp.expect + + rows.append(row) + + df = pd.DataFrame(rows) + if df.empty : return df + + df["ppos"] = df["hsp_pos"] * 100 / df["length"] + df["pident"] = df["hsp_ident"] * 100 / df["length"] + + df["qide_prc"] = df["hsp_ident"] * 100 / df["qlen"] + df["qpos_prc"] = df["hsp_pos"] * 100 / df["qlen"] + df["side_prc"] = df["hsp_ident"] * 100 / df["slen"] + df["spos_prc"] = df["hsp_pos"] * 100 / df["slen"] + + return df + + def hsp_fuse(self) : + records = self.parse_xml_files() + return BCLineHSPFuse.get_sum_sim_all(records) + + +# -------------------------------------------------- +# Called function +# -------------------------------------------------- + +def launch_qblasts(program, database, fasta_list, chunk_size=None, time_wait=15, ** kwargs) : + # Run parallel or single qblast queries over ncbi using threads + # https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=DeveloperInfo + # time wait corresponds to the time beetween two parallele requests, if you don't want that + # ncbi kicks your ass, do not lower this value + + fasta_list = glob.glob(fasta_list) if isinstance(fasta_list, str) else fasta_list + qbr, qm = QueriesManager.from_multiple_fastas(program, database, fasta_list, chunk_size=chunk_size, ** kwargs) + qm.run(time_wait) + return qbr diff --git a/build/lib/pyblast/utils.py b/build/lib/pyblast/utils.py new file mode 100644 index 0000000..bb6c515 --- /dev/null +++ b/build/lib/pyblast/utils.py @@ -0,0 +1,129 @@ +import os, tempfile, time, sys +from multiprocessing import Pool, cpu_count +from subprocess import check_output + +from Bio import SeqIO + +class TMPFname() : + + def __init__(self, delete=True, ext="", quiet=False, ** kwargs) : + suffix = self.format_ext(ext) + tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, ** kwargs) + self.fname = tmp.name + self.delete = delete + self.quiet = quiet + + def __str__(self) : + return self.fname + + def format_ext(self, ext) : + ext = str(ext) + if not ext : return ext + if not ext.startswith(".") : ext = "." + ext.strip() + return ext + + def exist(self) : + return os.path.isfile(self.fname) + + def remove(self) : + if self.exist() and self.delete : + if not self.quiet : print ("Delete temporary file at : %s" %(self.fname)) + os.remove(self.fname) + + def __del__(self) : + self.remove() + +class TMPFasta(TMPFname) : + + def __init__(self, data, delete=True, wodesc=False, ** kwargs) : + super(TMPFasta, self).__init__(delete=delete, ext="fa", ** kwargs) + if data : self.write(data, wodesc=wodesc) + + @staticmethod + def remove_desc(feature) : + feature.description = "" + return feature + + def write(self, data, mode="w", wodesc=False) : + if wodesc : data = (TMPFasta.remove_desc(feat) for feat in data) + with open(self.fname, mode) as handle : + SeqIO.write(data, handle, "fasta") + + +""" +PROCESSING +""" + +# decorator to time functions +def ftiming(f): + def wrap(*args, **kwargs): + # Receive 'quiet' from kwargs and use it directly + quiet = kwargs.get('quiet', False) # This will not modify kwargs + + funcname = kwargs.pop("funcname", f.__name__) + funcname = funcname or "Unknown funcname" + + time1 = time.time() + ret = f(*args, **kwargs) + time2 = time.time() + + # Only print if 'quiet' is False + if not quiet: + print('{:s} function took {:.3f} ms'.format(funcname, (time2 - time1) * 1000.0)) + + return ret + + return wrap + +def run_cmdline(cmdline, funcname=None, quiet=False): + # Pass all original parameters to the function + if not quiet: + return run_cmdline_loud(cmdline, funcname=funcname, quiet=quiet) + return check_output(cmdline, universal_newlines=True, shell=True) + +@ftiming +def run_cmdline_loud(cmdline, funcname=None, quiet=False): + return check_output(cmdline, universal_newlines=True, shell=True) + +""" +MULTIPROCESSING +""" + +class FunArgs() : + + def __init__(self, fun, * args, ** kwargs) : + self.fun = fun + self.args = args + self.kwargs = kwargs + + def launch_fun(self) : + return self.fun(* self.args, ** self.kwargs) + +class Multiprocess() : + + @staticmethod + def lambda_fun(farg) : + return farg.launch_fun() + + def run(self, fargs, ncore=1, quiet=False) : + if not quiet: print ("ncore : %i - available : %i" %(ncore, cpu_count())) + if ncore == 1 : return [farg.launch_fun() for farg in fargs] + + pool = Pool(ncore) + func = Multiprocess.lambda_fun + fargs = ((farg, ) for farg in fargs) + + try : + data = pool.starmap(func, fargs) + pool.close() + pool.join() + return data + + except KeyboardInterrupt : + print("Interrupt childs process") + pool.terminate() + sys.exit() + +def mproc(fargs, ncore=1, quiet=False) : + mp = Multiprocess() + return mp.run(fargs, ncore=ncore, quiet=quiet) \ No newline at end of file diff --git a/dist/pyblast-0.0.0-py3.10.egg b/dist/pyblast-0.0.0-py3.10.egg new file mode 100644 index 0000000000000000000000000000000000000000..8a13bc4242c1bd8a3755f7260ef91ca523223721 GIT binary patch literal 24709 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 literal 0 HcmV?d00001 diff --git a/pyblast.egg-info/PKG-INFO b/pyblast.egg-info/PKG-INFO new file mode 100644 index 0000000..8e88ab6 --- /dev/null +++ b/pyblast.egg-info/PKG-INFO @@ -0,0 +1,3 @@ +Metadata-Version: 2.1 +Name: pyblast +Version: 0.0.0 diff --git a/pyblast.egg-info/SOURCES.txt b/pyblast.egg-info/SOURCES.txt new file mode 100644 index 0000000..a895463 --- /dev/null +++ b/pyblast.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +README.md +setup.py +pyblast/__init__.py +pyblast/bloc.py +pyblast/qblast.py +pyblast/utils.py +pyblast.egg-info/PKG-INFO +pyblast.egg-info/SOURCES.txt +pyblast.egg-info/dependency_links.txt +pyblast.egg-info/top_level.txt \ No newline at end of file diff --git a/pyblast.egg-info/dependency_links.txt b/pyblast.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyblast.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pyblast.egg-info/top_level.txt b/pyblast.egg-info/top_level.txt new file mode 100644 index 0000000..c42613e --- /dev/null +++ b/pyblast.egg-info/top_level.txt @@ -0,0 +1 @@ +pyblast diff --git a/pyblast/utils.py b/pyblast/utils.py index f187687..bb6c515 100644 --- a/pyblast/utils.py +++ b/pyblast/utils.py @@ -56,28 +56,33 @@ def write(self, data, mode="w", wodesc=False) : # decorator to time functions def ftiming(f): + def wrap(*args, **kwargs): + # Receive 'quiet' from kwargs and use it directly + quiet = kwargs.get('quiet', False) # This will not modify kwargs - def wrap(* args, ** kwargs): - funcname = kwargs.pop("funcname", f.__name__) funcname = funcname or "Unknown funcname" time1 = time.time() - ret = f(* args, ** kwargs) + ret = f(*args, **kwargs) time2 = time.time() - - print('{:s} function took {:.3f} ms'.format(funcname, (time2-time1) * 1000.0)) - + + # Only print if 'quiet' is False + if not quiet: + print('{:s} function took {:.3f} ms'.format(funcname, (time2 - time1) * 1000.0)) + return ret return wrap -def run_cmdline(cmdline, funcname=None, quiet=False) : - if not quiet : return run_cmdline_loud(cmdline, funcname=funcname) +def run_cmdline(cmdline, funcname=None, quiet=False): + # Pass all original parameters to the function + if not quiet: + return run_cmdline_loud(cmdline, funcname=funcname, quiet=quiet) return check_output(cmdline, universal_newlines=True, shell=True) @ftiming -def run_cmdline_loud(cmdline, funcname=None) : +def run_cmdline_loud(cmdline, funcname=None, quiet=False): return check_output(cmdline, universal_newlines=True, shell=True) """ From 24bcc92615a31c02da30d4c744d31ffbe3cb9ca2 Mon Sep 17 00:00:00 2001 From: mmestreprofluent Date: Thu, 25 Apr 2024 19:37:33 +0000 Subject: [PATCH 2/2] Fix undesired print with quiet=True --- build/lib/pyblast/__init__.py | 3 - build/lib/pyblast/bloc.py | 438 -------------------------- build/lib/pyblast/qblast.py | 237 -------------- build/lib/pyblast/utils.py | 129 -------- dist/pyblast-0.0.0-py3.10.egg | Bin 24709 -> 0 bytes pyblast.egg-info/PKG-INFO | 3 - pyblast.egg-info/SOURCES.txt | 10 - pyblast.egg-info/dependency_links.txt | 1 - pyblast.egg-info/top_level.txt | 1 - 9 files changed, 822 deletions(-) delete mode 100644 build/lib/pyblast/__init__.py delete mode 100644 build/lib/pyblast/bloc.py delete mode 100644 build/lib/pyblast/qblast.py delete mode 100644 build/lib/pyblast/utils.py delete mode 100644 dist/pyblast-0.0.0-py3.10.egg delete mode 100644 pyblast.egg-info/PKG-INFO delete mode 100644 pyblast.egg-info/SOURCES.txt delete mode 100644 pyblast.egg-info/dependency_links.txt delete mode 100644 pyblast.egg-info/top_level.txt diff --git a/build/lib/pyblast/__init__.py b/build/lib/pyblast/__init__.py deleted file mode 100644 index 25555c7..0000000 --- a/build/lib/pyblast/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from pyblast.bloc import * -from pyblast.qblast import QBlastResult, launch_qblasts -from pyblast.utils import TMPFasta \ No newline at end of file diff --git a/build/lib/pyblast/bloc.py b/build/lib/pyblast/bloc.py deleted file mode 100644 index d6e4978..0000000 --- a/build/lib/pyblast/bloc.py +++ /dev/null @@ -1,438 +0,0 @@ -import sys -from io import StringIO -from itertools import zip_longest -from copy import deepcopy -from collections import defaultdict -from itertools import product - -import pandas as pd - -from Bio.Seq import Seq -from Bio.Blast import Applications, NCBIXML -from Bio import SeqIO -from Bio import pairwise2 - -from pyblast import utils - -OSP = sys.platform - -class BlocBlastException(Exception) : - pass - -class BCLine() : - - clines = { - "blastp" : Applications.NcbiblastpCommandline, - "blastn" : Applications.NcbiblastnCommandline, - "blastx" : Applications.NcbiblastxCommandline, - "tblastn" : Applications.NcbitblastnCommandline, - "tblastx" : Applications.NcbitblastxCommandline, - "psiblast" : Applications.NcbipsiblastCommandline, - "pstblastn" : Applications.NcbirpstblastnCommandline, - "rpstblastn" : Applications.NcbirpstblastnCommandline, - "deltablast" : Applications.NcbideltablastCommandline - } - - def __init__(self, bkind, ** kwargs) : - - self.query = kwargs.get("query", None) - if not self.query : - raise BlocBlastException("Query not filled") - - if bkind not in BCLine.clines : - raise BlocBlastException("Bkind not found in available blast executable : %s" %(" ".join(BCLine.clines))) - - self.cline = BCLine.clines[bkind](** kwargs) - - def __setitem__(self, name, value) : - for parameter in self.cline.parameters : - if name in parameter.names : - parameter.value = value - return - - raise BlocBlastException("Parameter not found : '%s'" %(name)) - - def __str__(self) : - return str(self.cline) - - @staticmethod - def grouper_fdata(fdata, gsize) : - # based on itertools recipy - # https://docs.python.org/3.6/library/itertools.html#itertools-recipes - - args = [iter(fdata)] * gsize - groups = zip_longest(*args, fillvalue=None) - for group in groups : - yield [seq for seq in group if seq is not None] - - @utils.ftiming - def run_single_core(self, funcname="Blast command line (single)", postfun=None, quiet=False) : - if not quiet : print (self) - res = self.cline() - return postfun(res) if postfun else res - - @staticmethod - def run_single_core_tmp(tmpbcline, postfun=None, quiet=False) : - # since we're not able to pickle a BioPython commande line - # we're juste gonna execute it as a string - - if not quiet : print (str(tmpbcline)) - res = (utils.run_cmdline(str(tmpbcline), funcname="Blast command line (ncore)", quiet=quiet), None) - return postfun(res) if postfun else res - - def run_chunked(self, ncore, chunksize=200, postfun=None, quiet=False) : - fdata = SeqIO.parse(self.query, "fasta") - fdata = BCLine.grouper_fdata(fdata, chunksize) - queries = (utils.TMPFasta(group, quiet=quiet) for group in fdata) - - fun = BCLine.run_single_core_tmp - args = [utils.FunArgs(fun, TMPBCLine(self, query), postfun=postfun, quiet=quiet) for query in queries] - - return utils.mproc(args, ncore, quiet) - - def run(self, ncore=1, chunksize=200, postfun=None, quiet=False) : - if ncore == 1 : - return self.run_single_core(postfun=postfun, quiet=quiet) - else : - return self.run_chunked(ncore, chunksize, postfun=postfun, quiet=quiet) - - @staticmethod - def mbdb(db, ** kwargs) : - # Applications.NcbimakeblastdbCommandline not found - kwargs["in"] = db - cmd = "makeblastdb %s" %(" ".join("-%s %s" %(key, value) for key, value in kwargs.items())) - utils.run_cmdline(cmd) - - @staticmethod - def clean_fasta(fname, astmp=True) : - # Change header to match correct id with _ - # Replace ? to N - # Return a new fasta (tmp) - - def clean_seqrecord(seqrecord) : - name = seqrecord.description.replace(" ", "_") - seqrecord.id = seqrecord.name = seqrecord.description = name - sequence = str(seqrecord.seq).upper().replace("?", "N") - seqrecord.seq = Seq(sequence) - return seqrecord - - fdata = SeqIO.parse(fname, "fasta") - fdata = (clean_seqrecord(sr) for sr in fdata) - return utils.TMPFasta(fdata) if astmp else fdata - -class TMPBCLine() : - - # A single class used when we multiprocess blast - # We cannot pickle BioPython command line - # And we have to keep the TMPFasta instance alive otherwise the file is removed - - def __init__(self, bcline, tmpfasta) : - bcline = deepcopy(bcline) - bcline["query"] = str(tmpfasta) - - self.tmpfasta = tmpfasta - self.cmdline = str(bcline) - - def __str__(self) : - return self.cmdline - -class BCLine6(BCLine) : - - def __init__(self, bkind, ** kwargs) : - - kwargs, self.spec = self.add_specifiers(kwargs) - super(BCLine6, self).__init__(bkind, ** kwargs) - - def add_specifiers(self, kwargs) : - current = kwargs.get("outfmt", "").split(" ") - current = [value for value in current if value] - - toadd = ["qseqid", "sseqid", "pident", "nident", "length", "slen", - "qlen", "qstart", "qend", "sstart", "send", "positive"] - used = list(set(current) | set(toadd)) - - # This is weird, in some mac the usual command line leads - # to an error but not for all of them - # I will set this as an option instead - - if kwargs.pop("macos_formating", False) == True : - kwargs["outfmt"] = "'%s'" %("6 " + " ".join(used)) - else : - kwargs["outfmt"] = "%s" %("6 " + " ".join(used)) - - return kwargs, used - - def treat_res(self, res) : - - if isinstance(res, list) : - # multiprocessing res - return pd.concat([self.treat_res(r) for r in res]) - - out, err = res - if err : print (err) - - out = StringIO(out) - df = pd.read_csv(out, sep="\t", names=self.spec) - - # check if a column is full of na - # may happend if one gave a wrong outfmt - for column in df.columns : - if df[column].isnull().all() and not df.empty : - print ("""WARNING : An output column is full of nan : - This could be a result of a wrong outfmt key, - and may break result outputs\n""") - - df["qlength"] = abs(df["qend"] - df["qstart"]) + 1 - df["slength"] = abs(df["send"] - df["sstart"]) + 1 - - prc = [("qide", "nident", "qlen"), ("qcov", "qlength", "qlen"), ("qpos", "positive", "qlen"), - ("side", "nident", "slen"), ("scov", "slength", "slen"), ("spos", "positive", "slen")] - - for name, divident, divisor in prc : - df[name] = df[divident] * 100 / df[divisor] - - return df - - @staticmethod - def get_best(df, query="qseqid", column="qpos", nmatches=1, use_max=True) : - if use_max : - return BCLine6.get_best_max(df, query, column, nmatches) - else : - return BCLine6.get_best_min(df, query, column, nmatches) - - @staticmethod - def get_best_max(df, query="qseqid", column="qpos", nmatches=1) : - fun = (lambda serie : serie.nlargest(nmatches).min()) if nmatches > 1 else max - mask = df.groupby(query)[column].transform(fun) - return df[df[column] >= mask] - - @staticmethod - def get_best_min(df, query="qseqid", column="qpos", nmatches=1) : - fun = (lambda serie : serie.nsmallest(nmatches).max()) if nmatches > 1 else min - mask = df.groupby(query)[column].transform(fun) - return df[df[column] <= mask] - - @staticmethod - def query_mdbs(bkind, query, dbs, best=False, chunksize=200, ncore=1, - mcolumn="qpos", nmatches=1, ** kwargs) : - - res = [] - for db in dbs : - df = BCLine6(bkind, query=query, subject=db, ** kwargs).run(ncore=ncore, chunksize=chunksize) - if df.empty : continue - df["db"] = db - res.append(df) - - if not res : return pd.DataFrame() - res = pd.concat(res) - - return BCLine6.get_best(res, column=mcolumn, nmatches=nmatches) if best else res - - def run(self, * args, ** kwargs) : - res = super(BCLine6, self).run(* args, ** kwargs) - return self.treat_res(res) - -class BCLineHSPFuse(BCLine) : - - def __init__(self, bkind, ** kwargs) : - - kwargs = self.add_specifiers(kwargs) - super(BCLineHSPFuse, self).__init__(bkind, ** kwargs) - - def add_specifiers(self, kwargs) : - current = kwargs.get("outfmt", "").split(" ") - current = current[1:] if len(current) > 1 else [] - - kwargs["outfmt"] = "'%s'" %("5 " + " ".join(current)) - return kwargs - - @staticmethod - def parse_xml(data) : - f = StringIO(data) - try : return [record for record in NCBIXML.parse(f)] - except ValueError : return [] # empty results - - @staticmethod - def iter_hsp(records) : - for record in records : - qname, qlength = record.query, record.query_length - for aln in record.alignments : - sname, slength = aln.hit_def, aln.length - for hsp in aln.hsps : - yield (qname, qlength), (sname, slength), hsp - - @staticmethod - def get_hsp_ident(hsp, query=True) : - start = hsp.query_start if query else hsp.sbjct_start - end = hsp.query_end if query else hsp.sbjct_end - sequence = hsp.query if query else hsp.sbjct - reverse = start > end - - positives, ident = set(), set() - psequence = -1 - - for idx, letter in enumerate(sequence) : - letter_match = hsp.match[idx] - - if letter_match != ' ' : - position = start - psequence if reverse else psequence + start - positives.add(position) - if letter_match != "+" : ident.add(position) - - psequence = psequence + 1 if letter != "-" else psequence - - return positives, ident - - @staticmethod - def get_sum_sim_all(records) : - qlength, slength = {}, {} - data = defaultdict(lambda : defaultdict(list)) - - for query, subject, aln in BCLineHSPFuse.iter_hsp(records) : - qname, sname = query[0], subject[0] - qlen, slen = query[1], subject[1] - qlength[qname], qlength[sname] = qlen, slen - - qposit, qident = BCLineHSPFuse.get_hsp_ident(aln, query=True) - sposit, sident = BCLineHSPFuse.get_hsp_ident(aln, query=False) - positions = {"qide" : qident, "qpos" : qposit, "side" : sident, "spos" : sposit} - data[qname][sname].append(positions) - - df = [] - kinds = ["qide", "qpos", "side", "spos"] - - for qname, subject in data.items() : - for sname, all_positions in subject.items() : - - hsp_count = len(all_positions) - - if len(all_positions) > 1 : - intersection = {kind + "_inter" : len(set.intersection(* [positions[kind] - for positions in all_positions])) for kind in kinds} - - else : - intersection = {kind + "_inter" : 0 for kind in kinds} - - union = {kind : len(set.union(* [positions[kind] - for positions in all_positions])) for kind in kinds} - - info = {"qseqid" : qname, "sseqid" : sname, "#hsp" : hsp_count, ** union, ** intersection} - df.append(info) - - df = pd.DataFrame(df) - if df.empty : return df - - df["qlen"], df["slen"] = df["qseqid"].map(qlength), df["sseqid"].map(qlength) - df["qide_prc"] = df["qide"] * 100 / df["qlen"] - df["qpos_prc"] = df["qpos"] * 100 / df["qlen"] - df["side_prc"] = df["side"] * 100 / df["slen"] - df["spos_prc"] = df["spos"] * 100 / df["slen"] - - df = df[sorted(df.columns)] - return df - - @staticmethod - def post_treat(res) : - out, err = res - records = BCLineHSPFuse.parse_xml(out) - return BCLineHSPFuse.get_sum_sim_all(records) - - def treat_res(self, res) : - - if isinstance(res, list) : - # multiprocessing res - return pd.concat(res) - - return res - - def run(self, * args, ** kwargs) : - res = super(BCLineHSPFuse, self).run(* args, postfun=BCLineHSPFuse.post_treat, ** kwargs) - return self.treat_res(res) - -class GlobalAlignment() : - - def __init__(self, inputs) : - # inputs are an iterable of pairs of SeqRecords - self.inputs = inputs - - @staticmethod - def get_alignfun(name="globalxx") : - return getattr(pairwise2.align, name) - - @staticmethod - def aln_identity(query, subject, gapless=True) : - if gapless : - return sum(1 if b1 == b2 and b1 != "-" and b2 != "-" else 0 for b1, b2 in zip(query, subject)) - else : - return sum(1 if b1 == b2 else 0 for b1, b2 in zip(query, subject)) - - @staticmethod - def run_pair(query, subject, * args, gfun="globalxx", ** kwargs) : - gfun = GlobalAlignment.get_alignfun(gfun) - alns = gfun(query.seq, subject.seq, * args, ** kwargs) - return query, subject, alns - - @staticmethod - def run_pair_identity(query, subject, * args, gapless=True, addnames=True, ** kwargs) : - query, subject, alns = GlobalAlignment.run_pair(query, subject, * args, ** kwargs) - values = [] - - for aln in alns : - alnID = GlobalAlignment.aln_identity(aln[0], aln[1], gapless=gapless) - values.append({"gID" : alnID, "gPID" : alnID / len(aln[0]), "gQPID" : alnID * 100 / len(query), - "gSPID" : alnID * 100 / len(subject), "gSCORE" : aln[2], "gLEN" : len(aln[0]), - "qseqid" : query.id, "sseqid" : subject.id}) - - return query, subject, pd.DataFrame(values) - - @staticmethod - def run_pair_score(query, subject, * args, ** kwargs) : - kwargs["one_alignment_only"] = True - kwargs["score_only"] = False - return GlobalAlignment.run_pair(query, subject, * args, ** kwargs) - - @staticmethod - def product_fasta(query, subject) : - query = SeqIO.parse(query, "fasta") - subject = SeqIO.parse(query, "fasta") - pairs = product(query, subject) - return GlobalAlignment(pairs) - - def run(self, * args, ncore=1, callback=None, ** kwargs) : - callback = callback or GlobalAlignment.run_pair - args = [utils.FunArgs(callback, query, subject, * args, ** kwargs) for query, subject in self.inputs] - return utils.mproc(args, ncore=ncore) - -class Local2Global() : - - def __init__(self, query, subject) : - # Perform local and global alignment based on the best results - self.query = query - self.subject = subject - - def retrieve_seq(self, query=True, ids=None) : - fname = self.query if query else self.subject - seqs = SeqIO.parse(fname, "fasta") - if ids : return (seq for seq in seqs if seq.id in ids) - return seqs - - def run(self, bkind, gargs=(), gkwargs={}, bkwargs={}, match_fun=BCLine6.get_best, chunksize=200, ncore=1, quiet=False) : - # Blast local alignments - bc = BCLine6(bkind, query=self.query, db=self.subject, ** bkwargs) - bres = bc.run(chunksize=chunksize, ncore=ncore, quiet=quiet) - if match_fun : bres = match_fun(bres) - - # retrieve subject and query sequences - seq_que = {seq.id : seq for seq in self.retrieve_seq(query=True, ids=set(bres["qseqid"].unique()))} - seq_sub = {seq.id : seq for seq in self.retrieve_seq(query=False, ids=set(bres["sseqid"].unique()))} - - # we run global alignments - pairs = bres[["qseqid", "sseqid"]].drop_duplicates(["qseqid", "sseqid"]) - seqs = [(seq_que[query], seq_sub[subject]) for query, subject in zip(pairs["qseqid"], pairs["sseqid"])] - - print ("%i global alignments to process" %(len(seqs))) - call = GlobalAlignment.run_pair_identity - gres = GlobalAlignment(seqs).run(* gargs, ncore=ncore, callback=call, ** gkwargs) - gres = pd.concat([res[2] for res in gres]) - - return bres.merge(gres, on=["qseqid", "sseqid"], how="left") \ No newline at end of file diff --git a/build/lib/pyblast/qblast.py b/build/lib/pyblast/qblast.py deleted file mode 100644 index c279008..0000000 --- a/build/lib/pyblast/qblast.py +++ /dev/null @@ -1,237 +0,0 @@ -# -*- coding: utf-8 -*- -# @Author: jsgounot -# @Date: 2019-04-03 16:04:11 -# @Last modified by: jsgounot -# @Last Modified time: 2019-04-08 09:56:37 - -import os, glob, shutil -import time, datetime - -from itertools import chain -from threading import Thread - -import pandas as pd - -from Bio import SeqIO -from Bio.Blast import NCBIWWW, NCBIXML - -from pyblast import BCLine, BCLineHSPFuse -from pyblast import utils - -""" - -Online blast query - -""" - -current_time = lambda : datetime.datetime.now().strftime("%H:%M:%S") - -class Query(object) : - - def __init__(self, qbr_instance, job_name, program, database, fasta_str, ** kwargs) : - self.qbr_instance = qbr_instance - self.job_name = job_name - - self.program = program - self.database = database - self.fasta_str = fasta_str - self.kwargs = kwargs - - if kwargs.get("format_type", "XML") != "XML" : - raise ValueError("Only XML output are managed") - - def trigger(self) : - print ("%s : Run qblast (%s - %s) - job name : %s" %(current_time(), self.program, self.database, self.job_name)) - res = NCBIWWW.qblast(self.program, self.database, self.fasta_str, ** self.kwargs) - print ("%s : End qblast - job name : %s" %(current_time(), self.job_name)) - - xml_file = utils.TMPFname() - with open(str(xml_file), "w") as f : - f.write(res.read()) - - res.close() - self.qbr_instance.append(xml_file) - -class QueriesManager(object) : - - """ - - Run multiple queries within threads - - """ - - def __init__(self, queries=[]) : - self.queries = queries - - def __add__(self, other) : - if not isinstance(other, QueriesManager) : - raise ValueError("Queries Manager can only be associated with other QM") - - new_queries = self.queries + other.queries - return QueriesManager(new_queries) - - def run(self, time_wait=15) : - # Run parallel qblast queries over ncbi using threads - # https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=DeveloperInfo - # time wait corresponds to the time beetween two parallele requests, if you don't want that - # ncbi kicks your ass, do not lower this value - - threads = [] - fun = lambda query : query.trigger() - - for query in self.queries : - thread = Thread(target=fun, args=(query,)) - thread.start() - threads.append(thread) - time.sleep(time_wait) - - for thread in threads : - thread.join() - - # -------------------------------------------------- - # Create the Queries Manager from data (fasta) - # -------------------------------------------------- - - @staticmethod - def records_to_str(records) : - strs = [] - - for record in records : - identifier, seq = record.id, str(record.seq) - strs.append(">%s" %(identifier)) - - for i in range(0, len(seq), 60) : - strs.append(seq[i:i+60]) - - return "\n".join(strs) - - @staticmethod - def extract_fasta_str(fasta, chunk_size=10) : - # return the fasta sequence as a - # string object - - fdata = list(SeqIO.parse(fasta, "fasta")) - - if chunk_size is None : - return [QueriesManager.records_to_str(fdata)] - - return [QueriesManager.records_to_str(records) - for records in BCLine.grouper_fdata(fdata, chunk_size)] - - @staticmethod - def from_fasta(program, database, fasta, chunk_size=None, ** kwargs) : - # Return the QBlastResult and the Queries manager based on qblast parameter and fasta chunk_size - - qbr = QBlastResult(fasta=fasta) - queries = [] - subsequences = list(QueriesManager.extract_fasta_str(fasta, chunk_size)) - - for idx, subsequence in enumerate(subsequences, start=1) : - job_name = "%s (n = %i)" %(fasta, idx) if len(subsequences) > 1 else fasta - queries.append(Query(qbr, job_name, program, database, subsequence, ** kwargs)) - - return qbr, QueriesManager(queries) - - @staticmethod - def from_multiple_fastas(program, database, fasta_list, chunk_size=None, ** kwargs) : - qbrs, qms = zip(* [QueriesManager.from_fasta(program, database, fasta, chunk_size, ** kwargs) - for fasta in fasta_list]) - - qm = sum(qms, QueriesManager()) - qbrs = qbrs if len(qbrs) > 1 else next(iter(qbrs)) - - return qbrs, qm - -class QBlastResult(object): - - """ - - Wrapper of NCBIWWW Qblast function - http://biopython.org/DIST/docs/api/Bio.Blast.NCBIWWW-module.html - - """ - - def __init__(self, xml_files=None, fasta=None) : - - self.fasta = fasta - - if xml_files is not None : - xml_files = glob.glob(xml_files) if isinstance(xml_files, str) else xml_files - self.xml_files = xml_files - - else : - self.xml_files = [] - - def append(self, xml_file) : - self.xml_files.append(xml_file) - - @staticmethod - def fuse_xml(xml_files) : - raise Exception() - - def save(self, outfile) : - if len(self.xml_files) == 0 : raise ValueError("Empty QBlastResult instance") - xml_file = self.xml_files[0] if len(self.xml_files) == 1 else fuse_xml(self.xml_files) - shutil.copy_file(xml_file, outfile) - - @staticmethod - def parse_xml_file(xml_file) : - with open(str(xml_file)) as f : - try : return [record for record in NCBIXML.parse(f)] - except ValueError : return [] # empty results - - def parse_xml_files(self) : - - if not self.xml_files : - raise ValueError("No xml files produced or provided") - - return chain(* (QBlastResult.parse_xml_file(xml_file) - for xml_file in self.xml_files)) - - def as_table(self) : - records = self.parse_xml_files() - rows = [] - - for query, subject, hsp in BCLineHSPFuse.iter_hsp(records) : - row = {} - - row["qname"], row["sname"] = query[0], subject[0] - row["qlen"], row["slen"] = query[1], subject[1] - row["length"], row["gap"] = hsp.align_length, hsp.gaps - row["hsp_ident"], row["hsp_pos"] = hsp.identities, hsp.positives - row["expect"] = hsp.expect - - rows.append(row) - - df = pd.DataFrame(rows) - if df.empty : return df - - df["ppos"] = df["hsp_pos"] * 100 / df["length"] - df["pident"] = df["hsp_ident"] * 100 / df["length"] - - df["qide_prc"] = df["hsp_ident"] * 100 / df["qlen"] - df["qpos_prc"] = df["hsp_pos"] * 100 / df["qlen"] - df["side_prc"] = df["hsp_ident"] * 100 / df["slen"] - df["spos_prc"] = df["hsp_pos"] * 100 / df["slen"] - - return df - - def hsp_fuse(self) : - records = self.parse_xml_files() - return BCLineHSPFuse.get_sum_sim_all(records) - - -# -------------------------------------------------- -# Called function -# -------------------------------------------------- - -def launch_qblasts(program, database, fasta_list, chunk_size=None, time_wait=15, ** kwargs) : - # Run parallel or single qblast queries over ncbi using threads - # https://blast.ncbi.nlm.nih.gov/Blast.cgi?CMD=Web&PAGE_TYPE=BlastDocs&DOC_TYPE=DeveloperInfo - # time wait corresponds to the time beetween two parallele requests, if you don't want that - # ncbi kicks your ass, do not lower this value - - fasta_list = glob.glob(fasta_list) if isinstance(fasta_list, str) else fasta_list - qbr, qm = QueriesManager.from_multiple_fastas(program, database, fasta_list, chunk_size=chunk_size, ** kwargs) - qm.run(time_wait) - return qbr diff --git a/build/lib/pyblast/utils.py b/build/lib/pyblast/utils.py deleted file mode 100644 index bb6c515..0000000 --- a/build/lib/pyblast/utils.py +++ /dev/null @@ -1,129 +0,0 @@ -import os, tempfile, time, sys -from multiprocessing import Pool, cpu_count -from subprocess import check_output - -from Bio import SeqIO - -class TMPFname() : - - def __init__(self, delete=True, ext="", quiet=False, ** kwargs) : - suffix = self.format_ext(ext) - tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, ** kwargs) - self.fname = tmp.name - self.delete = delete - self.quiet = quiet - - def __str__(self) : - return self.fname - - def format_ext(self, ext) : - ext = str(ext) - if not ext : return ext - if not ext.startswith(".") : ext = "." + ext.strip() - return ext - - def exist(self) : - return os.path.isfile(self.fname) - - def remove(self) : - if self.exist() and self.delete : - if not self.quiet : print ("Delete temporary file at : %s" %(self.fname)) - os.remove(self.fname) - - def __del__(self) : - self.remove() - -class TMPFasta(TMPFname) : - - def __init__(self, data, delete=True, wodesc=False, ** kwargs) : - super(TMPFasta, self).__init__(delete=delete, ext="fa", ** kwargs) - if data : self.write(data, wodesc=wodesc) - - @staticmethod - def remove_desc(feature) : - feature.description = "" - return feature - - def write(self, data, mode="w", wodesc=False) : - if wodesc : data = (TMPFasta.remove_desc(feat) for feat in data) - with open(self.fname, mode) as handle : - SeqIO.write(data, handle, "fasta") - - -""" -PROCESSING -""" - -# decorator to time functions -def ftiming(f): - def wrap(*args, **kwargs): - # Receive 'quiet' from kwargs and use it directly - quiet = kwargs.get('quiet', False) # This will not modify kwargs - - funcname = kwargs.pop("funcname", f.__name__) - funcname = funcname or "Unknown funcname" - - time1 = time.time() - ret = f(*args, **kwargs) - time2 = time.time() - - # Only print if 'quiet' is False - if not quiet: - print('{:s} function took {:.3f} ms'.format(funcname, (time2 - time1) * 1000.0)) - - return ret - - return wrap - -def run_cmdline(cmdline, funcname=None, quiet=False): - # Pass all original parameters to the function - if not quiet: - return run_cmdline_loud(cmdline, funcname=funcname, quiet=quiet) - return check_output(cmdline, universal_newlines=True, shell=True) - -@ftiming -def run_cmdline_loud(cmdline, funcname=None, quiet=False): - return check_output(cmdline, universal_newlines=True, shell=True) - -""" -MULTIPROCESSING -""" - -class FunArgs() : - - def __init__(self, fun, * args, ** kwargs) : - self.fun = fun - self.args = args - self.kwargs = kwargs - - def launch_fun(self) : - return self.fun(* self.args, ** self.kwargs) - -class Multiprocess() : - - @staticmethod - def lambda_fun(farg) : - return farg.launch_fun() - - def run(self, fargs, ncore=1, quiet=False) : - if not quiet: print ("ncore : %i - available : %i" %(ncore, cpu_count())) - if ncore == 1 : return [farg.launch_fun() for farg in fargs] - - pool = Pool(ncore) - func = Multiprocess.lambda_fun - fargs = ((farg, ) for farg in fargs) - - try : - data = pool.starmap(func, fargs) - pool.close() - pool.join() - return data - - except KeyboardInterrupt : - print("Interrupt childs process") - pool.terminate() - sys.exit() - -def mproc(fargs, ncore=1, quiet=False) : - mp = Multiprocess() - return mp.run(fargs, ncore=ncore, quiet=quiet) \ No newline at end of file diff --git a/dist/pyblast-0.0.0-py3.10.egg b/dist/pyblast-0.0.0-py3.10.egg deleted file mode 100644 index 8a13bc4242c1bd8a3755f7260ef91ca523223721..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24709 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 diff --git a/pyblast.egg-info/PKG-INFO b/pyblast.egg-info/PKG-INFO deleted file mode 100644 index 8e88ab6..0000000 --- a/pyblast.egg-info/PKG-INFO +++ /dev/null @@ -1,3 +0,0 @@ -Metadata-Version: 2.1 -Name: pyblast -Version: 0.0.0 diff --git a/pyblast.egg-info/SOURCES.txt b/pyblast.egg-info/SOURCES.txt deleted file mode 100644 index a895463..0000000 --- a/pyblast.egg-info/SOURCES.txt +++ /dev/null @@ -1,10 +0,0 @@ -README.md -setup.py -pyblast/__init__.py -pyblast/bloc.py -pyblast/qblast.py -pyblast/utils.py -pyblast.egg-info/PKG-INFO -pyblast.egg-info/SOURCES.txt -pyblast.egg-info/dependency_links.txt -pyblast.egg-info/top_level.txt \ No newline at end of file diff --git a/pyblast.egg-info/dependency_links.txt b/pyblast.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/pyblast.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pyblast.egg-info/top_level.txt b/pyblast.egg-info/top_level.txt deleted file mode 100644 index c42613e..0000000 --- a/pyblast.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pyblast