From 03fd66678a6c0e35c9920a00d9ef3d6f56422737 Mon Sep 17 00:00:00 2001 From: tdayris Date: Mon, 23 Mar 2026 11:32:08 +0100 Subject: [PATCH 1/7] feat: bcftools fixploidy wrapper --- .../fixploidy/environment.linux-64.pin.txt | 51 +++++++++++++++++++ bio/bcftools/fixploidy/environment.yaml | 7 +++ bio/bcftools/fixploidy/meta.yaml | 14 +++++ bio/bcftools/fixploidy/test/Snakefile | 12 +++++ bio/bcftools/fixploidy/test/a.bcf | 1 + bio/bcftools/fixploidy/wrapper.py | 45 ++++++++++++++++ test_wrappers.py | 6 +++ 7 files changed, 136 insertions(+) create mode 100644 bio/bcftools/fixploidy/environment.linux-64.pin.txt create mode 100644 bio/bcftools/fixploidy/environment.yaml create mode 100644 bio/bcftools/fixploidy/meta.yaml create mode 100644 bio/bcftools/fixploidy/test/Snakefile create mode 120000 bio/bcftools/fixploidy/test/a.bcf create mode 100644 bio/bcftools/fixploidy/wrapper.py diff --git a/bio/bcftools/fixploidy/environment.linux-64.pin.txt b/bio/bcftools/fixploidy/environment.linux-64.pin.txt new file mode 100644 index 00000000000..b627b75caf2 --- /dev/null +++ b/bio/bcftools/fixploidy/environment.linux-64.pin.txt @@ -0,0 +1,51 @@ +# This file may be used to create an environment using: +# $ conda create --name --file +# platform: linux-64 +# created-by: conda 24.9.2 +@EXPLICIT +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda#f0991f0f84902f6b6009b4d2350a83aa +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda#26c46f90d0e727e95c6c9498a33a09f3 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.conda#338201218b54cadff2e774ac27733990 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda#6d0363467e6ed84f11435eb309f2ff06 +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda#920bb03579f15389b9e512095ad995b7 +https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda#8b09ae86839581147ef2e5c5e229d164 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda#5a68259fac2da8f2ee6f7bfe49c9eb8b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda#39183d4e0c05609fd65f130633194e37 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda#68f68355000ec3f1d6f26ea13e8f525f +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-h5347b49_1.conda#41f5c09a211985c3ce642d60721e7c3e +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda#9ee58d5c534af06558933af3c845a780 +https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b +https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda#40d9b534410403c821ff64f00d0adc22 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_0.conda#2e1b84d273b01835256e53fd938de355 +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda#1b3152694d236cf233b76b8c56bf0eae +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc +https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda#d7d95fc8287ea7bf33e0e7116d2b95ec +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda#86bc20552bf46075e3d92b67f089172d +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13eeac0b5c8e5b8ab496e6c4ddd829 +https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda#a6abd2796fc332536735f68ba23f7901 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda#b499ce4b026493a13774bcf0f4c33849 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda#be43915efc66345cccb3c310b6ed0374 +https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda#f2cfec9406850991f4e3d960cc9e3321 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda#c160954f7418d7b6e87eaf05a8913fa9 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.conda#117499f93e892ea1e57fdca16c2e8351 +https://conda.anaconda.org/conda-forge/linux-64/python-3.14.2-h32b2ec7_100_cp314.conda#1cef1236a05c3a98f68c33ae9425f656 +https://conda.anaconda.org/bioconda/linux-64/htslib-1.23-h566b1c6_0.conda#307124911d36a3d976cd76f350085ead +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda#6636a2b6f1a87572df2970d3ebc87cc0 +https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh145f28c_0.conda#bf47878473e5ab9fdb4115735230e191 +https://conda.anaconda.org/bioconda/noarch/snakemake-wrapper-utils-0.8.0-pyhdfd78af_0.conda#1650e521333852f45468d97b1b2fdcce +https://conda.anaconda.org/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2#fec079ba39c9cca093bf4c00001825de +https://conda.anaconda.org/bioconda/linux-64/bcftools-1.23-h3a4d415_0.conda#51a78d8b2f5a3d373ce369803b5e76e3 diff --git a/bio/bcftools/fixploidy/environment.yaml b/bio/bcftools/fixploidy/environment.yaml new file mode 100644 index 00000000000..b9715446dd5 --- /dev/null +++ b/bio/bcftools/fixploidy/environment.yaml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda + - nodefaults +dependencies: + - bcftools =1.23 + - snakemake-wrapper-utils =0.8.0 diff --git a/bio/bcftools/fixploidy/meta.yaml b/bio/bcftools/fixploidy/meta.yaml new file mode 100644 index 00000000000..bbf342d6d24 --- /dev/null +++ b/bio/bcftools/fixploidy/meta.yaml @@ -0,0 +1,14 @@ +name: bcftools fixploidy +url: https://samtools.github.io/bcftools/bcftools.html +author: + - Thibault Dayris +description: | + Set correct ploidy on variants calls +input: + - call: Path to variants file (VCF/VCF.GZ/BCF) +output: + - call: Path to corrected variants file (VCF/BCF/VCF.GZ) +params: + - extra: Optional parameters besides IO and their formats. +notes: | + This wrapper requires additional threads for IO compression. diff --git a/bio/bcftools/fixploidy/test/Snakefile b/bio/bcftools/fixploidy/test/Snakefile new file mode 100644 index 00000000000..9afed3c8106 --- /dev/null +++ b/bio/bcftools/fixploidy/test/Snakefile @@ -0,0 +1,12 @@ +rule test_bcftools_fixploidy: + input: + call="a.bcf", + output: + call="a.fixed.bcf", + threads: 3 + params: + extra="", + log: + "test_bcftools_fixploidy.log", + wrapper: + "master/bio/bcftools/fixploidy" diff --git a/bio/bcftools/fixploidy/test/a.bcf b/bio/bcftools/fixploidy/test/a.bcf new file mode 120000 index 00000000000..83fe2b29e9f --- /dev/null +++ b/bio/bcftools/fixploidy/test/a.bcf @@ -0,0 +1 @@ +../../filter/test/a.bcf \ No newline at end of file diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py new file mode 100644 index 00000000000..f4f31bbbbdf --- /dev/null +++ b/bio/bcftools/fixploidy/wrapper.py @@ -0,0 +1,45 @@ +__author__ = "Thibault Dayris" +__copyright__ = "Copyright 2026, Thibault Dayris" +__email__ = "thibault.dayris@gustaveroussy.fr" +__license__ = "MIT" + + +from snakemake.shell import shell +from snakemake_wrapper_utils.bcftools import get_bcftools_opts + + +bcftools_opts = get_bcftools_opts(snakemake, parse_ref=False, parse_memory=False) +extra = snakemake.params.get("extra", "") +log = snakemake.log_fmt_shell(stdout=True, stderr=True) +min_threads = 1 + + +# Uncompression shall be done according to user-defined input +incall = snakemake.input["call"] +if snakemake.input["call"].endswith("bcf"): + min_threads += 1 + incall = "< <(bcftools view {})".format(incall) +elif snakemake.input["call"].endswith("gz"): + min_threads += 1 + incall = "< <(gunzip -c {})".format(incall) + +# Compression shall be done according to user-defined output +outcall = snakemake.output["call"] +if snakemake.output["call"].endswith("gz"): + min_threads += 1 + outcall = "| gzip -c > {}".format(outcall) +elif snakemake.output["call"].endswith("bcf"): + min_threads += 1 + outcall = "| bcftools view > {}".format(outcall) +else: + outcall = "> {}".format(outcall) + +# Each (un)compression step raises the threads requirements +if snakemake.threads < min_threads: + raise ValueError( + "At least {} threads required, {} provided".format( + min_threads, snakemake.threads + ) + ) + +shell("(bcftools plugin fixploidy {extra} {incall} {outcall}) {log}") diff --git a/test_wrappers.py b/test_wrappers.py index 78be44defb7..88a63dfaaed 100644 --- a/test_wrappers.py +++ b/test_wrappers.py @@ -2078,6 +2078,12 @@ def test_bcftools_filter_sample(run): ["snakemake", "--cores", "1", "a.filter_sample.vcf", "--use-conda", "-F"], ) +def test_bcftools_fixploidy(run): + run( + "bio/bcftools/fixploidy", + ["snakemake", "--cores", "3", "a.fixed.bcf", "--use-conda", "-F"], + ) + def test_bcftools_filter_vcf(run): run( From cd26351d3a18f8a537e6dd64b48cb3a5aa7b8fe3 Mon Sep 17 00:00:00 2001 From: tdayris Date: Mon, 23 Mar 2026 12:00:11 +0100 Subject: [PATCH 2/7] bccftools opts passed --- bio/bcftools/fixploidy/wrapper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index f4f31bbbbdf..7ff66dd8e12 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -8,7 +8,11 @@ from snakemake_wrapper_utils.bcftools import get_bcftools_opts -bcftools_opts = get_bcftools_opts(snakemake, parse_ref=False, parse_memory=False) +bcftools_opts = get_bcftools_opts( + snakemake, + parse_ref=False, # No use form + parse_memory=False, +) extra = snakemake.params.get("extra", "") log = snakemake.log_fmt_shell(stdout=True, stderr=True) min_threads = 1 @@ -30,7 +34,7 @@ outcall = "| gzip -c > {}".format(outcall) elif snakemake.output["call"].endswith("bcf"): min_threads += 1 - outcall = "| bcftools view > {}".format(outcall) + outcall = "| bcftools view {} > {}".format(bcftools_opts, outcall) else: outcall = "> {}".format(outcall) From 85f00cce1e4553a9eaeb865d10d33212247163b5 Mon Sep 17 00:00:00 2001 From: tdayris Date: Mon, 23 Mar 2026 12:03:49 +0100 Subject: [PATCH 3/7] format --- bio/bcftools/fixploidy/wrapper.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index 7ff66dd8e12..1a7aa7fd4dd 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -8,11 +8,7 @@ from snakemake_wrapper_utils.bcftools import get_bcftools_opts -bcftools_opts = get_bcftools_opts( - snakemake, - parse_ref=False, # No use form - parse_memory=False, -) +bcftools_opts = get_bcftools_opts(snakemake, parse_ref=False, parse_memory=False) extra = snakemake.params.get("extra", "") log = snakemake.log_fmt_shell(stdout=True, stderr=True) min_threads = 1 From 3a4e82f5fbea7aa4e77800bc0ce1697ffa8f34d6 Mon Sep 17 00:00:00 2001 From: tdayris Date: Mon, 23 Mar 2026 13:27:35 +0100 Subject: [PATCH 4/7] requested changes --- bio/bcftools/fixploidy/meta.yaml | 4 ++-- bio/bcftools/fixploidy/test/Snakefile | 4 ++-- bio/bcftools/fixploidy/wrapper.py | 14 ++++---------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/bio/bcftools/fixploidy/meta.yaml b/bio/bcftools/fixploidy/meta.yaml index bbf342d6d24..9cf13d92922 100644 --- a/bio/bcftools/fixploidy/meta.yaml +++ b/bio/bcftools/fixploidy/meta.yaml @@ -5,9 +5,9 @@ author: description: | Set correct ploidy on variants calls input: - - call: Path to variants file (VCF/VCF.GZ/BCF) + - Path to variants file (VCF/VCF.GZ/BCF) output: - - call: Path to corrected variants file (VCF/BCF/VCF.GZ) + - Path to corrected variants file (VCF/BCF/VCF.GZ) params: - extra: Optional parameters besides IO and their formats. notes: | diff --git a/bio/bcftools/fixploidy/test/Snakefile b/bio/bcftools/fixploidy/test/Snakefile index 9afed3c8106..fcdd0a7db2e 100644 --- a/bio/bcftools/fixploidy/test/Snakefile +++ b/bio/bcftools/fixploidy/test/Snakefile @@ -1,8 +1,8 @@ rule test_bcftools_fixploidy: input: - call="a.bcf", + "a.bcf", output: - call="a.fixed.bcf", + "a.fixed.bcf", threads: 3 params: extra="", diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index 1a7aa7fd4dd..80965005825 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -15,20 +15,14 @@ # Uncompression shall be done according to user-defined input -incall = snakemake.input["call"] -if snakemake.input["call"].endswith("bcf"): +incall = snakemake.input[0] +if snakemake.input["call"].endswith(("gz", "bcf")): min_threads += 1 incall = "< <(bcftools view {})".format(incall) -elif snakemake.input["call"].endswith("gz"): - min_threads += 1 - incall = "< <(gunzip -c {})".format(incall) # Compression shall be done according to user-defined output -outcall = snakemake.output["call"] -if snakemake.output["call"].endswith("gz"): - min_threads += 1 - outcall = "| gzip -c > {}".format(outcall) -elif snakemake.output["call"].endswith("bcf"): +outcall = snakemake.output[0] +if snakemake.output["call"].endswith(("gz", "bcf")): min_threads += 1 outcall = "| bcftools view {} > {}".format(bcftools_opts, outcall) else: From e1dd34d2501e71f4fed8f3a0088cc5929b750d07 Mon Sep 17 00:00:00 2001 From: tdayris Date: Mon, 23 Mar 2026 13:39:57 +0100 Subject: [PATCH 5/7] fix key error --- bio/bcftools/fixploidy/wrapper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index 80965005825..99f485e1bfe 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -16,13 +16,13 @@ # Uncompression shall be done according to user-defined input incall = snakemake.input[0] -if snakemake.input["call"].endswith(("gz", "bcf")): +if snakemake.input[0].endswith(("gz", "bcf")): min_threads += 1 incall = "< <(bcftools view {})".format(incall) # Compression shall be done according to user-defined output outcall = snakemake.output[0] -if snakemake.output["call"].endswith(("gz", "bcf")): +if snakemake.output[0].endswith(("gz", "bcf")): min_threads += 1 outcall = "| bcftools view {} > {}".format(bcftools_opts, outcall) else: From 26bc07b26e0649913eda24695ab17c5fdf728866 Mon Sep 17 00:00:00 2001 From: tdayris Date: Fri, 27 Mar 2026 13:37:32 +0100 Subject: [PATCH 6/7] Update bio/bcftools/fixploidy/wrapper.py Co-authored-by: Filipe G. Vieira <1151762+fgvieira@users.noreply.github.com> --- bio/bcftools/fixploidy/wrapper.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index 99f485e1bfe..110d11636c1 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -11,29 +11,5 @@ bcftools_opts = get_bcftools_opts(snakemake, parse_ref=False, parse_memory=False) extra = snakemake.params.get("extra", "") log = snakemake.log_fmt_shell(stdout=True, stderr=True) -min_threads = 1 - -# Uncompression shall be done according to user-defined input -incall = snakemake.input[0] -if snakemake.input[0].endswith(("gz", "bcf")): - min_threads += 1 - incall = "< <(bcftools view {})".format(incall) - -# Compression shall be done according to user-defined output -outcall = snakemake.output[0] -if snakemake.output[0].endswith(("gz", "bcf")): - min_threads += 1 - outcall = "| bcftools view {} > {}".format(bcftools_opts, outcall) -else: - outcall = "> {}".format(outcall) - -# Each (un)compression step raises the threads requirements -if snakemake.threads < min_threads: - raise ValueError( - "At least {} threads required, {} provided".format( - min_threads, snakemake.threads - ) - ) - -shell("(bcftools plugin fixploidy {extra} {incall} {outcall}) {log}") +shell("bcftools plugin fixploidy {bcftools_opts} {extra} {snakemake.input[0]} {log}") From d952415db2c3f4f029aaf1991b0ef9c852d377e9 Mon Sep 17 00:00:00 2001 From: "Filipe G. Vieira" <1151762+fgvieira@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:33:32 +0100 Subject: [PATCH 7/7] Code format --- bio/bcftools/fixploidy/wrapper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bio/bcftools/fixploidy/wrapper.py b/bio/bcftools/fixploidy/wrapper.py index 110d11636c1..41e42b797b5 100644 --- a/bio/bcftools/fixploidy/wrapper.py +++ b/bio/bcftools/fixploidy/wrapper.py @@ -7,7 +7,6 @@ from snakemake.shell import shell from snakemake_wrapper_utils.bcftools import get_bcftools_opts - bcftools_opts = get_bcftools_opts(snakemake, parse_ref=False, parse_memory=False) extra = snakemake.params.get("extra", "") log = snakemake.log_fmt_shell(stdout=True, stderr=True)