countmut 0.1.2__tar.gz → 0.1.4__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.
- {countmut-0.1.2 → countmut-0.1.4}/PKG-INFO +1 -1
- {countmut-0.1.2 → countmut-0.1.4}/countmut/backend.py +32 -5
- {countmut-0.1.2 → countmut-0.1.4}/countmut/cli.py +17 -2
- {countmut-0.1.2 → countmut-0.1.4}/countmut/model.py +1 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/PKG-INFO +1 -1
- {countmut-0.1.2 → countmut-0.1.4}/pyproject.toml +1 -1
- {countmut-0.1.2 → countmut-0.1.4}/LICENSE +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/README.md +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut/__init__.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut/bam_tags.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut/core.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut/utils.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/SOURCES.txt +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/dependency_links.txt +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/entry_points.txt +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/requires.txt +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/countmut.egg-info/top_level.txt +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/setup.cfg +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/tests/test_cli.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/tests/test_core.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/tests/test_correctness.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/tests/test_unified.py +0 -0
- {countmut-0.1.2 → countmut-0.1.4}/tests/test_utils.py +0 -0
|
@@ -100,7 +100,7 @@ def _build_cmd(
|
|
|
100
100
|
"--threads",
|
|
101
101
|
str(ecfg.threads or min(os.cpu_count() or 1, 8)),
|
|
102
102
|
"--max-depth",
|
|
103
|
-
str(fcfg.max_depth
|
|
103
|
+
str(fcfg.max_depth), # 0 = unlimited
|
|
104
104
|
]
|
|
105
105
|
if fcfg.max_unc is not None:
|
|
106
106
|
cmd += ["--max-unc", str(fcfg.max_unc)]
|
|
@@ -137,12 +137,24 @@ def _build_cmd(
|
|
|
137
137
|
cmd.append("--vcf")
|
|
138
138
|
if ecfg.min_depth:
|
|
139
139
|
cmd += ["--min-depth", str(ecfg.min_depth)]
|
|
140
|
+
if ecfg.verbose:
|
|
141
|
+
cmd.append("--verbose")
|
|
140
142
|
return cmd
|
|
141
143
|
|
|
142
144
|
|
|
143
145
|
# ---------------------------------------------------------------------------
|
|
144
146
|
# public entry point
|
|
145
147
|
# ---------------------------------------------------------------------------
|
|
148
|
+
def _forward_stderr(stream) -> None:
|
|
149
|
+
"""Forward a child process' stderr lines to our stderr (--verbose)."""
|
|
150
|
+
try:
|
|
151
|
+
for line in stream:
|
|
152
|
+
sys.stderr.write(line)
|
|
153
|
+
sys.stderr.flush()
|
|
154
|
+
except (ValueError, OSError):
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
|
|
146
158
|
def run_backend(
|
|
147
159
|
samfile: str,
|
|
148
160
|
reference: str,
|
|
@@ -190,12 +202,27 @@ def run_backend(
|
|
|
190
202
|
ecfg=ecfg,
|
|
191
203
|
)
|
|
192
204
|
start = time.time()
|
|
193
|
-
|
|
205
|
+
stdout = ""
|
|
206
|
+
if ecfg.verbose:
|
|
207
|
+
# stream the C core's stderr (progress) to our stderr in real time
|
|
208
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
209
|
+
import threading
|
|
210
|
+
|
|
211
|
+
fwd = threading.Thread(
|
|
212
|
+
target=_forward_stderr, args=(proc.stderr,), daemon=True
|
|
213
|
+
)
|
|
214
|
+
fwd.start()
|
|
215
|
+
out, _ = proc.communicate()
|
|
216
|
+
fwd.join(timeout=2)
|
|
217
|
+
stdout = out or ""
|
|
218
|
+
else:
|
|
219
|
+
proc = subprocess.run(cmd, capture_output=True, text=True)
|
|
220
|
+
stdout = proc.stdout
|
|
194
221
|
elapsed = time.time() - start
|
|
195
222
|
|
|
196
223
|
if output is None and proc.returncode == 0:
|
|
197
|
-
# stream the TSV from stdout
|
|
198
|
-
sys.stdout.write(
|
|
224
|
+
# stream the TSV from stdout
|
|
225
|
+
sys.stdout.write(stdout)
|
|
199
226
|
sys.stdout.flush()
|
|
200
227
|
|
|
201
228
|
rows = 0
|
|
@@ -203,7 +230,7 @@ def run_backend(
|
|
|
203
230
|
with open(output) as fh:
|
|
204
231
|
rows = sum(1 for _ in fh) - 1
|
|
205
232
|
else:
|
|
206
|
-
rows = len(
|
|
233
|
+
rows = len(stdout.splitlines()) - 1
|
|
207
234
|
|
|
208
235
|
if proc.returncode != 0:
|
|
209
236
|
sys.stderr.write(f"[countmut] backend error: {proc.stderr}\n")
|
|
@@ -26,7 +26,7 @@ from .model import EngineConfig, FilterConfig, MutationConfig, StrandConfig
|
|
|
26
26
|
try:
|
|
27
27
|
__version__ = importlib_metadata.version("countmut")
|
|
28
28
|
except importlib_metadata.PackageNotFoundError: # pragma: no cover
|
|
29
|
-
__version__ = "0.1.
|
|
29
|
+
__version__ = "0.1.4"
|
|
30
30
|
|
|
31
31
|
click.rich_click.TEXT_MARKUP = "rich"
|
|
32
32
|
click.rich_click.SHOW_ARGUMENTS = True
|
|
@@ -144,6 +144,13 @@ console = Console()
|
|
|
144
144
|
show_default=True,
|
|
145
145
|
help="Minimum site depth to report (base/allele mode)",
|
|
146
146
|
)
|
|
147
|
+
@click.option(
|
|
148
|
+
"--max-depth",
|
|
149
|
+
type=int,
|
|
150
|
+
default=0,
|
|
151
|
+
show_default=True,
|
|
152
|
+
help="Per-position depth cap (pileup engine; 0 = unlimited, counts all reads)",
|
|
153
|
+
)
|
|
147
154
|
@click.option(
|
|
148
155
|
"--min-allele-support",
|
|
149
156
|
type=int,
|
|
@@ -166,7 +173,12 @@ console = Console()
|
|
|
166
173
|
default=None,
|
|
167
174
|
help="Lua site filter (e.g. \"ref == 'A' and depth >= 5 and g > 2\")",
|
|
168
175
|
)
|
|
169
|
-
@click.option(
|
|
176
|
+
@click.option(
|
|
177
|
+
"--verbose",
|
|
178
|
+
is_flag=True,
|
|
179
|
+
default=False,
|
|
180
|
+
help="Real-time per-region progress on stderr (large BAMs)",
|
|
181
|
+
)
|
|
170
182
|
def main(
|
|
171
183
|
samfile,
|
|
172
184
|
reference,
|
|
@@ -182,6 +194,7 @@ def main(
|
|
|
182
194
|
split_strand,
|
|
183
195
|
count_indels,
|
|
184
196
|
min_depth,
|
|
197
|
+
max_depth,
|
|
185
198
|
min_allele_support,
|
|
186
199
|
vcf,
|
|
187
200
|
read_expr,
|
|
@@ -202,6 +215,7 @@ def main(
|
|
|
202
215
|
min_con=1,
|
|
203
216
|
trim_start=2,
|
|
204
217
|
trim_end=2,
|
|
218
|
+
max_depth=max_depth,
|
|
205
219
|
)
|
|
206
220
|
mcfg = (
|
|
207
221
|
MutationConfig(
|
|
@@ -223,6 +237,7 @@ def main(
|
|
|
223
237
|
vcf=vcf,
|
|
224
238
|
read_expr=read_expr,
|
|
225
239
|
pile_expr=pile_expr,
|
|
240
|
+
verbose=verbose,
|
|
226
241
|
)
|
|
227
242
|
|
|
228
243
|
config_table = Table(box=rich.box.MINIMAL, show_header=False)
|
|
@@ -85,5 +85,6 @@ class EngineConfig:
|
|
|
85
85
|
vcf: bool = False # allele mode: emit VCF
|
|
86
86
|
count_indels: bool = False
|
|
87
87
|
split_strand: bool = True
|
|
88
|
+
verbose: bool = False # real-time per-region progress on stderr
|
|
88
89
|
report_reference_bases: bool = False # -k flanking window (pbr)
|
|
89
90
|
flanking: int = 0
|
|
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
|