Skip to content

Commit 34ccd50

Browse files
dpark01claude
andcommitted
Fix variable shadowing and update instance method calls across codebase
- Fix samtools variable shadowing in bwa.py, novoalign.py, picard.py, reports.py - Fix bwa variable shadowing in reports.py - Fix picard variable shadowing in kb.py, kma.py, kraken2.py - Update minimap2.py to use samtools_tool instance for all method calls - Remove duplicate imports in minimap2.py and bwa.py - Fix kraken2 fixture shadowing in test_integration_kraken2.py - Pin muscle=3.8.1551 in phylo.txt to avoid MUSCLE v5 CLI incompatibility The pattern `var = module.Class()` shadows the module import, causing UnboundLocalError when the module is referenced later in the function. Fixed by renaming to `var_tool = module.Class()`. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 003552d commit 34ccd50

10 files changed

Lines changed: 79 additions & 80 deletions

File tree

docker/requirements/phylo.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ bamtools>=2.5.2
33
lofreq>=2.1.5
44
mafft>=7.508
55
mummer4>=4.0.0rc1
6-
muscle>=3.8
6+
muscle=3.8.1551
77
snpeff>=4.3.1t
88
vphaser2>=2.0

src/viral_ngs/classify/kb.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,12 @@ def classify(self, in_bam, index_file, out_dir, t2g_file, k=31, parity='single',
173173

174174
# Do not convert this to samtools bam2fq unless we can figure out how to replicate
175175
# the clipping functionality of Picard SamToFastq
176-
picard = picard.SamToFastqTool()
176+
picard_tool = picard.SamToFastqTool()
177177
picard_opts = {
178178
'CLIPPING_ATTRIBUTE': picard.SamToFastqTool.illumina_clipping_attribute,
179179
'CLIPPING_ACTION': 'X'
180180
}
181-
picard.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
181+
picard_tool.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
182182
picardOptions=picard.PicardTools.dict_to_picard_opts(picard_opts),
183183
JVMmemory=picard.jvmMemDefault)
184184

@@ -264,12 +264,12 @@ def extract(self, in_bam, index_file, target_ids, out_dir, t2g_file, protein=Fal
264264

265265
# Do not convert this to samtools bam2fq unless we can figure out how to replicate
266266
# the clipping functionality of Picard SamToFastq
267-
picard = picard.SamToFastqTool()
267+
picard_tool = picard.SamToFastqTool()
268268
picard_opts = {
269269
'CLIPPING_ATTRIBUTE': picard.SamToFastqTool.illumina_clipping_attribute,
270270
'CLIPPING_ACTION': 'X'
271271
}
272-
picard.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
272+
picard_tool.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
273273
picardOptions=picard.PicardTools.dict_to_picard_opts(picard_opts),
274274
JVMmemory=picard.jvmMemDefault)
275275

src/viral_ngs/classify/kma.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ def classify(self, in_bam, db, out_prefix, num_threads=None):
104104
tmp_fastq3 = file.mkstempfname('.s.fastq')
105105

106106
try:
107-
picard = picard.SamToFastqTool()
107+
picard_tool = picard.SamToFastqTool()
108108
picard_opts = {
109109
'CLIPPING_ATTRIBUTE': picard.SamToFastqTool.illumina_clipping_attribute,
110110
'CLIPPING_ACTION': 'X'
111111
}
112-
picard.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
112+
picard_tool.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
113113
picardOptions=picard.PicardTools.dict_to_picard_opts(picard_opts),
114114
JVMmemory=picard.jvmMemDefault)
115115

src/viral_ngs/classify/kraken2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ def classify(self, in_bam, db, out_reads=None, out_report=None,
201201
tmp_fastq3 = file.mkstempfname('.s.fastq')
202202
# Do not convert this to samtools bam2fq unless we can figure out how to replicate
203203
# the clipping functionality of Picard SamToFastq
204-
picard = picard.SamToFastqTool()
204+
picard_tool = picard.SamToFastqTool()
205205
picard_opts = {
206206
'CLIPPING_ATTRIBUTE': picard.SamToFastqTool.illumina_clipping_attribute,
207207
'CLIPPING_ACTION': 'X'
208208
}
209-
picard.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
209+
picard_tool.execute(in_bam, tmp_fastq1, tmp_fastq2, outFastq0=tmp_fastq3,
210210
picardOptions=picard.PicardTools.dict_to_picard_opts(picard_opts),
211211
JVMmemory=picard.jvmMemDefault)
212212

src/viral_ngs/core/bwa.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import shutil
1212
import concurrent.futures
1313

14-
from . import samtools, picard # was: from viral_ngs import tools
1514
from . import samtools
1615
from . import picard
1716
from . import file as util_file, misc as util_misc # was: from viral_ngs import util
@@ -58,11 +57,11 @@ def align_mem_bam(self, inBam, refDb, outBam, options=None,
5857
min_score_to_filter=None, threads=None, JVMmemory=None, invert_filter=False, should_index=True):
5958
options = options or []
6059

61-
samtools = samtools.SamtoolsTool()
60+
samtools_tool = samtools.SamtoolsTool()
6261
threads = util_misc.sanitize_thread_count(threads)
6362

6463
# fetch list of RGs
65-
rgs = list(samtools.getReadGroups(inBam).keys())
64+
rgs = list(samtools_tool.getReadGroups(inBam).keys())
6665

6766
if len(rgs) == 0:
6867
# Can't do this
@@ -138,10 +137,10 @@ def align_mem_one_rg(self, inBam, refDb, outBam, rgid=None, options=None,
138137
"""
139138
options = options or []
140139

141-
samtools = samtools.SamtoolsTool()
140+
samtools_tool = samtools.SamtoolsTool()
142141

143142
# Require exactly one RG
144-
rgs = samtools.getReadGroups(inBam)
143+
rgs = samtools_tool.getReadGroups(inBam)
145144
if len(rgs) == 0:
146145
raise InvalidBamHeaderError("{} lacks read groups".format(inBam))
147146
elif len(rgs) == 1:
@@ -157,27 +156,27 @@ def align_mem_one_rg(self, inBam, refDb, outBam, rgid=None, options=None,
157156
removeInput = False
158157
if len(rgs) == 1:
159158
one_rg_inBam = inBam
160-
samtools.SamtoolsTool().dumpHeader(one_rg_inBam, headerFile)
159+
samtools_tool.dumpHeader(one_rg_inBam, headerFile)
161160
else:
162161
# strip inBam to one read group
163162
with util_file.tempfname('.onebam.bam') as tmp_bam:
164-
samtools.view(['-b', '-r', rgid], inBam, tmp_bam)
163+
samtools_tool.view(['-b', '-r', rgid], inBam, tmp_bam)
165164
# special exit if this file is empty
166-
if samtools.count(tmp_bam) == 0:
165+
if samtools_tool.count(tmp_bam) == 0:
167166
log.warning("No reads present for RG %s in file: %s", rgid, inBam)
168167
return
169168
# simplify BAM header otherwise Novoalign gets confused
170169
one_rg_inBam = util_file.mkstempfname('.{}.in.bam'.format(rgid))
171170
removeInput = True
172-
171+
173172
with open(headerFile, 'wt') as outf:
174-
for row in samtools.getHeader(inBam):
173+
for row in samtools_tool.getHeader(inBam):
175174
if len(row) > 0 and row[0] == '@RG':
176175
if rgid != list(x[3:] for x in row if x.startswith('ID:'))[0]:
177176
# skip all read groups that are not rgid
178177
continue
179178
outf.write('\t'.join(row) + '\n')
180-
samtools.reheader(tmp_bam, headerFile, one_rg_inBam)
179+
samtools_tool.reheader(tmp_bam, headerFile, one_rg_inBam)
181180

182181
# perform actual alignment
183182

@@ -212,10 +211,10 @@ def mem(self, inReads, refDb, outAlign, options=None, min_score_to_filter=None,
212211
if '-t' not in options:
213212
options.extend(('-t', str(threads)))
214213

215-
samtools = samtools.SamtoolsTool()
214+
samtools_tool = samtools.SamtoolsTool()
216215

217216
aln_sam = util_file.mkstempfname('.aligned.sam')
218-
fastq_pipe = samtools.bam2fq_pipe(inReads)
217+
fastq_pipe = samtools_tool.bam2fq_pipe(inReads)
219218
self.execute('mem', options + ['-p', refDb, '-'], stdout=aln_sam, stdin=fastq_pipe.stdout)
220219

221220
if fastq_pipe.poll():
@@ -231,12 +230,12 @@ def mem(self, inReads, refDb, outAlign, options=None, min_score_to_filter=None,
231230
aln_sam_filtered = aln_sam
232231

233232

234-
samtools.sort(aln_sam_filtered, outAlign, threads=threads)
233+
samtools_tool.sort(aln_sam_filtered, outAlign, threads=threads)
235234
os.unlink(aln_sam_filtered)
236235

237236
# cannot index sam files; only do so if a bam/cram is desired
238237
if should_index and (outAlign.endswith(".bam") or outAlign.endswith(".cram")):
239-
samtools.index(outAlign)
238+
samtools_tool.index(outAlign)
240239

241240
def filter_sam_on_alignment_score(self, in_sam, out_sam, min_score_to_filter,
242241
bwa_options, invert_filter=False):

src/viral_ngs/core/minimap2.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
from Bio import SeqIO
1414

15-
from . import samtools, picard # was: from viral_ngs import tools
1615
from . import samtools
1716
from . import picard
1817
from . import file as util_file, misc as util_misc # was: from viral_ngs import util
@@ -54,7 +53,7 @@ def align_bam(self, inBam, refDb, outBam, options=None,
5453
threads = util_misc.sanitize_thread_count(threads)
5554

5655
# fetch list of RGs
57-
rgs = list(samtools.getReadGroups(inBam).keys())
56+
rgs = list(samtools_tool.getReadGroups(inBam).keys())
5857

5958
if len(rgs) == 0:
6059
# Can't do this
@@ -79,16 +78,16 @@ def align_bam(self, inBam, refDb, outBam, options=None,
7978
threads=threads,
8079
should_index=False # Don't index intermediate BAMs that will be merged
8180
)
82-
if not samtools.isEmpty(tmp_bam):
81+
if not samtools_tool.isEmpty(tmp_bam):
8382
align_bams.append(tmp_bam)
8483
else:
8584
log.warning("No alignment output for RG %s in file %s against %s", rg, inBam, refDb)
8685

8786
if len(align_bams)==0:
8887
log.warning("All read groups in file %s appear to be empty.", inBam)
8988
with util_file.tempfname('.empty.sam') as empty_sam:
90-
samtools.dumpHeader(inBam, empty_sam)
91-
samtools.sort(empty_sam, outBam)
89+
samtools_tool.dumpHeader(inBam, empty_sam)
90+
samtools_tool.sort(empty_sam, outBam)
9291
else:
9392
# Merge BAMs, sort, and index
9493
picardOptions = ['SORT_ORDER=coordinate', 'USE_THREADING=true', 'CREATE_INDEX=true']
@@ -117,7 +116,7 @@ def align_one_rg(self, inBam, refDb, outBam, rgid=None, preset=None, options=Non
117116
samtools_tool = samtools.SamtoolsTool()
118117

119118
# Require exactly one RG
120-
rgs = samtools.getReadGroups(inBam)
119+
rgs = samtools_tool.getReadGroups(inBam)
121120
if len(rgs) == 0:
122121
raise InvalidBamHeaderError("{} lacks read groups".format(inBam))
123122
elif len(rgs) == 1:
@@ -133,13 +132,13 @@ def align_one_rg(self, inBam, refDb, outBam, rgid=None, preset=None, options=Non
133132
removeInput = False
134133
if len(rgs) == 1:
135134
one_rg_inBam = inBam
136-
samtools.SamtoolsTool().dumpHeader(one_rg_inBam, headerFile)
135+
samtools_tool.dumpHeader(one_rg_inBam, headerFile)
137136
else:
138137
# strip inBam to one read group
139138
with util_file.tempfname('.onebam.bam') as tmp_bam:
140-
samtools.view(['-1', '-r', rgid], inBam, tmp_bam)
139+
samtools_tool.view(['-1', '-r', rgid], inBam, tmp_bam)
141140
# special exit if this file is empty
142-
if samtools.isEmpty(tmp_bam):
141+
if samtools_tool.isEmpty(tmp_bam):
143142
log.warning("No reads present for RG %s in file: %s", rgid, inBam)
144143
shutil.copyfile(tmp_bam, outBam)
145144
return
@@ -148,13 +147,13 @@ def align_one_rg(self, inBam, refDb, outBam, rgid=None, preset=None, options=Non
148147
removeInput = True
149148

150149
with open(headerFile, 'wt') as outf:
151-
for row in samtools.getHeader(inBam):
150+
for row in samtools_tool.getHeader(inBam):
152151
if len(row) > 0 and row[0] == '@RG':
153152
if rgid != list(x[3:] for x in row if x.startswith('ID:'))[0]:
154153
# skip all read groups that are not rgid
155154
continue
156155
outf.write('\t'.join(row) + '\n')
157-
samtools.reheader(tmp_bam, headerFile, one_rg_inBam)
156+
samtools_tool.reheader(tmp_bam, headerFile, one_rg_inBam)
158157

159158
# get the read group line to give to mm2
160159
readgroup_line = ""
@@ -187,7 +186,7 @@ def align_one_rg(self, inBam, refDb, outBam, rgid=None, preset=None, options=Non
187186
options.extend(('-x', preset))
188187

189188
# perform actual alignment
190-
if samtools.isEmpty(one_rg_inBam):
189+
if samtools_tool.isEmpty(one_rg_inBam):
191190
log.warning("Input file %s appears to lack reads for RG '%s'", inBam, rgid)
192191
# minimap doesn't like empty inputs, so copy empty bam through
193192
# samtools.sort(one_rg_inBam, outBam)
@@ -213,20 +212,21 @@ def align_cmd(self, inReads, refDb, outAlign, options=None, threads=None, should
213212
samtools_tool = samtools.SamtoolsTool()
214213

215214
with util_file.tempfname('.aligned.sam') as aln_sam:
216-
fastq_pipe = samtools.bam2fq_pipe(inReads)
215+
fastq_pipe = samtools_tool.bam2fq_pipe(inReads)
217216
options.extend(('-a', refDb, '-', '-o', aln_sam))
218217
self.execute(options, stdin=fastq_pipe.stdout)
219218
if fastq_pipe.wait():
220219
raise subprocess.CalledProcessError(fastq_pipe.returncode, "samtools.bam2fq_pipe() for {}".format(inReads))
221-
samtools.sort(aln_sam, outAlign, threads=threads)
220+
samtools_tool.sort(aln_sam, outAlign, threads=threads)
222221

223222
# cannot index sam files; only do so if a bam/cram is desired
224223
if should_index and (outAlign.endswith(".bam") or outAlign.endswith(".cram")):
225-
samtools.index(outAlign, threads=threads)
224+
samtools_tool.index(outAlign, threads=threads)
226225

227226
def scaffold(self, contigs_fasta, ref_fasta, outAlign, divergence=20, options=None, threads=None):
228227
options = [] if not options else options
229228

229+
samtools_tool = samtools.SamtoolsTool()
230230
threads = util_misc.sanitize_thread_count(threads)
231231
if '-t' not in options:
232232
options.extend(('-t', str(threads)))
@@ -243,11 +243,11 @@ def scaffold(self, contigs_fasta, ref_fasta, outAlign, divergence=20, options=No
243243
with util_file.tempfname('.aligned.sam') as aln_sam:
244244
options.extend(('-a', ref_fasta, contigs_fasta, '-o', aln_sam))
245245
self.execute(options)
246-
samtools.sort(aln_sam, outAlign, threads=threads)
246+
samtools_tool.sort(aln_sam, outAlign, threads=threads)
247247

248248
# cannot index sam files; only do so if a bam/cram is desired
249249
if (outAlign.endswith(".bam") or outAlign.endswith(".cram")):
250-
samtools.index(outAlign)
250+
samtools_tool.index(outAlign)
251251

252252
def idxstats(self, inReads, refDb, outIdxstats, outReadlist=None, threads=None):
253253
"""
@@ -302,7 +302,7 @@ def idxstats(self, inReads, refDb, outIdxstats, outReadlist=None, threads=None):
302302

303303
try:
304304
# Start samtools bam2fq pipe for input (use 4 threads for BAM decompression)
305-
fastq_pipe = samtools.bam2fq_pipe(inReads, threads=4)
305+
fastq_pipe = samtools_tool.bam2fq_pipe(inReads, threads=4)
306306

307307
# Start minimap2 process with PAF output to stdout
308308
tool_cmd = [self.install_and_get_path()] + options

src/viral_ngs/core/novoalign.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ def execute(self, inBam, refFasta, outBam, options=None, min_qual=0, JVMmemory=N
7777
'''
7878
options = options or ["-r", "Random"]
7979

80-
samtools = samtools.SamtoolsTool()
80+
samtools_tool = samtools.SamtoolsTool()
8181

8282
# fetch list of RGs
83-
rgs = samtools.getReadGroups(inBam)
83+
rgs = samtools_tool.getReadGroups(inBam)
8484

85-
rgs_list = list(samtools.getReadGroups(inBam).keys())
85+
rgs_list = list(samtools_tool.getReadGroups(inBam).keys())
8686
if len(rgs) == 0:
8787
# Can't do this
8888
raise InvalidBamHeaderError("{} lacks read groups".format(inBam))
@@ -127,10 +127,10 @@ def align_one_rg_bam(self, inBam, refFasta, outBam, rgid=None, rgs=None, options
127127
'''
128128
options = options or ["-r", "Random"]
129129

130-
samtools = samtools.SamtoolsTool()
130+
samtools_tool = samtools.SamtoolsTool()
131131

132132
# Require exactly one RG
133-
rgs = rgs if rgs is not None else samtools.getReadGroups(inBam)
133+
rgs = rgs if rgs is not None else samtools_tool.getReadGroups(inBam)
134134
if len(rgs) == 0:
135135
raise InvalidBamHeaderError("{} lacks read groups".format(inBam))
136136
elif len(rgs) == 1:
@@ -148,22 +148,22 @@ def align_one_rg_bam(self, inBam, refFasta, outBam, rgid=None, rgs=None, options
148148
else:
149149
# strip inBam to one read group
150150
tmp_bam = util_file.mkstempfname('.onebam.bam')
151-
samtools.view(['-b', '-r', rgid], inBam, tmp_bam)
151+
samtools_tool.view(['-b', '-r', rgid], inBam, tmp_bam)
152152
# special exit if this file is empty
153-
if samtools.count(tmp_bam) == 0:
153+
if samtools_tool.count(tmp_bam) == 0:
154154
return
155155

156156
# simplify BAM header otherwise Novoalign gets confused
157157
one_rg_inBam = util_file.mkstempfname('.{}.in.bam'.format(rgid))
158158
headerFile = util_file.mkstempfname('.{}.header.txt'.format(rgid))
159159
with open(headerFile, 'wt') as outf:
160-
for row in samtools.getHeader(inBam):
160+
for row in samtools_tool.getHeader(inBam):
161161
if len(row) > 0 and row[0] == '@RG':
162162
if rgid != list(x[3:] for x in row if x.startswith('ID:'))[0]:
163163
# skip all read groups that are not rgid
164164
continue
165165
outf.write('\t'.join(row) + '\n')
166-
samtools.reheader(tmp_bam, headerFile, one_rg_inBam)
166+
samtools_tool.reheader(tmp_bam, headerFile, one_rg_inBam)
167167
os.unlink(tmp_bam)
168168
os.unlink(headerFile)
169169

@@ -179,7 +179,7 @@ def align_one_rg_bam(self, inBam, refFasta, outBam, rgid=None, rgs=None, options
179179
# Samtools filter (optional)
180180
if min_qual:
181181
tmp_bam2 = util_file.mkstempfname('.filtered.bam')
182-
samtools.view(['-b', '-S', '-1', '-q', str(min_qual)], tmp_sam, tmp_bam2)
182+
samtools_tool.view(['-b', '-S', '-1', '-q', str(min_qual)], tmp_sam, tmp_bam2)
183183
os.unlink(tmp_sam)
184184
tmp_sam = tmp_bam2
185185

src/viral_ngs/core/picard.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ def downsample_to_approx_count(
389389
JVMmemory=None
390390
): # pylint: disable=W0221):
391391

392-
samtools = samtools.SamtoolsTool()
393-
total_read_count = samtools.count(inBam)
392+
samtools_tool = samtools.SamtoolsTool()
393+
total_read_count = samtools_tool.count(inBam)
394394

395395
if total_read_count == 0:
396396
_log.info("Input BAM has no reads. Copying to output.")

0 commit comments

Comments
 (0)