From 4826ebd86b6fc851e184cc50ad2cf4600e194c34 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:08:47 +0200 Subject: [PATCH 01/77] bin execs replaced by toml scripts --- bin/alignmentSieve | 12 ---- bin/bamCompare | 12 ---- bin/bamCoverage | 12 ---- bin/bamPEFragmentSize | 12 ---- bin/bigwigAverage | 12 ---- bin/bigwigCompare | 12 ---- bin/computeGCBias | 12 ---- bin/computeMatrix | 14 ----- bin/computeMatrixOperations | 12 ---- bin/correctGCBias | 12 ---- bin/deeptools | 12 ---- bin/estimateReadFiltering | 12 ---- bin/estimateScaleFactor | 115 ------------------------------------ bin/multiBamSummary | 14 ----- bin/multiBigwigSummary | 14 ----- bin/plotCorrelation | 12 ---- bin/plotCoverage | 12 ---- bin/plotEnrichment | 12 ---- bin/plotFingerprint | 12 ---- bin/plotHeatmap | 12 ---- bin/plotPCA | 12 ---- bin/plotProfile | 12 ---- 22 files changed, 373 deletions(-) delete mode 100755 bin/alignmentSieve delete mode 100755 bin/bamCompare delete mode 100755 bin/bamCoverage delete mode 100755 bin/bamPEFragmentSize delete mode 100755 bin/bigwigAverage delete mode 100755 bin/bigwigCompare delete mode 100755 bin/computeGCBias delete mode 100755 bin/computeMatrix delete mode 100755 bin/computeMatrixOperations delete mode 100755 bin/correctGCBias delete mode 100755 bin/deeptools delete mode 100755 bin/estimateReadFiltering delete mode 100755 bin/estimateScaleFactor delete mode 100755 bin/multiBamSummary delete mode 100755 bin/multiBigwigSummary delete mode 100755 bin/plotCorrelation delete mode 100755 bin/plotCoverage delete mode 100755 bin/plotEnrichment delete mode 100755 bin/plotFingerprint delete mode 100755 bin/plotHeatmap delete mode 100755 bin/plotPCA delete mode 100755 bin/plotProfile diff --git a/bin/alignmentSieve b/bin/alignmentSieve deleted file mode 100755 index 6d35603cec..0000000000 --- a/bin/alignmentSieve +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.alignmentSieve import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/bamCompare b/bin/bamCompare deleted file mode 100755 index 18cc2c12cb..0000000000 --- a/bin/bamCompare +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.bamCompare import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/bamCoverage b/bin/bamCoverage deleted file mode 100755 index 75fde98990..0000000000 --- a/bin/bamCoverage +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.bamCoverage import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/bamPEFragmentSize b/bin/bamPEFragmentSize deleted file mode 100755 index f38d51fa67..0000000000 --- a/bin/bamPEFragmentSize +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.bamPEFragmentSize import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/bigwigAverage b/bin/bigwigAverage deleted file mode 100755 index 3c01a85ef1..0000000000 --- a/bin/bigwigAverage +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.bigwigAverage import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/bigwigCompare b/bin/bigwigCompare deleted file mode 100755 index 61d0ce112e..0000000000 --- a/bin/bigwigCompare +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.bigwigCompare import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/computeGCBias b/bin/computeGCBias deleted file mode 100755 index bd1726bd09..0000000000 --- a/bin/computeGCBias +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.computeGCBias import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/computeMatrix b/bin/computeMatrix deleted file mode 100755 index 0e4ed9096a..0000000000 --- a/bin/computeMatrix +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.computeMatrix import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - elif len(sys.argv) == 2 and sys.argv[1] != '--version': - sys.argv.append("--help") - main(args) diff --git a/bin/computeMatrixOperations b/bin/computeMatrixOperations deleted file mode 100755 index 4e72fbea2b..0000000000 --- a/bin/computeMatrixOperations +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.computeMatrixOperations import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/correctGCBias b/bin/correctGCBias deleted file mode 100755 index 2906632c88..0000000000 --- a/bin/correctGCBias +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.correctGCBias import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/deeptools b/bin/deeptools deleted file mode 100755 index f5cd8a02e5..0000000000 --- a/bin/deeptools +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.deeptools_list_tools import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/estimateReadFiltering b/bin/estimateReadFiltering deleted file mode 100755 index 4948d4897e..0000000000 --- a/bin/estimateReadFiltering +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.estimateReadFiltering import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/estimateScaleFactor b/bin/estimateScaleFactor deleted file mode 100755 index 58b6e4ed36..0000000000 --- a/bin/estimateScaleFactor +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -import argparse -import sys - -from deeptools.SES_scaleFactor import estimateScaleFactor -from deeptools.parserCommon import numberOfProcessors -from deeptools._version import __version__ - -debug = 0 - - -def parseArguments(args=None): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description='Given two BAM files, this estimates scaling factors ' - '(bigger to smaller).') - - # define the arguments - parser.add_argument('--bamfiles', '-b', - metavar='list of bam files', - help='List of indexed BAM files, space delineated', - nargs='+', - required=True) - - - parser.add_argument('--ignoreForNormalization', '-ignore', - help='A comma-separated list of chromosome names, ' - 'limited by quotes, ' - 'containing those ' - 'chromosomes that should be excluded ' - 'during normalization computations. For example, ' - '--ignoreForNormalization "chrX, chrM" ') - - parser.add_argument('--sampleWindowLength', '-l', - help='Length in bases for a window used to ' - 'sample the genome and compute the size or scaling ' - 'factors', - default=1000, - type=int) - - parser.add_argument('--numberOfSamples', '-n', - help='Number of samplings taken from the genome ' - 'to compute the scaling factors', - default=100000, - type=int) - - parser.add_argument('--normalizationLength', '-nl', - help='By default, data is normalized to 1 ' - 'fragment per 100 bases. The expected value is an ' - 'integer. For example, if normalizationLength ' - 'is 1000, then the resulting scaling factor ' - 'will cause the average coverage of the BAM file to ' - 'have on average 1 fragment per kilobase', - type=int, - default=10) - - parser.add_argument('--skipZeros', - help='If set, then zero counts that happen for *all* ' - 'BAM files given are ignored. This will result in a ' - 'reduced number of read counts than that specified ' - 'in --numberOfSamples', - action='store_true', - required=False) - - parser.add_argument('--numberOfProcessors', '-p', - help='Number of processors to use. The default is ' - 'to use half the maximum number of processors.', - metavar="INT", - type=numberOfProcessors, - default="max/2", - required=False) - - parser.add_argument('--verbose', '-v', - help='Set to see processing messages.', - action='store_true') - - parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) - - args=parser.parse_args(args) - if args.ignoreForNormalization: - args.ignoreForNormalization=[x.strip() for x in args.ignoreForNormalization.split(',')] - else: - args.ignoreForNormalization = [] - return args - -def main(args): - """ - The algorithm samples the genome a number of times as specified - by the --numberOfSamples parameter to estimate scaling factors of - betweeen to samples - - """ - if len(args.bamfiles) > 2: - print("SES method to stimate scale factors only works for two samples") - exit(0) - - sys.stderr.write("{:,} number of samples will be computed.\n".format(args.numberOfSamples)) - sizeFactorsDict = estimateScaleFactor(args.bamfiles, args.sampleWindowLength, - args.numberOfSamples, - args.normalizationLength, - numberOfProcessors=args.numberOfProcessors, - chrsToSkip=args.ignoreForNormalization, - verbose=args.verbose) - - for k, v in sizeFactorsDict.items(): - print("{}: {}".format(k, v)) - - -if __name__ == "__main__": - args = parseArguments() - main(args) diff --git a/bin/multiBamSummary b/bin/multiBamSummary deleted file mode 100755 index 1dc376ea4c..0000000000 --- a/bin/multiBamSummary +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.multiBamSummary import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - if len(sys.argv) == 2 and sys.argv[1] != "--version": - sys.argv.append("--help") - main(args) diff --git a/bin/multiBigwigSummary b/bin/multiBigwigSummary deleted file mode 100755 index 37108ed934..0000000000 --- a/bin/multiBigwigSummary +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.multiBigwigSummary import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - if len(sys.argv) == 2 and sys.argv[1] != "--version": - sys.argv.append("--help") - main(args) diff --git a/bin/plotCorrelation b/bin/plotCorrelation deleted file mode 100755 index 560c15b7ef..0000000000 --- a/bin/plotCorrelation +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotCorrelation import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotCoverage b/bin/plotCoverage deleted file mode 100755 index 6957ff57e9..0000000000 --- a/bin/plotCoverage +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotCoverage import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotEnrichment b/bin/plotEnrichment deleted file mode 100755 index c1debcb766..0000000000 --- a/bin/plotEnrichment +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotEnrichment import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotFingerprint b/bin/plotFingerprint deleted file mode 100755 index c170dc005e..0000000000 --- a/bin/plotFingerprint +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotFingerprint import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotHeatmap b/bin/plotHeatmap deleted file mode 100755 index cb38dff76c..0000000000 --- a/bin/plotHeatmap +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotHeatmap import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotPCA b/bin/plotPCA deleted file mode 100755 index 636dbcbb8e..0000000000 --- a/bin/plotPCA +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotPCA import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) diff --git a/bin/plotProfile b/bin/plotProfile deleted file mode 100755 index b39f5261e0..0000000000 --- a/bin/plotProfile +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -#-*- coding: utf-8 -*- - -import deeptools.misc -from deeptools.plotProfile import main -import sys - -if __name__ == "__main__": - args = None - if len(sys.argv) == 1: - args = ["--help"] - main(args) From 0c609ded838130093bf37199ca6c193b11cab768 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:09:14 +0200 Subject: [PATCH 02/77] replace version check for tests with toml version definition --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d35d5517d7..d83fb359f0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,9 +8,9 @@ jobs: steps: - uses: actions/checkout@v1 - name: Check path - run: find /home/runner/work/deepTools/deepTools -name "_version.py" + run: find /home/runner/work/deepTools/deepTools -name "pyproject.toml" - name: Get Version of Deeptools - run: echo "deeptools_version=$(grep "__version__" /home/runner/work/deepTools/deepTools/deeptools/_version.py | awk '{print substr($NF, 2, length($NF) - 2)}')" >> $GITHUB_ENV + run: echo "deeptools_version=$(grep "version" /home/runner/work/deepTools/deepTools/pyproject.toml | awk '{print substr($NF, 2, length($NF) - 2)}')" >> $GITHUB_ENV - name: Get Version of Galaxy tools run: echo "galaxy_deeptools_version=$(grep "token.*TOOL_VERSION" /home/runner/work/deepTools/deepTools/galaxy/wrapper/deepTools_macros.xml | awk -F '>|<' '{print $3}')" >> $GITHUB_ENV - name: Versions From 288ba70d68cfa61f6cba0f47ec962c95d869aa1c Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:09:33 +0200 Subject: [PATCH 03/77] remove _version file --- deeptools/_version.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 deeptools/_version.py diff --git a/deeptools/_version.py b/deeptools/_version.py deleted file mode 100755 index cb8316dc0a..0000000000 --- a/deeptools/_version.py +++ /dev/null @@ -1,5 +0,0 @@ - -# This file is originally generated from Git information by running 'setup.py -# version'. Distribution tarballs contain a pre-generated copy of this file. - -__version__ = '3.5.2' From fb0a242a698fb729f1f5de9a62fcdb7fee36fdc6 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:09:57 +0200 Subject: [PATCH 04/77] version checks with importlib --- deeptools/alignmentSieve.py | 4 ++-- deeptools/bamPEFragmentSize.py | 4 ++-- deeptools/computeMatrix.py | 6 +++--- deeptools/computeMatrixOperations.py | 4 ++-- deeptools/deeptools_list_tools.py | 4 ++-- deeptools/estimateReadFiltering.py | 4 ++-- deeptools/multiBamSummary.py | 4 ++-- deeptools/multiBigwigSummary.py | 4 ++-- deeptools/parserCommon.py | 6 +++--- deeptools/plotCorrelation.py | 4 ++-- deeptools/plotCoverage.py | 4 ++-- deeptools/plotPCA.py | 5 ++--- 12 files changed, 26 insertions(+), 27 deletions(-) diff --git a/deeptools/alignmentSieve.py b/deeptools/alignmentSieve.py index 5e28b2ceb5..9e1d621b88 100644 --- a/deeptools/alignmentSieve.py +++ b/deeptools/alignmentSieve.py @@ -7,7 +7,7 @@ from deeptools import parserCommon from deeptools.bamHandler import openBam from deeptools.mapReduce import mapReduce -from deeptools._version import __version__ +from importlib.metadata import version from deeptools.utilities import getTLen, smartLabels, getTempFileName @@ -60,7 +60,7 @@ def parseArguments(): action='store_true') general.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) general.add_argument('--shift', nargs='+', diff --git a/deeptools/bamPEFragmentSize.py b/deeptools/bamPEFragmentSize.py index 646b51cae1..35cedfa432 100755 --- a/deeptools/bamPEFragmentSize.py +++ b/deeptools/bamPEFragmentSize.py @@ -18,7 +18,7 @@ # own tools from deeptools.parserCommon import writableFile from deeptools.getFragmentAndReadSize import get_read_and_fragment_length -from deeptools._version import __version__ +from importlib.metadata import version def parse_arguments(): @@ -109,7 +109,7 @@ def parse_arguments(): action='store_true', required=False) parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) return parser diff --git a/deeptools/computeMatrix.py b/deeptools/computeMatrix.py index 99c1283248..25cf95216e 100644 --- a/deeptools/computeMatrix.py +++ b/deeptools/computeMatrix.py @@ -7,7 +7,7 @@ import multiprocessing from deeptools.parserCommon import writableFile, numberOfProcessors -from deeptools._version import __version__ +from importlib.metadata import version from deeptools import parserCommon from deeptools import heatmapper import deeptools.computeMatrixOperations as cmo @@ -37,7 +37,7 @@ def parse_arguments(args=None): ' -R -b 1000\n \n') parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) subparsers = parser.add_subparsers( title='Commands', @@ -137,7 +137,7 @@ def computeMatrixOptArgs(case=['scale-regions', 'reference-point'][0]): parser = argparse.ArgumentParser(add_help=False) optional = parser.add_argument_group('Optional arguments') optional.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) if case == 'scale-regions': optional.add_argument('--regionBodyLength', '-m', diff --git a/deeptools/computeMatrixOperations.py b/deeptools/computeMatrixOperations.py index deb62076cf..0abf4b7e2d 100755 --- a/deeptools/computeMatrixOperations.py +++ b/deeptools/computeMatrixOperations.py @@ -6,7 +6,7 @@ import sys import os import csv -from deeptools._version import __version__ +from importlib.metadata import version def parse_arguments(): @@ -138,7 +138,7 @@ def parse_arguments(): usage='Example usage:\n computeMatrixOperations dataRange -m input.mat.gz\n\n') parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) return parser diff --git a/deeptools/deeptools_list_tools.py b/deeptools/deeptools_list_tools.py index 15b0e6a8d1..32dcf7021f 100644 --- a/deeptools/deeptools_list_tools.py +++ b/deeptools/deeptools_list_tools.py @@ -3,7 +3,7 @@ import argparse import sys -from deeptools._version import __version__ +from importlib.metadata import version def parse_arguments(args=None): @@ -61,7 +61,7 @@ def parse_arguments(args=None): """) parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) return parser diff --git a/deeptools/estimateReadFiltering.py b/deeptools/estimateReadFiltering.py index 464fe09997..63b02327d8 100644 --- a/deeptools/estimateReadFiltering.py +++ b/deeptools/estimateReadFiltering.py @@ -5,7 +5,7 @@ from deeptools import parserCommon, bamHandler, utilities from deeptools.mapReduce import mapReduce from deeptools.utilities import smartLabels -from deeptools._version import __version__ +from importlib.metadata import version def parseArguments(): @@ -92,7 +92,7 @@ def parseArguments(): action='store_true') general.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) filtering = parser.add_argument_group('Optional arguments') diff --git a/deeptools/multiBamSummary.py b/deeptools/multiBamSummary.py index a588c09c98..433adb4e42 100644 --- a/deeptools/multiBamSummary.py +++ b/deeptools/multiBamSummary.py @@ -9,7 +9,7 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -from deeptools._version import __version__ +from importlib.metadata import version old_settings = np.seterr(all='ignore') @@ -44,7 +44,7 @@ def parse_arguments(args=None): conflict_handler='resolve') parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) subparsers = parser.add_subparsers( title="commands", dest='command', diff --git a/deeptools/multiBigwigSummary.py b/deeptools/multiBigwigSummary.py index 8d89421f13..ad61609071 100644 --- a/deeptools/multiBigwigSummary.py +++ b/deeptools/multiBigwigSummary.py @@ -7,7 +7,7 @@ import numpy as np import multiprocessing from deeptools import parserCommon -from deeptools._version import __version__ +from importlib.metadata import version from deeptools.utilities import smartLabels import deeptools.getScorePerBigWigBin as score_bw import deeptools.deepBlue as db @@ -43,7 +43,7 @@ def parse_arguments(args=None): conflict_handler='resolve') parser.add_argument('--version', action='version', - version='multiBigwigSummary {}'.format(__version__)) + version='multiBigwigSummary {}'.format(version('deeptools'))) subparsers = parser.add_subparsers( title="commands", dest='command', diff --git a/deeptools/parserCommon.py b/deeptools/parserCommon.py index ef4f4d0748..8e726ea000 100755 --- a/deeptools/parserCommon.py +++ b/deeptools/parserCommon.py @@ -1,6 +1,6 @@ import argparse import os -from deeptools._version import __version__ +from importlib.metadata import version def check_float_0_1(value): @@ -297,7 +297,7 @@ def getParentArgParse(args=None, binSize=True, blackList=True): optional = parser.add_argument_group('Optional arguments') optional.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) if binSize: optional.add_argument('--binSize', '-bs', @@ -521,7 +521,7 @@ def heatmapperOptionalArgs(mode=['heatmap', 'profile'][0]): optional.add_argument("--help", "-h", action="help", help="show this help message and exit") optional.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) if mode == 'profile': optional.add_argument( '--averageType', diff --git a/deeptools/plotCorrelation.py b/deeptools/plotCorrelation.py index a03839baa1..e3a553f567 100644 --- a/deeptools/plotCorrelation.py +++ b/deeptools/plotCorrelation.py @@ -13,7 +13,7 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -from deeptools._version import __version__ +from importlib.metadata import version old_settings = np.seterr(all='ignore') @@ -117,7 +117,7 @@ def plot_correlation_args(): action='store_true') optional.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) group = parser.add_argument_group('Output optional options') diff --git a/deeptools/plotCoverage.py b/deeptools/plotCoverage.py index 02ce25dadb..d0b8613bbf 100755 --- a/deeptools/plotCoverage.py +++ b/deeptools/plotCoverage.py @@ -18,7 +18,7 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -from deeptools._version import __version__ +from importlib.metadata import version old_settings = np.seterr(all='ignore') @@ -49,7 +49,7 @@ def parse_arguments(args=None): conflict_handler='resolve') parser.add_argument('--version', action='version', - version='plotCoverage {}'.format(__version__)) + version='plotCoverage {}'.format(version('deeptools'))) return parser diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index d12eac8d4b..75dc8a4bb3 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -11,8 +11,7 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -from deeptools._version import __version__ - +from importlib.metadata import version def parse_arguments(args=None): basic_args = plotCorrelationArgs() @@ -133,7 +132,7 @@ def plotCorrelationArgs(): help="A list of markers for the symbols. (e.g., '<','>','o') are accepted. The marker values should be space separated. For example, --markers 's' 'o' 's' 'o'. If not specified, the symbols will be given automatic shapes.") optional.add_argument('--version', action='version', - version='%(prog)s {}'.format(__version__)) + version='%(prog)s {}'.format(version('deeptools'))) optionalEx = optional.add_mutually_exclusive_group() optionalEx.add_argument('--transpose', From 45dea0000ac85478347bef903e1bb3f471ab97bc Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:10:24 +0200 Subject: [PATCH 05/77] version check docs update to importlib --- docs/conf.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f88dd260ff..cd8b3abc2f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,7 @@ import sys import os +import tomllib # to allow readthedocs to compile without installing some dependencies import mock @@ -81,18 +82,12 @@ def get_version(): - import re + with open('../pyproject.toml', 'rb') as f: + d = tomllib.load(f) try: - f = open("../deeptools/_version.py") - except EnvironmentError: + return d['project']['version'] + except: return None - for line in f.readlines(): - mo = re.match("__version__ = '([^']+)'", line) - if mo: - ver = mo.group(1) - return ver - return None - version = get_version() # The full version, including alpha/beta/rc tags. From 6ca54b0bbb9976da20e91e5f8f20be2f0714b729 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:11:44 +0200 Subject: [PATCH 06/77] purge reqs & setup.py --- requirements.txt | 99 ------------------------------------------------ setup.py | 97 ----------------------------------------------- 2 files changed, 196 deletions(-) delete mode 100644 requirements.txt delete mode 100755 setup.py diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index bdca34316f..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,99 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile -# -alabaster==0.7.12 - # via sphinx -babel==2.10.1 - # via sphinx -certifi==2023.7.22 - # via requests -charset-normalizer==2.0.12 - # via requests -cycler==0.11.0 - # via matplotlib -deeptoolsintervals==0.1.9 - # via -r requirements.in -docutils==0.17.1 - # via sphinx -fonttools==4.33.3 - # via matplotlib -idna==3.3 - # via requests -imagesize==1.3.0 - # via sphinx -importlib-metadata==4.11.3 - # via sphinx -jinja2==3.1.2 - # via - # numpydoc - # sphinx -kiwisolver==1.4.2 - # via matplotlib -markupsafe==2.1.1 - # via jinja2 -matplotlib>=3.5<3.6 - # We're manually setting matplitlib <3.6 to avoide issue #1172 -numpy==1.22.3 - # via - # -r requirements.in - # matplotlib - # scipy -numpydoc==1.3.1 - # via -r requirements.in -packaging==21.3 - # via - # matplotlib - # sphinx -pillow==9.3.0 - # via matplotlib -plotly==5.7.0 - # via -r requirements.in -py2bit==0.3.0 - # via -r requirements.in -pybigwig==0.3.18 - # via -r requirements.in -pygments==2.15.0 - # via sphinx -pyparsing==3.0.8 - # via - # matplotlib - # packaging -pysam==0.19.0 - # via -r requirements.in -python-dateutil==2.8.2 - # via matplotlib -pytz==2022.1 - # via babel -requests==2.31.0 - # via sphinx -scipy==1.10.0 - # via -r requirements.in -six==1.16.0 - # via - # plotly - # python-dateutil -snowballstemmer==2.2.0 - # via sphinx -sphinx==4.5.0 - # via numpydoc -sphinxcontrib-applehelp==1.0.2 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.0 - # via sphinx -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -tenacity==8.0.1 - # via plotly -urllib3==1.26.9 - # via requests -zipp==3.8.0 - # via importlib-metadata \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100755 index 76514364de..0000000000 --- a/setup.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - -from setuptools import setup, find_packages -from setuptools.command.sdist import sdist as _sdist -from setuptools.command.install import install as _install - -VERSION_PY = """ -# This file is originally generated from Git information by running 'setup.py -# version'. Distribution tarballs contain a pre-generated copy of this file. - -__version__ = '%s' -""" - - -def get_version(): - try: - f = open("deeptools/_version.py") - except EnvironmentError: - return None - for line in f.readlines(): - mo = re.match("__version__ = '([^']+)'", line) - if mo: - ver = mo.group(1) - return ver - return None - - -class sdist(_sdist): - - def run(self): - self.distribution.metadata.version = get_version() - return _sdist.run(self) - - -class install(_install): - - def run(self): - self.distribution.metadata.version = get_version() - _install.run(self) - return - - -def openREADME(): - """ - This is only needed because README.rst is UTF-8 encoded and that won't work - under python3 iff sys.getfilesystemencoding() returns 'ascii' - - Since open() doesn't accept an encoding in python2... - """ - try: - f = open("README.rst", encoding="utf-8") - except: - f = open("README.rst") - - foo = f.read() - f.close() - return foo - - -setup( - name='deepTools', - version=get_version(), - author='Fidel Ramirez, Devon P Ryan, Björn Grüning, Friederike Dündar, Sarah Diehl,' - ' Vivek Bhardwaj, Fabian Kilpert, Andreas S Richter, Steffen Heyne, Thomas Manke', - author_email='dpryan79@gmail.com', - packages=find_packages(), - scripts=['bin/bamCompare', 'bin/bamCoverage', 'bin/multiBamSummary', - 'bin/plotHeatmap', 'bin/plotFingerprint', 'bin/estimateScaleFactor', - 'bin/bamPEFragmentSize', 'bin/computeMatrix', 'bin/plotProfile', - 'bin/computeGCBias', 'bin/correctGCBias', 'bin/multiBigwigSummary', - 'bin/bigwigCompare', 'bin/plotCoverage', 'bin/plotPCA', 'bin/plotCorrelation', - 'bin/plotEnrichment', 'bin/deeptools', 'bin/computeMatrixOperations', - 'bin/estimateReadFiltering', 'bin/alignmentSieve', 'bin/bigwigAverage'], - include_package_data=True, - url='http://pypi.python.org/pypi/deepTools/', - license='LICENSE.txt', - description='Useful tools for exploring deep sequencing data ', - long_description=openREADME(), - classifiers=[ - 'Intended Audience :: Science/Research', - 'Topic :: Scientific/Engineering :: Bio-Informatics'], - install_requires=[ - "numpy >= 1.9.0", - "scipy >= 0.17.0", - "matplotlib >= 3.3.0", - "pysam >= 0.14.0", - "numpydoc >= 0.5", - "pyBigWig >= 0.2.1", - "py2bit >= 0.2.0", - "plotly >= 4.9", - "deeptoolsintervals >= 0.1.8" - ], - zip_safe=True, - cmdclass={'sdist': sdist, 'install': install} -) From c986db967e429bf9d3fff5c4c286c6b6fc889d1f Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:11:54 +0200 Subject: [PATCH 07/77] init toml --- pyproject.toml | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..c5dfe3e896 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,72 @@ +[build-system] +requires = [ + "setuptools" +] + +[project] +name = "deepTools" +version = "3.5.3.dev1" +authors = [ + {name="Fidel Ramirez"}, + {name="Devon P Ryan"}, + {name="Björn Grüning"}, + {name="Friederike Dündar"}, + {name="Sarah Diehl"}, + {name="Vivek Bhardwaj"}, + {name="Fabian Kilpert"}, + {name="Andreas S Richter"}, + {name="Steffen Heyne"}, + {name="Thomas Manke"}, + {email="bioinfo-core@ie-freiburg.mpg.de"} +] +requires-python = ">=3.7" +dependencies = [ + "numpy >= 1.9.0", + "scipy >= 0.17.0", + "matplotlib >= 3.3.0", + "pysam >= 0.14.0", + "numpydoc >= 0.5", + "pyBigWig >= 0.2.1", + "py2bit >= 0.2.0", + "plotly >= 4.9", + "deeptoolsintervals >= 0.1.8" +] +description = "Useful tools for exploring deep sequencing data." +license = {file = "LICENSE.txt"} +classifiers = [ + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Bio-Informatics" +] +readme = "README.rst" + +[project.urls] +homepage = "https://pypi.python.org/pypi/deepTools/" +documentation = "https://deeptools.readthedocs.io/en/latest/" +repository = "https://github.com/deeptools/deepTools" + +[tool.setuptools] +packages = ["deeptools"] + +[project.scripts] +alignmentSieve = "deeptools.alignmentSieve:main" +bamCompare = "deeptools.bamCompare:main" +bamCoverage = "deeptools.bamCoverage:main" +bamPEFragmentSize = "deeptools.bamPEFragmentSize:main" +bigwigAverage = "deeptools.bigwigAverage:main" +bigwigCompare = "deeptools.bigwigCompare:main" +computeGCBias = "deeptools.computeGCBias:main" +computeMatrix = "deeptools.computeMatrix:main" +computeMatrixOperations = "deeptools.computeMatrixOperations:main" +correctGCBias = "deeptools.correctGCBias:main" +deeptools = "deeptools.deeptools_list_tools:main" +estimateReadFiltering = "deeptools.estimateReadFiltering:main" +estimateScaleFactor = "deeptools.estimateScaleFactor:main" +multiBamSummary = "deeptools.multiBamSummary:main" +multiBigwigSummary = "deeptools.multiBigwigSummary:main" +plotCorrelation = "deeptools.plotCorrelation:main" +plotCoverage = "deeptools.plotCoverage:main" +plotEnrichment = "deeptools.plotEnrichment:main" +plotFingerprint = "deeptools.plotFingerPrint:main" +plotHeatmap = "deeptools.plotHeatmap:main" +plotPCA = "deeptools.plotPCA:main" +plotProfile = "deeptools.plotProfile:main" From ce327bcdfa80fbdc7bd82b39e424bb03f3f15450 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 14:26:39 +0200 Subject: [PATCH 08/77] update cm for mpl > 3.7 --- deeptools/cm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deeptools/cm.py b/deeptools/cm.py index fcb7c20ff6..47bcf16285 100644 --- a/deeptools/cm.py +++ b/deeptools/cm.py @@ -30,7 +30,7 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from matplotlib import colors, cm as mpl_cm +from matplotlib import colors, colormaps as mpl_cm _rocket_lut = [ @@ -1084,5 +1084,5 @@ _cmap_r = colors.ListedColormap(_lut[::-1], _name + "_r") locals()[_name + "_r"] = _cmap_r - mpl_cm.register_cmap(_name, _cmap) - mpl_cm.register_cmap(_name + "_r", _cmap_r) + mpl_cm.register(_cmap, name=_name) + mpl_cm.register(_cmap_r, name=_name + "_r") From 63526573ae9d6f38acd77c6b365d068ff25aa3e4 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 16:27:17 +0200 Subject: [PATCH 09/77] port nosetests to pytest --- .../test/test_bamCoverage_and_bamCompare.py | 31 ++- .../test/test_computeMatrixOperations.py | 18 +- deeptools/test/test_countReadsPerBin.py | 217 ++++++++---------- ...er_images.py => test_heatmapper_images.py} | 5 - deeptools/test/test_readFiltering.py | 11 +- deeptools/test/test_writeBedGraph.py | 151 +++++------- 6 files changed, 181 insertions(+), 252 deletions(-) rename deeptools/test/{testskip_heatmapper_images.py => test_heatmapper_images.py} (98%) diff --git a/deeptools/test/test_bamCoverage_and_bamCompare.py b/deeptools/test/test_bamCoverage_and_bamCompare.py index fbc8b9fd6d..a7e5e4ad59 100644 --- a/deeptools/test/test_bamCoverage_and_bamCompare.py +++ b/deeptools/test/test_bamCoverage_and_bamCompare.py @@ -1,4 +1,3 @@ -from nose.tools import assert_equal import deeptools.bamCoverage as bam_cov import deeptools.bamCompare as bam_comp import deeptools.getScaleFactor as gs @@ -46,7 +45,7 @@ def test_bam_coverage_arguments(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t150\t1\n', '3R\t150\t200\t2\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -59,7 +58,7 @@ def test_bam_coverage_extend(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t150\t1\n', '3R\t150\t200\t3\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -76,7 +75,7 @@ def test_bam_coverage_extend_and_normalizeUsingRPGC(): # the scale factor should be 0.5, thus the result is similar to # that of the previous test divided by 0.5 expected = ['3R\t0\t150\t0.5\n', '3R\t150\t200\t1.5\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -90,7 +89,7 @@ def test_bam_coverage_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t50\t150\t1\n', '3R\t150\t200\t2\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -104,7 +103,7 @@ def test_bam_coverage_filtering(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t200\t1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -124,7 +123,7 @@ def test_bam_compare_arguments(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t200\t1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -142,7 +141,7 @@ def test_bam_compare_diff_files(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t-1\n', '3R\t100\t150\t0\n', '3R\t150\t200\t-1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -159,7 +158,7 @@ def test_bam_compare_pseudocounts(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\tinf\n', '3R\t50\t100\t0\n', '3R\t100\t150\t1\n', '3R\t150\t200\t0\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -176,7 +175,7 @@ def test_bam_compare_ZoverZ(): resp = _foo.readlines() _foo.close() expected = ['3R\t50\t100\t-1\n', '3R\t100\t150\t0\n', '3R\t150\t200\t-0.584963\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -228,7 +227,7 @@ def test_bam_compare_diff_files_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t100\t150\t0\n', '3R\t150\t200\t-1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -246,7 +245,7 @@ def test_bam_compare_extend(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t100\t-1\n', '3R\t100\t150\t1\n', '3R\t150\t200\t-1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -286,7 +285,7 @@ def test_bam_compare_scale_factors_ratio(): """ expected = ['3R\t0\t50\t1\n', '3R\t50\t100\t0.666667\n', '3R\t100\t150\t1.33333\n', '3R\t150\t200\t1\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -328,7 +327,7 @@ def test_bam_compare_scale_factors_subtract(): """ expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t-250000\n', '3R\t100\t150\t250000\n', '3R\t150\t200\t0\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -358,7 +357,7 @@ def test_bam_coverage_filter_blacklist(): '3R\t950\t1000\t1.62672\n', '3R\t1000\t1050\t0.813362\n', '3R\t1050\t1500\t0\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) @@ -459,5 +458,5 @@ def test_bam_compare_filter_blacklist(): '3R\t750\t800\t-0.123451\n', '3R\t900\t950\t0.212545\n', '3R\t950\t1000\t0.199309\n', '3R\t1000\t1050\t0.167945\n', '3R\t1050\t1500\t0\n'] - assert_equal(resp, expected) + assert resp == expected, "{} != {}".format(resp, expected) unlink(outfile) diff --git a/deeptools/test/test_computeMatrixOperations.py b/deeptools/test/test_computeMatrixOperations.py index 27f2e633e9..df81d54595 100644 --- a/deeptools/test/test_computeMatrixOperations.py +++ b/deeptools/test/test_computeMatrixOperations.py @@ -8,9 +8,6 @@ __author__ = 'Devon' -ROOT = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" - - def getHeader(fp): s = fp.readline() if isinstance(s, bytes): @@ -20,20 +17,21 @@ def getHeader(fp): class TestComputeMatrixOperations(object): - def setUp(self): - self.root = ROOT - self.matrix = self.root + "computeMatrixOperations.mat.gz" - self.bed = self.root + "computeMatrixOperations.bed" - self.rbindMatrix1 = self.root + "somegenes.txt.gz" - self.rbindMatrix2 = self.root + "othergenes.txt.gz" + root = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" + matrix = root + "computeMatrixOperations.mat.gz" + bed = root + "computeMatrixOperations.bed" + rbindMatrix1 = root + "somegenes.txt.gz" + rbindMatrix2 = root + "othergenes.txt.gz" def testSubset(self): """ computeMatrixOperations subset """ + dCorrect = {"verbose": True, "scale": 1, "skip zeros": False, "nan after end": False, "sort using": "mean", "unscaled 5 prime": [0, 0, 0, 0], "body": [1000, 1000, 1000, 1000], "sample_labels": ["SRR648667.forward", "SRR648668.forward", "SRR648669.forward", "SRR648670.forward"], "downstream": [0, 0, 0, 0], "unscaled 3 prime": [0, 0, 0, 0], "group_labels": ["genes"], "bin size": [10, 10, 10, 10], "upstream": [0, 0, 0, 0], "group_boundaries": [0, 196], "sample_boundaries": [0, 100, 200, 300, 400], "max threshold": None, "ref point": [None, None, None, None], "min threshold": None, "sort regions": "no", "proc number": 20, "bin avg type": "mean", "missing data as zero": False} oname = "/tmp/subset.mat.gz" args = "subset -m {} --sample SRR648667.forward SRR648668.forward SRR648669.forward SRR648670.forward -o {}".format(self.matrix, oname) + print(args) args = args.split() cmo.main(args) f = gzip.GzipFile(oname) @@ -68,7 +66,7 @@ def testfilterStrand(self): dCorrect = {"verbose": True, "scale": 1, "skip zeros": False, "nan after end": False, "sort using": "mean", "unscaled 5 prime": [0, 0, 0, 0, 0, 0, 0, 0], "body": [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], "sample_labels": ["SRR648667.forward", "SRR648668.forward", "SRR648669.forward", "SRR648670.forward", "SRR648667.reverse", "SRR648668.reverse", "SRR648669.reverse", "SRR648670.reverse"], "downstream": [0, 0, 0, 0, 0, 0, 0, 0], "unscaled 3 prime": [0, 0, 0, 0, 0, 0, 0, 0], "group_labels": ["genes"], "bin size": [10, 10, 10, 10, 10, 10, 10, 10], "upstream": [0, 0, 0, 0, 0, 0, 0, 0], "group_boundaries": [0, 107], "sample_boundaries": [0, 100, 200, 300, 400, 500, 600, 700, 800], "max threshold": None, "ref point": [None, None, None, None, None, None, None, None], "min threshold": None, "sort regions": "no", "proc number": 20, "bin avg type": "mean", "missing data as zero": False} oname = "/tmp/filterStrand1.mat.gz" args = "filterStrand -m {} -o {} --strand +".format(self.matrix, oname) - args = args.split() + args = args.split(' ') cmo.main(args) f = gzip.GzipFile(oname) d = getHeader(f) # Skip the header, which can be in a different order diff --git a/deeptools/test/test_countReadsPerBin.py b/deeptools/test/test_countReadsPerBin.py index 52941071d3..882ca603f6 100644 --- a/deeptools/test/test_countReadsPerBin.py +++ b/deeptools/test/test_countReadsPerBin.py @@ -4,157 +4,163 @@ import numpy as np import numpy.testing as nt import os.path +import pytest __author__ = 'Fidel' -ROOT = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" +@pytest.mark.parametrize("bc", ["bam", 'cram']) +class TestCountReadsPerBin(): - -class TestCountReadsPerBin(object): - - def setUp(self): - """ - The distribution of reads between the two bam files is as follows. - - They cover 200 bp:: - - 0 100 200 - |------------------------------------------------------------| - A ==============> - <============== - - - B <============== ==============> - ==============> - ==============> - """ - self.root = ROOT - self.bamFile1 = self.root + "testA.bam" - self.bamFile2 = self.root + "testB.bam" - self.bamFile_PE = self.root + "test_paired2.bam" - self.chrom = '3R' + def ifiles(self, ext='bam'): + root = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" + bamFile1 = root + "testA." + ext + bamFile2 = root + "testB." + ext + bamFile_PE = root + "test_paired2." + ext + chrom = '3R' step_size = 50 bin_length = 25 - - self.c = cr.CountReadsPerBin([self.bamFile1, self.bamFile2], - binLength=bin_length, - stepSize=step_size) - - def test_count_reads_in_region(self): - self.c.skipZeros = False - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200) + c = cr.CountReadsPerBin([bamFile1, bamFile2], + binLength=bin_length, + stepSize=step_size) + return c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length + """ + The distribution of reads between the two bam files is as follows. + + They cover 200 bp:: + + 0 100 200 + |------------------------------------------------------------| + A ==============> + <============== + + + B <============== ==============> + ==============> + ==============> + """ + + def test_count_reads_in_region(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.skipZeros = False + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[0, 0.], [0, 1.], [1, 1.], [1, 2.]])) - def test_count_reads_in_region_extension_1(self): + def test_count_reads_in_region_extension_1(self, bc): """ In this case when read extension is smaller than read length extension is turned off and a warning is printed. """ - self.c = cr.CountReadsPerBin([self.bamFile1, self.bamFile2], + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c = cr.CountReadsPerBin([bamFile1, bamFile2], binLength=1, stepSize=50, extendReads=25) - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200) + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[0, 0.], [0, 1.], [1, 1.], [1, 2.]])) - def test_count_reads_in_region_total(self): + def test_count_reads_in_region_total(self, bc): """ count the reads over the whole region 2 for the first case, and 4 for the second """ - self.c.skipZeros = False - self.c.stepSize = 200 - self.c.binLength = 200 - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200) + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.skipZeros = False + c.stepSize = 200 + c.binLength = 200 + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[2, 4.]])) - def test_countReadsInRegions_min_mapping_quality(self): + def test_countReadsInRegions_min_mapping_quality(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) # Test min mapping quality. - self.c.minMappingQuality = 40 - self.c.skipZeros = False + c.minMappingQuality = 40 + c.skipZeros = False - resp, _ = self.c.count_reads_in_region(self. chrom, 0, 200) + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[0, 0, 0, 1.], [0, 0, 0, 1.]]).T) - def test_count_reads_in_region_ignore_duplicates(self): - + def test_count_reads_in_region_ignore_duplicates(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) # Test ignore duplicates - self.c.skipZeros = False - self.c.ignoreDuplicates = True - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200) + c.skipZeros = False + c.ignoreDuplicates = True + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[0, 0, 1, 1.], [0, 1, 1, 1.]]).T) - def test_count_reads_in_region_ignore_bed_regions(self): + def test_count_reads_in_region_ignore_bed_regions(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) # Test bed regions: - bed_regions = [[self.chrom, [(10, 20)], "."], [self.chrom, [(150, 160)], "."]] - self.c.skipZeros = False - self.c.binLength = 10 - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200, bed_regions_list=bed_regions) + bed_regions = [[chrom, [(10, 20)], "."], [chrom, [(150, 160)], "."]] + c.skipZeros = False + c.binLength = 10 + resp, _ = c.count_reads_in_region(chrom, 0, 200, bed_regions_list=bed_regions) nt.assert_equal(resp, np.array([[0, 1.], [0, 2.]]).T) - def test_get_coverage_of_region_sam_flag_include(self): - - self.c.samFlag_include = 16 # include reverse reads only - self.c.bamFilesList = [self.bamFile1] - resp, _ = self.c.count_reads_in_region('3R', 0, 200) + def test_get_coverage_of_region_sam_flag_include(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.samFlag_include = 16 # include reverse reads only + c.bamFilesList = [bamFile1] + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_array_equal(resp, np.array([[0], [0], [0], [1]])) - def test_get_coverage_of_region_sam_flag_exclude(self): - - self.c.samFlag_exclude = 16 # exclude reverse reads - self.c.bamFilesList = [self.bamFile1] - resp, _ = self.c.count_reads_in_region('3R', 0, 200) + def test_get_coverage_of_region_sam_flag_exclude(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.samFlag_exclude = 16 # exclude reverse reads + c.bamFilesList = [bamFile1] + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_array_equal(resp, np.array([[0], [0], [1], [0]])) - def test_get_coverage_of_region_large_bin(self): - self.c.bamFilesList = [self.bamFile2] - self.c.binLength = 200 - self.c.stepSize = 200 - resp, _ = self.c.count_reads_in_region('3R', 0, 200) + def test_get_coverage_of_region_large_bin(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.bamFilesList = [bamFile2] + c.binLength = 200 + c.stepSize = 200 + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_array_equal(resp, np.array([[4]])) - def test_get_coverage_of_region_ignore_duplicates(self): - self.c.ignoreDuplicates = True - self.c.bamFilesList = [self.bamFile2] - resp, _ = self.c.count_reads_in_region('3R', 0, 200) + def test_get_coverage_of_region_ignore_duplicates(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.ignoreDuplicates = True + c.bamFilesList = [bamFile2] + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_array_equal(resp, np.array([[0.], [1.], [1.], [1.]])) # check zero to nans - self.c.zerosToNans = True - resp, _ = self.c.count_reads_in_region('3R', 0, 200) + c.zerosToNans = True + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_array_equal(resp, np.array([[np.nan], [1.], [1.], [1.]])) - def test_get_coverage_of_region_split_read(self): + def test_get_coverage_of_region_split_read(self, bc): """ The bamFile1 contains a read at position 10 with the following CIGAR: 10S20M10N10M10S that maps to a chromosome named chr_cigar. """ - + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) # turn of read extension - self.c.extendPairedEnds = False - self.c.bamFilesList = [self.bamFile1] - self.c.binLength = 10 - self.c.stepSize = 10 - resp, _ = self.c.count_reads_in_region('chr_cigar', 0, 100) + c.extendPairedEnds = False + c.bamFilesList = [bamFile1] + c.binLength = 10 + c.stepSize = 10 + resp, _ = c.count_reads_in_region('chr_cigar', 0, 100) nt.assert_array_equal(resp, np.array([[0.], [1.], [1.], @@ -166,60 +172,31 @@ def test_get_coverage_of_region_split_read(self): [0.], [0.]])) - def test_get_coverage_of_region_zeros_to_nan(self): - self.c.zerosToNans = True - resp, _ = self.c.count_reads_in_region(self.chrom, 0, 200) + def test_get_coverage_of_region_zeros_to_nan(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) + c.zerosToNans = True + resp, _ = c.count_reads_in_region(chrom, 0, 200) nt.assert_equal(resp, np.array([[np.nan, np.nan], [np.nan, 1], [1, 1], [1, 2]])) - def test_bed_file(self): + def test_bed_file(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) bed = "chr3R\t0\t10\nchr3R\t110\t120\nchr3R\t160\t180" import tempfile bed_file = tempfile.NamedTemporaryFile(suffix=".bed", delete=False, mode="w") bed_file.write(bed) bed_file.close() - self.c = cr.CountReadsPerBin([self.bamFile2], + c = cr.CountReadsPerBin([bamFile2], bedFile=[bed_file.name]) - resp = self.c.run() + resp = c.run() nt.assert_equal(resp, np.array([[0.], [1.], [2.]])) import os os.unlink(bed_file.name) - - -class TestCountReadsPerBinCRAM(TestCountReadsPerBin): - def setUp(self): - """ - As above, but using CRAM rather than BAM - The distribution of reads between the two bam files is as follows. - - They cover 200 bp:: - - 0 100 200 - |------------------------------------------------------------| - A ==============> - <============== - - - B <============== ==============> - ==============> - ==============> - """ - self.root = ROOT - self.bamFile1 = self.root + "testA.cram" - self.bamFile2 = self.root + "testB.cram" - self.bamFile_PE = self.root + "test_paired2.cram" - self.chrom = '3R' - step_size = 50 - bin_length = 25 - - self.c = cr.CountReadsPerBin([self.bamFile1, self.bamFile2], - binLength=bin_length, - stepSize=step_size) diff --git a/deeptools/test/testskip_heatmapper_images.py b/deeptools/test/test_heatmapper_images.py similarity index 98% rename from deeptools/test/testskip_heatmapper_images.py rename to deeptools/test/test_heatmapper_images.py index 8c8d56e953..7f5d5255cf 100644 --- a/deeptools/test/testskip_heatmapper_images.py +++ b/deeptools/test/test_heatmapper_images.py @@ -14,11 +14,6 @@ ROOT = os.path.dirname(os.path.abspath(__file__)) + "/test_heatmapper/" tolerance = 30 -skip = False -if matplotlib.__version__ != "3.1.1": - skip = True - - def test_plotHeatmap_simple_plot(): """ Test a simple plot generated using a matrix from diff --git a/deeptools/test/test_readFiltering.py b/deeptools/test/test_readFiltering.py index 6e0b0e81ae..07f19b32e6 100644 --- a/deeptools/test/test_readFiltering.py +++ b/deeptools/test/test_readFiltering.py @@ -1,4 +1,3 @@ -from nose.tools import assert_equal import deeptools.estimateReadFiltering as est import deeptools.alignmentSieve as sieve import os.path @@ -30,7 +29,7 @@ def test_estimate_read_filtering_minimal(): _ = resp[1].split("\t") _[0] = os.path.basename(_[0]) resp[1] = "\t".join(_) - assert_equal(resp, expected) + assert resp == expected unlink(outfile) @@ -51,7 +50,7 @@ def test_estimate_read_filtering_params(): resp[1] = "\t".join(_) expected = ['Sample\tTotal Reads\tMapped Reads\tAlignments in blacklisted regions\tEstimated mapped reads filtered\tBelow MAPQ\tMissing Flags\tExcluded Flags\tInternally-determined Duplicates\tMarked Duplicates\tSingletons\tWrong strand\n', 'test_filtering.bam\t193\t193\t7\t193\t41.4\t0.0\t186.5\t31.6\t0.0\t0.0\t0.0\n'] - assert_equal(resp, expected) + assert resp == expected unlink(outfile) @@ -72,7 +71,7 @@ def test_sieve(): expected = ['#bamFilterReads --filterMetrics\n', '#File\tReads Remaining\tTotal Initial Reads\n', 'test_filtering\t5\t193\n'] - assert_equal(resp, expected) + assert resp == expected unlink(outlog) h = hashlib.md5(pysam.view(outfile).encode('utf-8')).hexdigest() assert h == "acbc4443fb0387bfd6c412af9d4fc414" @@ -121,7 +120,7 @@ def test_sieve_BED(): 'chr2\t5001491\t5001527\n', 'chr2\t5001700\t5001736\n'] - assert_equal(resp, expected) + assert resp == expected unlink(outfile) @@ -161,5 +160,5 @@ def test_sieve_BED_shift(): 'chr2\t5001119\t5001266\n', 'chr2\t5001230\t5001600\n'] - assert_equal(resp, expected) + assert resp == expected unlink(outfile) diff --git a/deeptools/test/test_writeBedGraph.py b/deeptools/test/test_writeBedGraph.py index c419684f37..97e9ba07f2 100644 --- a/deeptools/test/test_writeBedGraph.py +++ b/deeptools/test/test_writeBedGraph.py @@ -1,143 +1,104 @@ from unittest import TestCase -from nose.tools import * import os - +import pytest import deeptools.writeBedGraph as wr from deeptools.writeBedGraph import scaleCoverage -ROOT = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" - -__author__ = 'fidel' - - -class TestWriteBedGraph(TestCase): - - def setUp(self): - """ - The distribution of reads between the two bam files is as follows. - - They cover 200 bp:: - - 0 100 200 - |------------------------------------------------------------| - A ==============> - <============== - - - B <============== ==============> - ==============> - ==============> - """ - - self.root = ROOT - self.bamFile1 = self.root + "testA.bam" - self.bamFile2 = self.root + "testB.bam" - self.bamFile_PE = self.root + "test_paired2.bam" - self.chrom = '3R' - - self.step_size = 50 - self.bin_length = 50 - self.func_args = {'scaleFactor': 1.0} - - self.c = wr.WriteBedGraph([self.bamFile1], - binLength=self.bin_length, - stepSize=self.step_size) - - def test_writeBedGraph_worker(self): - self.c.zerosToNans = False - self.c.skipZeros = False - - tempFile = self.c.writeBedGraph_worker('3R', 0, 200, scaleCoverage, self.func_args) +@pytest.mark.parametrize("bc", ["bam", 'cram']) +class TestWriteBedGraph(): + def ifiles(self, ext='bam'): + root = os.path.dirname(os.path.abspath(__file__)) + "/test_data/" + bamFile1 = root + "testA." + ext + bamFile2 = root + "testB." + ext + bamFile_PE = root + "test_paired2." + ext + chrom = '3R' + step_size = 50 + bin_length = 50 + func_args = {'scaleFactor': 1.0} + c = wr.WriteBedGraph([bamFile1], + binLength=bin_length, + stepSize=step_size) + return c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args + + def test_writeBedGraph_worker(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) + c.zerosToNans = False + c.skipZeros = False + + tempFile = c.writeBedGraph_worker(chrom, 0, 200, scaleCoverage, func_args) _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert_equal(res, ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n']) + assert res == ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n'] os.remove(tempFile[3]) - def test_writeBedGraph_worker_zerotonan(self): + def test_writeBedGraph_worker_zerotonan(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) # turn on zeroToNan - self.c.zerosToNans = True - tempFile2 = self.c.writeBedGraph_worker('3R', 0, 200, scaleCoverage, self.func_args) + c.zerosToNans = True + tempFile2 = c.writeBedGraph_worker(chrom, 0, 200, scaleCoverage, func_args) _foo = open(tempFile2[3], 'r') res = _foo.readlines() _foo.close() - assert_equal(res, ['3R\t100\t200\t1\n']) + assert res == ['3R\t100\t200\t1\n'] os.remove(tempFile2[3]) - def test_writeBedGraph_worker_scaling(self): + def test_writeBedGraph_worker_scaling(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) func_args = {'scaleFactor': 3.0} - tempFile = self.c.writeBedGraph_worker('3R', 0, 200, scaleCoverage, func_args) + tempFile = c.writeBedGraph_worker(chrom, 0, 200, scaleCoverage, func_args) _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert_equal(res, ['3R\t0\t100\t0\n', '3R\t100\t200\t3\n']) + assert res == ['3R\t0\t100\t0\n', '3R\t100\t200\t3\n'] os.remove(tempFile[3]) - def test_writeBedGraph_worker_ignore_duplicates(self): - self.c = wr.WriteBedGraph([self.bamFile2], - binLength=self.bin_length, - stepSize=self.step_size, ignoreDuplicates=True) - self.c.zerosToNans = True + def test_writeBedGraph_worker_ignore_duplicates(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) + c = wr.WriteBedGraph([bamFile2], + binLength=bin_length, + stepSize=step_size, ignoreDuplicates=True) + c.zerosToNans = True - tempFile = self.c.writeBedGraph_worker('3R', 0, 200, scaleCoverage, self.func_args) + tempFile = c.writeBedGraph_worker(chrom, 0, 200, scaleCoverage, func_args) _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert_equal(res, ['3R\t50\t200\t1\n']) + assert res == ['3R\t50\t200\t1\n'] os.remove(tempFile[3]) - def test_writeBedGraph_worker_smoothing(self): - self.c.binLength = 20 - self.c.stepSize = 20 - self.c.smoothLength = 60 - tempFile = self.c.writeBedGraph_worker('3R', 100, 200, scaleCoverage, self.func_args) + def test_writeBedGraph_worker_smoothing(self, bc): + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) + c.binLength = 20 + c.stepSize = 20 + c.smoothLength = 60 + tempFile = c.writeBedGraph_worker(chrom, 100, 200, scaleCoverage, func_args) _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert_equal(res, ['3R\t100\t120\t1\n', '3R\t120\t180\t1.33333\n', '3R\t180\t200\t1\n']) + assert res == ['3R\t100\t120\t1\n', '3R\t120\t180\t1.33333\n', '3R\t180\t200\t1\n'] os.remove(tempFile[3]) - def test_writeBedGraph_cigar(self): + def test_writeBedGraph_cigar(self, bc): """ The bamFile1 contains a read at position 10 with the following CIGAR: 10S20M10N10M10S that maps to a chromosome named chr_cigar. """ - + c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) # turn of read extension - self.c.extendPairedEnds = False - self.c.binLength = 10 - self.c.stepSize = 10 - tempFile = self.c.writeBedGraph_worker('chr_cigar', 0, 100, scaleCoverage, self.func_args) + c.extendPairedEnds = False + c.binLength = 10 + c.stepSize = 10 + tempFile = c.writeBedGraph_worker('chr_cigar', 0, 100, scaleCoverage, func_args) _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() # the sigle read is split into bin 10-30, and then 40-50 - assert_equal(res, ['chr_cigar\t0\t10\t0\n', + assert res == ['chr_cigar\t0\t10\t0\n', 'chr_cigar\t10\t30\t1\n', 'chr_cigar\t30\t40\t0\n', 'chr_cigar\t40\t50\t1\n', - 'chr_cigar\t50\t100\t0\n']) + 'chr_cigar\t50\t100\t0\n'] os.remove(tempFile[3]) - - -class TestWriteBedGraphCRAM(TestWriteBedGraph): - def setUp(self): - """ - As above, but for CRAM files - """ - - self.root = ROOT - self.bamFile1 = self.root + "testA.cram" - self.bamFile2 = self.root + "testB.cram" - self.bamFile_PE = self.root + "test_paired2.cram" - self.chrom = '3R' - - self.step_size = 50 - self.bin_length = 50 - self.func_args = {'scaleFactor': 1.0} - - self.c = wr.WriteBedGraph([self.bamFile1], - binLength=self.bin_length, - stepSize=self.step_size) From 2e7e4ac1048ac6ea161f3b35487963f6420ae2c9 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 16:55:07 +0200 Subject: [PATCH 10/77] escape sysargv check in case of pytests --- deeptools/computeMatrixOperations.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/deeptools/computeMatrixOperations.py b/deeptools/computeMatrixOperations.py index 0abf4b7e2d..d3014973bb 100755 --- a/deeptools/computeMatrixOperations.py +++ b/deeptools/computeMatrixOperations.py @@ -786,10 +786,13 @@ def sortMatrix(hm, regionsFileName, transcriptID, transcript_id_designator, verb def main(args=None): - if len(sys.argv) == 1: - args = ["-h"] - if len(sys.argv) == 2: - args = [sys.argv[1], "-h"] + # if args none is need since otherwise pytest passes 'pytest' as sys.argv + if args == None: + if len(sys.argv) == 1: + args = ["-h"] + if len(sys.argv) == 2: + args = [sys.argv[1], "-h"] + args = parse_arguments().parse_args(args) hm = heatmapper.heatmapper() From 4b8d38cc0d6acead2564848dc69687d3a244dcb0 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 16:56:28 +0200 Subject: [PATCH 11/77] deprecate nosetest in favor of pytest --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d83fb359f0..e819d6f0b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - uses: "dpryan79/github-actions/@master" + - uses: "deeptools/github-actions/@master" - name: PEP8 run: | source activate foo @@ -44,7 +44,7 @@ jobs: - name: Test deepTools run: | source activate foo - nosetests --with-doctest -sv deeptools + pytest - name: make an artifact run: | source activate foo @@ -59,8 +59,8 @@ jobs: runs-on: macOS-latest steps: - uses: actions/checkout@v1 - - uses: "dpryan79/github-actions/@master" + - uses: "deeptools/github-actions/@master" - name: Test deepTools run: | source activate foo - nosetests --with-doctest -sv deeptools + pytest From 2cc7977fc9db85ff17648f1b6abde09e7938ea17 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 16:57:30 +0200 Subject: [PATCH 12/77] rollback heatmapperr imgs test until later --- ...mages.py => skiptest_heatmapper_images.py} | 24 ------------------- 1 file changed, 24 deletions(-) rename deeptools/test/{test_heatmapper_images.py => skiptest_heatmapper_images.py} (94%) diff --git a/deeptools/test/test_heatmapper_images.py b/deeptools/test/skiptest_heatmapper_images.py similarity index 94% rename from deeptools/test/test_heatmapper_images.py rename to deeptools/test/skiptest_heatmapper_images.py index 7f5d5255cf..c4891aa916 100644 --- a/deeptools/test/test_heatmapper_images.py +++ b/deeptools/test/skiptest_heatmapper_images.py @@ -23,8 +23,6 @@ def test_plotHeatmap_simple_plot(): -R {test_path}/test.bed -o /tmp/mat.gz -bs 25 """ - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master.mat.gz --outFileName {}".format(ROOT, outfile.name).split() deeptools.plotHeatmap.main(args) @@ -34,8 +32,6 @@ def test_plotHeatmap_simple_plot(): def test_plotHeatmap_rename_labels(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master.mat.gz --outFileName {} --regionsLabel uno dos".format(ROOT, outfile.name).split() @@ -46,8 +42,6 @@ def test_plotHeatmap_rename_labels(): def test_plotHeatmap_scale_regions(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_scale_reg.mat.gz --outFileName {}".format(ROOT, outfile.name).split() deeptools.plotHeatmap.main(args) @@ -57,8 +51,6 @@ def test_plotHeatmap_scale_regions(): def test_plotHeatmap_multi_bigwig_pergroup(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_multi.mat.gz --perGroup --samplesLabel file1 file2 file3 file4 " \ "--outFileName {}".format(ROOT, outfile.name).split() @@ -69,8 +61,6 @@ def test_plotHeatmap_multi_bigwig_pergroup(): def test_plotHeatmap_multiple_colors_muti_scales(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_multi.mat.gz --colorList white,blue white,red --zMin 1 0 --zMax 4 5 " \ "--outFileName {}".format(ROOT, outfile.name).split() @@ -81,8 +71,6 @@ def test_plotHeatmap_multiple_colors_muti_scales(): def test_plotHeatmap_multiple_colormap_no_boxes(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_multi.mat.gz --colorMap Reds binary terrain --boxAroundHeatmaps no " \ "--outFileName {}".format(ROOT, outfile.name).split() @@ -93,8 +81,6 @@ def test_plotHeatmap_multiple_colormap_no_boxes(): def test_plotHeatmap_interpolation(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/large_matrix.mat.gz --interpolation bilinear " \ "--outFileName {}".format(ROOT, outfile.name).split() @@ -105,8 +91,6 @@ def test_plotHeatmap_interpolation(): def test_plotProfiler(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master.mat.gz --outFileName {} --regionsLabel uno dos " \ "--plotType std".format(ROOT, outfile.name).split() @@ -117,8 +101,6 @@ def test_plotProfiler(): def test_plotProfiler_heatmap(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master.mat.gz --outFileName {} --plotType heatmap".format(ROOT, outfile.name).split() deeptools.plotProfile.main(args) @@ -128,8 +110,6 @@ def test_plotProfiler_heatmap(): def test_plotProfiler_overlapped_lines(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master.mat.gz --outFileName {} " \ "--plotType overlapped_lines --yMin -1".format(ROOT, outfile.name).split() @@ -140,8 +120,6 @@ def test_plotProfiler_overlapped_lines(): def test_plotProfiler_multibigwig(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_multi.mat.gz --outFileName {} " \ "--numPlotsPerRow 2 --yMax 1.5".format(ROOT, outfile.name).split() @@ -152,8 +130,6 @@ def test_plotProfiler_multibigwig(): def test_plotProfiler_multibigwig_pergroup(): - if skip: - return outfile = NamedTemporaryFile(suffix='.png', prefix='plotHeatmap_test_', delete=False) args = "-m {}/master_multi.mat.gz --outFileName {} " \ "--perGroup --yMax 1.5".format(ROOT, outfile.name).split() From df18b1d833a07f5ab4dfcfb98ea6f82c94166cea Mon Sep 17 00:00:00 2001 From: WardDeb Date: Wed, 16 Aug 2023 17:24:10 +0200 Subject: [PATCH 13/77] flake8 --- deeptools/computeMatrixOperations.py | 2 +- deeptools/plotPCA.py | 1 + deeptools/test/skiptest_heatmapper_images.py | 1 + .../test/test_computeMatrixOperations.py | 1 + deeptools/test/test_countReadsPerBin.py | 27 ++++++++++------ deeptools/test/test_writeBedGraph.py | 31 ++++++++++++------- docs/conf.py | 1 + 7 files changed, 41 insertions(+), 23 deletions(-) diff --git a/deeptools/computeMatrixOperations.py b/deeptools/computeMatrixOperations.py index d3014973bb..6b3272d4bd 100755 --- a/deeptools/computeMatrixOperations.py +++ b/deeptools/computeMatrixOperations.py @@ -787,7 +787,7 @@ def sortMatrix(hm, regionsFileName, transcriptID, transcript_id_designator, verb def main(args=None): # if args none is need since otherwise pytest passes 'pytest' as sys.argv - if args == None: + if args is None: if len(sys.argv) == 1: args = ["-h"] if len(sys.argv) == 2: diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index 75dc8a4bb3..394dcfba3b 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -13,6 +13,7 @@ from deeptools.parserCommon import writableFile from importlib.metadata import version + def parse_arguments(args=None): basic_args = plotCorrelationArgs() parser = argparse.ArgumentParser( diff --git a/deeptools/test/skiptest_heatmapper_images.py b/deeptools/test/skiptest_heatmapper_images.py index c4891aa916..1e1026504b 100644 --- a/deeptools/test/skiptest_heatmapper_images.py +++ b/deeptools/test/skiptest_heatmapper_images.py @@ -14,6 +14,7 @@ ROOT = os.path.dirname(os.path.abspath(__file__)) + "/test_heatmapper/" tolerance = 30 + def test_plotHeatmap_simple_plot(): """ Test a simple plot generated using a matrix from diff --git a/deeptools/test/test_computeMatrixOperations.py b/deeptools/test/test_computeMatrixOperations.py index df81d54595..433e25bdd7 100644 --- a/deeptools/test/test_computeMatrixOperations.py +++ b/deeptools/test/test_computeMatrixOperations.py @@ -8,6 +8,7 @@ __author__ = 'Devon' + def getHeader(fp): s = fp.readline() if isinstance(s, bytes): diff --git a/deeptools/test/test_countReadsPerBin.py b/deeptools/test/test_countReadsPerBin.py index 882ca603f6..eb09a9684c 100644 --- a/deeptools/test/test_countReadsPerBin.py +++ b/deeptools/test/test_countReadsPerBin.py @@ -8,6 +8,7 @@ __author__ = 'Fidel' + @pytest.mark.parametrize("bc", ["bam", 'cram']) class TestCountReadsPerBin(): @@ -19,9 +20,11 @@ def ifiles(self, ext='bam'): chrom = '3R' step_size = 50 bin_length = 25 - c = cr.CountReadsPerBin([bamFile1, bamFile2], - binLength=bin_length, - stepSize=step_size) + c = cr.CountReadsPerBin( + [bamFile1, bamFile2], + binLength=bin_length, + stepSize=step_size + ) return c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length """ The distribution of reads between the two bam files is as follows. @@ -38,7 +41,7 @@ def ifiles(self, ext='bam'): ==============> ==============> """ - + def test_count_reads_in_region(self, bc): c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) c.skipZeros = False @@ -55,10 +58,12 @@ def test_count_reads_in_region_extension_1(self, bc): extension is turned off and a warning is printed. """ c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length = self.ifiles(bc) - c = cr.CountReadsPerBin([bamFile1, bamFile2], - binLength=1, - stepSize=50, - extendReads=25) + c = cr.CountReadsPerBin( + [bamFile1, bamFile2], + binLength=1, + stepSize=50, + extendReads=25 + ) resp, _ = c.count_reads_in_region(chrom, 0, 200) @@ -190,8 +195,10 @@ def test_bed_file(self, bc): bed_file.write(bed) bed_file.close() - c = cr.CountReadsPerBin([bamFile2], - bedFile=[bed_file.name]) + c = cr.CountReadsPerBin( + [bamFile2], + bedFile=[bed_file.name] + ) resp = c.run() nt.assert_equal(resp, np.array([[0.], diff --git a/deeptools/test/test_writeBedGraph.py b/deeptools/test/test_writeBedGraph.py index 97e9ba07f2..bdf0bca176 100644 --- a/deeptools/test/test_writeBedGraph.py +++ b/deeptools/test/test_writeBedGraph.py @@ -1,9 +1,9 @@ -from unittest import TestCase import os import pytest import deeptools.writeBedGraph as wr from deeptools.writeBedGraph import scaleCoverage + @pytest.mark.parametrize("bc", ["bam", 'cram']) class TestWriteBedGraph(): def ifiles(self, ext='bam'): @@ -15,9 +15,11 @@ def ifiles(self, ext='bam'): step_size = 50 bin_length = 50 func_args = {'scaleFactor': 1.0} - c = wr.WriteBedGraph([bamFile1], - binLength=bin_length, - stepSize=step_size) + c = wr.WriteBedGraph( + [bamFile1], + binLength=bin_length, + stepSize=step_size + ) return c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args def test_writeBedGraph_worker(self, bc): @@ -55,9 +57,12 @@ def test_writeBedGraph_worker_scaling(self, bc): def test_writeBedGraph_worker_ignore_duplicates(self, bc): c, bamFile1, bamFile2, bamFile_PE, chrom, step_size, bin_length, func_args = self.ifiles(bc) - c = wr.WriteBedGraph([bamFile2], - binLength=bin_length, - stepSize=step_size, ignoreDuplicates=True) + c = wr.WriteBedGraph( + [bamFile2], + binLength=bin_length, + stepSize=step_size, + ignoreDuplicates=True + ) c.zerosToNans = True tempFile = c.writeBedGraph_worker(chrom, 0, 200, scaleCoverage, func_args) @@ -96,9 +101,11 @@ def test_writeBedGraph_cigar(self, bc): _foo.close() # the sigle read is split into bin 10-30, and then 40-50 - assert res == ['chr_cigar\t0\t10\t0\n', - 'chr_cigar\t10\t30\t1\n', - 'chr_cigar\t30\t40\t0\n', - 'chr_cigar\t40\t50\t1\n', - 'chr_cigar\t50\t100\t0\n'] + assert res == [ + 'chr_cigar\t0\t10\t0\n', + 'chr_cigar\t10\t30\t1\n', + 'chr_cigar\t30\t40\t0\n', + 'chr_cigar\t40\t50\t1\n', + 'chr_cigar\t50\t100\t0\n' + ] os.remove(tempFile[3]) diff --git a/docs/conf.py b/docs/conf.py index cd8b3abc2f..8e647cfc23 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -89,6 +89,7 @@ def get_version(): except: return None + version = get_version() # The full version, including alpha/beta/rc tags. release = version From 4294a3b4717ccb6fc384ee5eaace1c8ebb65fedd Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 08:26:04 +0200 Subject: [PATCH 14/77] add conda env for workflows --- .github/env.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/env.yml diff --git a/.github/env.yml b/.github/env.yml new file mode 100644 index 0000000000..c9e1a63a24 --- /dev/null +++ b/.github/env.yml @@ -0,0 +1,8 @@ +channels: + - conda-forge + - bioconda +dependencies: + - python = 3.11 + - flake8 + - pytest + - planemo \ No newline at end of file From 70004ab1cba65014380559771d9ec56d09ec756c Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 08:26:20 +0200 Subject: [PATCH 15/77] switch test to micromamba --- .github/workflows/test.yml | 55 ++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e819d6f0b3..01974c2a5f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,17 @@ name: Test on: [push, pull_request] + +defaults: + run: + shell: bash -l {0} + jobs: check_versions_matches: name: Check deeptools version matches galaxy tools runs-on: ubuntu-latest if: github.base_ref == 'master' steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Check path run: find /home/runner/work/deepTools/deepTools -name "pyproject.toml" - name: Get Version of Deeptools @@ -35,32 +40,48 @@ jobs: name: Test on Linux runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: "deeptools/github-actions/@master" + - uses: actions/checkout@v3 + - uses: mamba-org/setup-micromamba@main + with: + environment-file: .github/env.yml + cache-downloads: true + environment-name: foo + - name: pip install + run: | + micromamba activate foo + pip install . - name: PEP8 run: | - source activate foo + micromamba activate foo flake8 . --exclude=.venv,.build,build --ignore=E501,F403,E402,F999,F405,E722,W504,W605 - name: Test deepTools run: | - source activate foo + mamba activate foo pytest - - name: make an artifact - run: | - source activate foo - rm -f dist/* - python setup.py sdist - - uses: actions/upload-artifact@master - with: - name: "Dist files" - path: "dist" + #- name: make an artifact + # run: | + # source activate foo + # rm -f dist/* + # python setup.py sdist + #- uses: actions/upload-artifact@master + # with: + # name: "Dist files" + # path: "dist" build-osx: name: Test on OSX runs-on: macOS-latest steps: - - uses: actions/checkout@v1 - - uses: "deeptools/github-actions/@master" + - uses: actions/checkout@v3 + - uses: mamba-org/setup-micromamba@main + with: + environment-file: .github/env.yml + cache-downloads: true + environment-name: foo + - name: pip install + run: | + micromamba activate foo + pip install . - name: Test deepTools run: | - source activate foo + micromamba activate foo pytest From 427b0afbf64636dc0945249955c6c11798fd25b6 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 08:38:29 +0200 Subject: [PATCH 16/77] planemo tests to python 3.11, include samtools in test env --- .github/env.yml | 3 ++- .github/workflows/planemo.yml | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index c9e1a63a24..896bed2706 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -5,4 +5,5 @@ dependencies: - python = 3.11 - flake8 - pytest - - planemo \ No newline at end of file + - planemo + - samtools \ No newline at end of file diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 44baae7083..04e82d566b 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -10,12 +10,15 @@ jobs: matrix: chunk: [1, 2, 3] steps: - - uses: actions/checkout@v1 - - uses: "dpryan79/github-actions/@master" + - uses: actions/checkout@v3 + - uses: mamba-org/setup-micromamba@main + with: + environment-file: .github/env.yml + cache-downloads: true + environment-name: foo - name: planemo run: | - source activate foo - conda update -c conda-forge -c bioconda samtools + micromamba activate foo ./.planemo.sh ${{ matrix.chunk }} ${{ env.GALAXY_BRANCH }} - uses: actions/upload-artifact@v3 with: @@ -27,7 +30,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.7'] + python-version: ['3.11'] steps: - uses: actions/download-artifact@v3 with: From e355cb1ade4f02a244652d55ea14ad694fe8fe54 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 08:47:08 +0200 Subject: [PATCH 17/77] shell defaults planemo test --- .github/workflows/planemo.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 04e82d566b..11040879c0 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -1,7 +1,13 @@ name: Planemo on: [push, pull_request] + env: GALAXY_BRANCH: release_22.05 + +defaults: + run: + shell: bash -l {0} + jobs: planemo_test: name: Planemo test From c273c83a538c4cd35a08742db091875897a49986 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 09:04:01 +0200 Subject: [PATCH 18/77] pin allure version for planemo tests --- .github/env.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/env.yml b/.github/env.yml index 896bed2706..fdbb7a0626 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -6,4 +6,5 @@ dependencies: - flake8 - pytest - planemo - - samtools \ No newline at end of file + - samtools + - allure-python-commons==2.12.0 \ No newline at end of file From c9fc79d2179643dfeea7f0d972fbfb2913929a7f Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 10:14:53 +0200 Subject: [PATCH 19/77] pin python to 3.10 in test env --- .github/env.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index fdbb7a0626..80cabc1084 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -2,9 +2,11 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.11 + - python = 3.10 - flake8 - pytest - planemo - samtools - - allure-python-commons==2.12.0 \ No newline at end of file + - allure-python-commons==2.12.0 # pinned for planemo + - pycryptodome # pinned for galaxy-util + - rich # pinned for ephemeris \ No newline at end of file From f6e76d9654bf5907324b048a87c081c4443db272 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 10:29:16 +0200 Subject: [PATCH 20/77] pin 3.10 in planemo wf as well --- .github/workflows/planemo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 11040879c0..925d9b438c 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11'] + python-version: ['3.10'] steps: - uses: actions/download-artifact@v3 with: From 15aeb57b8206820441c7ded6e011c973a3dc2c2e Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 11:32:30 +0200 Subject: [PATCH 21/77] test env channel priority into condarc --- .github/env.yml | 3 --- .github/workflows/planemo.yml | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index 80cabc1084..617db68d7e 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -1,6 +1,3 @@ -channels: - - conda-forge - - bioconda dependencies: - python = 3.10 - flake8 diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 925d9b438c..edc1b9c2da 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -22,6 +22,10 @@ jobs: environment-file: .github/env.yml cache-downloads: true environment-name: foo + condarc: | + channels: + - conda-forge + - bioconda - name: planemo run: | micromamba activate foo From 1ce04249ca0fa402b1d414ec1f4a76fe9a6d1242 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 11:59:51 +0200 Subject: [PATCH 22/77] planemo on 3.9, test on 3.11 --- .github/env.yml | 9 ++------- .github/planemo.yml | 12 ++++++++++++ .github/workflows/planemo.yml | 8 ++------ .github/workflows/test.yml | 8 ++++++++ 4 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 .github/planemo.yml diff --git a/.github/env.yml b/.github/env.yml index 617db68d7e..a226adc86f 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -1,9 +1,4 @@ dependencies: - - python = 3.10 + - python = 3.11 - flake8 - - pytest - - planemo - - samtools - - allure-python-commons==2.12.0 # pinned for planemo - - pycryptodome # pinned for galaxy-util - - rich # pinned for ephemeris \ No newline at end of file + - pytest \ No newline at end of file diff --git a/.github/planemo.yml b/.github/planemo.yml new file mode 100644 index 0000000000..a0ea025ee6 --- /dev/null +++ b/.github/planemo.yml @@ -0,0 +1,12 @@ +channels: + - conda-forge + - bioconda +dependencies: + - python = 3.9 + - flake8 + - pytest + - planemo + - samtools + - allure-python-commons==2.12.0 # pinned for planemo + - pycryptodome # pinned for galaxy-util + - rich # pinned for ephemeris diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index edc1b9c2da..3c706aa879 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -19,13 +19,9 @@ jobs: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main with: - environment-file: .github/env.yml + environment-file: .github/planemo.yml cache-downloads: true environment-name: foo - condarc: | - channels: - - conda-forge - - bioconda - name: planemo run: | micromamba activate foo @@ -40,7 +36,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10'] + python-version: ['3.9'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01974c2a5f..1fb1c5b85d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,6 +46,10 @@ jobs: environment-file: .github/env.yml cache-downloads: true environment-name: foo + condarc: | + channels: + - conda-forge + - bioconda - name: pip install run: | micromamba activate foo @@ -77,6 +81,10 @@ jobs: environment-file: .github/env.yml cache-downloads: true environment-name: foo + condarc: | + channels: + - conda-forge + - bioconda - name: pip install run: | micromamba activate foo From e4af3cda4f828987784bd2b8aa01f3791b70448d Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 12:28:51 +0200 Subject: [PATCH 23/77] complete rollback to 3.7 planemo with same deps as previous action --- .github/planemo.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/planemo.yml b/.github/planemo.yml index a0ea025ee6..cc151ff579 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -2,11 +2,15 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.9 + - python = 3.7 + - numpy + - scipy - flake8 + - pysam + - piBigWig + - py2bit + - deeptoolsintervals - pytest - planemo - samtools - - allure-python-commons==2.12.0 # pinned for planemo - - pycryptodome # pinned for galaxy-util - - rich # pinned for ephemeris + - allure-python-commons==2.12.0 From 911875eee997e9de683b4fc90197ec20f208b417 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 12:30:33 +0200 Subject: [PATCH 24/77] pybw typo --- .github/planemo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/planemo.yml b/.github/planemo.yml index cc151ff579..5aab25d129 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -7,7 +7,7 @@ dependencies: - scipy - flake8 - pysam - - piBigWig + - pyBigWig - py2bit - deeptoolsintervals - pytest From 541e3bddc2df63a1878a7ce2b245ccd05463d460 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 12:41:03 +0200 Subject: [PATCH 25/77] include pip install in planemo.. --- .github/env.yml | 3 +++ .github/workflows/planemo.yml | 6 +++++- .github/workflows/test.yml | 4 ---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index a226adc86f..d5c47454d4 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -1,3 +1,6 @@ +channels: + - conda-forge + - bioconda dependencies: - python = 3.11 - flake8 diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 3c706aa879..f6b315c37c 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -22,6 +22,10 @@ jobs: environment-file: .github/planemo.yml cache-downloads: true environment-name: foo + - name: pip install + run: | + micromamba activate foo + pip install . - name: planemo run: | micromamba activate foo @@ -36,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9'] + python-version: ['3.7'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fb1c5b85d..9bb7fcb026 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,10 +46,6 @@ jobs: environment-file: .github/env.yml cache-downloads: true environment-name: foo - condarc: | - channels: - - conda-forge - - bioconda - name: pip install run: | micromamba activate foo From c9b4fdac88e08a67f6fef66042a6653e2ac2c828 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 17:12:20 +0200 Subject: [PATCH 26/77] test with planemo test run python 3.10 --- .github/planemo.yml | 2 +- .github/workflows/planemo.yml | 2 +- .planemo.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/planemo.yml b/.github/planemo.yml index 5aab25d129..46deee953e 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -2,7 +2,7 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.7 + - python = 3.10 - numpy - scipy - flake8 diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index f6b315c37c..c66fc22809 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.7'] + python-version: ['3.10'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.planemo.sh b/.planemo.sh index 1dfc672229..6f9e0810ce 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --no_dependency_resolution --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --no_dependency_resolution --galaxy_python_version 3.10 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From 5232024f3ee53f77d72861c75eb08c2594cd317a Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 17:29:03 +0200 Subject: [PATCH 27/77] planemo tests with python 3.11 under galaxy --- .github/planemo.yml | 2 +- .github/workflows/planemo.yml | 2 +- .planemo.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/planemo.yml b/.github/planemo.yml index 46deee953e..eb0bf99dc7 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -2,7 +2,7 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.10 + - python = 3.11 - numpy - scipy - flake8 diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index c66fc22809..48cfc04bcc 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10'] + python-version: ['3.11'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.planemo.sh b/.planemo.sh index 6f9e0810ce..a4e1bd2dae 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --no_dependency_resolution --galaxy_python_version 3.10 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --no_dependency_resolution --galaxy_python_version 3.11 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From d5fb5b6966fd7835f3690379740875a8e5351cdb Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 17 Aug 2023 17:36:44 +0200 Subject: [PATCH 28/77] py2bit as pip dep --- .github/planemo.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/planemo.yml b/.github/planemo.yml index eb0bf99dc7..78eaf2586d 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -7,10 +7,12 @@ dependencies: - scipy - flake8 - pysam - - pyBigWig - - py2bit - deeptoolsintervals - pytest - planemo - samtools - allure-python-commons==2.12.0 + - pip + - pip: + - py2bit + - pyBigWig From 7729d1540a93890220d6b0a7db1eb1ff707b8ae2 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 08:01:25 +0200 Subject: [PATCH 29/77] python 3.10 for tests & planemo galaxy version --- .github/env.yml | 2 +- .github/planemo.yml | 8 +++----- .github/workflows/planemo.yml | 2 +- .planemo.sh | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index d5c47454d4..718f888d0d 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -2,6 +2,6 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.11 + - python = 3.10 - flake8 - pytest \ No newline at end of file diff --git a/.github/planemo.yml b/.github/planemo.yml index 78eaf2586d..8716e588a1 100644 --- a/.github/planemo.yml +++ b/.github/planemo.yml @@ -2,7 +2,7 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.11 + - python = 3.10 - numpy - scipy - flake8 @@ -12,7 +12,5 @@ dependencies: - planemo - samtools - allure-python-commons==2.12.0 - - pip - - pip: - - py2bit - - pyBigWig + - py2bit + - pyBigWig diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 48cfc04bcc..c66fc22809 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11'] + python-version: ['3.10'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.planemo.sh b/.planemo.sh index a4e1bd2dae..6f9e0810ce 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --no_dependency_resolution --galaxy_python_version 3.11 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --no_dependency_resolution --galaxy_python_version 3.10 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From 65f4f11625cfff58f9cada7493dde67a21e5b122 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 09:49:00 +0200 Subject: [PATCH 30/77] plotcorr - parsed labels changed slightly due to mpl versions (assumed) --- .../test-data/plotCorrelation_result2.png | Bin 12275 -> 11943 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/galaxy/wrapper/test-data/plotCorrelation_result2.png b/galaxy/wrapper/test-data/plotCorrelation_result2.png index 58ade4f24164b4243ea4894a31bf5660d3c7b7d2..c144cf38ab99b8069a2159c0d12971294a8d6728 100644 GIT binary patch literal 11943 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 Date: Fri, 18 Aug 2023 13:06:35 +0200 Subject: [PATCH 31/77] typo in pltfingerprint comment, typo in bin script, include 1 extra significant digit in synthetic JS distance test (np default changed?) --- deeptools/plotFingerprint.py | 2 +- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deeptools/plotFingerprint.py b/deeptools/plotFingerprint.py index 4ea0b50a81..f5e26072b4 100755 --- a/deeptools/plotFingerprint.py +++ b/deeptools/plotFingerprint.py @@ -393,7 +393,7 @@ def main(args=None): sys.stderr.write( "\nNo reads were found in {} regions sampled. Check that the\n" "min mapping quality is not overly high and that the \n" - "chromosome names between bam files are consistant.\n" + "chromosome names between bam files are consistent.\n" "For small genomes, decrease the --numberOfSamples.\n" "\n".format(num_reads_per_bin.shape[0])) exit(1) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index f2e1608825..6ca5018192 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.26900449806812143 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.26900449806812143 0 0 0 diff --git a/pyproject.toml b/pyproject.toml index c5dfe3e896..7c3d2a0d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ multiBigwigSummary = "deeptools.multiBigwigSummary:main" plotCorrelation = "deeptools.plotCorrelation:main" plotCoverage = "deeptools.plotCoverage:main" plotEnrichment = "deeptools.plotEnrichment:main" -plotFingerprint = "deeptools.plotFingerPrint:main" +plotFingerprint = "deeptools.plotFingerprint:main" plotHeatmap = "deeptools.plotHeatmap:main" plotPCA = "deeptools.plotPCA:main" plotProfile = "deeptools.plotProfile:main" From 3973e28074d01051a8001768d95d965790a2e8ea Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 13:24:08 +0200 Subject: [PATCH 32/77] replace both expected heatmaps in planemo tests (mpl version boost == size difference) --- .../wrapper/test-data/heatmapper_result1.png | Bin 71326 -> 53462 bytes .../wrapper/test-data/heatmapper_result2.png | Bin 56250 -> 39981 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/galaxy/wrapper/test-data/heatmapper_result1.png b/galaxy/wrapper/test-data/heatmapper_result1.png index 2ba154b086d919b60438be89653abbbcdfabe66b..fa3686351dc31c66c6c89118fc65396e0bbed693 100644 GIT binary patch literal 53462 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

Ewb4I- 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 Tdrn=V;E%$7Wx1F8PG0&S-cmfk diff --git a/galaxy/wrapper/test-data/heatmapper_result2.png b/galaxy/wrapper/test-data/heatmapper_result2.png index 6b49b4a368b78e8ec69a898e197d696cc5b67614..fb3bd36759988f9325382fc6389083ec6d135d66 100644 GIT binary patch literal 39981 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 Bq8|VN literal 56250 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

0S%*-xlE^3i@Vj+h8mTCe%iX&gN>P&|bF>HU z+wn$Hr`QPj{YL%|BS+?=Z9f<=iiIxTIHa4>%;|~_(R}szUQsA9CcW%L{*!H*s?=rC$MaHYgPoGX_hGA$@ z(S+1<%4^H7s@&)~{LaP?O6`0#8RQZnswD`Z)=K3mU=8Nro!-Nwp5nP#JiYjA=NL6$ z6cGGeuSMkv_9U*9T0z~M6wfb9R~a4}V(Ku_jqQrceJgmEwhdnqFZ^-(ITg&WONW_pnSWL4|!_?|9dYUdg!C+0K=F%P1Mss=5HbAB5nsNz1FEiH*edf zjkN@+>ydb~*PC408;QGyq%|15@N03(=%G&FGPG)f>#9MWD3O8^*SC%8Ia>kAU&=D{=?d3}i;?Tw_e+?#@(YAUU z9@9Y-UieboyUXK%i@D+aCAAqqYhNIfxZ;E85Y Date: Fri, 18 Aug 2023 13:36:44 +0200 Subject: [PATCH 33/77] replace plotprofile png in planemo test --- galaxy/wrapper/test-data/profiler_result1.png | Bin 46658 -> 42550 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/galaxy/wrapper/test-data/profiler_result1.png b/galaxy/wrapper/test-data/profiler_result1.png index dcb1d2602c657dc703a92def8af9f647c13b8714..a0f14d8b351d0c64028f5ae6d694f9849fe2d1ec 100644 GIT binary patch literal 42550 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 From 2d2088571d0da8e77bad9e09761ac42ccb4f574b Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 14:38:34 +0200 Subject: [PATCH 34/77] test with NA in fp qualmets --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index 6ca5018192..1a5bb2d70c 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.26900449806812143 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 NA 0.26900449806812143 NA NA NA bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.26900449806812143 0 0 0 From 02d0d11348d5dd90318c24f492920b121cf995f0 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 15:03:40 +0200 Subject: [PATCH 35/77] remove extra float + switch back to lower case nan qual metrics --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index 1a5bb2d70c..f2e1608825 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 NA 0.26900449806812143 NA NA NA -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.26900449806812143 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 From 6fc8baea415f4093263333cab13e22eb7504a907 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 15:50:41 +0200 Subject: [PATCH 36/77] include dist creation + test wheel install --- .github/workflows/test.yml | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9bb7fcb026..2638132db4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,15 +58,30 @@ jobs: run: | mamba activate foo pytest - #- name: make an artifact - # run: | - # source activate foo - # rm -f dist/* - # python setup.py sdist - #- uses: actions/upload-artifact@master - # with: - # name: "Dist files" - # path: "dist" + - name: make an artifact + run: | + micromamba activate foo + rm -f dist/* + python -m build + - uses: actions/upload-artifact@master + with: + name: "Dist files" + path: "dist" + test-wheel: + needs: build-linux + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.7','3.8','3.9','3.10'] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + run: | + pip install dist/*whl + deeptools --version build-osx: name: Test on OSX runs-on: macOS-latest From 60e81f9aecacef3c4bc5c7afbbf77e3e5cefe869 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 15:51:36 +0200 Subject: [PATCH 37/77] add name to test wheel wf --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2638132db4..7f0d3d60f5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,6 +68,7 @@ jobs: name: "Dist files" path: "dist" test-wheel: + name: test wheel needs: build-linux runs-on: ubuntu-latest strategy: From 250d4f7185313400e4bfa29c2dca9935a21dff2d Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 15:53:22 +0200 Subject: [PATCH 38/77] name in diff place --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f0d3d60f5..212d48cb3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,7 +68,6 @@ jobs: name: "Dist files" path: "dist" test-wheel: - name: test wheel needs: build-linux runs-on: ubuntu-latest strategy: @@ -80,6 +79,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: 'pip' + - name: test wheel run: | pip install dist/*whl deeptools --version From 8c2d070e027820e07a20efc8bfa809be56521637 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 15:56:33 +0200 Subject: [PATCH 39/77] include build in building env --- .github/env.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/env.yml b/.github/env.yml index 718f888d0d..ce2b2008fa 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -4,4 +4,7 @@ channels: dependencies: - python = 3.10 - flake8 - - pytest \ No newline at end of file + - pytest + - pip + - pip: + - build \ No newline at end of file From 755a084535efda8bbcdeb9e181d74f351fab5262 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:04:42 +0200 Subject: [PATCH 40/77] purge checkout from testwheel --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 212d48cb3d..a585d3569d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,6 @@ jobs: matrix: python-version: ['3.7','3.8','3.9','3.10'] steps: - - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} From 2ee0a98d54b3a6c07761452c70d7ba0e03c57f3a Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:08:34 +0200 Subject: [PATCH 41/77] ship test wheels inside build-linux job --- .github/workflows/test.yml | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a585d3569d..bb5fa13018 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,9 @@ jobs: build-linux: name: Test on Linux runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.7','3.8','3.9','3.10'] steps: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main @@ -67,21 +70,14 @@ jobs: with: name: "Dist files" path: "dist" - test-wheel: - needs: build-linux - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.7','3.8','3.9','3.10'] - steps: - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: test wheel - run: | - pip install dist/*whl - deeptools --version + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: test wheel + run: | + pip install dist/*whl + deeptools --version build-osx: name: Test on OSX runs-on: macOS-latest From c7374db4bfc11508e26bfb6c763ec047f7a27497 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:14:18 +0200 Subject: [PATCH 42/77] testwheels as separate job with artifact download --- .github/workflows/test.yml | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb5fa13018..af1d657916 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,9 +39,6 @@ jobs: build-linux: name: Test on Linux runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.7','3.8','3.9','3.10'] steps: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main @@ -70,14 +67,24 @@ jobs: with: name: "Dist files" path: "dist" - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: test wheel - run: | - pip install dist/*whl - deeptools --version + test-wheels: + name: test wheel + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.7','3.8','3.9','3.10'] + steps: + - uses: actions/download-artifact@v3 + with: + name: "Dist files" + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: install wheel + run: | + pip install dist/*whl + deeptools --version build-osx: name: Test on OSX runs-on: macOS-latest From 4f8a44766f6965388d7cab2f1e6cda3201c1e818 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:15:24 +0200 Subject: [PATCH 43/77] add depends on --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af1d657916..1dfcf3c5e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,7 @@ jobs: test-wheels: name: test wheel runs-on: ubuntu-latest + needs: build-linux strategy: matrix: python-version: ['3.7','3.8','3.9','3.10'] From 9410a11d7fdfb3cf2356f747fac7588eac4f3013 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:22:51 +0200 Subject: [PATCH 44/77] ls to see if download works --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dfcf3c5e5..61df959724 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,6 +84,8 @@ jobs: cache: 'pip' - name: install wheel run: | + ls -al + ls dist pip install dist/*whl deeptools --version build-osx: From d3d56048f74eb6f3b6aa9d69c8752a28c4b90989 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:27:26 +0200 Subject: [PATCH 45/77] list wdir after artifact dl --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61df959724..57027d78d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,10 +84,8 @@ jobs: cache: 'pip' - name: install wheel run: | - ls -al + ls ls dist - pip install dist/*whl - deeptools --version build-osx: name: Test on OSX runs-on: macOS-latest From e6bc30d1b27e862279a82ff96c98ef0cad74c747 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:31:01 +0200 Subject: [PATCH 46/77] add checkout to test-wheels --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 57027d78d3..115da07d9e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,7 @@ jobs: flake8 . --exclude=.venv,.build,build --ignore=E501,F403,E402,F999,F405,E722,W504,W605 - name: Test deepTools run: | - mamba activate foo + micromamba activate foo pytest - name: make an artifact run: | @@ -75,6 +75,7 @@ jobs: matrix: python-version: ['3.7','3.8','3.9','3.10'] steps: + - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: name: "Dist files" From c836875b587ff318392699cf7e6fd12a68002f91 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:36:54 +0200 Subject: [PATCH 47/77] whl in dist folder --- .github/workflows/test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 115da07d9e..647b389fc1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,14 +79,15 @@ jobs: - uses: actions/download-artifact@v3 with: name: "Dist files" + path: ~/dist/ - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: install wheel run: | - ls - ls dist + pip install dist/*whl + deeptools -h build-osx: name: Test on OSX runs-on: macOS-latest From 8bcb1731ba19c00a288706a9d9c775010d01cc19 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 18 Aug 2023 16:39:47 +0200 Subject: [PATCH 48/77] tilde in pip install --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 647b389fc1..e6f686d33e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,7 +86,7 @@ jobs: cache: 'pip' - name: install wheel run: | - pip install dist/*whl + pip install ~/dist/*whl deeptools -h build-osx: name: Test on OSX From b84e4edbdffd1bf4a1074ee50b61bacbec10a67a Mon Sep 17 00:00:00 2001 From: WardDeb Date: Sat, 19 Aug 2023 09:45:03 +0200 Subject: [PATCH 49/77] importlib toml req for python 3.7 --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7c3d2a0d8c..fbc65a2eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,8 @@ dependencies = [ "pyBigWig >= 0.2.1", "py2bit >= 0.2.0", "plotly >= 4.9", - "deeptoolsintervals >= 0.1.8" + "deeptoolsintervals >= 0.1.8", + "importlib-metadata" # python 3.7 support ] description = "Useful tools for exploring deep sequencing data." license = {file = "LICENSE.txt"} From 2efc3f2a1d97cf39e4f25a52046dee5ff88a6a01 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Sat, 19 Aug 2023 10:09:46 +0200 Subject: [PATCH 50/77] importlib escapes for python 3.7 --- deeptools/alignmentSieve.py | 5 ++++- deeptools/bamPEFragmentSize.py | 5 ++++- deeptools/computeMatrix.py | 5 ++++- deeptools/computeMatrixOperations.py | 5 ++++- deeptools/deeptools_list_tools.py | 5 ++++- deeptools/estimateReadFiltering.py | 5 ++++- deeptools/multiBamSummary.py | 5 ++++- deeptools/multiBigwigSummary.py | 5 ++++- deeptools/parserCommon.py | 5 ++++- deeptools/plotCorrelation.py | 5 ++++- deeptools/plotCoverage.py | 5 ++++- deeptools/plotPCA.py | 5 ++++- 12 files changed, 48 insertions(+), 12 deletions(-) diff --git a/deeptools/alignmentSieve.py b/deeptools/alignmentSieve.py index 9e1d621b88..7a04b242a8 100644 --- a/deeptools/alignmentSieve.py +++ b/deeptools/alignmentSieve.py @@ -7,7 +7,10 @@ from deeptools import parserCommon from deeptools.bamHandler import openBam from deeptools.mapReduce import mapReduce -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version from deeptools.utilities import getTLen, smartLabels, getTempFileName diff --git a/deeptools/bamPEFragmentSize.py b/deeptools/bamPEFragmentSize.py index 35cedfa432..cb02f2c046 100755 --- a/deeptools/bamPEFragmentSize.py +++ b/deeptools/bamPEFragmentSize.py @@ -18,7 +18,10 @@ # own tools from deeptools.parserCommon import writableFile from deeptools.getFragmentAndReadSize import get_read_and_fragment_length -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def parse_arguments(): diff --git a/deeptools/computeMatrix.py b/deeptools/computeMatrix.py index 25cf95216e..5fe77678f2 100644 --- a/deeptools/computeMatrix.py +++ b/deeptools/computeMatrix.py @@ -7,7 +7,10 @@ import multiprocessing from deeptools.parserCommon import writableFile, numberOfProcessors -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version from deeptools import parserCommon from deeptools import heatmapper import deeptools.computeMatrixOperations as cmo diff --git a/deeptools/computeMatrixOperations.py b/deeptools/computeMatrixOperations.py index 6b3272d4bd..d0172bc1e8 100755 --- a/deeptools/computeMatrixOperations.py +++ b/deeptools/computeMatrixOperations.py @@ -6,7 +6,10 @@ import sys import os import csv -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def parse_arguments(): diff --git a/deeptools/deeptools_list_tools.py b/deeptools/deeptools_list_tools.py index 32dcf7021f..3d530c623a 100644 --- a/deeptools/deeptools_list_tools.py +++ b/deeptools/deeptools_list_tools.py @@ -3,7 +3,10 @@ import argparse import sys -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def parse_arguments(args=None): diff --git a/deeptools/estimateReadFiltering.py b/deeptools/estimateReadFiltering.py index 63b02327d8..051ad55daf 100644 --- a/deeptools/estimateReadFiltering.py +++ b/deeptools/estimateReadFiltering.py @@ -5,7 +5,10 @@ from deeptools import parserCommon, bamHandler, utilities from deeptools.mapReduce import mapReduce from deeptools.utilities import smartLabels -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def parseArguments(): diff --git a/deeptools/multiBamSummary.py b/deeptools/multiBamSummary.py index 433adb4e42..04cf376c47 100644 --- a/deeptools/multiBamSummary.py +++ b/deeptools/multiBamSummary.py @@ -9,7 +9,10 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version old_settings = np.seterr(all='ignore') diff --git a/deeptools/multiBigwigSummary.py b/deeptools/multiBigwigSummary.py index ad61609071..4653769e38 100644 --- a/deeptools/multiBigwigSummary.py +++ b/deeptools/multiBigwigSummary.py @@ -7,10 +7,13 @@ import numpy as np import multiprocessing from deeptools import parserCommon -from importlib.metadata import version from deeptools.utilities import smartLabels import deeptools.getScorePerBigWigBin as score_bw import deeptools.deepBlue as db +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version old_settings = np.seterr(all='ignore') diff --git a/deeptools/parserCommon.py b/deeptools/parserCommon.py index 8e726ea000..c144745b7c 100755 --- a/deeptools/parserCommon.py +++ b/deeptools/parserCommon.py @@ -1,6 +1,9 @@ import argparse import os -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def check_float_0_1(value): diff --git a/deeptools/plotCorrelation.py b/deeptools/plotCorrelation.py index e3a553f567..fe3303351c 100644 --- a/deeptools/plotCorrelation.py +++ b/deeptools/plotCorrelation.py @@ -13,7 +13,10 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version old_settings = np.seterr(all='ignore') diff --git a/deeptools/plotCoverage.py b/deeptools/plotCoverage.py index d0b8613bbf..35e22a9dd9 100755 --- a/deeptools/plotCoverage.py +++ b/deeptools/plotCoverage.py @@ -18,7 +18,10 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version old_settings = np.seterr(all='ignore') diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index 394dcfba3b..23a964c4dd 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -11,7 +11,10 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -from importlib.metadata import version +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version def parse_arguments(args=None): From da21abe67b24b31eb328b0e9522beec9e9529134 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Sat, 19 Aug 2023 10:14:09 +0200 Subject: [PATCH 51/77] flake fix try except --- deeptools/alignmentSieve.py | 2 +- deeptools/bamPEFragmentSize.py | 2 +- deeptools/computeMatrix.py | 2 +- deeptools/computeMatrixOperations.py | 2 +- deeptools/deeptools_list_tools.py | 2 +- deeptools/estimateReadFiltering.py | 2 +- deeptools/multiBamSummary.py | 2 +- deeptools/multiBigwigSummary.py | 2 +- deeptools/parserCommon.py | 2 +- deeptools/plotCorrelation.py | 2 +- deeptools/plotCoverage.py | 2 +- deeptools/plotPCA.py | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/deeptools/alignmentSieve.py b/deeptools/alignmentSieve.py index 7a04b242a8..c795c470ad 100644 --- a/deeptools/alignmentSieve.py +++ b/deeptools/alignmentSieve.py @@ -7,7 +7,7 @@ from deeptools import parserCommon from deeptools.bamHandler import openBam from deeptools.mapReduce import mapReduce -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/bamPEFragmentSize.py b/deeptools/bamPEFragmentSize.py index cb02f2c046..72590c380e 100755 --- a/deeptools/bamPEFragmentSize.py +++ b/deeptools/bamPEFragmentSize.py @@ -18,7 +18,7 @@ # own tools from deeptools.parserCommon import writableFile from deeptools.getFragmentAndReadSize import get_read_and_fragment_length -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/computeMatrix.py b/deeptools/computeMatrix.py index 5fe77678f2..8351b1d28d 100644 --- a/deeptools/computeMatrix.py +++ b/deeptools/computeMatrix.py @@ -7,7 +7,7 @@ import multiprocessing from deeptools.parserCommon import writableFile, numberOfProcessors -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/computeMatrixOperations.py b/deeptools/computeMatrixOperations.py index d0172bc1e8..b246b9ce08 100755 --- a/deeptools/computeMatrixOperations.py +++ b/deeptools/computeMatrixOperations.py @@ -6,7 +6,7 @@ import sys import os import csv -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/deeptools_list_tools.py b/deeptools/deeptools_list_tools.py index 3d530c623a..0e4b6a387f 100644 --- a/deeptools/deeptools_list_tools.py +++ b/deeptools/deeptools_list_tools.py @@ -3,7 +3,7 @@ import argparse import sys -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/estimateReadFiltering.py b/deeptools/estimateReadFiltering.py index 051ad55daf..148006d0c2 100644 --- a/deeptools/estimateReadFiltering.py +++ b/deeptools/estimateReadFiltering.py @@ -5,7 +5,7 @@ from deeptools import parserCommon, bamHandler, utilities from deeptools.mapReduce import mapReduce from deeptools.utilities import smartLabels -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/multiBamSummary.py b/deeptools/multiBamSummary.py index 04cf376c47..f0caadb556 100644 --- a/deeptools/multiBamSummary.py +++ b/deeptools/multiBamSummary.py @@ -9,7 +9,7 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/multiBigwigSummary.py b/deeptools/multiBigwigSummary.py index 4653769e38..cafee2242c 100644 --- a/deeptools/multiBigwigSummary.py +++ b/deeptools/multiBigwigSummary.py @@ -10,7 +10,7 @@ from deeptools.utilities import smartLabels import deeptools.getScorePerBigWigBin as score_bw import deeptools.deepBlue as db -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/parserCommon.py b/deeptools/parserCommon.py index c144745b7c..37e9f359ab 100755 --- a/deeptools/parserCommon.py +++ b/deeptools/parserCommon.py @@ -1,6 +1,6 @@ import argparse import os -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/plotCorrelation.py b/deeptools/plotCorrelation.py index fe3303351c..f427d769ed 100644 --- a/deeptools/plotCorrelation.py +++ b/deeptools/plotCorrelation.py @@ -13,7 +13,7 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/plotCoverage.py b/deeptools/plotCoverage.py index 35e22a9dd9..1aeacdf757 100755 --- a/deeptools/plotCoverage.py +++ b/deeptools/plotCoverage.py @@ -18,7 +18,7 @@ import deeptools.countReadsPerBin as countR from deeptools import parserCommon from deeptools.utilities import smartLabels -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index 23a964c4dd..409f9639b6 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -11,7 +11,7 @@ from deeptools.correlation import Correlation from deeptools.parserCommon import writableFile -try: # keep python 3.7 support. +try: # keep python 3.7 support. from importlib.metadata import version except ModuleNotFoundError: from importlib_metadata import version From a1eb5558148621fdc0a68399f709795b9a0062f7 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Sat, 19 Aug 2023 15:02:04 +0200 Subject: [PATCH 52/77] test pypi build with micromamba --- .github/env.yml | 1 + .github/workflows/pypi.yml | 54 +++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/.github/env.yml b/.github/env.yml index ce2b2008fa..e216f344b5 100644 --- a/.github/env.yml +++ b/.github/env.yml @@ -5,6 +5,7 @@ dependencies: - python = 3.10 - flake8 - pytest + - twine - pip - pip: - build \ No newline at end of file diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index e30e57712a..f8df1391c8 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -1,35 +1,35 @@ name: pypi -on: - create: - tags: +on: [push, pull_request] +#on: +# create: +# tags: + +defaults: + run: + shell: bash -l {0} + jobs: pypi: name: upload to pypi runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Setup conda + - uses: actions/checkout@v3 + - uses: mamba-org/setup-micromamba@main + with: + environment-file: .github/env.yml + cache-downloads: true + environment-name: foo + - name: build run: | - curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o miniconda.sh - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - hash -r - conda config --set always_yes yes --set changeps1 no - - name: create env - run: | - export PATH=$HOME/miniconda/bin:$PATH - conda create -n foo -q --yes -c conda-forge -c bioconda python=3.7 twine - - name: sdist - run: | - export PATH=$HOME/miniconda/bin:$PATH - source activate foo + micromamba activate foo rm -f dist/* - python setup.py sdist - - name: upload - env: - TWINE_USERNAME: "__token__" - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: | - export PATH=$HOME/miniconda/bin:$PATH - source activate foo - twine upload dist/* + python -m build + which twine + #- name: upload + # env: + # TWINE_USERNAME: "__token__" + # TWINE_PASSWORD: ${{ secrets.pypi_password }} + # run: | + # export PATH=$HOME/miniconda/bin:$PATH + # source activate foo + # twine upload dist/* From e27b9080d0867abec559767df111c30c481117d3 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Sat, 19 Aug 2023 15:08:50 +0200 Subject: [PATCH 53/77] pypi push on tag --- .github/workflows/pypi.yml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index f8df1391c8..5f973d3759 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -1,8 +1,8 @@ name: pypi -on: [push, pull_request] -#on: -# create: -# tags: + +on: + create: + tags: defaults: run: @@ -24,12 +24,10 @@ jobs: micromamba activate foo rm -f dist/* python -m build - which twine - #- name: upload - # env: - # TWINE_USERNAME: "__token__" - # TWINE_PASSWORD: ${{ secrets.pypi_password }} - # run: | - # export PATH=$HOME/miniconda/bin:$PATH - # source activate foo - # twine upload dist/* + - name: upload + env: + TWINE_USERNAME: "__token__" + TWINE_PASSWORD: ${{ secrets.pypi_password }} + run: | + micromamba activate foo + twine upload dist/* From 9c2a86dd02ba324e74ebed2752f4642d97fabf70 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 13:35:41 +0200 Subject: [PATCH 54/77] include pytest in test-wheels --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6f686d33e..241463779c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -88,6 +88,10 @@ jobs: run: | pip install ~/dist/*whl deeptools -h + - name: pytest + run: | + pip install pytest + pytest build-osx: name: Test on OSX runs-on: macOS-latest From a11eedcf6e31b0b2659446f2cd3afb18a244d03d Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 14:28:35 +0200 Subject: [PATCH 55/77] rounding qual metrics fingerprint galaxy test --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index f2e1608825..84d5d720e8 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681213 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681213 0 0 0 From 09a7a9547828429aed555f96e84b9c69fc1cd809 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 14:45:56 +0200 Subject: [PATCH 56/77] set version to 3.5.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fbc65a2eda..68e9cabb45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = [ [project] name = "deepTools" -version = "3.5.3.dev1" +version = "3.5.3" authors = [ {name="Fidel Ramirez"}, {name="Devon P Ryan"}, From 1b3ae45029c4fde32c4bebd721632be42db67aef Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 15:03:44 +0200 Subject: [PATCH 57/77] revert back to fingerprint qual metrics --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index 84d5d720e8..f2e1608825 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681213 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681213 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 From 6b4ad368179fb03a81c59698a4c4ef7bf893c07c Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 15:23:08 +0200 Subject: [PATCH 58/77] rollback galaxy again --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index f2e1608825..84d5d720e8 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681213 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681213 0 0 0 From f0ad1f0c1631fc0cc4d7d3f055c9eb7ee3c3e9ad Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 15:45:52 +0200 Subject: [PATCH 59/77] update installation instructions doc --- docs/content/installation.rst | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/content/installation.rst b/docs/content/installation.rst index c45ecacfed..d0f77ac718 100644 --- a/docs/content/installation.rst +++ b/docs/content/installation.rst @@ -10,26 +10,26 @@ Remember -- deepTools are available for **command line usage** as well as for Requirements ------------- -* Python 2.7 or Python 3.x -* numpy >= 1.8.0 +* Python >= 3.7 +* numpy >= 1.9.0 * scipy >= 0.17.0 -* py2bit >= 0.1.0 +* matplotlib >= 3.3.0 +* pysam >= 0.14.0 +* numpydoc >= 0.5 * pyBigWig >= 0.2.1 -* pysam >= 0.8 -* matplotlib >= 1.4.0 +* py2bit >= 0.2.0 +* plotly >= 4.9 +* deeptoolsintervals >= 0.1.8 +* importlib-metadata (when running python 3.7) -The fastest way to obtain **Python 2.7 or Python 3.x together with numpy and scipy** is -via the `Anaconda Scientific Python -Distribution `_. -Just download the version that's suitable for your operating system and -follow the directions for its installation. All of the requirements for deepTools can be installed in Anaconda with: +DeepTools (including the requirements) can be installed with conda: .. code:: bash $ conda install -c bioconda deeptools -Command line installation using ``pip`` ------------------------------------------ +Command line installation using ``pip`` from pypi +-------------------------------------------------- Install deepTools using the following command: :: @@ -45,10 +45,10 @@ If you need to specify a specific path for the installation of the tools, make u $ pip install --install-option="--prefix=/MyPath/Tools/deepTools2.0" git+https://github.com/deeptools/deepTools.git -Command line installation without ``pip`` -------------------------------------------- +Command line installation using ``pip`` from source +--------------------------------------------------- -You are highly recommended to use `pip` rather than these more complicated steps. +You are highly recommended to use the 'pypi installation' rather than these more complicated steps. 1. Install the requirements listed above in the "requirements" section. This is done automatically by `pip`. @@ -63,11 +63,11 @@ or if you want a particular release, choose one from https://github.com/deeptool $ wget https://github.com/deeptools/deepTools/archive/1.5.12.tar.gz $ tar -xzvf -3. install the source code (if you don't have root permission, you can set -a specific folder using the ``--prefix`` option) +3. install the source code :: - $ python setup.py install --prefix /User/Tools/deepTools2.0 + $ python -m build + $ pip install dist/*whl Galaxy installation -------------------- From e5a051da6654bbe1204c883ee0a28dc22307c46d Mon Sep 17 00:00:00 2001 From: WardDeb Date: Mon, 21 Aug 2023 16:18:19 +0200 Subject: [PATCH 60/77] qual metrics 14 --- .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index 84d5d720e8..f2e1608825 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681213 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681213 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 From d7543b1a51502df172c26546b64d10f4b26445b5 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Tue, 22 Aug 2023 11:50:04 +0200 Subject: [PATCH 61/77] usage and -h for all executables --- deeptools/alignmentSieve.py | 3 +- deeptools/bamCompare.py | 3 +- deeptools/bamCoverage.py | 4 +- deeptools/bamPEFragmentSize.py | 9 ++- deeptools/bigwigAverage.py | 7 +- deeptools/bigwigCompare.py | 4 +- deeptools/computeGCBias.py | 5 +- deeptools/computeMatrix.py | 4 + deeptools/correctGCBias.py | 8 +- deeptools/estimateReadFiltering.py | 4 +- deeptools/estimateScaleFactor.py | 117 +++++++++++++++++++++++++++++ deeptools/multiBamSummary.py | 12 ++- deeptools/multiBigwigSummary.py | 10 ++- deeptools/plotCorrelation.py | 4 +- deeptools/plotCoverage.py | 4 +- deeptools/plotEnrichment.py | 4 +- deeptools/plotFingerprint.py | 6 +- deeptools/plotHeatmap.py | 4 +- deeptools/plotPCA.py | 4 +- deeptools/plotProfile.py | 6 +- 20 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 deeptools/estimateScaleFactor.py diff --git a/deeptools/alignmentSieve.py b/deeptools/alignmentSieve.py index c795c470ad..4f2aa18793 100644 --- a/deeptools/alignmentSieve.py +++ b/deeptools/alignmentSieve.py @@ -18,7 +18,8 @@ def parseArguments(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="This tool filters alignments in a BAM/CRAM file according the the specified parameters. It can optionally output to BEDPE format.", - usage='Example usage: alignmentSieve.py -b sample1.bam -o sample1.filtered.bam --minMappingQuality 10 --filterMetrics log.txt') + usage='alignmentSieve -b sample1.bam -o sample1.filtered.bam --minMappingQuality 10 --filterMetrics log.txt\n' + 'help: alignmentSieve -h / alignmentSieve --help') required = parser.add_argument_group('Required arguments') required.add_argument('--bam', '-b', diff --git a/deeptools/bamCompare.py b/deeptools/bamCompare.py index 9f19321f92..223bc06c95 100644 --- a/deeptools/bamCompare.py +++ b/deeptools/bamCompare.py @@ -44,7 +44,8 @@ def parseArguments(): 'independently. If this is undesirable, then use the --samFlagInclude ' 'or --samFlagExclude options.', - usage=' bamCompare -b1 treatment.bam -b2 control.bam -o log2ratio.bw', + usage='bamCompare -b1 treatment.bam -b2 control.bam -o log2ratio.bw\n' + 'help: bamCompare -h / bamCompare --help', add_help=False) diff --git a/deeptools/bamCoverage.py b/deeptools/bamCoverage.py index c0002a59d7..acca196fc1 100644 --- a/deeptools/bamCoverage.py +++ b/deeptools/bamCoverage.py @@ -36,8 +36,8 @@ def parseArguments(): 'Million mapped reads (RPKM), counts per million (CPM), bins per ' 'million mapped reads (BPM) and 1x depth (reads per genome ' 'coverage, RPGC).\n', - usage='An example usage is:' - '$ bamCoverage -b reads.bam -o coverage.bw', + usage='bamCoverage -b reads.bam -o coverage.bw\n' + 'help: bamCoverage -h / bamCoverage --help', add_help=False) return parser diff --git a/deeptools/bamPEFragmentSize.py b/deeptools/bamPEFragmentSize.py index 72590c380e..9c9e92f20d 100755 --- a/deeptools/bamPEFragmentSize.py +++ b/deeptools/bamPEFragmentSize.py @@ -33,7 +33,10 @@ def parse_arguments(): 'Properly paired reads are preferred for computation, i.e., ' 'it will only use discordant pairs if no concordant alignments ' 'overlap with a given region. ' - 'The default setting simply prints the summary statistics to the screen.') + 'The default setting simply prints the summary statistics to the screen.', + usage='bamPEFragmentSize -b sample1.bam sample2.bam -o hist.png\n' + 'help: bamPEFragmentSize -h / bamPEFragmentSize --help' + ) parser.add_argument('--bamfiles', '-b', help='List of BAM files to process', nargs='+', @@ -293,6 +296,10 @@ def printTable(args, fragDict, readDict): def main(args=None): args = parse_arguments().parse_args(args) + if len(sys.argv) == 1: + parse_arguments().print_help() + sys.exit() + fraglengths = {} readlengths = {} of = None diff --git a/deeptools/bigwigAverage.py b/deeptools/bigwigAverage.py index 5cc41553b1..7153d98f4f 100644 --- a/deeptools/bigwigAverage.py +++ b/deeptools/bigwigAverage.py @@ -23,7 +23,9 @@ def parse_arguments(args=None): 'of mapped reads. To average the bigWig files, the genome is ' 'partitioned into bins of equal size, then the scores ' 'in each bigwig file are computed per bin.' - 'These scores are averaged and scaleFactors can be applied before the average.') + 'These scores are averaged and scaleFactors can be applied before the average.', + usage='bigwigAverage -b sample1.bw sample2.bw -o outfile.bw\n' + 'help: bigwigAverage -h / bigwigAverage --help') # define the arguments parser.add_argument('--bigwigs', '-b', @@ -94,6 +96,9 @@ def average(tileCoverage, args): def main(args=None): args = parse_arguments().parse_args(args) + if len(sys.argv) == 1: + parse_arguments().print_help() + sys.exit() nFiles = len(args.bigwigs) diff --git a/deeptools/bigwigCompare.py b/deeptools/bigwigCompare.py index dc1a70e08b..4e15c7df8e 100644 --- a/deeptools/bigwigCompare.py +++ b/deeptools/bigwigCompare.py @@ -24,7 +24,9 @@ def parse_arguments(args=None): 'partitioned into bins of equal size, then the number of reads found ' 'in each BAM file are counted per bin and finally a summary ' 'value is reported. This value can be the ratio of the number of reads' - 'per bin, the log2 of the ratio, the sum or the difference.') + 'per bin, the log2 of the ratio, the sum or the difference.', + usage='bigwigCompare -b1 sample1.bw -b2 sample2.bw -o log2.bw\n' + 'help: bigwigCompare -h / bigwigCompare --help') # define the arguments parser.add_argument('--bigwig1', '-b1', diff --git a/deeptools/computeGCBias.py b/deeptools/computeGCBias.py index 0c51f906b5..f261a9fc14 100755 --- a/deeptools/computeGCBias.py +++ b/deeptools/computeGCBias.py @@ -30,8 +30,9 @@ def parse_arguments(args=None): '[Benjamini & Speed (2012). Nucleic Acids Research, 40(10). doi: 10.1093/nar/gks001]. ' 'The GC-bias is visualized and the resulting table can be used to' 'correct the bias with `correctGCBias`.', - usage='\n computeGCBias ' - '-b file.bam --effectiveGenomeSize 2150570000 -g mm9.2bit -l 200 --GCbiasFrequenciesFile freq.txt [options]', + usage='computeGCBias ' + '-b file.bam --effectiveGenomeSize 2150570000 -g mm9.2bit -l 200 --GCbiasFrequenciesFile freq.txt\n' + 'help: computeGCBias -h / computeGCBias --help', conflict_handler='resolve', add_help=False) diff --git a/deeptools/computeMatrix.py b/deeptools/computeMatrix.py index 8351b1d28d..440358c9b2 100644 --- a/deeptools/computeMatrix.py +++ b/deeptools/computeMatrix.py @@ -356,6 +356,10 @@ def computeMatrixOptArgs(case=['scale-regions', 'reference-point'][0]): def process_args(args=None): args = parse_arguments().parse_args(args) + if len(sys.argv) == 1: + parse_arguments().print_help() + sys.exit() + if args.quiet is True: args.verbose = False diff --git a/deeptools/correctGCBias.py b/deeptools/correctGCBias.py index ba6d893a98..404b874056 100755 --- a/deeptools/correctGCBias.py +++ b/deeptools/correctGCBias.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python + #!/usr/bin/env python # -*- coding: utf-8 -*- import os @@ -38,10 +38,10 @@ def parse_arguments(args=None): '(typically AT-rich regions). ' 'The tool ``computeGCBias`` needs to be run first to generate the ' 'frequency table needed here.', - usage='An example usage is:\n correctGCBias ' + usage='correctGCBias ' '-b file.bam --effectiveGenomeSize 2150570000 -g mm9.2bit ' - '--GCbiasFrequenciesFile freq.txt -o gc_corrected.bam ' - '[options]', + '--GCbiasFrequenciesFile freq.txt -o gc_corrected.bam\n' + 'help: correctGCBias -h / correctGCBias --help', conflict_handler='resolve', add_help=False) return parser diff --git a/deeptools/estimateReadFiltering.py b/deeptools/estimateReadFiltering.py index 148006d0c2..d7427403fa 100644 --- a/deeptools/estimateReadFiltering.py +++ b/deeptools/estimateReadFiltering.py @@ -34,7 +34,9 @@ def parseArguments(): The sum of these may be more than the total number of reads. Note that alignments are sampled from bins of size --binSize spaced --distanceBetweenBins apart. """, - usage='Example usage: estimateReadFiltering.py -b sample1.bam sample2.bam > log.txt') + usage='estimateReadFiltering -b sample1.bam sample2.bam\n' + 'help: estimateReadFiltering -h / estimateReadFiltering --help' + ) required = parser.add_argument_group('Required arguments') required.add_argument('--bamfiles', '-b', diff --git a/deeptools/estimateScaleFactor.py b/deeptools/estimateScaleFactor.py new file mode 100644 index 0000000000..c3650ad492 --- /dev/null +++ b/deeptools/estimateScaleFactor.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- + +import deeptools.misc +import argparse +import sys + +from deeptools.SES_scaleFactor import estimateScaleFactor +from deeptools.parserCommon import numberOfProcessors +try: # keep python 3.7 support. + from importlib.metadata import version +except ModuleNotFoundError: + from importlib_metadata import version + +debug = 0 + + +def parseArguments(args=None): + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description='Given two BAM files, this estimates scaling factors ' + '(bigger to smaller).', + usage='estimateScaleFactor -b sample1.bam sample2.bam\n' + 'help: estimateScaleFactor -h / estimateScaleFactor --help' + ) + + # define the arguments + parser.add_argument('--bamfiles', '-b', + metavar='list of bam files', + help='List of indexed BAM files, space delineated', + nargs='+', + required=True) + + + parser.add_argument('--ignoreForNormalization', '-ignore', + help='A comma-separated list of chromosome names, ' + 'limited by quotes, ' + 'containing those ' + 'chromosomes that should be excluded ' + 'during normalization computations. For example, ' + '--ignoreForNormalization "chrX, chrM" ') + + parser.add_argument('--sampleWindowLength', '-l', + help='Length in bases for a window used to ' + 'sample the genome and compute the size or scaling ' + 'factors', + default=1000, + type=int) + + parser.add_argument('--numberOfSamples', '-n', + help='Number of samplings taken from the genome ' + 'to compute the scaling factors', + default=100000, + type=int) + + parser.add_argument('--normalizationLength', '-nl', + help='By default, data is normalized to 1 ' + 'fragment per 100 bases. The expected value is an ' + 'integer. For example, if normalizationLength ' + 'is 1000, then the resulting scaling factor ' + 'will cause the average coverage of the BAM file to ' + 'have on average 1 fragment per kilobase', + type=int, + default=10) + + parser.add_argument('--skipZeros', + help='If set, then zero counts that happen for *all* ' + 'BAM files given are ignored. This will result in a ' + 'reduced number of read counts than that specified ' + 'in --numberOfSamples', + action='store_true', + required=False) + + parser.add_argument('--numberOfProcessors', '-p', + help='Number of processors to use. The default is ' + 'to use half the maximum number of processors.', + metavar="INT", + type=numberOfProcessors, + default="max/2", + required=False) + + parser.add_argument('--verbose', '-v', + help='Set to see processing messages.', + action='store_true') + + parser.add_argument('--version', action='version', + version='%(prog)s {}'.format(version('deeptools'))) + + args=parser.parse_args(args) + if args.ignoreForNormalization: + args.ignoreForNormalization=[x.strip() for x in args.ignoreForNormalization.split(',')] + else: + args.ignoreForNormalization = [] + return args + +def main(args=None): + """ + The algorithm samples the genome a number of times as specified + by the --numberOfSamples parameter to estimate scaling factors of + between to samples + + """ + args = parseArguments().parse_args(args) + if len(args.bamfiles) > 2: + print("SES method to estimate scale factors only works for two samples") + exit(0) + + sys.stderr.write("{:,} number of samples will be computed.\n".format(args.numberOfSamples)) + sizeFactorsDict = estimateScaleFactor(args.bamfiles, args.sampleWindowLength, + args.numberOfSamples, + args.normalizationLength, + numberOfProcessors=args.numberOfProcessors, + chrsToSkip=args.ignoreForNormalization, + verbose=args.verbose) + + for k, v in sizeFactorsDict.items(): + print("{}: {}".format(k, v)) \ No newline at end of file diff --git a/deeptools/multiBamSummary.py b/deeptools/multiBamSummary.py index f0caadb556..892295116f 100644 --- a/deeptools/multiBamSummary.py +++ b/deeptools/multiBamSummary.py @@ -73,7 +73,8 @@ def parse_arguments(args=None): add_help=False, usage='%(prog)s ' '--bamfiles file1.bam file2.bam ' - '-o results.npz \n') + '-o results.npz \n' + 'help: multiBamSummary bins -h / multiBamSummary bins --help\n') # BED file arguments subparsers.add_parser( @@ -87,7 +88,8 @@ def parse_arguments(args=None): "that should be considered for the coverage analysis. A " "common use is to compare ChIP-seq coverages between two " "different samples for a set of peak regions.", - usage='%(prog)s --BED selection.bed --bamfiles file1.bam file2.bam -o results.npz\n', + usage='%(prog)s --BED selection.bed --bamfiles file1.bam file2.bam -o results.npz\n' + 'help: multiBamSummary BED-file -h / multiBamSummary bins --help\n', add_help=False) return parser @@ -194,7 +196,11 @@ def bamcorrelate_args(case='bins'): def process_args(args=None): args = parse_arguments().parse_args(args) - + + if len(sys.argv) == 1: + parse_arguments().print_help() + sys.exit() + if args.labels and len(args.bamfiles) != len(args.labels): print("The number of labels does not match the number of bam files.") exit(0) diff --git a/deeptools/multiBigwigSummary.py b/deeptools/multiBigwigSummary.py index cafee2242c..e06f3264a7 100644 --- a/deeptools/multiBigwigSummary.py +++ b/deeptools/multiBigwigSummary.py @@ -72,7 +72,8 @@ def parse_arguments(args=None): add_help=False, usage='multiBigwigSummary bins ' '-b file1.bw file2.bw ' - '-o results.npz\n') + '-o results.npz\n' + 'help: multiBigwigSummary bins -h / multiBigwigSummary bins --help\n') # BED file arguments subparsers.add_parser( @@ -89,7 +90,8 @@ def parse_arguments(args=None): "different samples over a set of pre-defined peak regions.", usage='multiBigwigSummary BED-file ' '-b file1.bw file2.bw ' - '-o results.npz --BED selection.bed\n', + '-o results.npz --BED selection.bed\n' + 'help: multiBigwigSummary BED-file -h / multiBigwigSummary BED-file --help\n', add_help=False) return parser @@ -97,6 +99,10 @@ def parse_arguments(args=None): def process_args(args=None): args = parse_arguments().parse_args(args) + + if len(sys.argv) == 1: + parse_arguments().print_help() + sys.exit() if not args.labels and args.smartLabels: args.labels = smartLabels(args.bwfiles) diff --git a/deeptools/plotCorrelation.py b/deeptools/plotCorrelation.py index f427d769ed..2b8d9f790a 100644 --- a/deeptools/plotCorrelation.py +++ b/deeptools/plotCorrelation.py @@ -44,7 +44,9 @@ def parse_arguments(args=None): epilog='example usages:\n' 'plotCorrelation -in results_file --whatToPlot heatmap --corMethod pearson -o heatmap.png\n\n' ' \n\n', - parents=[basic_args, heatmap_parser, scatter_parser]) + parents=[basic_args, heatmap_parser, scatter_parser], + usage='plotCorrelation -in matrix.gz -c spearman -p heatmap -o plot.png\n' + 'help: plotCorrelation -h / plotCorrelation --help\n') return parser diff --git a/deeptools/plotCoverage.py b/deeptools/plotCoverage.py index 1aeacdf757..d748a5a8db 100755 --- a/deeptools/plotCoverage.py +++ b/deeptools/plotCoverage.py @@ -49,7 +49,9 @@ def parse_arguments(args=None): epilog='example usages:\nplotCoverage ' '--bamfiles file1.bam file2.bam -o results.png\n\n' ' \n\n', - conflict_handler='resolve') + conflict_handler='resolve', + usage='plotCoverage -b sample1.bam sample2.bam -o coverage.png \n' + 'help: plotCoverage -h / plotCoverage --help\n') parser.add_argument('--version', action='version', version='plotCoverage {}'.format(version('deeptools'))) diff --git a/deeptools/plotEnrichment.py b/deeptools/plotEnrichment.py index 7ef474effd..bbd53f90d5 100755 --- a/deeptools/plotEnrichment.py +++ b/deeptools/plotEnrichment.py @@ -51,7 +51,9 @@ def parse_arguments(args=None): epilog='example usages:\n' 'plotEnrichment -b file1.bam file2.bam --BED peaks.bed -o enrichment.png\n\n' ' \n\n', - parents=[basic_args, parent_parser, read_options]) + parents=[basic_args, parent_parser, read_options], + usage='plotEnrichment -b sample1.bam sample2.bam --BED peaks.bed -o enrichment.png\n' + 'help: plotEnrichment -h / plotEnrichment --help\n') return parser diff --git a/deeptools/plotFingerprint.py b/deeptools/plotFingerprint.py index f5e26072b4..646da2c714 100755 --- a/deeptools/plotFingerprint.py +++ b/deeptools/plotFingerprint.py @@ -42,8 +42,10 @@ def parse_arguments(args=None): 'these counts are sorted ' 'and the cumulative sum is finally plotted. ', conflict_handler='resolve', - usage='An example usage is: plotFingerprint -b treatment.bam control.bam ' - '-plot fingerprint.png', + usage='plotFingerprint -b treatment.bam control.bam ' + '-plot fingerprint.png\n' + 'help: plotFingerprint -h / plotFingerprint --help' + , add_help=False) return parser diff --git a/deeptools/plotHeatmap.py b/deeptools/plotHeatmap.py index 6b0f21337c..ad666998e5 100755 --- a/deeptools/plotHeatmap.py +++ b/deeptools/plotHeatmap.py @@ -41,7 +41,9 @@ def parse_arguments(args=None): 'scores associated with genomic regions. ' 'The program requires a matrix file ' 'generated by the tool ``computeMatrix``.', - epilog='An example usage is: plotHeatmap -m ', + epilog='An example usage is: plotHeatmap -m matrix.gz', + usage='plotHeatmap -m matrix.gz\n' + 'help: plotHeatmap -h / plotHeatmap --help', add_help=False) return parser diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index 409f9639b6..c6fdedbe82 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -33,7 +33,9 @@ def parse_arguments(args=None): epilog='example usages:\n' 'plotPCA -in coverages.npz -o pca.png\n\n' ' \n\n', - parents=[basic_args, ]) + parents=[basic_args, ], + usage='plotPCA -in coverage.npz -o pca.png\n', + 'help: plotPCA -h / plotPCA --help\n') return parser diff --git a/deeptools/plotProfile.py b/deeptools/plotProfile.py index 415a487c17..7497875f20 100755 --- a/deeptools/plotProfile.py +++ b/deeptools/plotProfile.py @@ -44,8 +44,10 @@ def parse_arguments(args=None): 'any other regions defined in BED ' ' will work. A matrix generated ' 'by computeMatrix is required.', - epilog='An example usage is: plotProfile -m ', - add_help=False) + epilog='An example usage is: plotProfile -m matrix.gz', + add_help=False, + usage='plotProfile -m matrix.gz\n' + 'help: plotProfile -h / plotProfile --help') return parser From 3a5279c5faf84503f26137bfd8858950da4c2218 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Tue, 22 Aug 2023 12:15:18 +0200 Subject: [PATCH 62/77] flakes --- deeptools/bamPEFragmentSize.py | 2 +- deeptools/correctGCBias.py | 2 +- deeptools/estimateReadFiltering.py | 2 +- deeptools/estimateScaleFactor.py | 36 ++++++++++++++++-------------- deeptools/multiBamSummary.py | 4 ++-- deeptools/multiBigwigSummary.py | 2 +- deeptools/plotCoverage.py | 2 +- deeptools/plotFingerprint.py | 3 +-- deeptools/plotPCA.py | 2 +- 9 files changed, 28 insertions(+), 27 deletions(-) diff --git a/deeptools/bamPEFragmentSize.py b/deeptools/bamPEFragmentSize.py index 9c9e92f20d..ad63fa14fd 100755 --- a/deeptools/bamPEFragmentSize.py +++ b/deeptools/bamPEFragmentSize.py @@ -36,7 +36,7 @@ def parse_arguments(): 'The default setting simply prints the summary statistics to the screen.', usage='bamPEFragmentSize -b sample1.bam sample2.bam -o hist.png\n' 'help: bamPEFragmentSize -h / bamPEFragmentSize --help' - ) + ) parser.add_argument('--bamfiles', '-b', help='List of BAM files to process', nargs='+', diff --git a/deeptools/correctGCBias.py b/deeptools/correctGCBias.py index 404b874056..1154b93688 100755 --- a/deeptools/correctGCBias.py +++ b/deeptools/correctGCBias.py @@ -1,4 +1,4 @@ - #!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- import os diff --git a/deeptools/estimateReadFiltering.py b/deeptools/estimateReadFiltering.py index d7427403fa..52fded5382 100644 --- a/deeptools/estimateReadFiltering.py +++ b/deeptools/estimateReadFiltering.py @@ -36,7 +36,7 @@ def parseArguments(): """, usage='estimateReadFiltering -b sample1.bam sample2.bam\n' 'help: estimateReadFiltering -h / estimateReadFiltering --help' - ) + ) required = parser.add_argument_group('Required arguments') required.add_argument('--bamfiles', '-b', diff --git a/deeptools/estimateScaleFactor.py b/deeptools/estimateScaleFactor.py index c3650ad492..31acea3f57 100644 --- a/deeptools/estimateScaleFactor.py +++ b/deeptools/estimateScaleFactor.py @@ -1,7 +1,6 @@ #!/usr/bin/env python -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -import deeptools.misc import argparse import sys @@ -17,12 +16,12 @@ def parseArguments(args=None): parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description='Given two BAM files, this estimates scaling factors ' - '(bigger to smaller).', - usage='estimateScaleFactor -b sample1.bam sample2.bam\n' - 'help: estimateScaleFactor -h / estimateScaleFactor --help' - ) + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description='Given two BAM files, this estimates scaling factors ' + '(bigger to smaller).', + usage='estimateScaleFactor -b sample1.bam sample2.bam\n' + 'help: estimateScaleFactor -h / estimateScaleFactor --help' + ) # define the arguments parser.add_argument('--bamfiles', '-b', @@ -31,7 +30,6 @@ def parseArguments(args=None): nargs='+', required=True) - parser.add_argument('--ignoreForNormalization', '-ignore', help='A comma-separated list of chromosome names, ' 'limited by quotes, ' @@ -80,19 +78,23 @@ def parseArguments(args=None): required=False) parser.add_argument('--verbose', '-v', - help='Set to see processing messages.', - action='store_true') + help='Set to see processing messages.', + action='store_true') - parser.add_argument('--version', action='version', - version='%(prog)s {}'.format(version('deeptools'))) + parser.add_argument('--version', + action='version', + version='%(prog)s {}'.format(version('deeptools'))) - args=parser.parse_args(args) + args = parser.parse_args(args) if args.ignoreForNormalization: - args.ignoreForNormalization=[x.strip() for x in args.ignoreForNormalization.split(',')] + args.ignoreForNormalization = [ + x.strip() for x in args.ignoreForNormalization.split(',') + ] else: - args.ignoreForNormalization = [] + args.ignoreForNormalization = [] return args + def main(args=None): """ The algorithm samples the genome a number of times as specified @@ -114,4 +116,4 @@ def main(args=None): verbose=args.verbose) for k, v in sizeFactorsDict.items(): - print("{}: {}".format(k, v)) \ No newline at end of file + print("{}: {}".format(k, v)) diff --git a/deeptools/multiBamSummary.py b/deeptools/multiBamSummary.py index 892295116f..b010001ff6 100644 --- a/deeptools/multiBamSummary.py +++ b/deeptools/multiBamSummary.py @@ -196,11 +196,11 @@ def bamcorrelate_args(case='bins'): def process_args(args=None): args = parse_arguments().parse_args(args) - + if len(sys.argv) == 1: parse_arguments().print_help() sys.exit() - + if args.labels and len(args.bamfiles) != len(args.labels): print("The number of labels does not match the number of bam files.") exit(0) diff --git a/deeptools/multiBigwigSummary.py b/deeptools/multiBigwigSummary.py index e06f3264a7..50f40beef5 100644 --- a/deeptools/multiBigwigSummary.py +++ b/deeptools/multiBigwigSummary.py @@ -99,7 +99,7 @@ def parse_arguments(args=None): def process_args(args=None): args = parse_arguments().parse_args(args) - + if len(sys.argv) == 1: parse_arguments().print_help() sys.exit() diff --git a/deeptools/plotCoverage.py b/deeptools/plotCoverage.py index d748a5a8db..e233dcb717 100755 --- a/deeptools/plotCoverage.py +++ b/deeptools/plotCoverage.py @@ -51,7 +51,7 @@ def parse_arguments(args=None): ' \n\n', conflict_handler='resolve', usage='plotCoverage -b sample1.bam sample2.bam -o coverage.png \n' - 'help: plotCoverage -h / plotCoverage --help\n') + 'help: plotCoverage -h / plotCoverage --help\n') parser.add_argument('--version', action='version', version='plotCoverage {}'.format(version('deeptools'))) diff --git a/deeptools/plotFingerprint.py b/deeptools/plotFingerprint.py index 646da2c714..550931f934 100755 --- a/deeptools/plotFingerprint.py +++ b/deeptools/plotFingerprint.py @@ -44,8 +44,7 @@ def parse_arguments(args=None): conflict_handler='resolve', usage='plotFingerprint -b treatment.bam control.bam ' '-plot fingerprint.png\n' - 'help: plotFingerprint -h / plotFingerprint --help' - , + 'help: plotFingerprint -h / plotFingerprint --help', add_help=False) return parser diff --git a/deeptools/plotPCA.py b/deeptools/plotPCA.py index c6fdedbe82..c43942b85b 100644 --- a/deeptools/plotPCA.py +++ b/deeptools/plotPCA.py @@ -34,7 +34,7 @@ def parse_arguments(args=None): 'plotPCA -in coverages.npz -o pca.png\n\n' ' \n\n', parents=[basic_args, ], - usage='plotPCA -in coverage.npz -o pca.png\n', + usage='plotPCA -in coverage.npz -o pca.png\n' 'help: plotPCA -h / plotPCA --help\n') return parser From fff9904b460658ef0d6087877dbae7ab4201052d Mon Sep 17 00:00:00 2001 From: WardDeb Date: Tue, 22 Aug 2023 12:29:47 +0200 Subject: [PATCH 63/77] switch pytest to -v --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 241463779c..1d3e5f6e47 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,7 +91,7 @@ jobs: - name: pytest run: | pip install pytest - pytest + pytest -v build-osx: name: Test on OSX runs-on: macOS-latest @@ -113,4 +113,4 @@ jobs: - name: Test deepTools run: | micromamba activate foo - pytest + pytest -v From ac433560a22010908753351efe5bbc6c7d01eb86 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Tue, 22 Aug 2023 12:35:28 +0200 Subject: [PATCH 64/77] switch pytest to -v in init build as well --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1d3e5f6e47..9de35bbe01 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,7 @@ jobs: - name: Test deepTools run: | micromamba activate foo - pytest + pytest -v - name: make an artifact run: | micromamba activate foo From 70c827444519aba762191f4d7ce9bffe8bd928b6 Mon Sep 17 00:00:00 2001 From: Ward D Date: Thu, 24 Aug 2023 08:43:11 +0200 Subject: [PATCH 65/77] Update .github/workflows/planemo.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Grüning --- .github/workflows/planemo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index c66fc22809..8a992156a8 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -2,7 +2,7 @@ name: Planemo on: [push, pull_request] env: - GALAXY_BRANCH: release_22.05 + GALAXY_BRANCH: release_23.1 defaults: run: From 8d3c964da90dad00e4af0ac8c444654cfd92a556 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 24 Aug 2023 10:01:18 +0200 Subject: [PATCH 66/77] python >=3.7, combine env/planemo envs, planemo test galaxy 23.1 --- .github/env.yml | 11 ---------- .github/{planemo.yml => test_and_build.yml} | 8 +++++-- .github/workflows/planemo.yml | 10 ++++----- .github/workflows/pypi.yml | 8 +++---- .github/workflows/test.yml | 24 +++++++++------------ .planemo.sh | 2 +- 6 files changed, 26 insertions(+), 37 deletions(-) delete mode 100644 .github/env.yml rename .github/{planemo.yml => test_and_build.yml} (66%) diff --git a/.github/env.yml b/.github/env.yml deleted file mode 100644 index e216f344b5..0000000000 --- a/.github/env.yml +++ /dev/null @@ -1,11 +0,0 @@ -channels: - - conda-forge - - bioconda -dependencies: - - python = 3.10 - - flake8 - - pytest - - twine - - pip - - pip: - - build \ No newline at end of file diff --git a/.github/planemo.yml b/.github/test_and_build.yml similarity index 66% rename from .github/planemo.yml rename to .github/test_and_build.yml index 8716e588a1..a3e6ea06f6 100644 --- a/.github/planemo.yml +++ b/.github/test_and_build.yml @@ -2,7 +2,7 @@ channels: - conda-forge - bioconda dependencies: - - python = 3.10 + - python >= 3.7 - numpy - scipy - flake8 @@ -11,6 +11,10 @@ dependencies: - pytest - planemo - samtools - - allure-python-commons==2.12.0 + #- allure-python-commons==2.12.0 - py2bit - pyBigWig + - twine + - pip + - pip: + - build \ No newline at end of file diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 8a992156a8..45ed8c19f8 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -19,16 +19,16 @@ jobs: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main with: - environment-file: .github/planemo.yml + environment-file: .github/test_and_build.yml cache-downloads: true - environment-name: foo + environment-name: test_and_build - name: pip install run: | - micromamba activate foo + micromamba activate test_and_build pip install . - name: planemo run: | - micromamba activate foo + micromamba activate test_and_build ./.planemo.sh ${{ matrix.chunk }} ${{ env.GALAXY_BRANCH }} - uses: actions/upload-artifact@v3 with: @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10'] + python-version: ['3.7', '3.11'] steps: - uses: actions/download-artifact@v3 with: diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 5f973d3759..e97cfbdb28 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -16,12 +16,12 @@ jobs: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main with: - environment-file: .github/env.yml + environment-file: .github/test_and_build.yml cache-downloads: true - environment-name: foo + environment-name: test_and_build - name: build run: | - micromamba activate foo + micromamba activate test_and_build rm -f dist/* python -m build - name: upload @@ -29,5 +29,5 @@ jobs: TWINE_USERNAME: "__token__" TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | - micromamba activate foo + micromamba activate test_and_build twine upload dist/* diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9de35bbe01..124fbacfee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,24 +43,24 @@ jobs: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main with: - environment-file: .github/env.yml + environment-file: .github/test_and_build.yml cache-downloads: true - environment-name: foo + environment-name: test_and_build - name: pip install run: | - micromamba activate foo + micromamba activate test_and_build pip install . - name: PEP8 run: | - micromamba activate foo + micromamba activate test_and_build flake8 . --exclude=.venv,.build,build --ignore=E501,F403,E402,F999,F405,E722,W504,W605 - name: Test deepTools run: | - micromamba activate foo + micromamba activate test_and_build pytest -v - name: make an artifact run: | - micromamba activate foo + micromamba activate test_and_build rm -f dist/* python -m build - uses: actions/upload-artifact@master @@ -99,18 +99,14 @@ jobs: - uses: actions/checkout@v3 - uses: mamba-org/setup-micromamba@main with: - environment-file: .github/env.yml + environment-file: .github/test_and_build.yml cache-downloads: true - environment-name: foo - condarc: | - channels: - - conda-forge - - bioconda + environment-name: test_and_build - name: pip install run: | - micromamba activate foo + micromamba activate test_and_build pip install . - name: Test deepTools run: | - micromamba activate foo + micromamba activate test_and_build pytest -v diff --git a/.planemo.sh b/.planemo.sh index 6f9e0810ce..7411ccc4db 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --no_dependency_resolution --galaxy_python_version 3.10 --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From b0496a70fd4c4e00cb69062e6e782236ea1dea8b Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 24 Aug 2023 10:11:14 +0200 Subject: [PATCH 67/77] force conda prefix to micromamba env dir on runner --- .planemo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.planemo.sh b/.planemo.sh index 7411ccc4db..7a51d6df56 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --conda_prefix /home/runner/micromamba/envs --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From 558442d12c9c8eb9cf3301b850f01fdcb25374f9 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 24 Aug 2023 10:28:29 +0200 Subject: [PATCH 68/77] include pytests for 3.11 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 124fbacfee..8a4789073d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,7 +73,7 @@ jobs: needs: build-linux strategy: matrix: - python-version: ['3.7','3.8','3.9','3.10'] + python-version: ['3.7','3.8','3.9','3.10', '3.11'] steps: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 From ad574352483983268d1b018d27ff7361696eb5c9 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Thu, 24 Aug 2023 10:28:48 +0200 Subject: [PATCH 69/77] bring back no dependency resolution planemo --- .planemo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.planemo.sh b/.planemo.sh index 7a51d6df56..8a6fd9785e 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -29,6 +29,6 @@ else fi planemo lint ${wrappers} -planemo test --conda_prefix /home/runner/micromamba/envs --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --no_dependency_resolution --conda_prefix /home/runner/micromamba/envs --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From 8df4889e5309cacc09faa4566a140a5be4e7396c Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 12:35:44 +0200 Subject: [PATCH 70/77] parse toml for tool tests + verify version --- deeptools/test/test_tools.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/deeptools/test/test_tools.py b/deeptools/test/test_tools.py index 6d4a5a07c6..e06390e89e 100644 --- a/deeptools/test/test_tools.py +++ b/deeptools/test/test_tools.py @@ -1,17 +1,28 @@ -import subprocess +from subprocess import PIPE, run import os +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib -ROOT = os.path.dirname(os.path.abspath(__file__)) + "/../../bin" +TOMLFILE = os.path.dirname(os.path.abspath(__file__)) + "/../../pyproject.toml" def test_tools(): """ - Checks everything that is in /bin/ - and tries to run it - + Check every script in 'pyproject.toml' + makes sure the version of all tools == version set in toml file + makes sure exitcodes are all 0 """ - if os.path.isdir(ROOT): - for _file in os.listdir(ROOT): - print(_file) - if os.path.isfile(os.path.join(ROOT, _file)): - subprocess.check_call("{}/{} --version".format(ROOT, _file).split()) + with open(TOMLFILE, 'rb') as f: + _toml = tomllib.load(f) + for _p in _toml['project']['scripts'].keys(): + _res = run( + [_p, f"--version"], + stdout=PIPE, + stderr=PIPE + ) + _version = _res.stdout.decode().splitlines()[0] + assert f"{_version}" == f"{_p} {_toml['project']['version']}" + assert f"{_res.returncode}" == f"0" + From bfda73784615c7bfeafa4841641eb8a57ec1e4f5 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 12:36:00 +0200 Subject: [PATCH 71/77] fstring tests --- .../test/test_bamCoverage_and_bamCompare.py | 22 +-- deeptools/test/test_bigwigAverage.py | 6 +- ...st_bigwigCompare_and_multiBigwigSummary.py | 8 +- .../test/test_computeMatrixOperations.py | 15 +- deeptools/test/test_heatmapper.py | 132 +++++++++--------- deeptools/test/test_readFiltering.py | 14 +- deeptools/test/test_writeBedGraph.py | 20 ++- 7 files changed, 111 insertions(+), 106 deletions(-) diff --git a/deeptools/test/test_bamCoverage_and_bamCompare.py b/deeptools/test/test_bamCoverage_and_bamCompare.py index a7e5e4ad59..ac1f23ce20 100644 --- a/deeptools/test/test_bamCoverage_and_bamCompare.py +++ b/deeptools/test/test_bamCoverage_and_bamCompare.py @@ -45,7 +45,7 @@ def test_bam_coverage_arguments(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t150\t1\n', '3R\t150\t200\t2\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -58,7 +58,7 @@ def test_bam_coverage_extend(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t150\t1\n', '3R\t150\t200\t3\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -75,7 +75,7 @@ def test_bam_coverage_extend_and_normalizeUsingRPGC(): # the scale factor should be 0.5, thus the result is similar to # that of the previous test divided by 0.5 expected = ['3R\t0\t150\t0.5\n', '3R\t150\t200\t1.5\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -89,7 +89,7 @@ def test_bam_coverage_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t50\t150\t1\n', '3R\t150\t200\t2\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -175,7 +175,7 @@ def test_bam_compare_ZoverZ(): resp = _foo.readlines() _foo.close() expected = ['3R\t50\t100\t-1\n', '3R\t100\t150\t0\n', '3R\t150\t200\t-0.584963\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -227,7 +227,7 @@ def test_bam_compare_diff_files_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t100\t150\t0\n', '3R\t150\t200\t-1\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -245,7 +245,7 @@ def test_bam_compare_extend(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t100\t-1\n', '3R\t100\t150\t1\n', '3R\t150\t200\t-1\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -285,7 +285,7 @@ def test_bam_compare_scale_factors_ratio(): """ expected = ['3R\t0\t50\t1\n', '3R\t50\t100\t0.666667\n', '3R\t100\t150\t1.33333\n', '3R\t150\t200\t1\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -327,7 +327,7 @@ def test_bam_compare_scale_factors_subtract(): """ expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t-250000\n', '3R\t100\t150\t250000\n', '3R\t150\t200\t0\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -357,7 +357,7 @@ def test_bam_coverage_filter_blacklist(): '3R\t950\t1000\t1.62672\n', '3R\t1000\t1050\t0.813362\n', '3R\t1050\t1500\t0\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -458,5 +458,5 @@ def test_bam_compare_filter_blacklist(): '3R\t750\t800\t-0.123451\n', '3R\t900\t950\t0.212545\n', '3R\t950\t1000\t0.199309\n', '3R\t1000\t1050\t0.167945\n', '3R\t1050\t1500\t0\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) diff --git a/deeptools/test/test_bigwigAverage.py b/deeptools/test/test_bigwigAverage.py index 22f5427eb8..fed8f21629 100644 --- a/deeptools/test/test_bigwigAverage.py +++ b/deeptools/test/test_bigwigAverage.py @@ -42,7 +42,7 @@ def test_bigwigAverage(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t0.5\n', '3R\t100\t150\t1\n', '3R\t150\t200\t1.5\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -55,7 +55,7 @@ def test_bigwigAverage_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t100\t150\t1\n', '3R\t150\t200\t1.5\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -67,5 +67,5 @@ def test_bigwigAverageWithScale(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t0.25\n', '3R\t100\t150\t0.75\n', '3R\t150\t200\t1\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) diff --git a/deeptools/test/test_bigwigCompare_and_multiBigwigSummary.py b/deeptools/test/test_bigwigCompare_and_multiBigwigSummary.py index 8319242779..076baa2195 100644 --- a/deeptools/test/test_bigwigCompare_and_multiBigwigSummary.py +++ b/deeptools/test/test_bigwigCompare_and_multiBigwigSummary.py @@ -45,7 +45,7 @@ def test_bigwigCompare(): resp = _foo.readlines() _foo.close() expected = ['3R\t0\t50\t0\n', '3R\t50\t100\t1\n', '3R\t100\t150\t2\n', '3R\t150\t200\t3\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -58,7 +58,7 @@ def test_bigwigCompare_skipnas(): resp = _foo.readlines() _foo.close() expected = ['3R\t100\t150\t2\n', '3R\t150\t200\t3\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -70,7 +70,7 @@ def test_bigwigCompare_skipZeroOverZero(): resp = _foo.readlines() _foo.close() expected = ['3R\t100\t200\t-1\n'] - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -105,7 +105,7 @@ def test_multiBigwigSummary_outrawcounts(): 3R 100 150 1.0 1.0 3R 150 200 1.0 2.0 """ - assert resp == expected, "{} != {}".format(resp, expected) + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) unlink("/tmp/null") diff --git a/deeptools/test/test_computeMatrixOperations.py b/deeptools/test/test_computeMatrixOperations.py index 433e25bdd7..d19ceb9d9d 100644 --- a/deeptools/test/test_computeMatrixOperations.py +++ b/deeptools/test/test_computeMatrixOperations.py @@ -32,7 +32,6 @@ def testSubset(self): dCorrect = {"verbose": True, "scale": 1, "skip zeros": False, "nan after end": False, "sort using": "mean", "unscaled 5 prime": [0, 0, 0, 0], "body": [1000, 1000, 1000, 1000], "sample_labels": ["SRR648667.forward", "SRR648668.forward", "SRR648669.forward", "SRR648670.forward"], "downstream": [0, 0, 0, 0], "unscaled 3 prime": [0, 0, 0, 0], "group_labels": ["genes"], "bin size": [10, 10, 10, 10], "upstream": [0, 0, 0, 0], "group_boundaries": [0, 196], "sample_boundaries": [0, 100, 200, 300, 400], "max threshold": None, "ref point": [None, None, None, None], "min threshold": None, "sort regions": "no", "proc number": 20, "bin avg type": "mean", "missing data as zero": False} oname = "/tmp/subset.mat.gz" args = "subset -m {} --sample SRR648667.forward SRR648668.forward SRR648669.forward SRR648670.forward -o {}".format(self.matrix, oname) - print(args) args = args.split() cmo.main(args) f = gzip.GzipFile(oname) @@ -40,7 +39,7 @@ def testSubset(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "edb3c8506c3f27ebb8c7ddf94d5ba594" + assert f"{h}" == f"edb3c8506c3f27ebb8c7ddf94d5ba594" os.remove(oname) def testRelabel(self): @@ -74,7 +73,7 @@ def testfilterStrand(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "300f8000be5b5f51e803b57ef08f1c9e" + assert f"{h}" == f"300f8000be5b5f51e803b57ef08f1c9e" os.remove(oname) dCorrect = {u'verbose': True, u'scale': 1, u'skip zeros': False, u'nan after end': False, u'sort using': u'mean', u'unscaled 5 prime': [0, 0, 0, 0, 0, 0, 0, 0], u'body': [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], u'sample_labels': [u'SRR648667.forward', u'SRR648668.forward', u'SRR648669.forward', u'SRR648670.forward', u'SRR648667.reverse', u'SRR648668.reverse', u'SRR648669.reverse', u'SRR648670.reverse'], u'downstream': [0, 0, 0, 0, 0, 0, 0, 0], u'unscaled 3 prime': [0, 0, 0, 0, 0, 0, 0, 0], u'group_labels': [u'genes'], u'bin size': [10, 10, 10, 10, 10, 10, 10, 10], u'upstream': [0, 0, 0, 0, 0, 0, 0, 0], u'group_boundaries': [0, 89], u'sample_boundaries': [0, 100, 200, 300, 400, 500, 600, 700, 800], u'missing data as zero': False, u'ref point': [None, None, None, None, None, None, None, None], u'min threshold': None, u'sort regions': u'no', u'proc number': 20, u'bin avg type': u'mean', u'max threshold': None} @@ -87,7 +86,7 @@ def testfilterStrand(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "0a6ca070a5ba4564f1ab950ac3b7c8f1" + assert f"{h}" == f"0a6ca070a5ba4564f1ab950ac3b7c8f1" os.remove(oname) def testrbind(self): @@ -104,7 +103,7 @@ def testrbind(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "3dd96c7b05e0ca5ada21212defe57fba" + assert f"{h}" == f"3dd96c7b05e0ca5ada21212defe57fba" os.remove(oname) def testrbind2(self): @@ -121,7 +120,7 @@ def testrbind2(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "5d8b1517fc4c63d000b6b37f70ee163b" + assert f"{h}" == f"5d8b1517fc4c63d000b6b37f70ee163b" os.remove(oname) def testcbind(self): @@ -138,7 +137,7 @@ def testcbind(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "e55d89704bb16a11f366663a8fd90a47" + assert f"{h}" == f"e55d89704bb16a11f366663a8fd90a47" os.remove(oname) def testsort(self): @@ -155,5 +154,5 @@ def testsort(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert h == "10ea07d1aa58f44625abe2142ef76094" + assert f"{h}" == f"10ea07d1aa58f44625abe2142ef76094" os.remove(oname) diff --git a/deeptools/test/test_heatmapper.py b/deeptools/test/test_heatmapper.py index 800d4d7bed..2b14826bfd 100644 --- a/deeptools/test/test_heatmapper.py +++ b/deeptools/test/test_heatmapper.py @@ -142,107 +142,107 @@ def test_computeMatrix_metagene(): def test_chopRegions_body(): region = [(0, 200), (300, 400), (800, 900)] lbins, bodybins, rbins, padLeft, padRight = deeptools.heatmapper.chopRegions(region, left=0, right=0) - assert lbins == [] - assert rbins == [] - assert bodybins == region - assert padLeft == 0 - assert padRight == 0 + assert f"{lbins}" == f"[]" + assert f"{rbins}" == f"[]" + assert f"{bodybins}" == f"{region}" + assert f"{padLeft}" == f"0" + assert f"{padRight}" == f"0" # Unscaled 5', 3' lbins, bodybins, rbins, padLeft, padRight = deeptools.heatmapper.chopRegions(region, left=150, right=150) - assert lbins == [(0, 150)] - assert rbins == [(350, 400), (800, 900)] - assert bodybins == [(150, 200), (300, 350)] - assert padLeft == 0 - assert padRight == 0 + assert f"{lbins}" == f"[(0, 150)]" + assert f"{rbins}" == f"[(350, 400), (800, 900)]" + assert f"{bodybins}" == f"[(150, 200), (300, 350)]" + assert f"{padLeft}" == f"0" + assert f"{padRight}" == f"0" def test_chopRegions_TSS(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 250 downstream downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=250) - assert downstream == [(0, 200), (300, 350)] - assert body == [(350, 400), (800, 900)] - assert unscaled3prime == [] - assert padRight == 0 - assert _ == 0 + assert f"{downstream}" == f"[(0, 200), (300, 350)]" + assert f"{body}" == f"[(350, 400), (800, 900)]" + assert f"{unscaled3prime}" == f"[]" + assert f"{padRight}" == f"0" + assert f"{_}" == f"0" # + strand, 500 downstream downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=500) - assert downstream == region - assert body == [] - assert unscaled3prime == [] - assert padRight == 100 - assert _ == 0 + assert f"{downstream}" == f"{region}" + assert f"{body}" == f"[]" + assert f"{unscaled3prime}" == f"[]" + assert f"{padRight}" == f"100" + assert f"{_}" == f"0" # - strand, 250 downstream (labeled "upstream" due to being on the - strand) unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=250) - assert upstream == [(150, 200), (300, 400), (800, 900)] - assert body == [(0, 150)] - assert unscaled5prime == [] - assert padLeft == 0 - assert _ == 0 + assert f"{upstream}" == f"[(150, 200), (300, 400), (800, 900)]" + assert f"{body}" == f"[(0, 150)]" + assert f"{unscaled5prime}" == f"[]" + assert f"{padLeft}" == f"0" + assert f"{_}" == f"0" # - strand, 500 downstream (labeled "upstream" due to being on the - strand) unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=500) - assert upstream == region - assert body == [] - assert unscaled5prime == [] - assert padLeft == 100 - assert _ == 0 + assert f"{upstream}" == f"{region}" + assert f"{body}" == f"[]" + assert f"{unscaled5prime}" == f"[]" + assert f"{padLeft}" == f"100" + assert f"{_}" == f"0" def test_chopRegions_TES(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 250 upstream unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=250) - assert unscaled5prime == [] - assert body == [(0, 150)] - assert upstream == [(150, 200), (300, 400), (800, 900)] - assert _ == 0 - assert padLeft == 0 + assert f"{unscaled5prime}" == f"[]" + assert f"{body}" == f"[(0, 150)]" + assert f"{upstream}" == f"[(150, 200), (300, 400), (800, 900)]" + assert f"{_}" == f"0" + assert f"{padLeft}" == f"0" # + strand, 500 upstream unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=500) - assert unscaled5prime == [] - assert body == [] - assert upstream == region - assert _ == 0 - assert padLeft == 100 + assert f"{unscaled5prime}" == f"[]" + assert f"{body}" == f"[]" + assert f"{upstream}" == f"{region}" + assert f"{_}" == f"0" + assert f"{padLeft}" == f"100" # + strand, 250 downstream (labeled "upstream" due to being on the - strand) downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=250) - assert downstream == [(0, 200), (300, 350)] - assert body == [(350, 400), (800, 900)] - assert unscaled3prime == [] - assert padRight == 0 - assert _ == 0 + assert f"{downstream}" == f"[(0, 200), (300, 350)]" + assert f"{body}" == f"[(350, 400), (800, 900)]" + assert f"{unscaled3prime}" == f"[]" + assert f"{padRight}" == f"0" + assert f"{_}" == f"0" # + strand, 500 downstream (labeled "upstream" due to being on the - strand) downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=500) - assert downstream == region - assert body == [] - assert unscaled3prime == [] - assert padRight == 100 - assert _ == 0 + assert f"{downstream}" == f"{region}" + assert f"{body}" == f"[]" + assert f"{unscaled3prime}" == f"[]" + assert f"{padRight}" == f"100" + assert f"{_}" == f"0" def test_chopRegionsFromMiddle(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 100 upstream/200 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=100, right=200) - assert upstream == [(100, 200)] - assert downstream == [(300, 400), (800, 900)] - assert padLeft == 0 - assert padRight == 0 + assert f"{upstream}" == f"[(100, 200)]" + assert f"{downstream}" ==f"[(300, 400), (800, 900)]" + assert f"{padLeft}" == f"0" + assert f"{padRight}" == f"0" # + strand, 250 upstream/300 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=250, right=300) - assert upstream == [(0, 200)] - assert downstream == [(300, 400), (800, 900)] - assert padLeft == 50 - assert padRight == 100 + assert f"{upstream}" == f"[(0, 200)]" + assert f"{downstream}" == f"[(300, 400), (800, 900)]" + assert f"{padLeft}" == f"50" + assert f"{padRight}" == f"100" # - strand, 100 upstream/200 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=200, right=100) - assert upstream == [(0, 200)] - assert downstream == [(300, 400)] - assert padLeft == 0 - assert padRight == 0 + assert f"{upstream}" == f"[(0, 200)]" + assert f"{downstream}" == f"[(300, 400)]" + assert f"{padLeft}" == f"0" + assert f"{padRight}" == f"0" # - strand, 250 upstream/300 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=300, right=250) - assert upstream == [(0, 200)] - assert downstream == [(300, 400), (800, 900)] - assert padLeft == 100 - assert padRight == 50 + assert f"{upstream}" == f"[(0, 200)]" + assert f"{downstream}" == f"[(300, 400), (800, 900)]" + assert f"{padLeft}" == f"100" + assert f"{padRight}" == f"50" diff --git a/deeptools/test/test_readFiltering.py b/deeptools/test/test_readFiltering.py index 07f19b32e6..cfce0530f0 100644 --- a/deeptools/test/test_readFiltering.py +++ b/deeptools/test/test_readFiltering.py @@ -29,7 +29,7 @@ def test_estimate_read_filtering_minimal(): _ = resp[1].split("\t") _[0] = os.path.basename(_[0]) resp[1] = "\t".join(_) - assert resp == expected + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -50,7 +50,7 @@ def test_estimate_read_filtering_params(): resp[1] = "\t".join(_) expected = ['Sample\tTotal Reads\tMapped Reads\tAlignments in blacklisted regions\tEstimated mapped reads filtered\tBelow MAPQ\tMissing Flags\tExcluded Flags\tInternally-determined Duplicates\tMarked Duplicates\tSingletons\tWrong strand\n', 'test_filtering.bam\t193\t193\t7\t193\t41.4\t0.0\t186.5\t31.6\t0.0\t0.0\t0.0\n'] - assert resp == expected + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -71,14 +71,14 @@ def test_sieve(): expected = ['#bamFilterReads --filterMetrics\n', '#File\tReads Remaining\tTotal Initial Reads\n', 'test_filtering\t5\t193\n'] - assert resp == expected + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outlog) h = hashlib.md5(pysam.view(outfile).encode('utf-8')).hexdigest() - assert h == "acbc4443fb0387bfd6c412af9d4fc414" + assert f"{h}" == f"acbc4443fb0387bfd6c412af9d4fc414" unlink(outfile) h1 = hashlib.md5(pysam.view(outfiltered).encode('utf-8')).hexdigest() - assert h1 == "b90befdd5f073f14acb9a38661f301ad" + assert f"{h1}" == f"b90befdd5f073f14acb9a38661f301ad" unlink(outfiltered) @@ -120,7 +120,7 @@ def test_sieve_BED(): 'chr2\t5001491\t5001527\n', 'chr2\t5001700\t5001736\n'] - assert resp == expected + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) @@ -160,5 +160,5 @@ def test_sieve_BED_shift(): 'chr2\t5001119\t5001266\n', 'chr2\t5001230\t5001600\n'] - assert resp == expected + assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outfile) diff --git a/deeptools/test/test_writeBedGraph.py b/deeptools/test/test_writeBedGraph.py index bdf0bca176..1a9c6ec4e7 100644 --- a/deeptools/test/test_writeBedGraph.py +++ b/deeptools/test/test_writeBedGraph.py @@ -31,7 +31,8 @@ def test_writeBedGraph_worker(self, bc): _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert res == ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n'] + expected = ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n'] + assert f"{res}" == f"{expected}" os.remove(tempFile[3]) def test_writeBedGraph_worker_zerotonan(self, bc): @@ -42,7 +43,8 @@ def test_writeBedGraph_worker_zerotonan(self, bc): _foo = open(tempFile2[3], 'r') res = _foo.readlines() _foo.close() - assert res == ['3R\t100\t200\t1\n'] + expected = ['3R\t100\t200\t1\n'] + assert f"{res}" == f"{expected}" os.remove(tempFile2[3]) def test_writeBedGraph_worker_scaling(self, bc): @@ -52,7 +54,8 @@ def test_writeBedGraph_worker_scaling(self, bc): _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert res == ['3R\t0\t100\t0\n', '3R\t100\t200\t3\n'] + expected = ['3R\t0\t100\t0\n', '3R\t100\t200\t3\n'] + assert f"{res}" == f"{expected}" os.remove(tempFile[3]) def test_writeBedGraph_worker_ignore_duplicates(self, bc): @@ -69,7 +72,8 @@ def test_writeBedGraph_worker_ignore_duplicates(self, bc): _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert res == ['3R\t50\t200\t1\n'] + expected = ['3R\t50\t200\t1\n'] + assert f"{res}" == f"{expected}" os.remove(tempFile[3]) def test_writeBedGraph_worker_smoothing(self, bc): @@ -81,7 +85,8 @@ def test_writeBedGraph_worker_smoothing(self, bc): _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - assert res == ['3R\t100\t120\t1\n', '3R\t120\t180\t1.33333\n', '3R\t180\t200\t1\n'] + expected = ['3R\t100\t120\t1\n', '3R\t120\t180\t1.33333\n', '3R\t180\t200\t1\n'] + assert f"{res}" == f"{expected}" os.remove(tempFile[3]) def test_writeBedGraph_cigar(self, bc): @@ -100,12 +105,13 @@ def test_writeBedGraph_cigar(self, bc): res = _foo.readlines() _foo.close() - # the sigle read is split into bin 10-30, and then 40-50 - assert res == [ + # the single read is split into bin 10-30, and then 40-50 + expected = [ 'chr_cigar\t0\t10\t0\n', 'chr_cigar\t10\t30\t1\n', 'chr_cigar\t30\t40\t0\n', 'chr_cigar\t40\t50\t1\n', 'chr_cigar\t50\t100\t0\n' ] + assert f"{res}" == f"{expected}" os.remove(tempFile[3]) From 21d1326205412ff7010d83224f96e8a84b933a9e Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 12:37:05 +0200 Subject: [PATCH 72/77] 1 env for test/builds, pip planemo, python min version 3.7, drop allure restriction --- .github/test_and_build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/test_and_build.yml b/.github/test_and_build.yml index a3e6ea06f6..e2dcd08be6 100644 --- a/.github/test_and_build.yml +++ b/.github/test_and_build.yml @@ -9,12 +9,12 @@ dependencies: - pysam - deeptoolsintervals - pytest - - planemo - samtools - #- allure-python-commons==2.12.0 - py2bit - pyBigWig - twine - pip + - tomli # remove dependency when lowest supported version is py 3.11 - pip: - - build \ No newline at end of file + - build + - planemo \ No newline at end of file From c238997e6cda944ded277eb1ab7a2d5ce523a994 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 12:37:39 +0200 Subject: [PATCH 73/77] setup-miniconda runner instead of micromamba for planemo test --- .github/workflows/planemo.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 45ed8c19f8..0677670f6f 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -8,6 +8,10 @@ defaults: run: shell: bash -l {0} +# setup micromamba doesn't work as galaxy setup by planemo requires conda. +# installing conda over into micromamba built env screws up the PATH +# setup-miniconda + changing over to libmamba to solve is the easiest workaround + jobs: planemo_test: name: Planemo test @@ -17,18 +21,24 @@ jobs: chunk: [1, 2, 3] steps: - uses: actions/checkout@v3 - - uses: mamba-org/setup-micromamba@main + - uses: conda-incubator/setup-miniconda@v2 with: - environment-file: .github/test_and_build.yml - cache-downloads: true - environment-name: test_and_build + miniconda-version: "latest" + auto-activate-base: true + channels-priority: strict + - name: setup env + run: | + conda env list + conda install -n base conda-libmamba-solver + conda config --set solver libmamba + conda env create -f .github/test_and_build.yml -n test_and_build - name: pip install run: | - micromamba activate test_and_build + conda activate test_and_build pip install . - name: planemo run: | - micromamba activate test_and_build + conda activate test_and_build ./.planemo.sh ${{ matrix.chunk }} ${{ env.GALAXY_BRANCH }} - uses: actions/upload-artifact@v3 with: From b80278aa04df9ab29eb3abf34aad8ba8a5a28c88 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 12:37:54 +0200 Subject: [PATCH 74/77] print planemo version in shell runner --- .planemo.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.planemo.sh b/.planemo.sh index 8a6fd9785e..0a10b8830a 100755 --- a/.planemo.sh +++ b/.planemo.sh @@ -28,7 +28,8 @@ else galaxy/wrapper/plotProfiler.xml" fi +planemo --version planemo lint ${wrappers} -planemo test --no_dependency_resolution --conda_prefix /home/runner/micromamba/envs --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 +planemo test --no_dependency_resolution --galaxy_branch $2 --install_galaxy ${wrappers} 2>&1 mkdir upload mv tool_test_output* upload/ From 5d00cb57025c9f93820475ce800aa48f6e8d9932 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 14:28:07 +0200 Subject: [PATCH 75/77] flake fixes tests --- .../test/test_computeMatrixOperations.py | 21 +- deeptools/test/test_heatmapper.py | 183 ++++++++++++------ deeptools/test/test_readFiltering.py | 6 +- deeptools/test/test_tools.py | 9 +- deeptools/test/test_writeBedGraph.py | 2 +- 5 files changed, 146 insertions(+), 75 deletions(-) diff --git a/deeptools/test/test_computeMatrixOperations.py b/deeptools/test/test_computeMatrixOperations.py index d19ceb9d9d..c253431d7d 100644 --- a/deeptools/test/test_computeMatrixOperations.py +++ b/deeptools/test/test_computeMatrixOperations.py @@ -39,7 +39,8 @@ def testSubset(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"edb3c8506c3f27ebb8c7ddf94d5ba594" + expectedh = 'edb3c8506c3f27ebb8c7ddf94d5ba594' + assert f'{h}' == f'{expectedh}' os.remove(oname) def testRelabel(self): @@ -73,7 +74,8 @@ def testfilterStrand(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"300f8000be5b5f51e803b57ef08f1c9e" + expectedh = '300f8000be5b5f51e803b57ef08f1c9e' + assert f'{h}' == f'{expectedh}' os.remove(oname) dCorrect = {u'verbose': True, u'scale': 1, u'skip zeros': False, u'nan after end': False, u'sort using': u'mean', u'unscaled 5 prime': [0, 0, 0, 0, 0, 0, 0, 0], u'body': [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], u'sample_labels': [u'SRR648667.forward', u'SRR648668.forward', u'SRR648669.forward', u'SRR648670.forward', u'SRR648667.reverse', u'SRR648668.reverse', u'SRR648669.reverse', u'SRR648670.reverse'], u'downstream': [0, 0, 0, 0, 0, 0, 0, 0], u'unscaled 3 prime': [0, 0, 0, 0, 0, 0, 0, 0], u'group_labels': [u'genes'], u'bin size': [10, 10, 10, 10, 10, 10, 10, 10], u'upstream': [0, 0, 0, 0, 0, 0, 0, 0], u'group_boundaries': [0, 89], u'sample_boundaries': [0, 100, 200, 300, 400, 500, 600, 700, 800], u'missing data as zero': False, u'ref point': [None, None, None, None, None, None, None, None], u'min threshold': None, u'sort regions': u'no', u'proc number': 20, u'bin avg type': u'mean', u'max threshold': None} @@ -86,7 +88,8 @@ def testfilterStrand(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"0a6ca070a5ba4564f1ab950ac3b7c8f1" + expectedh = '0a6ca070a5ba4564f1ab950ac3b7c8f1' + assert f'{h}' == f'{expectedh}' os.remove(oname) def testrbind(self): @@ -103,7 +106,8 @@ def testrbind(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"3dd96c7b05e0ca5ada21212defe57fba" + expectedh = '3dd96c7b05e0ca5ada21212defe57fba' + assert f'{h}' == f'{expectedh}' os.remove(oname) def testrbind2(self): @@ -120,7 +124,8 @@ def testrbind2(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"5d8b1517fc4c63d000b6b37f70ee163b" + expectedh = '5d8b1517fc4c63d000b6b37f70ee163b' + assert f'{h}' == f'{expectedh}' os.remove(oname) def testcbind(self): @@ -137,7 +142,8 @@ def testcbind(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"e55d89704bb16a11f366663a8fd90a47" + expectedh = 'e55d89704bb16a11f366663a8fd90a47' + assert f'{h}' == f'{expectedh}' os.remove(oname) def testsort(self): @@ -154,5 +160,6 @@ def testsort(self): h = hashlib.md5(f.read()).hexdigest() f.close() assert d == dCorrect - assert f"{h}" == f"10ea07d1aa58f44625abe2142ef76094" + expectedh = '10ea07d1aa58f44625abe2142ef76094' + assert f'{h}' == f'{expectedh}' os.remove(oname) diff --git a/deeptools/test/test_heatmapper.py b/deeptools/test/test_heatmapper.py index 2b14826bfd..7eb9d6d78f 100644 --- a/deeptools/test/test_heatmapper.py +++ b/deeptools/test/test_heatmapper.py @@ -142,107 +142,168 @@ def test_computeMatrix_metagene(): def test_chopRegions_body(): region = [(0, 200), (300, 400), (800, 900)] lbins, bodybins, rbins, padLeft, padRight = deeptools.heatmapper.chopRegions(region, left=0, right=0) - assert f"{lbins}" == f"[]" - assert f"{rbins}" == f"[]" + e_lbins = [] + e_rbins = [] + e_padLeft = 0 + e_padRight = 0 + assert f"{lbins}" == f"{e_lbins}" + assert f"{rbins}" == f"{e_rbins}" assert f"{bodybins}" == f"{region}" - assert f"{padLeft}" == f"0" - assert f"{padRight}" == f"0" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" # Unscaled 5', 3' lbins, bodybins, rbins, padLeft, padRight = deeptools.heatmapper.chopRegions(region, left=150, right=150) - assert f"{lbins}" == f"[(0, 150)]" - assert f"{rbins}" == f"[(350, 400), (800, 900)]" - assert f"{bodybins}" == f"[(150, 200), (300, 350)]" - assert f"{padLeft}" == f"0" - assert f"{padRight}" == f"0" + e_lbins = [(0, 150)] + e_rbins = [(350, 400), (800, 900)] + e_bodybins = [(150, 200), (300, 350)] + e_padLeft = 0 + e_padRight = 0 + assert f"{lbins}" == f"{e_lbins}" + assert f"{rbins}" == f"{e_rbins}" + assert f"{bodybins}" == f"{e_bodybins}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" def test_chopRegions_TSS(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 250 downstream downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=250) - assert f"{downstream}" == f"[(0, 200), (300, 350)]" - assert f"{body}" == f"[(350, 400), (800, 900)]" - assert f"{unscaled3prime}" == f"[]" - assert f"{padRight}" == f"0" - assert f"{_}" == f"0" + e_downstream = [(0, 200), (300, 350)] + e_body = [(350, 400), (800, 900)] + e_unscaled3prime = [] + e_padRight = 0 + e_ = 0 + assert f"{downstream}" == f"{e_downstream}" + assert f"{body}" == f"{e_body}" + assert f"{unscaled3prime}" == f"{e_unscaled3prime}" + assert f"{padRight}" == f"{e_padRight}" + assert f"{_}" == f"{e_}" # + strand, 500 downstream downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=500) + e_body = [] + e_unscaled3prime = [] + e_padRight = 100 + e_ = 0 assert f"{downstream}" == f"{region}" - assert f"{body}" == f"[]" - assert f"{unscaled3prime}" == f"[]" - assert f"{padRight}" == f"100" - assert f"{_}" == f"0" + assert f"{body}" == f"{e_body}" + assert f"{unscaled3prime}" == f"{e_unscaled3prime}" + assert f"{padRight}" == f"{e_padRight}" + assert f"{_}" == f"{e_}" # - strand, 250 downstream (labeled "upstream" due to being on the - strand) unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=250) - assert f"{upstream}" == f"[(150, 200), (300, 400), (800, 900)]" - assert f"{body}" == f"[(0, 150)]" - assert f"{unscaled5prime}" == f"[]" - assert f"{padLeft}" == f"0" - assert f"{_}" == f"0" + e_upstream = [(150, 200), (300, 400), (800, 900)] + e_body = [(0, 150)] + e_unscaled5prime = [] + e_padLeft = 0 + e_ = 0 + assert f"{upstream}" == f"{e_upstream}" + assert f"{body}" == f"{e_body}" + assert f"{unscaled5prime}" == f"{e_unscaled5prime}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{_}" == f"{e_}" # - strand, 500 downstream (labeled "upstream" due to being on the - strand) unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=500) + e_body = [] + e_unscaled5prime = [] + e_padLeft = 100 + e_ = 0 assert f"{upstream}" == f"{region}" - assert f"{body}" == f"[]" - assert f"{unscaled5prime}" == f"[]" - assert f"{padLeft}" == f"100" - assert f"{_}" == f"0" + assert f"{body}" == f"{e_body}" + assert f"{unscaled5prime}" == f"{e_unscaled5prime}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{_}" == f"{e_}" def test_chopRegions_TES(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 250 upstream unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=250) - assert f"{unscaled5prime}" == f"[]" - assert f"{body}" == f"[(0, 150)]" - assert f"{upstream}" == f"[(150, 200), (300, 400), (800, 900)]" - assert f"{_}" == f"0" - assert f"{padLeft}" == f"0" + e_unscaled5prime = [] + e_body = [(0, 150)] + e_upstream = [(150, 200), (300, 400), (800, 900)] + e_ = 0 + e_padLeft = 0 + assert f"{unscaled5prime}" == f"{e_unscaled5prime}" + assert f"{body}" == f"{e_body}" + assert f"{upstream}" == f"{e_upstream}" + assert f"{_}" == f"{e_}" + assert f"{padLeft}" == f"{e_padLeft}" # + strand, 500 upstream unscaled5prime, body, upstream, _, padLeft = deeptools.heatmapper.chopRegions(region, right=500) - assert f"{unscaled5prime}" == f"[]" - assert f"{body}" == f"[]" + e_unscaled5prime = [] + e_body = [] + e_ = 0 + e_padLeft = 100 + assert f"{unscaled5prime}" == f"{e_unscaled5prime}" + assert f"{body}" == f"{e_body}" assert f"{upstream}" == f"{region}" - assert f"{_}" == f"0" - assert f"{padLeft}" == f"100" + assert f"{_}" == f"{e_}" + assert f"{padLeft}" == f"{e_padLeft}" # + strand, 250 downstream (labeled "upstream" due to being on the - strand) downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=250) - assert f"{downstream}" == f"[(0, 200), (300, 350)]" - assert f"{body}" == f"[(350, 400), (800, 900)]" - assert f"{unscaled3prime}" == f"[]" - assert f"{padRight}" == f"0" - assert f"{_}" == f"0" + e_downstream = [(0, 200), (300, 350)] + e_body = [(350, 400), (800, 900)] + e_unscaled3prime = [] + e_padRight = 0 + e_ = 0 + assert f"{downstream}" == f"{e_downstream}" + assert f"{body}" == f"{e_body}" + assert f"{unscaled3prime}" == f"{e_unscaled3prime}" + assert f"{padRight}" == f"{e_padRight}" + assert f"{_}" == f"{e_}" # + strand, 500 downstream (labeled "upstream" due to being on the - strand) downstream, body, unscaled3prime, padRight, _ = deeptools.heatmapper.chopRegions(region, left=500) + e_body = [] + e_unscaled3prime = [] + e_padRight = 100 + e_ = 0 assert f"{downstream}" == f"{region}" - assert f"{body}" == f"[]" - assert f"{unscaled3prime}" == f"[]" - assert f"{padRight}" == f"100" - assert f"{_}" == f"0" + assert f"{body}" == f"{e_body}" + assert f"{unscaled3prime}" == f"{e_unscaled3prime}" + assert f"{padRight}" == f"{e_padRight}" + assert f"{_}" == f"{e_}" def test_chopRegionsFromMiddle(): region = [(0, 200), (300, 400), (800, 900)] # + strand, 100 upstream/200 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=100, right=200) - assert f"{upstream}" == f"[(100, 200)]" - assert f"{downstream}" ==f"[(300, 400), (800, 900)]" - assert f"{padLeft}" == f"0" - assert f"{padRight}" == f"0" + e_upstream = [(100, 200)] + e_downstream = [(300, 400), (800, 900)] + e_padLeft = 0 + e_padRight = 0 + assert f"{upstream}" == f"{e_upstream}" + assert f"{downstream}" == f"{e_downstream}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" # + strand, 250 upstream/300 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=250, right=300) - assert f"{upstream}" == f"[(0, 200)]" - assert f"{downstream}" == f"[(300, 400), (800, 900)]" - assert f"{padLeft}" == f"50" - assert f"{padRight}" == f"100" + e_upstream = [(0, 200)] + e_downstream = [(300, 400), (800, 900)] + e_padLeft = 50 + e_padRight = 100 + assert f"{upstream}" == f"{e_upstream}" + assert f"{downstream}" == f"{e_downstream}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" # - strand, 100 upstream/200 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=200, right=100) - assert f"{upstream}" == f"[(0, 200)]" - assert f"{downstream}" == f"[(300, 400)]" - assert f"{padLeft}" == f"0" - assert f"{padRight}" == f"0" + e_upstream = [(0, 200)] + e_downstream = [(300, 400)] + e_padLeft = 0 + e_padRight = 0 + assert f"{upstream}" == f"{e_upstream}" + assert f"{downstream}" == f"{e_downstream}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" # - strand, 250 upstream/300 downstream upstream, downstream, padLeft, padRight = deeptools.heatmapper.chopRegionsFromMiddle(region, left=300, right=250) - assert f"{upstream}" == f"[(0, 200)]" - assert f"{downstream}" == f"[(300, 400), (800, 900)]" - assert f"{padLeft}" == f"100" - assert f"{padRight}" == f"50" + e_upstream = [(0, 200)] + e_downstream = [(300, 400), (800, 900)] + e_padLeft = 100 + e_padRight = 50 + assert f"{upstream}" == f"{e_upstream}" + assert f"{downstream}" == f"{e_downstream}" + assert f"{padLeft}" == f"{e_padLeft}" + assert f"{padRight}" == f"{e_padRight}" diff --git a/deeptools/test/test_readFiltering.py b/deeptools/test/test_readFiltering.py index cfce0530f0..8227530fbd 100644 --- a/deeptools/test/test_readFiltering.py +++ b/deeptools/test/test_readFiltering.py @@ -74,11 +74,13 @@ def test_sieve(): assert f"{resp}" == f"{expected}", f"{resp} != {expected}" unlink(outlog) h = hashlib.md5(pysam.view(outfile).encode('utf-8')).hexdigest() - assert f"{h}" == f"acbc4443fb0387bfd6c412af9d4fc414" + expectedh = 'acbc4443fb0387bfd6c412af9d4fc414' + assert f'{h}' == f'{expectedh}' unlink(outfile) h1 = hashlib.md5(pysam.view(outfiltered).encode('utf-8')).hexdigest() - assert f"{h1}" == f"b90befdd5f073f14acb9a38661f301ad" + expectedh = 'b90befdd5f073f14acb9a38661f301ad' + assert f"{h1}" == f"{expectedh}" unlink(outfiltered) diff --git a/deeptools/test/test_tools.py b/deeptools/test/test_tools.py index e06390e89e..c9b7a80613 100644 --- a/deeptools/test/test_tools.py +++ b/deeptools/test/test_tools.py @@ -18,11 +18,12 @@ def test_tools(): _toml = tomllib.load(f) for _p in _toml['project']['scripts'].keys(): _res = run( - [_p, f"--version"], + [_p, "--version"], stdout=PIPE, stderr=PIPE ) _version = _res.stdout.decode().splitlines()[0] - assert f"{_version}" == f"{_p} {_toml['project']['version']}" - assert f"{_res.returncode}" == f"0" - + e_ver = _p + " " + _toml['project']['version'] + assert f"{_version}" == f"{e_ver}" + e_retc = 0 + assert f"{_res.returncode}" == f"{e_retc}" diff --git a/deeptools/test/test_writeBedGraph.py b/deeptools/test/test_writeBedGraph.py index 1a9c6ec4e7..1ca05e7c8b 100644 --- a/deeptools/test/test_writeBedGraph.py +++ b/deeptools/test/test_writeBedGraph.py @@ -31,7 +31,7 @@ def test_writeBedGraph_worker(self, bc): _foo = open(tempFile[3], 'r') res = _foo.readlines() _foo.close() - expected = ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n'] + expected = ['3R\t0\t100\t0\n', '3R\t100\t200\t1\n'] assert f"{res}" == f"{expected}" os.remove(tempFile[3]) From 04b7a670a7fd29c0b466d96e25a5686d5f5c88d0 Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 14:44:07 +0200 Subject: [PATCH 76/77] drop channelprio / planemo fingerprint back to 13 (again) --- .github/workflows/planemo.yml | 1 - .../wrapper/test-data/plotFingerprint_quality_metrics.tabular | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/planemo.yml b/.github/workflows/planemo.yml index 0677670f6f..bc7e4b0bbf 100644 --- a/.github/workflows/planemo.yml +++ b/.github/workflows/planemo.yml @@ -25,7 +25,6 @@ jobs: with: miniconda-version: "latest" auto-activate-base: true - channels-priority: strict - name: setup env run: | conda env list diff --git a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular index f2e1608825..84d5d720e8 100644 --- a/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular +++ b/galaxy/wrapper/test-data/plotFingerprint_quality_metrics.tabular @@ -1,3 +1,3 @@ Sample AUC Synthetic AUC X-intercept Synthetic X-intercept Elbow Point Synthetic Elbow Point JS Distance Synthetic JS Distance % genome enriched diff. enrichment CHANCE divergence -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681214 nan nan nan -bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681214 0 0 0 +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 nan 0.2690044980681213 nan nan nan +bowtie2 test1.bam 0.00493632029863651 0.481650684757865 0.984443061605476 1.1531044350267195e-24 0.9849408836341008 0.5232688298112538 0.0 0.2690044980681213 0 0 0 From ba71cbe6c98d833f56d4f7cdfd957ffeab56319e Mon Sep 17 00:00:00 2001 From: WardDeb Date: Fri, 25 Aug 2023 15:19:26 +0200 Subject: [PATCH 77/77] update changelog --- CHANGES.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index d17ab90f2b..335dbc80bb 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +3.5.3 +* requirement cap for matplotlib lifted (changes in plotting can occur) +* nose has been deprecated in favor of pytests +* pytests run with python 3.7 - 3.11 +* toml file for installation, requirements, versioning and executables +* planemo tests updated to galaxy 23.1 +* custom github action runner deprecated +* deprecation of np types for builtin types +* stricter label checks and validator in galaxy + 3.5.2 * new subcommand: Bigwig average #1169 * dendogram of plotCorrelation now matches each cell correctly