viralunity 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- viralunity-1.2.0/LICENSE +21 -0
- viralunity-1.2.0/MANIFEST.in +4 -0
- viralunity-1.2.0/PKG-INFO +121 -0
- viralunity-1.2.0/README.md +64 -0
- viralunity-1.2.0/pyproject.toml +108 -0
- viralunity-1.2.0/setup.cfg +9 -0
- viralunity-1.2.0/viralunity/__init__.py +4 -0
- viralunity-1.2.0/viralunity/_orchestrator.py +135 -0
- viralunity-1.2.0/viralunity/_subprocess.py +52 -0
- viralunity-1.2.0/viralunity/config_generator.py +587 -0
- viralunity-1.2.0/viralunity/constants.py +151 -0
- viralunity-1.2.0/viralunity/exceptions.py +100 -0
- viralunity-1.2.0/viralunity/logging_config.py +101 -0
- viralunity-1.2.0/viralunity/provenance.py +102 -0
- viralunity-1.2.0/viralunity/scripts/__init__.py +0 -0
- viralunity-1.2.0/viralunity/scripts/consensus_illumina.smk +142 -0
- viralunity-1.2.0/viralunity/scripts/consensus_illumina_segmented.smk +203 -0
- viralunity-1.2.0/viralunity/scripts/consensus_nanopore.smk +128 -0
- viralunity-1.2.0/viralunity/scripts/consensus_nanopore_segmented.smk +172 -0
- viralunity-1.2.0/viralunity/scripts/envs/alignment.yaml +11 -0
- viralunity-1.2.0/viralunity/scripts/envs/assembly.yaml +6 -0
- viralunity-1.2.0/viralunity/scripts/envs/clair3.yaml +7 -0
- viralunity-1.2.0/viralunity/scripts/envs/consensus.yaml +10 -0
- viralunity-1.2.0/viralunity/scripts/envs/genome_selection.yaml +8 -0
- viralunity-1.2.0/viralunity/scripts/envs/medaka.yaml +6 -0
- viralunity-1.2.0/viralunity/scripts/envs/qc.yaml +8 -0
- viralunity-1.2.0/viralunity/scripts/envs/taxonomy.yaml +8 -0
- viralunity-1.2.0/viralunity/scripts/envs/utils.yaml +10 -0
- viralunity-1.2.0/viralunity/scripts/metagenomics_illumina.smk +279 -0
- viralunity-1.2.0/viralunity/scripts/metagenomics_nanopore.smk +278 -0
- viralunity-1.2.0/viralunity/scripts/python/__init__.py +0 -0
- viralunity-1.2.0/viralunity/scripts/python/add_RPM_to_summary.py +143 -0
- viralunity-1.2.0/viralunity/scripts/python/add_negative_control_enrichment.py +418 -0
- viralunity-1.2.0/viralunity/scripts/python/add_rpkm_to_summary.py +125 -0
- viralunity-1.2.0/viralunity/scripts/python/annotate_diamond_taxonomy.py +90 -0
- viralunity-1.2.0/viralunity/scripts/python/apply_max_rpm_bleed_filter.py +172 -0
- viralunity-1.2.0/viralunity/scripts/python/build_genome_length_table.py +197 -0
- viralunity-1.2.0/viralunity/scripts/python/calculate_assembly_stats.py +151 -0
- viralunity-1.2.0/viralunity/scripts/python/convert_diamond_output_to_krona_input.py +142 -0
- viralunity-1.2.0/viralunity/scripts/python/filter_diamond_by_idxstats.py +131 -0
- viralunity-1.2.0/viralunity/scripts/python/filter_krona_by_pass_taxids.py +224 -0
- viralunity-1.2.0/viralunity/scripts/python/filter_taxids.py +82 -0
- viralunity-1.2.0/viralunity/scripts/python/rename_sequences.py +67 -0
- viralunity-1.2.0/viralunity/scripts/python/select_reference_genomes.py +470 -0
- viralunity-1.2.0/viralunity/scripts/python/summarize_krona_taxa.py +163 -0
- viralunity-1.2.0/viralunity/scripts/python/taxonomy.py +77 -0
- viralunity-1.2.0/viralunity/scripts/rules/__init__.py +0 -0
- viralunity-1.2.0/viralunity/scripts/rules/alignment_illumina.smk +73 -0
- viralunity-1.2.0/viralunity/scripts/rules/alignment_nanopore.smk +66 -0
- viralunity-1.2.0/viralunity/scripts/rules/consensus_illumina.smk +105 -0
- viralunity-1.2.0/viralunity/scripts/rules/consensus_illumina_common.smk +86 -0
- viralunity-1.2.0/viralunity/scripts/rules/consensus_nanopore.smk +67 -0
- viralunity-1.2.0/viralunity/scripts/rules/consensus_nanopore_common.smk +57 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_assembly_illumina.smk +39 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_assembly_nanopore.smk +109 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_dehost_illumina.smk +130 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_dehost_nanopore.smk +97 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_diamond_contigs_illumina.smk +338 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_diamond_contigs_nanopore.smk +379 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_diamond_reads_illumina.smk +226 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_diamond_reads_nanopore.smk +226 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_genome_lengths.smk +37 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_kraken2_contigs_illumina.smk +210 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_kraken2_contigs_nanopore.smk +210 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_kraken2_reads_illumina.smk +214 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_kraken2_reads_nanopore.smk +213 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_multiqc_illumina.smk +16 -0
- viralunity-1.2.0/viralunity/scripts/rules/metagenomics_reference_assembly.smk +151 -0
- viralunity-1.2.0/viralunity/scripts/rules/qc_illumina.smk +65 -0
- viralunity-1.2.0/viralunity/scripts/rules/stats.smk +28 -0
- viralunity-1.2.0/viralunity/validators.py +614 -0
- viralunity-1.2.0/viralunity/viralunity_build_deacon_index_cli.py +60 -0
- viralunity-1.2.0/viralunity/viralunity_cli.py +60 -0
- viralunity-1.2.0/viralunity/viralunity_consensus.py +160 -0
- viralunity-1.2.0/viralunity/viralunity_consensus_cli.py +423 -0
- viralunity-1.2.0/viralunity/viralunity_create_samplesheet.py +277 -0
- viralunity-1.2.0/viralunity/viralunity_get_databases_cli.py +862 -0
- viralunity-1.2.0/viralunity/viralunity_meta.py +200 -0
- viralunity-1.2.0/viralunity/viralunity_meta_cli.py +469 -0
- viralunity-1.2.0/viralunity/viralunity_setup_cli.py +212 -0
- viralunity-1.2.0/viralunity.egg-info/PKG-INFO +121 -0
- viralunity-1.2.0/viralunity.egg-info/SOURCES.txt +85 -0
- viralunity-1.2.0/viralunity.egg-info/dependency_links.txt +1 -0
- viralunity-1.2.0/viralunity.egg-info/entry_points.txt +2 -0
- viralunity-1.2.0/viralunity.egg-info/requires.txt +13 -0
- viralunity-1.2.0/viralunity.egg-info/top_level.txt +1 -0
viralunity-1.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-2026 filiperomero2
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: viralunity
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: A pipeline for viral metagenomics analysis.
|
|
5
|
+
Author: Felippe Nacif
|
|
6
|
+
Author-email: Filipe Moreira <filiperomero2@gmail.com>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2021-2026 filiperomero2
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
|
|
29
|
+
Project-URL: Homepage, https://github.com/InstitutoTodosPelaSaude/ViralUnity
|
|
30
|
+
Project-URL: Documentation, https://viralunity.readthedocs.io/en/latest/
|
|
31
|
+
Project-URL: Changelog, https://github.com/InstitutoTodosPelaSaude/ViralUnity/blob/main/CHANGELOG.md
|
|
32
|
+
Project-URL: Issues, https://github.com/InstitutoTodosPelaSaude/ViralUnity/issues
|
|
33
|
+
Keywords: metagenomics,viral,high-throughput sequencing,bioinformatics,viral genome assembly
|
|
34
|
+
Classifier: Development Status :: 4 - Beta
|
|
35
|
+
Classifier: Intended Audience :: Science/Research
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: POSIX
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
40
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
41
|
+
Requires-Python: <3.12,>=3.10
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
License-File: LICENSE
|
|
44
|
+
Requires-Dist: pyyaml>=6.0
|
|
45
|
+
Requires-Dist: click>=8.0
|
|
46
|
+
Requires-Dist: biopython>=1.81
|
|
47
|
+
Requires-Dist: snakemake<8,>=7.32
|
|
48
|
+
Requires-Dist: pandas>=1.5
|
|
49
|
+
Requires-Dist: pulp<2.8
|
|
50
|
+
Provides-Extra: dev
|
|
51
|
+
Requires-Dist: black<27,>=26; extra == "dev"
|
|
52
|
+
Requires-Dist: pytest>=9.0; extra == "dev"
|
|
53
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
54
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
55
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
# ViralUnity
|
|
59
|
+
|
|
60
|
+
ViralUnity is a tool for analysing viral high-throughput sequencing data. It is a Python package that orchestrates Snakemake workflows for data quality control, taxonomic assignment, and reference genome assembly. ViralUnity runs on *nix systems and can process entire sequencing runs in minimal time on a regular computer.
|
|
61
|
+
|
|
62
|
+
> **Full documentation:** <https://viralunity.readthedocs.io/en/latest/>
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Install the ViralUnity CLI from PyPI:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install viralunity
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **conda/mamba is still required at runtime.** ViralUnity orchestrates Snakemake, which
|
|
73
|
+
> builds the per-rule tool environments (aligners, classifiers, assemblers) via
|
|
74
|
+
> `--use-conda` on first run. Make sure conda or mamba is installed and on your `PATH`;
|
|
75
|
+
> you can pre-build those environments up front with `viralunity setup --pipelines all`.
|
|
76
|
+
|
|
77
|
+
To install from source for development instead:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/InstitutoTodosPelaSaude/ViralUnity.git
|
|
81
|
+
cd ViralUnity
|
|
82
|
+
conda env create -n viralunity -f environment.yml
|
|
83
|
+
conda activate viralunity
|
|
84
|
+
pip install -e .
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Per-rule conda environments under `viralunity/scripts/envs/` are managed automatically by Snakemake; the top-level `environment.yml` only installs ViralUnity itself and its core runtime dependencies.
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
Six top-level subcommands are exposed via the `viralunity` CLI:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
viralunity create-samplesheet --input <runs-dir> --output samples.csv
|
|
95
|
+
viralunity get-databases all --output databases/
|
|
96
|
+
viralunity setup --pipelines all # pre-build per-rule conda envs
|
|
97
|
+
viralunity consensus illumina --sample-sheet samples.csv --reference ref.fasta --output run/
|
|
98
|
+
viralunity meta illumina --sample-sheet samples.csv --kraken2-database <db> --output run/
|
|
99
|
+
viralunity build-deacon-index --input host.fasta --output host.dcn
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Global options: `--log-level {DEBUG,INFO,WARNING,ERROR}` and `--json-logs`
|
|
103
|
+
(e.g. `viralunity --log-level DEBUG meta ...`).
|
|
104
|
+
|
|
105
|
+
Each subcommand has its own `--help`; the same information is exhaustively documented in the `docs/` Sphinx site (rendered on ReadTheDocs at the link above).
|
|
106
|
+
|
|
107
|
+
## Tests
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
make test
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This installs the package in editable mode (if not already installed) and runs the `unittest` suite under `test/`. Snakemake dry-run tests live in `test/viralunity_dryrun_test.py` and use `pytest`.
|
|
114
|
+
|
|
115
|
+
## Citation
|
|
116
|
+
|
|
117
|
+
A scientific publication describing this pipeline is being prepared. Meanwhile, please cite this repository. Primary references for upstream tools (fastp, MultiQC, Minimap2, Samtools, BCFtools, BEDtools, gofasta, MEGAHIT, Racon, BLAST, Kraken2, Krona, DIAMOND, Clair3, Medaka, Deacon) are listed in the ReadTheDocs site.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT — see `LICENSE`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ViralUnity
|
|
2
|
+
|
|
3
|
+
ViralUnity is a tool for analysing viral high-throughput sequencing data. It is a Python package that orchestrates Snakemake workflows for data quality control, taxonomic assignment, and reference genome assembly. ViralUnity runs on *nix systems and can process entire sequencing runs in minimal time on a regular computer.
|
|
4
|
+
|
|
5
|
+
> **Full documentation:** <https://viralunity.readthedocs.io/en/latest/>
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install the ViralUnity CLI from PyPI:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install viralunity
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **conda/mamba is still required at runtime.** ViralUnity orchestrates Snakemake, which
|
|
16
|
+
> builds the per-rule tool environments (aligners, classifiers, assemblers) via
|
|
17
|
+
> `--use-conda` on first run. Make sure conda or mamba is installed and on your `PATH`;
|
|
18
|
+
> you can pre-build those environments up front with `viralunity setup --pipelines all`.
|
|
19
|
+
|
|
20
|
+
To install from source for development instead:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/InstitutoTodosPelaSaude/ViralUnity.git
|
|
24
|
+
cd ViralUnity
|
|
25
|
+
conda env create -n viralunity -f environment.yml
|
|
26
|
+
conda activate viralunity
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Per-rule conda environments under `viralunity/scripts/envs/` are managed automatically by Snakemake; the top-level `environment.yml` only installs ViralUnity itself and its core runtime dependencies.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
Six top-level subcommands are exposed via the `viralunity` CLI:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
viralunity create-samplesheet --input <runs-dir> --output samples.csv
|
|
38
|
+
viralunity get-databases all --output databases/
|
|
39
|
+
viralunity setup --pipelines all # pre-build per-rule conda envs
|
|
40
|
+
viralunity consensus illumina --sample-sheet samples.csv --reference ref.fasta --output run/
|
|
41
|
+
viralunity meta illumina --sample-sheet samples.csv --kraken2-database <db> --output run/
|
|
42
|
+
viralunity build-deacon-index --input host.fasta --output host.dcn
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Global options: `--log-level {DEBUG,INFO,WARNING,ERROR}` and `--json-logs`
|
|
46
|
+
(e.g. `viralunity --log-level DEBUG meta ...`).
|
|
47
|
+
|
|
48
|
+
Each subcommand has its own `--help`; the same information is exhaustively documented in the `docs/` Sphinx site (rendered on ReadTheDocs at the link above).
|
|
49
|
+
|
|
50
|
+
## Tests
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
make test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This installs the package in editable mode (if not already installed) and runs the `unittest` suite under `test/`. Snakemake dry-run tests live in `test/viralunity_dryrun_test.py` and use `pytest`.
|
|
57
|
+
|
|
58
|
+
## Citation
|
|
59
|
+
|
|
60
|
+
A scientific publication describing this pipeline is being prepared. Meanwhile, please cite this repository. Primary references for upstream tools (fastp, MultiQC, Minimap2, Samtools, BCFtools, BEDtools, gofasta, MEGAHIT, Racon, BLAST, Kraken2, Krona, DIAMOND, Clair3, Medaka, Deacon) are listed in the ReadTheDocs site.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT — see `LICENSE`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "viralunity"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "A pipeline for viral metagenomics analysis."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Filipe Moreira", email = "filiperomero2@gmail.com" },
|
|
12
|
+
{ name = "Felippe Nacif" },
|
|
13
|
+
]
|
|
14
|
+
license = { file = "LICENSE" }
|
|
15
|
+
requires-python = ">=3.10,<3.12"
|
|
16
|
+
keywords = [
|
|
17
|
+
"metagenomics",
|
|
18
|
+
"viral",
|
|
19
|
+
"high-throughput sequencing",
|
|
20
|
+
"bioinformatics",
|
|
21
|
+
"viral genome assembly",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
|
+
"Intended Audience :: Science/Research",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: POSIX",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
31
|
+
]
|
|
32
|
+
dependencies = [
|
|
33
|
+
"pyyaml>=6.0",
|
|
34
|
+
"click>=8.0",
|
|
35
|
+
"biopython>=1.81",
|
|
36
|
+
"snakemake>=7.32,<8",
|
|
37
|
+
"pandas>=1.5",
|
|
38
|
+
# Snakemake 7.32 calls pulp.list_solvers(), which was removed in pulp 2.8.
|
|
39
|
+
"pulp<2.8",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = [
|
|
44
|
+
"black>=26,<27",
|
|
45
|
+
"pytest>=9.0",
|
|
46
|
+
"pytest-cov>=5.0",
|
|
47
|
+
"mypy>=1.0",
|
|
48
|
+
"ruff>=0.5",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.urls]
|
|
52
|
+
Homepage = "https://github.com/InstitutoTodosPelaSaude/ViralUnity"
|
|
53
|
+
Documentation = "https://viralunity.readthedocs.io/en/latest/"
|
|
54
|
+
Changelog = "https://github.com/InstitutoTodosPelaSaude/ViralUnity/blob/main/CHANGELOG.md"
|
|
55
|
+
Issues = "https://github.com/InstitutoTodosPelaSaude/ViralUnity/issues"
|
|
56
|
+
|
|
57
|
+
[project.scripts]
|
|
58
|
+
viralunity = "viralunity.viralunity_cli:main"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.dynamic]
|
|
61
|
+
version = { attr = "viralunity.__version__" }
|
|
62
|
+
|
|
63
|
+
[tool.setuptools.packages.find]
|
|
64
|
+
include = ["viralunity*"]
|
|
65
|
+
exclude = ["test*", "docs*", "docs-pt*"]
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.package-data]
|
|
68
|
+
"viralunity.scripts" = ["*.smk", "**/*.smk", "envs/*.yaml"]
|
|
69
|
+
|
|
70
|
+
[tool.black]
|
|
71
|
+
line-length = 100
|
|
72
|
+
target-version = ["py310", "py311"]
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 100
|
|
76
|
+
target-version = "py310"
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
# Conservative starting set; can expand later.
|
|
80
|
+
select = ["E", "F", "W", "I"]
|
|
81
|
+
ignore = [
|
|
82
|
+
"E501", # line length is handled by black where it cares; do not double-flag
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint.per-file-ignores]
|
|
86
|
+
# Snakemake injects a `snakemake` global into `script:` files at runtime.
|
|
87
|
+
# Ruff cannot see this, so silence F821 for those files.
|
|
88
|
+
"viralunity/scripts/python/*.py" = ["F821"]
|
|
89
|
+
|
|
90
|
+
[tool.mypy]
|
|
91
|
+
python_version = "3.10"
|
|
92
|
+
ignore_missing_imports = true
|
|
93
|
+
warn_unused_ignores = true
|
|
94
|
+
|
|
95
|
+
[[tool.mypy.overrides]]
|
|
96
|
+
# Snakemake injects a `snakemake` global into `script:` files at runtime.
|
|
97
|
+
module = "viralunity.scripts.python.*"
|
|
98
|
+
ignore_errors = true
|
|
99
|
+
|
|
100
|
+
[tool.pytest.ini_options]
|
|
101
|
+
testpaths = ["test"]
|
|
102
|
+
python_files = ["*_test.py"]
|
|
103
|
+
python_classes = ["Test_*", "Test*"]
|
|
104
|
+
python_functions = ["test_*"]
|
|
105
|
+
addopts = "-m 'not empirical'"
|
|
106
|
+
markers = [
|
|
107
|
+
"empirical: end-to-end tests that download real data and run full pipelines (slow, opt-in)",
|
|
108
|
+
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Shared pipeline-orchestration helpers used by ``viralunity_consensus`` and
|
|
2
|
+
``viralunity_meta``.
|
|
3
|
+
|
|
4
|
+
Each pipeline still owns its own ``validate_args``, ``generate_config_file``,
|
|
5
|
+
``run_snakemake_workflow``, and ``main`` so that existing test patches at
|
|
6
|
+
those module-level names continue to work; the helpers in this module are
|
|
7
|
+
called from inside those functions to remove what would otherwise be
|
|
8
|
+
duplicated boilerplate.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
from typing import Any, Callable, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from snakemake import snakemake
|
|
16
|
+
|
|
17
|
+
from viralunity.config_generator import ConfigGenerator
|
|
18
|
+
from viralunity.exceptions import ValidationError, ViralUnityError
|
|
19
|
+
from viralunity.provenance import write_run_manifest
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def start_config(args: Dict[str, Any], samples: Dict[str, list]) -> ConfigGenerator:
|
|
25
|
+
"""Instantiate a ``ConfigGenerator`` and add the keys both pipelines
|
|
26
|
+
always set: samples, output, threads.
|
|
27
|
+
|
|
28
|
+
The caller is responsible for adding pipeline-specific keys (consensus
|
|
29
|
+
or metagenomics settings), per-rule resource settings, and calling
|
|
30
|
+
``generator.save()``.
|
|
31
|
+
"""
|
|
32
|
+
logger.info("Generating configuration file")
|
|
33
|
+
generator = ConfigGenerator(args["config_file"])
|
|
34
|
+
generator.add_samples(samples, args["data_type"])
|
|
35
|
+
generator.add_output(args["output"], args["run_name"])
|
|
36
|
+
generator.add_threads(args["threads"])
|
|
37
|
+
return generator
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run_workflow(workflow_path: str, args: Dict[str, Any]) -> bool:
|
|
41
|
+
"""Run a Snakemake workflow with the kwargs both pipelines share.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
workflow_path: Absolute path to the ``.smk`` file to execute.
|
|
45
|
+
args: Pipeline argument dict. Must contain ``config_file`` and
|
|
46
|
+
``threads_total``.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValidationError: If ``workflow_path`` does not exist.
|
|
50
|
+
"""
|
|
51
|
+
logger.info("Starting Snakemake workflow")
|
|
52
|
+
|
|
53
|
+
if not os.path.isfile(workflow_path):
|
|
54
|
+
raise ValidationError(f"Workflow file not found: {workflow_path}")
|
|
55
|
+
|
|
56
|
+
successful = snakemake(
|
|
57
|
+
workflow_path,
|
|
58
|
+
configfiles=[args["config_file"]],
|
|
59
|
+
cores=args["threads_total"],
|
|
60
|
+
use_conda=True,
|
|
61
|
+
conda_prefix=args.get("conda_prefix"),
|
|
62
|
+
targets=["all"],
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if successful:
|
|
66
|
+
logger.info("Snakemake workflow completed successfully")
|
|
67
|
+
else:
|
|
68
|
+
logger.error("Snakemake workflow failed")
|
|
69
|
+
|
|
70
|
+
return successful
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def run_pipeline(
|
|
74
|
+
args: Dict[str, Any],
|
|
75
|
+
*,
|
|
76
|
+
resolve_paths: Callable[[Dict[str, Any]], None],
|
|
77
|
+
validate: Callable[[Dict[str, Any]], Optional[Dict[str, list]]],
|
|
78
|
+
generate_config: Callable[[Dict[str, list], Dict[str, Any]], None],
|
|
79
|
+
run_workflow_fn: Callable[[Dict[str, Any]], bool],
|
|
80
|
+
skip_when_no_samples: bool = False,
|
|
81
|
+
) -> int:
|
|
82
|
+
"""The try/except ``main`` skeleton shared by both pipelines.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
args: Pipeline argument dict.
|
|
86
|
+
resolve_paths: Called first to convert relative path args to
|
|
87
|
+
absolute paths.
|
|
88
|
+
validate: Called to validate args and return the samples dict.
|
|
89
|
+
generate_config: Called to write the Snakemake config file.
|
|
90
|
+
run_workflow_fn: Called to execute the Snakemake workflow. The
|
|
91
|
+
pipeline-specific module passes its own
|
|
92
|
+
``run_snakemake_workflow`` so test patches at that name
|
|
93
|
+
keep working.
|
|
94
|
+
skip_when_no_samples: If ``True``, return 0 (success) when the
|
|
95
|
+
validated samples dict is empty. ``viralunity_meta`` opts in;
|
|
96
|
+
``viralunity_consensus`` does not.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Exit code (0 for success, 1 for failure).
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
resolve_paths(args)
|
|
103
|
+
samples = validate(args)
|
|
104
|
+
|
|
105
|
+
if skip_when_no_samples and (samples is None or len(samples) == 0):
|
|
106
|
+
logger.warning("No samples were provided.")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
generate_config(samples, args)
|
|
110
|
+
|
|
111
|
+
if args.get("create_config_only", False):
|
|
112
|
+
logger.info("Config file created. Exiting without running workflow.")
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
# Provenance: record version, config, and input checksums for the run.
|
|
116
|
+
# Best-effort — a manifest failure must never abort an analysis.
|
|
117
|
+
try:
|
|
118
|
+
manifest_path = write_run_manifest(args, samples)
|
|
119
|
+
logger.info(f"Wrote run manifest: {manifest_path}")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.warning(f"Could not write run manifest: {e}")
|
|
122
|
+
|
|
123
|
+
successful = run_workflow_fn(args)
|
|
124
|
+
return 0 if successful else 1
|
|
125
|
+
|
|
126
|
+
except ViralUnityError as e:
|
|
127
|
+
# Expected, user-facing failures (bad inputs, missing files/DBs, config
|
|
128
|
+
# errors). Log a clean message with the machine-readable error code so a
|
|
129
|
+
# caller/service can key off it; no stack trace for these.
|
|
130
|
+
logger.error(f"[{e.code}] {e}")
|
|
131
|
+
return 1
|
|
132
|
+
except Exception as e:
|
|
133
|
+
# Genuinely unexpected: keep the traceback for debugging.
|
|
134
|
+
logger.exception(f"Unexpected error: {e}")
|
|
135
|
+
return 1
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Shared subprocess helper for ViralUnity CLI commands.
|
|
2
|
+
|
|
3
|
+
Internal module — not part of the public API. Used by the `get-databases`
|
|
4
|
+
and `build-deacon-index` subcommands to invoke external tools (`wget`,
|
|
5
|
+
`tar`, `diamond`, `deacon`, `datasets`, ...) with consistent echoing and
|
|
6
|
+
error handling.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
from typing import Optional, Sequence
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
#: Default wall-clock ceiling for a single external command (seconds). Long
|
|
17
|
+
#: enough for large downloads/DB builds, but bounded so a hung tool cannot block
|
|
18
|
+
#: a run (or a service worker) indefinitely. Override per-call via ``timeout``.
|
|
19
|
+
DEFAULT_TIMEOUT = 6 * 60 * 60 # 6 hours
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run_command(
|
|
23
|
+
cmd: Sequence[object],
|
|
24
|
+
cwd: Optional[str] = None,
|
|
25
|
+
timeout: Optional[float] = DEFAULT_TIMEOUT,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Run a subprocess command, streaming output and raising on failure.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
cmd: Argument list (no shell). Each element is coerced to ``str``
|
|
31
|
+
when echoed; pass paths and flags as separate elements.
|
|
32
|
+
cwd: Optional working directory in which to run the command.
|
|
33
|
+
timeout: Wall-clock ceiling in seconds. Defaults to
|
|
34
|
+
``DEFAULT_TIMEOUT``; pass ``None`` to disable.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
click.ClickException: If the subprocess exits non-zero or times out.
|
|
38
|
+
The exit code/timeout and command are included in the message so
|
|
39
|
+
the failure surfaces cleanly in the CLI output.
|
|
40
|
+
"""
|
|
41
|
+
click.echo(f"$ {' '.join(str(c) for c in cmd)}")
|
|
42
|
+
try:
|
|
43
|
+
result = subprocess.run(list(cmd), cwd=cwd, timeout=timeout)
|
|
44
|
+
except subprocess.TimeoutExpired as e:
|
|
45
|
+
raise click.ClickException(
|
|
46
|
+
f"Command timed out after {e.timeout:.0f}s: " f"{' '.join(str(c) for c in cmd)}"
|
|
47
|
+
) from e
|
|
48
|
+
if result.returncode != 0:
|
|
49
|
+
raise click.ClickException(
|
|
50
|
+
f"Command failed with exit code {result.returncode}: "
|
|
51
|
+
f"{' '.join(str(c) for c in cmd)}"
|
|
52
|
+
)
|