cutseq 0.0.5__tar.gz → 0.0.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cutseq
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Automatically cut adapter / barcode / UMI from NGS data
5
5
  Home-page: https://github.com/y9c/cutseq
6
6
  License: MIT
@@ -7,6 +7,7 @@
7
7
  # Created: 2024-04-19 18:57
8
8
 
9
9
  import argparse
10
+ import importlib.metadata
10
11
  import logging
11
12
  import re
12
13
  import subprocess
@@ -26,11 +27,13 @@ from cutadapt.modifiers import (
26
27
  PairedEndRenamer,
27
28
  QualityTrimmer,
28
29
  Renamer,
30
+ SingleEndModifier,
29
31
  SuffixRemover,
30
32
  UnconditionalCutter,
31
33
  )
32
34
  from cutadapt.pipeline import PairedEndPipeline, SingleEndPipeline
33
35
  from cutadapt.predicates import Predicate, TooShort
36
+ from cutadapt.report import Statistics
34
37
  from cutadapt.runners import make_runner
35
38
  from cutadapt.steps import (
36
39
  PairedEndFilter,
@@ -40,6 +43,54 @@ from cutadapt.steps import (
40
43
  )
41
44
  from cutadapt.utils import Progress
42
45
 
46
+ # monkey patching ....
47
+ original_method = Statistics._collect_modifier
48
+
49
+
50
+ def patched_problematic_method(self, *args, **kwargs):
51
+ try:
52
+ return original_method(self, *args, **kwargs)
53
+ except AssertionError:
54
+ pass
55
+
56
+
57
+ Statistics._collect_modifier = patched_problematic_method
58
+
59
+ ## Enhnace function for cutadapt
60
+
61
+
62
+ class IsUntrimmedAny(Predicate):
63
+ """
64
+ Select reads for which no adapter match was found
65
+ """
66
+
67
+ def __init__(self, ref_adapters):
68
+ self.ref_adapters = ref_adapters
69
+
70
+ def __repr__(self):
71
+ return "IsUntrimmedAny()"
72
+
73
+ def test(self, read, info):
74
+ # check not all adapters with ref_adapters are exist in the matches
75
+ match_adapters = [match.adapter for match in info.matches]
76
+ if any([adapter not in match_adapters for adapter in self.ref_adapters]):
77
+ return True
78
+ return False
79
+
80
+
81
+ class ReverseComplementConverter(SingleEndModifier):
82
+ def __init__(self):
83
+ self.rcd = False
84
+
85
+ def __repr__(self):
86
+ return f"UnconditionalCutter(length={self.length})"
87
+
88
+ def __call__(self, read, info):
89
+ return read.reverse_complement()
90
+
91
+
92
+ __version__ = importlib.metadata.version(__package__ or __name__)
93
+
43
94
  logging.basicConfig(
44
95
  level=logging.INFO,
45
96
  format="%(asctime)s - %(levelname)s - %(message)s",
@@ -125,25 +176,6 @@ class CutadaptConfig:
125
176
  self.threads = 1
126
177
 
127
178
 
128
- class IsUntrimmedRef(Predicate):
129
- """
130
- Select reads for which no adapter match was found
131
- """
132
-
133
- def __init__(self, ref_adapters):
134
- self.ref_adapters = ref_adapters
135
-
136
- def __repr__(self):
137
- return "IsUntrimmedRef()"
138
-
139
- def test(self, read, info):
140
- # check not all adapters with ref_adapters are exist in the matches
141
- match_adapters = [match.adapter for match in info.matches]
142
- if any([adapter not in match_adapters for adapter in self.ref_adapters]):
143
- return True
144
- return False
145
-
146
-
147
179
  def run_steps(steps, dry_run=False):
148
180
  if dry_run:
149
181
  print(
@@ -222,6 +254,10 @@ def pipeline_single(input1, output1, short1, untrimmed1, barcode, settings):
222
254
  QualityTrimmer(cutoff_front=0, cutoff_back=settings.min_quality),
223
255
  )
224
256
 
257
+ # step 9: reverse complement the read
258
+ if settings.reverse_complement:
259
+ modifiers.append(ReverseComplementConverter())
260
+
225
261
  inpaths = InputPaths(input1)
226
262
 
227
263
  with make_runner(inpaths, cores=settings.threads) as runner:
@@ -251,7 +287,7 @@ def pipeline_single(input1, output1, short1, untrimmed1, barcode, settings):
251
287
  steps.append(
252
288
  # TODO: --max-n=0 support
253
289
  SingleEndFilter(
254
- IsUntrimmedRef(ref_adapters),
290
+ IsUntrimmedAny(ref_adapters),
255
291
  outfiles.open_record_writer(untrimmed1, interleaved=False),
256
292
  ),
257
293
  )
@@ -406,6 +442,10 @@ def pipeline_paired(
406
442
  )
407
443
  )
408
444
 
445
+ # step 9: reverse complement the read
446
+ if settings.reverse_complement:
447
+ modifiers.append((ReverseComplementConverter(), ReverseComplementConverter()))
448
+
409
449
  inpaths = InputPaths(input1, input2)
410
450
 
411
451
  with make_runner(inpaths, cores=settings.threads) as runner:
@@ -432,10 +472,10 @@ def pipeline_paired(
432
472
  steps.append(
433
473
  # TODO: --max-n=0 support
434
474
  PairedEndFilter(
435
- IsUntrimmedRef([adapter_inline5])
475
+ IsUntrimmedAny([adapter_inline5])
436
476
  if barcode.inline5.len > 0
437
477
  else None,
438
- IsUntrimmedRef([adapter_inline3])
478
+ IsUntrimmedAny([adapter_inline3])
439
479
  if barcode.inline3.len > 0
440
480
  else None,
441
481
  outfiles.open_record_writer(
@@ -463,6 +503,7 @@ def run_cutseq(args):
463
503
  settings.threads = args.threads
464
504
  settings.min_length = args.min_length
465
505
  settings.dry_run = args.dry_run
506
+ settings.reverse_complement = args.reverse_complement
466
507
  if len(args.input_file) == 1:
467
508
  pipeline_single(
468
509
  args.input_file[0],
@@ -565,6 +606,12 @@ def main():
565
606
 
566
607
  parser.add_argument("--trim-polyA", action="store_true", help="Trim polyA tail.")
567
608
 
609
+ parser.add_argument(
610
+ "--reverse-complement",
611
+ action="store_true",
612
+ help="Reverse complement the reads.",
613
+ )
614
+
568
615
  parser.add_argument(
569
616
  "-t",
570
617
  "--threads",
@@ -578,6 +625,11 @@ def main():
578
625
  action="store_true",
579
626
  help="Print command instead of running it.",
580
627
  )
628
+
629
+ parser.add_argument(
630
+ "-V", "--version", action="version", version=f"%(prog)s {__version__}"
631
+ )
632
+
581
633
  args = parser.parse_args()
582
634
 
583
635
  if args.adapter_name is not None:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cutseq"
3
- version = "0.0.5"
3
+ version = "0.0.7"
4
4
  description = "Automatically cut adapter / barcode / UMI from NGS data"
5
5
  authors = ["Ye Chang <yech1990@gmail.com>"]
6
6
  license = "MIT"
File without changes
File without changes