From 23423e3d0c985a3d422b8410559cc1e5e0f297ae Mon Sep 17 00:00:00 2001 From: Vuk Manojlovic Date: Fri, 14 Jul 2023 17:30:03 +0200 Subject: [PATCH 1/5] CTX-4247: Created a bioinformatics folder, moved the qiime2 folder there and migrated shared code from templates related to sequence alignment to sequence_alignment --- .../{ => bioinformatics}/qiime2/__init__.py | 54 +++----- coretex/{ => bioinformatics}/qiime2/utils.py | 2 +- .../sequence_alignment/__init__.py | 126 ++++++++++++++++++ coretex/bioinformatics/utils.py | 30 +++++ 4 files changed, 179 insertions(+), 33 deletions(-) rename coretex/{ => bioinformatics}/qiime2/__init__.py (91%) rename coretex/{ => bioinformatics}/qiime2/utils.py (98%) create mode 100644 coretex/bioinformatics/sequence_alignment/__init__.py create mode 100644 coretex/bioinformatics/utils.py diff --git a/coretex/qiime2/__init__.py b/coretex/bioinformatics/qiime2/__init__.py similarity index 91% rename from coretex/qiime2/__init__.py rename to coretex/bioinformatics/qiime2/__init__.py index 23c552ab..28e6d97d 100644 --- a/coretex/qiime2/__init__.py +++ b/coretex/bioinformatics/qiime2/__init__.py @@ -21,24 +21,14 @@ import subprocess import logging +from ..utils import logProcessOutput + class QiimeCommandException(Exception): pass -def logProcessOutput(output: bytes, severity: int) -> None: - decoded = output.decode("UTF-8") - - for line in decoded.split("\n"): - # skip empty lines - if line.strip() == "": - continue - - # ignoring type for now, has to be fixed in coretexpylib - logging.getLogger("coretexpylib").log(severity, line) - - -def QiimeCommand(args: List[str]) -> None: +def command(args: List[str]) -> None: process = subprocess.Popen( args, shell = False, @@ -82,7 +72,7 @@ def toolsImport( "--input-format" , inputFormat ]) - QiimeCommand(args) + command(args) def demuxEmpSingle( @@ -93,7 +83,7 @@ def demuxEmpSingle( errorCorretctionDetailsPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "demux", "emp-single", "--i-seqs", sequencesPath, "--m-barcodes-file", barcodesPath, @@ -105,7 +95,7 @@ def demuxEmpSingle( def demuxSummarize(dataPath: str, visualizationPath: str) -> None: - QiimeCommand([ + command([ "qiime", "demux", "summarize", "--i-data", dataPath, "--o-visualization", visualizationPath @@ -121,7 +111,7 @@ def dada2DenoiseSingle( denoisingStatsPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "dada2", "denoise-single", "--i-demultiplexed-seqs", inputPath, "--p-trim-left", str(trimLeft), @@ -133,7 +123,7 @@ def dada2DenoiseSingle( def metadataTabulate(inputFile: str, visualizationPath: str) -> None: - QiimeCommand([ + command([ "qiime", "metadata", "tabulate", "--m-input-file", inputFile, "--o-visualization", visualizationPath @@ -141,7 +131,7 @@ def metadataTabulate(inputFile: str, visualizationPath: str) -> None: def featureTableSummarize(inputPath: str, visualizationPath: str, metadataPath: str) -> None: - QiimeCommand([ + command([ "qiime", "feature-table", "summarize", "--i-table", inputPath, "--o-visualization", visualizationPath, @@ -150,7 +140,7 @@ def featureTableSummarize(inputPath: str, visualizationPath: str, metadataPath: def featureTableTabulateSeqs(inputPath: str, visualizationPath: str) -> None: - QiimeCommand([ + command([ "qiime", "feature-table", "tabulate-seqs", "--i-data", inputPath, "--o-visualization", visualizationPath @@ -165,7 +155,7 @@ def phylogenyAlignToTreeMafftFasttree( rootedTreePath: str ) -> None: - QiimeCommand([ + command([ "qiime", "phylogeny", "align-to-tree-mafft-fasttree", "--i-sequences", sequencesPath, "--o-alignment", aligmentPath, @@ -183,7 +173,7 @@ def diversityCoreMetricsPhylogenetic( outputDir: str ) -> None: - QiimeCommand([ + command([ "qiime", "diversity", "core-metrics-phylogenetic", "--i-phylogeny", phlogenyPath, "--i-table", tablePath, @@ -199,7 +189,7 @@ def diversityAlphaGroupSignificance( visualizationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "diversity", "alpha-group-significance", "--i-alpha-diversity", alphaDiversityPath, "--m-metadata-file", metadataPath, @@ -215,7 +205,7 @@ def diversityBetaGroupSignificance( pairwise: bool ) -> None: - QiimeCommand([ + command([ "qiime", "diversity", "beta-group-significance", "--i-distance-matrix", distanceMatrixPath, "--m-metadata-file", metadataPath, @@ -232,7 +222,7 @@ def emperorPlot( visualizationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "emperor", "plot", "--i-pcoa", pcoaPath, "--m-metadata-file", metadataPath, @@ -249,7 +239,7 @@ def diversityAlphaRarefaction( visualizationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "diversity", "alpha-rarefaction", "--i-table", tablePath, "--i-phylogeny", phylogenyPath, @@ -265,7 +255,7 @@ def featureClassifierClassifySklearn( classificationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "feature-classifier", "classify-sklearn", "--i-classifier", classifierPath, "--i-reads", readsPath, @@ -280,7 +270,7 @@ def taxaBarplot( visualizationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "taxa", "barplot", "--i-table", tablePath, "--i-taxonomy", taxonomyPath, @@ -296,7 +286,7 @@ def featureTableFilterSamples( filteredTablePath: str ) -> None: - QiimeCommand([ + command([ "qiime", "feature-table", "filter-samples", "--i-table", tablePath, "--m-metadata-file", metadataPath, @@ -306,7 +296,7 @@ def featureTableFilterSamples( def compositionAddPseudocount(tablePath: str, compositionTablePath: str) -> None: - QiimeCommand([ + command([ "qiime", "composition", "add-pseudocount", "--i-table", tablePath, "--o-composition-table", compositionTablePath @@ -320,7 +310,7 @@ def compositionAncom( visualizationPath: str ) -> None: - QiimeCommand([ + command([ "qiime", "composition", "ancom", "--i-table", tablePath, "--m-metadata-file", metadataPath, @@ -336,7 +326,7 @@ def taxaCollapse( collapsedTablePath: str ) -> None: - QiimeCommand([ + command([ "qiime", "taxa", "collapse", "--i-table", tablePath, "--i-taxonomy", taxonomyPath, diff --git a/coretex/qiime2/utils.py b/coretex/bioinformatics/qiime2/utils.py similarity index 98% rename from coretex/qiime2/utils.py rename to coretex/bioinformatics/qiime2/utils.py index 3246d11d..59abff50 100644 --- a/coretex/qiime2/utils.py +++ b/coretex/bioinformatics/qiime2/utils.py @@ -21,7 +21,7 @@ import logging import gzip -from ..coretex import Experiment, CustomSample, CustomDataset +from ...coretex import Experiment, CustomSample, CustomDataset def createSample(name: str, datasetId: int, path: Path, experiment: Experiment, stepName: str, retryCount: int = 0) -> CustomSample: diff --git a/coretex/bioinformatics/sequence_alignment/__init__.py b/coretex/bioinformatics/sequence_alignment/__init__.py new file mode 100644 index 00000000..cc53890d --- /dev/null +++ b/coretex/bioinformatics/sequence_alignment/__init__.py @@ -0,0 +1,126 @@ +# Copyright (C) 2023 Coretex LLC + +# This file is part of Coretex.ai + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from typing import List, Tuple +from pathlib import Path + +import subprocess +import logging + +from ...coretex import CustomDataset +from ..utils import logProcessOutput + + +def command(args: List[str], ignoreOutput: bool = False) -> None: + process = subprocess.Popen( + args, + shell = False, + cwd = Path(__file__).parent, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE + ) + + while process.poll() is None: + stdout = process.stdout.readline() + stderr = process.stderr.readline() + + if len(stdout) > 0 and not ignoreOutput: + logProcessOutput(stdout, logging.INFO) + + if len(stderr) > 0 and not ignoreOutput: + if process.returncode == 0: + logProcessOutput(stderr, logging.INFO) + else: + logProcessOutput(stderr, logging.CRITICAL) + + if process.returncode != 0: + raise RuntimeError(f">> [Coretex] Falied to execute command. Returncode: {process.returncode}") + + +def indexCommand(bwaPath: Path, sequencePath: Path, prefix: Path) -> None: + command([ + str(bwaPath.absolute()), "index", + "-p", str(prefix.absolute()), + str(sequencePath.absolute()) + ]) + + +def alignCommand(bwaPath: Path, prefix: Path, sequencePath: Path, outputPath: Path) -> None: + args = [ + str(bwaPath.absolute()), "mem", + "-o", str(outputPath.absolute()), + str(prefix.absolute()), + str(sequencePath.absolute()) + ] + + command(args, True) + + +def sam2bamCommand(samtoolsPath: Path, samPath: Path, outputPath: Path) -> None: + command([ + str(samtoolsPath.absolute()), "view", + "-b", "-S", "-o", + str(outputPath.absolute()), + str(samPath.absolute()) + ]) + + +def extractData(samtoolsPath: Path, file: Path) -> Tuple[List[int], List[int], List[int]]: + scores: list[int] = [] + positions: list[int] = [] + sequenceLengths: list[int] = [] + + args = [ + str(samtoolsPath.absolute()), + "view", "-F", "256", "-F", "2048", "-F", "512", + str(file.absolute()) + ] + + process = subprocess.Popen( + args, + shell = False, + cwd = Path(__file__).parent, + stdout = subprocess.PIPE + ) + + for line in process.stdout: + fields = line.decode("UTF-8").strip().split("\t") + scores.append(int(fields[4])) + positions.append(int(fields[3])) + sequenceLengths.append(len(fields[9])) + + return scores, positions, sequenceLengths + + +def chmodX(file: Path) -> None: + command(["chmod", "+x", str(file.absolute())]) + + +def loadFa(dataset: CustomDataset) -> List[Path]: + inputFiles: list[Path] = [] + + for sample in dataset.samples: + sample.unzip() + + for file in Path(sample.path).iterdir(): + if file.suffix.startswith(".fa"): + inputFiles.append(file) + + if len(inputFiles) == 0: + raise ValueError(">> [Coretex] No sequence reads found") + + return inputFiles diff --git a/coretex/bioinformatics/utils.py b/coretex/bioinformatics/utils.py new file mode 100644 index 00000000..72f55659 --- /dev/null +++ b/coretex/bioinformatics/utils.py @@ -0,0 +1,30 @@ +# Copyright (C) 2023 Coretex LLC + +# This file is part of Coretex.ai + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import logging + + +def logProcessOutput(output: bytes, severity: int) -> None: + decoded = output.decode("UTF-8") + + for line in decoded.split("\n"): + # skip empty lines + if line.strip() == "": + continue + + # ignoring type for now, has to be fixed in coretexpylib + logging.getLogger("coretexpylib").log(severity, line) From 77dff7e20bff9ee70da08cde17415500aa474770 Mon Sep 17 00:00:00 2001 From: Vuk Manojlovic Date: Fri, 14 Jul 2023 17:51:31 +0200 Subject: [PATCH 2/5] CTX-4247: Cleaned up code --- coretex/bioinformatics/sequence_alignment/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coretex/bioinformatics/sequence_alignment/__init__.py b/coretex/bioinformatics/sequence_alignment/__init__.py index cc53890d..59d4da59 100644 --- a/coretex/bioinformatics/sequence_alignment/__init__.py +++ b/coretex/bioinformatics/sequence_alignment/__init__.py @@ -35,6 +35,9 @@ def command(args: List[str], ignoreOutput: bool = False) -> None: ) while process.poll() is None: + if process.stdout is None or process.stderr is None: + continue + stdout = process.stdout.readline() stderr = process.stderr.readline() @@ -97,6 +100,9 @@ def extractData(samtoolsPath: Path, file: Path) -> Tuple[List[int], List[int], L stdout = subprocess.PIPE ) + if process.stdout is None: + raise RuntimeError(">> [Coretex] Output is none for samtools view command. Could not extract data") + for line in process.stdout: fields = line.decode("UTF-8").strip().split("\t") scores.append(int(fields[4])) From d698be9027999f7e37eab56838975f021e9790e6 Mon Sep 17 00:00:00 2001 From: Vuk Manojlovic Date: Tue, 18 Jul 2023 17:26:14 +0200 Subject: [PATCH 3/5] CTX-4247: Improved code based on discussions --- coretex/bioinformatics/qiime2/__init__.py | 40 +---- .../sequence_alignment/__init__.py | 138 +++++++++++++----- coretex/bioinformatics/utils.py | 37 +++++ 3 files changed, 148 insertions(+), 67 deletions(-) diff --git a/coretex/bioinformatics/qiime2/__init__.py b/coretex/bioinformatics/qiime2/__init__.py index 28e6d97d..c6ed6bad 100644 --- a/coretex/bioinformatics/qiime2/__init__.py +++ b/coretex/bioinformatics/qiime2/__init__.py @@ -15,42 +15,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from typing import List, Optional -from pathlib import Path +from typing import Optional -import subprocess -import logging +from .utils import compressGzip, createSample, getDemuxSamples, getDenoisedSamples, \ + getFastqDPSamples, getFastqMPSamples, getMetadataSample, getPhylogeneticTreeSamples, \ + isDemultiplexedSample, isDenoisedSample, isFastqDPSample, isFastqMPSample, \ + isMetadataSample, isPhylogeneticTreeSample, sampleNumber -from ..utils import logProcessOutput - - -class QiimeCommandException(Exception): - pass - - -def command(args: List[str]) -> None: - process = subprocess.Popen( - args, - shell = False, - cwd = Path(__file__).parent, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE - ) - - stdout, stderr = process.communicate() - - if len(stdout) > 0: - logProcessOutput(stdout, logging.INFO) - - if len(stderr) > 0: - severity = logging.CRITICAL - if process.returncode == 0: - severity = logging.WARNING - - logProcessOutput(stderr, severity) - - if process.returncode != 0: - raise QiimeCommandException +from ..utils import command def toolsImport( diff --git a/coretex/bioinformatics/sequence_alignment/__init__.py b/coretex/bioinformatics/sequence_alignment/__init__.py index 59d4da59..bb2f7579 100644 --- a/coretex/bioinformatics/sequence_alignment/__init__.py +++ b/coretex/bioinformatics/sequence_alignment/__init__.py @@ -19,42 +19,39 @@ from pathlib import Path import subprocess -import logging +from ..utils import command from ...coretex import CustomDataset -from ..utils import logProcessOutput - - -def command(args: List[str], ignoreOutput: bool = False) -> None: - process = subprocess.Popen( - args, - shell = False, - cwd = Path(__file__).parent, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE - ) - - while process.poll() is None: - if process.stdout is None or process.stderr is None: - continue - - stdout = process.stdout.readline() - stderr = process.stderr.readline() - - if len(stdout) > 0 and not ignoreOutput: - logProcessOutput(stdout, logging.INFO) - - if len(stderr) > 0 and not ignoreOutput: - if process.returncode == 0: - logProcessOutput(stderr, logging.INFO) - else: - logProcessOutput(stderr, logging.CRITICAL) - - if process.returncode != 0: - raise RuntimeError(f">> [Coretex] Falied to execute command. Returncode: {process.returncode}") def indexCommand(bwaPath: Path, sequencePath: Path, prefix: Path) -> None: + """ + This function acts as a wrapper for the index command of BWA + (Burrows-Wheeler Aligner). It is necessary to perform indexing + on the reference sequence before alignment. + + Parameters + ---------- + bwaPath : Path + Path pointing to the bwa binary + sequencePath : Path + Path pointing to the reference sequence + prefix : Path + Path serving as the prefix for indexing. This command will + generate 5 new files that are needed for alignment. These files will + have the path "{prefix}.{suffix}", each with a different suffix / extension + + Example + ------- + >>> from pathlib import Path + >>> bwaPath = Path("tools/bwa") + >>> sequencePath = Path("sequences/hg18.fa") + >>> prefix = Path("output/hg18") + >>> indexCommand(bwaPath, sequencePath, prefix) + + Link to BWA: https://bio-bwa.sourceforge.net + """ + command([ str(bwaPath.absolute()), "index", "-p", str(prefix.absolute()), @@ -63,17 +60,69 @@ def indexCommand(bwaPath: Path, sequencePath: Path, prefix: Path) -> None: def alignCommand(bwaPath: Path, prefix: Path, sequencePath: Path, outputPath: Path) -> None: + """ + This function acts as a wrapper for the mem command of BWA + (Burrows-Wheeler Aligner). It perfoms alignment of a given sequence read + to the reference sequence and outputs a SAM file. + + Parameters + ---------- + bwaPath : Path + Path pointing to the bwa binary + prefix : Path + Path that serves as the prefix for the 5 files generated during indexing + sequencePath : Path + Path pointing to the sequence read file on which alignment should be performed + outputPath : Path + Path of the output file + + Example + ------- + >>> from pathlib import Path + >>> bwaPath = Path("tools/bwa") + >>> prefix = Path("reference/hg18") + >>> sequencePath = Path("input/R34D.fastq") + >>> outputPath = Path("output/R34D.sam") + >>> alignCommand(bwaPath, prefix, sequencePath, outputPath) + + Link to BWA: https://bio-bwa.sourceforge.net + """ + args = [ str(bwaPath.absolute()), "mem", "-o", str(outputPath.absolute()), str(prefix.absolute()), str(sequencePath.absolute()) - ] + ] command(args, True) def sam2bamCommand(samtoolsPath: Path, samPath: Path, outputPath: Path) -> None: + """ + This function uses the CLI tool "samtools" to convert SAM files into their binary + version, BAM. + + Parameters + ---------- + samtoolsPath : Path + Path pointing to the samtools binary + samPath : Path + Path pointing to the input .sam file + outputPath : Path + Path pointing to the output, .bam, file + + Example + ------- + >>> from pathlib import Path + >>> samtoolsPath = Path("tools/samtools") + >>> samPath = Path("input/R34D.sam") + >>> outputPath = Path("output/R34D.bam") + >>> sam2bamCommand(samtoolsPath, samPath, outputPath) + + Link to samtools: http://htslib.org/ + """ + command([ str(samtoolsPath.absolute()), "view", "-b", "-S", "-o", @@ -83,13 +132,36 @@ def sam2bamCommand(samtoolsPath: Path, samPath: Path, outputPath: Path) -> None: def extractData(samtoolsPath: Path, file: Path) -> Tuple[List[int], List[int], List[int]]: + """ + Takes an aligned sequence file (SAM/BAM) and returns three lists holding + MAPQ scores, leftmost position and sequence length for each read in the + file. This is done using the samtools view command. + + Parameters + ---------- + samtoolsPath : Path + Path pointing to the samtools binary + samPath : Path + Path pointing to the input .sam or .bam file + + Example + ------- + >>> from pathlib import Path + >>> samtoolsPath = Path("tools/samtools") + >>> file = Path("R34D.bam") + >>> scores, positions, lengths = extractData(samtoolsPath, file) + + Link to samtools: http://htslib.org/ + """ + scores: list[int] = [] positions: list[int] = [] sequenceLengths: list[int] = [] args = [ str(samtoolsPath.absolute()), - "view", "-F", "256", "-F", "2048", "-F", "512", + "view", + "-F", "256", "-F", "2048", "-F", "512", # Will ignore all duplicate reads str(file.absolute()) ] diff --git a/coretex/bioinformatics/utils.py b/coretex/bioinformatics/utils.py index 72f55659..ca2d684b 100644 --- a/coretex/bioinformatics/utils.py +++ b/coretex/bioinformatics/utils.py @@ -15,7 +15,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from typing import List +from pathlib import Path + import logging +import subprocess def logProcessOutput(output: bytes, severity: int) -> None: @@ -28,3 +32,36 @@ def logProcessOutput(output: bytes, severity: int) -> None: # ignoring type for now, has to be fixed in coretexpylib logging.getLogger("coretexpylib").log(severity, line) + + +class CommandException(Exception): + pass + + +def command(args: List[str], ignoreOutput: bool = False) -> None: + process = subprocess.Popen( + args, + shell = False, + cwd = Path(__file__).parent, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE + ) + + while process.poll() is None: + if process.stdout is None or process.stderr is None: + continue + + stdout = process.stdout.readline() + stderr = process.stderr.readline() + + if len(stdout) > 0 and not ignoreOutput: + logProcessOutput(stdout, logging.INFO) + + if len(stderr) > 0 and not ignoreOutput: + if process.returncode == 0: + logProcessOutput(stderr, logging.INFO) + else: + logProcessOutput(stderr, logging.CRITICAL) + + if process.returncode != 0: + raise CommandException(f">> [Coretex] Falied to execute command. Returncode: {process.returncode}") From 92ff2c75c2415b7815ea71d4d2b2f2cca6e7b1e6 Mon Sep 17 00:00:00 2001 From: Vuk Manojlovic Date: Wed, 19 Jul 2023 17:29:55 +0200 Subject: [PATCH 4/5] CTX-4247: Added error logging for extractData function --- .../sequence_alignment/__init__.py | 34 ++++++++++++++----- coretex/bioinformatics/utils.py | 4 +-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/coretex/bioinformatics/sequence_alignment/__init__.py b/coretex/bioinformatics/sequence_alignment/__init__.py index bb2f7579..4838ff37 100644 --- a/coretex/bioinformatics/sequence_alignment/__init__.py +++ b/coretex/bioinformatics/sequence_alignment/__init__.py @@ -19,8 +19,9 @@ from pathlib import Path import subprocess +import logging -from ..utils import command +from ..utils import command, logProcessOutput, CommandException from ...coretex import CustomDataset @@ -169,17 +170,32 @@ def extractData(samtoolsPath: Path, file: Path) -> Tuple[List[int], List[int], L args, shell = False, cwd = Path(__file__).parent, - stdout = subprocess.PIPE + stdout = subprocess.PIPE, + stderr = subprocess.PIPE ) - if process.stdout is None: - raise RuntimeError(">> [Coretex] Output is none for samtools view command. Could not extract data") + while process.poll() is None: + if process.stdout is None: + continue - for line in process.stdout: - fields = line.decode("UTF-8").strip().split("\t") - scores.append(int(fields[4])) - positions.append(int(fields[3])) - sequenceLengths.append(len(fields[9])) + stdout = process.stdout.readlines() + + for line in stdout: + if len(line) > 0: + fields = line.decode("UTF-8").strip().split("\t") + scores.append(int(fields[4])) + positions.append(int(fields[3])) + sequenceLengths.append(len(fields[9])) + + for stderr in process.stderr: + if len(stderr) > 0: + if process.returncode == 0: + logProcessOutput(stderr, logging.WARNING) + else: + logProcessOutput(stderr, logging.CRITICAL) + + if process.returncode != 0: + raise CommandException(f">> [Coretex] Falied to execute command. Returncode: {process.returncode}") return scores, positions, sequenceLengths diff --git a/coretex/bioinformatics/utils.py b/coretex/bioinformatics/utils.py index ca2d684b..22f039fa 100644 --- a/coretex/bioinformatics/utils.py +++ b/coretex/bioinformatics/utils.py @@ -38,10 +38,10 @@ class CommandException(Exception): pass -def command(args: List[str], ignoreOutput: bool = False) -> None: +def command(args: List[str], ignoreOutput: bool = False, shell: bool = False) -> None: process = subprocess.Popen( args, - shell = False, + shell = shell, cwd = Path(__file__).parent, stdout = subprocess.PIPE, stderr = subprocess.PIPE From 1ede1db14c4a3fcbd4ffc78130b16279b7553a64 Mon Sep 17 00:00:00 2001 From: Vuk Manojlovic Date: Thu, 20 Jul 2023 10:50:41 +0200 Subject: [PATCH 5/5] CTX-4247: Fixed linting error --- .../bioinformatics/sequence_alignment/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/coretex/bioinformatics/sequence_alignment/__init__.py b/coretex/bioinformatics/sequence_alignment/__init__.py index 4838ff37..19806730 100644 --- a/coretex/bioinformatics/sequence_alignment/__init__.py +++ b/coretex/bioinformatics/sequence_alignment/__init__.py @@ -187,12 +187,13 @@ def extractData(samtoolsPath: Path, file: Path) -> Tuple[List[int], List[int], L positions.append(int(fields[3])) sequenceLengths.append(len(fields[9])) - for stderr in process.stderr: - if len(stderr) > 0: - if process.returncode == 0: - logProcessOutput(stderr, logging.WARNING) - else: - logProcessOutput(stderr, logging.CRITICAL) + if process.stderr is not None: + for stderr in process.stderr: + if len(stderr) > 0: + if process.returncode == 0: + logProcessOutput(stderr, logging.WARNING) + else: + logProcessOutput(stderr, logging.CRITICAL) if process.returncode != 0: raise CommandException(f">> [Coretex] Falied to execute command. Returncode: {process.returncode}")