pav3 3.0.0.dev22__tar.gz → 3.0.0.dev23__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.
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/PKG-INFO +2 -2
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/pyproject.toml +2 -1
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/__init__.py +1 -1
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/Snakefile +1 -1
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/rules/call.smk +2 -2
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/rules/data.smk +1 -2
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/path.py +11 -3
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/pipeline.py +94 -19
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/workflow/call.py +64 -4
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/PKG-INFO +2 -2
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/requires.txt +1 -1
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/CHANGELOG.md +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/CODE_OF_CONDUCT.md +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/CONTRIBUTING.md +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/LICENSE +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/MANIFEST.in +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/README.md +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/setup.cfg +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/__main__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/features.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lcmodel/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lcmodel/_lcmodel.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lcmodel/_lcmodel_logistic.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lcmodel/_lcmodel_null.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lcmodel/_util.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/lift.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/op.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/records.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/score.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/tables.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/align/trim.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/anno.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/call/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/call/expr.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/call/integrate.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/call/intra.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/call/util.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/_cli.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/_common_opt.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/_subcommand_batch.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/_subcommand_call.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/cli/_subcommand_license.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/const.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/default/model.json +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_95_80/model.json +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_95_80/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_95_80/weights.npz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_98_80/model.json +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_98_80/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/human_98_80/weights.npz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/prim_90_50/model.json +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/prim_90_50/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/prim_90_50/weights.npz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/primdiv_85_50/model.json +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/primdiv_85_50/model_info.tar.gz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/lcmodel/primdiv_85_50/weights.npz +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/tracks/alignment_track_fields.tsv +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/tracks/variant_track_fields.tsv +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/profiles/pav_default/config.yaml +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/rules/align.smk +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/rules/pipeline.smk +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/rules/tracks.smk +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/imports.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/inter.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/sm.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/dev/tables.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/fig/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/fig/const.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/fig/density.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/fig/tracks.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/fig/util.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/inv.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/io.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/kde.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/call.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/chain.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/interval.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/io.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/region_kde.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/resources.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/struct.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/lgsv/variant.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/params.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/region.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/schema.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/seq.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/util.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/vcf.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/workflow/__init__.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/workflow/resource.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/SOURCES.txt +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/dependency_links.txt +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/entry_points.txt +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3.egg-info/top_level.txt +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/TESTING_TODO.md +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/align/.gitkeep +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/align/test_score.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/align/test_tables.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/call/.gitkeep +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/cli/.gitkeep +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/conftest.py +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tests/lgsv/.gitkeep +0 -0
- {pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/tox.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pav3
|
|
3
|
-
Version: 3.0.0.
|
|
3
|
+
Version: 3.0.0.dev23
|
|
4
4
|
Summary: Assembly-based variant discovery
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -13,7 +13,7 @@ Requires-Dist: frozendict>=2.4.6
|
|
|
13
13
|
Requires-Dist: hmmlearn>=0.3.3
|
|
14
14
|
Requires-Dist: intervaltree>=3.1.0
|
|
15
15
|
Requires-Dist: numpy>=2.3.2
|
|
16
|
-
Requires-Dist: polars[numpy,pandas,pyarrow]>=1.
|
|
16
|
+
Requires-Dist: polars[numpy,pandas,pyarrow]>=1.41.0
|
|
17
17
|
Requires-Dist: pyarrow>=21.0.0
|
|
18
18
|
Requires-Dist: pysam>=0.23.3
|
|
19
19
|
Requires-Dist: scipy>=1.16.1
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
"hmmlearn>=0.3.3",
|
|
19
19
|
"intervaltree>=3.1.0",
|
|
20
20
|
"numpy>=2.3.2",
|
|
21
|
-
"polars[numpy,pandas,pyarrow]>=1.
|
|
21
|
+
"polars[numpy,pandas,pyarrow]>=1.41.0",
|
|
22
22
|
"pyarrow>=21.0.0",
|
|
23
23
|
"pysam>=0.23.3",
|
|
24
24
|
"scipy>=1.16.1",
|
|
@@ -80,6 +80,7 @@ dev = [
|
|
|
80
80
|
"flake8-rst-docstrings>=0.3.1",
|
|
81
81
|
"ipython>=9.7.0",
|
|
82
82
|
"ply>=3.11",
|
|
83
|
+
"pygithub>=2.9.1",
|
|
83
84
|
"pylint>=3.3.7",
|
|
84
85
|
"pytest>=8.4.1",
|
|
85
86
|
"pytest-cov>=6.2.1",
|
|
@@ -286,7 +286,7 @@ rule call_tables:
|
|
|
286
286
|
ref_path = Path(next(in_file).strip())
|
|
287
287
|
|
|
288
288
|
callsets = tuple(
|
|
289
|
-
|
|
289
|
+
(
|
|
290
290
|
(
|
|
291
291
|
pl.scan_parquet(
|
|
292
292
|
f'results/{wildcards.asm_name}/call_hap/call_{wildcards.vartype}_{hap}.parquet'
|
|
@@ -294,7 +294,7 @@ rule call_tables:
|
|
|
294
294
|
.with_row_index('_index')
|
|
295
295
|
),
|
|
296
296
|
f'{wildcards.asm_name}-{hap}',
|
|
297
|
-
)
|
|
297
|
+
)
|
|
298
298
|
for hap in pav3.pipeline.get_hap_list(wildcards.asm_name, ASM_TABLE)
|
|
299
299
|
)
|
|
300
300
|
|
|
@@ -59,8 +59,7 @@ rule align_get_qry_fa:
|
|
|
59
59
|
out_filename = f'data/query/{wildcards.asm_name}/query_{wildcards.hap}.fa' # ".gz" is appended as needed
|
|
60
60
|
|
|
61
61
|
if len(input_tuples) == 1 and input_tuples[0][1] == 'fasta':
|
|
62
|
-
|
|
63
|
-
fa_path_list = pav3.pipeline.link_fasta(input_tuples[0][0], out_filename)
|
|
62
|
+
fa_path_list = pav3.pipeline.link_fasta(Path(input_tuples[0][0]), out_filename)
|
|
64
63
|
|
|
65
64
|
else:
|
|
66
65
|
# Merge/write FASTA from multiple sources and/or GFA files
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Path utilities for development environments."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
import tomllib
|
|
5
6
|
|
|
@@ -19,8 +20,16 @@ def get_pav_dir(
|
|
|
19
20
|
directory.
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
org_path = path
|
|
24
|
+
|
|
25
|
+
if path is None or not Path(path).exists():
|
|
26
|
+
path = os.environ.get('PAV_PATH', None)
|
|
27
|
+
|
|
28
|
+
if path is None:
|
|
29
|
+
path = os.environ.get('UV_PROJECT', None)
|
|
30
|
+
|
|
31
|
+
if path is None:
|
|
32
|
+
raise ValueError(f'Path is missing: {org_path}')
|
|
24
33
|
|
|
25
34
|
path = Path(path)
|
|
26
35
|
|
|
@@ -36,5 +45,4 @@ def get_pav_dir(
|
|
|
36
45
|
== 'pav3'
|
|
37
46
|
):
|
|
38
47
|
return parent
|
|
39
|
-
|
|
40
48
|
raise FileNotFoundError(f'path {path} does not point to a resource within a PAV source directory')
|
|
@@ -866,16 +866,25 @@ def link_fasta(
|
|
|
866
866
|
|
|
867
867
|
# Link FASTA
|
|
868
868
|
dest_fa_path.unlink(missing_ok=True)
|
|
869
|
-
dest_fa_path.
|
|
869
|
+
dest_fa_path.parent.mkdir(parents=True, exist_ok=True)
|
|
870
|
+
|
|
871
|
+
dest_fa_path.symlink_to(
|
|
872
|
+
resolve_relative(source, dest_fa_path)
|
|
873
|
+
)
|
|
870
874
|
|
|
871
875
|
# Link indexes (if present)
|
|
872
876
|
if source_is_indexed:
|
|
873
877
|
dest_fai_path.unlink(missing_ok=True)
|
|
874
|
-
|
|
878
|
+
|
|
879
|
+
dest_fai_path.symlink_to(
|
|
880
|
+
resolve_relative(source_fai, dest_fai_path)
|
|
881
|
+
)
|
|
875
882
|
|
|
876
883
|
if dest_gzi_path is not None:
|
|
877
884
|
dest_gzi_path.unlink(missing_ok=True)
|
|
878
|
-
dest_gzi_path.symlink_to(
|
|
885
|
+
dest_gzi_path.symlink_to(
|
|
886
|
+
resolve_relative(source_gzi, dest_gzi_path)
|
|
887
|
+
)
|
|
879
888
|
|
|
880
889
|
else:
|
|
881
890
|
# Create indexes
|
|
@@ -886,6 +895,37 @@ def link_fasta(
|
|
|
886
895
|
dest_fa_path, dest_fai_path
|
|
887
896
|
] + [dest_gzi_path] if dest_gzi_path is not None else []
|
|
888
897
|
|
|
898
|
+
def resolve_relative(
|
|
899
|
+
source_path: str | Path,
|
|
900
|
+
dest_path: str | Path,
|
|
901
|
+
) -> Path:
|
|
902
|
+
"""Resolve a path relative to another path.
|
|
903
|
+
|
|
904
|
+
If both source and destination paths are relative, assume they hare relative to the current
|
|
905
|
+
working directory and return a relative path from the destination path to the source path.
|
|
906
|
+
This can be used to make symbolic links within a directory structure allowing the directories
|
|
907
|
+
to move locations without breaking absolute paths.
|
|
908
|
+
|
|
909
|
+
If either the source or the destination path is absolute, the absolute source path is
|
|
910
|
+
returned.
|
|
911
|
+
|
|
912
|
+
:param source_path: Source path.
|
|
913
|
+
:param dest_path: Destination path.
|
|
914
|
+
|
|
915
|
+
:returns: Resolved path.
|
|
916
|
+
"""
|
|
917
|
+
source_path = Path(source_path)
|
|
918
|
+
dest_path = Path(dest_path)
|
|
919
|
+
|
|
920
|
+
if source_path.is_absolute():
|
|
921
|
+
return source_path
|
|
922
|
+
|
|
923
|
+
if dest_path.is_absolute():
|
|
924
|
+
return source_path.absolute()
|
|
925
|
+
|
|
926
|
+
# Return a path relative to the destination path
|
|
927
|
+
return source_path.relative_to(dest_path.parent, walk_up=True)
|
|
928
|
+
|
|
889
929
|
|
|
890
930
|
def get_rules_path(
|
|
891
931
|
rule_filename: str | Path,
|
|
@@ -923,24 +963,32 @@ def get_rules_path(
|
|
|
923
963
|
|
|
924
964
|
def get_pav_config_path(
|
|
925
965
|
config_filename: Optional[str | Path] = None,
|
|
966
|
+
root_path: Optional[str | Path] = None,
|
|
926
967
|
) -> Path:
|
|
927
968
|
"""Locate the PAV configuration filename.
|
|
928
969
|
|
|
929
970
|
:param config_filename: Path to the config file. If None, the default config file is used.
|
|
971
|
+
:param root_path: Root path to search for the config file. If None, the current directory
|
|
972
|
+
is used.
|
|
930
973
|
|
|
931
974
|
:return: Path to the config file.
|
|
932
975
|
|
|
933
976
|
:raises FileNotFoundError: If the specified configuration (`config_filename`) or default
|
|
934
977
|
configuration file is not found.
|
|
935
978
|
"""
|
|
979
|
+
if root_path is None:
|
|
980
|
+
root_path = Path('.')
|
|
981
|
+
else:
|
|
982
|
+
root_path = Path(root_path)
|
|
983
|
+
|
|
936
984
|
default_paths = (
|
|
937
|
-
|
|
938
|
-
|
|
985
|
+
root_path / 'pav.json',
|
|
986
|
+
root_path / 'pav.json.txt',
|
|
939
987
|
)
|
|
940
988
|
|
|
941
989
|
deprecated_paths = (
|
|
942
|
-
|
|
943
|
-
|
|
990
|
+
root_path / 'config.json',
|
|
991
|
+
root_path / 'config.json.txt',
|
|
944
992
|
)
|
|
945
993
|
|
|
946
994
|
if config_filename is not None:
|
|
@@ -988,33 +1036,58 @@ def get_pav_config_path(
|
|
|
988
1036
|
|
|
989
1037
|
|
|
990
1038
|
def get_pav_asm_table_path(
|
|
991
|
-
|
|
1039
|
+
pav_config: Optional[dict] = None,
|
|
1040
|
+
root_path: Optional[str | Path] = Path('.'),
|
|
992
1041
|
) -> Path:
|
|
993
1042
|
"""Get a path to the assembly table.
|
|
994
1043
|
|
|
995
|
-
|
|
1044
|
+
The assembly table path is read from the PAV configuration if it is specified there. If it is
|
|
1045
|
+
not specified in the configuration, the default assembly table paths are searched. All paths
|
|
1046
|
+
are relative to `root_path` (defaults to the current directory).
|
|
1047
|
+
|
|
1048
|
+
:param pav_config: PAV configuration, if specified. Reads the assembly table path from the
|
|
1049
|
+
configuration if it is specified there.
|
|
1050
|
+
:param root_path: Root path to search for the table file. If None, the current directory
|
|
1051
|
+
is used.
|
|
996
1052
|
|
|
997
1053
|
:return: Path to the assembly table.
|
|
998
1054
|
|
|
999
|
-
:raises FileNotFoundError: If the specified assembly table or default assembly table is not
|
|
1055
|
+
:raises FileNotFoundError: If the specified assembly table or default assembly table is not
|
|
1056
|
+
found.
|
|
1000
1057
|
"""
|
|
1001
1058
|
|
|
1002
|
-
if
|
|
1003
|
-
|
|
1059
|
+
if root_path is None:
|
|
1060
|
+
root_path = Path('.')
|
|
1061
|
+
else:
|
|
1062
|
+
root_path = Path(root_path)
|
|
1063
|
+
|
|
1064
|
+
if pav_config is not None and 'assembly_table' in pav_config:
|
|
1065
|
+
asm_table_path = root_path / str(pav_config['assembly_table'])
|
|
1004
1066
|
|
|
1005
1067
|
if not asm_table_path.is_file():
|
|
1006
|
-
raise FileNotFoundError(
|
|
1068
|
+
raise FileNotFoundError(
|
|
1069
|
+
f'Assembly table file defined in the PAV configuration is not found '
|
|
1070
|
+
f'or not a regular file: {asm_table_path}'
|
|
1071
|
+
)
|
|
1007
1072
|
|
|
1008
1073
|
return asm_table_path
|
|
1009
1074
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1075
|
+
default_paths = [
|
|
1076
|
+
root_path / 'assemblies.tsv',
|
|
1077
|
+
root_path / 'assemblies.xlsx',
|
|
1078
|
+
]
|
|
1079
|
+
|
|
1080
|
+
for asm_table_path in default_paths:
|
|
1014
1081
|
if asm_table_path.is_file():
|
|
1015
1082
|
return asm_table_path
|
|
1016
1083
|
|
|
1017
|
-
|
|
1084
|
+
default_path_str = ', '.join(
|
|
1085
|
+
[f'"{str(p)}"' for p in default_paths]
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
raise FileNotFoundError(
|
|
1089
|
+
f'No assembly table file found (tried default paths: {default_path_str})'
|
|
1090
|
+
)
|
|
1018
1091
|
|
|
1019
1092
|
|
|
1020
1093
|
def get_pav_env_path(
|
|
@@ -1031,7 +1104,9 @@ def get_pav_env_path(
|
|
|
1031
1104
|
|
|
1032
1105
|
if env_path is not None:
|
|
1033
1106
|
if not env_path.is_file():
|
|
1034
|
-
raise FileNotFoundError(
|
|
1107
|
+
raise FileNotFoundError(
|
|
1108
|
+
f'Specified environment file not found or not a regular file: {env_path}'
|
|
1109
|
+
)
|
|
1035
1110
|
|
|
1036
1111
|
return env_path
|
|
1037
1112
|
|
|
@@ -24,10 +24,61 @@ from ..call.expr import sort_expr
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def _materialize(
|
|
28
|
+
df: pl.LazyFrame,
|
|
29
|
+
temp_file_container: TempDirContainer,
|
|
30
|
+
label: str,
|
|
31
|
+
) -> pl.LazyFrame:
|
|
32
|
+
"""Write a lazy frame to a temporary Parquet file and return a streaming scan of it.
|
|
33
|
+
|
|
34
|
+
Used as a materialization barrier between lazy operations. It serves two purposes:
|
|
35
|
+
|
|
36
|
+
1. Memory: Variant tables may be very large. Writing intermediate results to a temporary file and re-reading
|
|
37
|
+
them as streaming scans keeps them off the Python heap instead of accumulating collected frames in memory
|
|
38
|
+
across the integration loop.
|
|
39
|
+
|
|
40
|
+
2. Correctness: `apply_trim_filter` and `apply_discord_and_inner_filter` each add a row-index column
|
|
41
|
+
(`with_row_index`). When both are chained into a single lazy plan and that plan is sunk, the Polars
|
|
42
|
+
new-streaming engine mis-optimizes the chained row-index operations and raises a spurious
|
|
43
|
+
"duplicate name when building schema" / "column not found" error. The same plan collects correctly with
|
|
44
|
+
the in-memory engine, confirming the logical plan is valid and the fault is in the streaming optimizer.
|
|
45
|
+
Breaking the plan with a Parquet round-trip isolates each helper's row index and avoids the bug.
|
|
46
|
+
|
|
47
|
+
:param df: Lazy frame to materialize.
|
|
48
|
+
:param temp_file_container: Active temporary file container (must be inside its context).
|
|
49
|
+
:param label: Label used in the temporary file name.
|
|
50
|
+
|
|
51
|
+
:return: Streaming scan over the materialized Parquet file.
|
|
52
|
+
"""
|
|
53
|
+
temp_file = temp_file_container.next(prefix=f'integrate_{label}_', suffix='.parquet')
|
|
54
|
+
df.sink_parquet(temp_file)
|
|
55
|
+
return pl.scan_parquet(temp_file)
|
|
56
|
+
|
|
57
|
+
|
|
27
58
|
def integrate_sources(
|
|
28
59
|
pav_params,
|
|
29
60
|
input: dict[str, str],
|
|
30
61
|
output: dict[str, str],
|
|
62
|
+
temp_dir_name: Optional[str] = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Integrate variant calls from all sources into the final per-haplotype call tables.
|
|
65
|
+
|
|
66
|
+
:param pav_params: PAV parameters.
|
|
67
|
+
:param input: Input file dictionary (Snakemake rule input).
|
|
68
|
+
:param output: Output file dictionary (Snakemake rule output).
|
|
69
|
+
:param temp_dir_name: Parent directory for temporary files. Defaults to the system temporary directory.
|
|
70
|
+
"""
|
|
71
|
+
# Intermediate variant tables are materialized to a temporary directory and re-read as streaming scans (see
|
|
72
|
+
# `_materialize`). This bounds memory for large callsets and works around a Polars new-streaming engine bug.
|
|
73
|
+
with TempDirContainer(temp_dir_name, prefix='pav_integrate_') as temp_file_container:
|
|
74
|
+
_integrate_sources(pav_params, input, output, temp_file_container)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _integrate_sources(
|
|
78
|
+
pav_params,
|
|
79
|
+
input: dict[str, str],
|
|
80
|
+
output: dict[str, str],
|
|
81
|
+
temp_file_container: TempDirContainer,
|
|
31
82
|
) -> None:
|
|
32
83
|
inv_min = pav_params.inv_min
|
|
33
84
|
inv_max = pav_params.inv_max if pav_params.inv_max > 0 else float('inf')
|
|
@@ -130,6 +181,11 @@ def integrate_sources(
|
|
|
130
181
|
if not (is_lg or is_inv):
|
|
131
182
|
df = pav3.call.integrate.apply_trim_filter(df, df_trim)
|
|
132
183
|
|
|
184
|
+
# Materialization barrier between the two row-index helpers. `apply_trim_filter` and
|
|
185
|
+
# `apply_discord_and_inner_filter` both call `with_row_index`; chaining them into one sunk plan trips a
|
|
186
|
+
# Polars streaming-engine bug (see `_materialize`). The round-trip isolates the two row indices.
|
|
187
|
+
df = _materialize(df, temp_file_container, f'{sourcetype_vartype}_trim')
|
|
188
|
+
|
|
133
189
|
# Filter DISCORD
|
|
134
190
|
if filter_discord or filter_inner:
|
|
135
191
|
df = pav3.call.integrate.apply_discord_and_inner_filter(
|
|
@@ -145,6 +201,11 @@ def integrate_sources(
|
|
|
145
201
|
existing_ids=collect_list[vartype]
|
|
146
202
|
)
|
|
147
203
|
|
|
204
|
+
# Materialize the fully-processed variant table and continue from a streaming scan. Every downstream use
|
|
205
|
+
# (DISCORD regions, CPX-derived variants, writes, and cross-source aggregation in `collect_list`) reads from
|
|
206
|
+
# this scan. This keeps large tables off the heap and replaces the previous in-memory `df.collect().lazy()`.
|
|
207
|
+
df = _materialize(df, temp_file_container, sourcetype_vartype)
|
|
208
|
+
|
|
148
209
|
# Read CPX segment table
|
|
149
210
|
if sourcetype == 'inter':
|
|
150
211
|
df_segment_var = df_segment.join(df.select(['var_index', 'id']), on='var_index', how='left')
|
|
@@ -180,11 +241,10 @@ def integrate_sources(
|
|
|
180
241
|
collect_list=collect_list,
|
|
181
242
|
)
|
|
182
243
|
|
|
183
|
-
# Aggregate if type is split over multiple inputs
|
|
244
|
+
# Aggregate if type is split over multiple inputs. `df` is already a disk-backed scan (materialized above),
|
|
245
|
+
# so this keeps memory bounded even when several sources accumulate in `collect_list`.
|
|
184
246
|
if not do_write or len(collect_list[vartype]) > 0:
|
|
185
|
-
|
|
186
|
-
# Note: May consume significant memory for some callsets, consider writing to temp and re-reading lazy
|
|
187
|
-
collect_list[vartype].append(df.collect().lazy())
|
|
247
|
+
collect_list[vartype].append(df)
|
|
188
248
|
|
|
189
249
|
# Write
|
|
190
250
|
write_list = [update_discord_frame]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pav3
|
|
3
|
-
Version: 3.0.0.
|
|
3
|
+
Version: 3.0.0.dev23
|
|
4
4
|
Summary: Assembly-based variant discovery
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -13,7 +13,7 @@ Requires-Dist: frozendict>=2.4.6
|
|
|
13
13
|
Requires-Dist: hmmlearn>=0.3.3
|
|
14
14
|
Requires-Dist: intervaltree>=3.1.0
|
|
15
15
|
Requires-Dist: numpy>=2.3.2
|
|
16
|
-
Requires-Dist: polars[numpy,pandas,pyarrow]>=1.
|
|
16
|
+
Requires-Dist: polars[numpy,pandas,pyarrow]>=1.41.0
|
|
17
17
|
Requires-Dist: pyarrow>=21.0.0
|
|
18
18
|
Requires-Dist: pysam>=0.23.3
|
|
19
19
|
Requires-Dist: scipy>=1.16.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pav3-3.0.0.dev22 → pav3-3.0.0.dev23}/src/pav3/data/workflow/profiles/pav_default/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|