seqcounter 1.0.0__tar.gz → 1.0.2__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.
Files changed (28) hide show
  1. {seqcounter-1.0.0/seqcounter.egg-info → seqcounter-1.0.2}/PKG-INFO +15 -6
  2. {seqcounter-1.0.0 → seqcounter-1.0.2}/README.md +14 -5
  3. {seqcounter-1.0.0 → seqcounter-1.0.2}/pyproject.toml +5 -0
  4. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/__init__.py +2 -1
  5. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/counts.py +13 -2
  6. {seqcounter-1.0.0 → seqcounter-1.0.2/seqcounter.egg-info}/PKG-INFO +15 -6
  7. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_counts.py +13 -3
  8. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_seal.py +2 -2
  9. {seqcounter-1.0.0 → seqcounter-1.0.2}/LICENSE +0 -0
  10. {seqcounter-1.0.0 → seqcounter-1.0.2}/MANIFEST.in +0 -0
  11. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/__main__.py +0 -0
  12. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/cli.py +0 -0
  13. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/errors.py +0 -0
  14. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/references.py +0 -0
  15. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/samples.py +0 -0
  16. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter/seal.py +0 -0
  17. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter.egg-info/SOURCES.txt +0 -0
  18. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter.egg-info/dependency_links.txt +0 -0
  19. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter.egg-info/entry_points.txt +0 -0
  20. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter.egg-info/requires.txt +0 -0
  21. {seqcounter-1.0.0 → seqcounter-1.0.2}/seqcounter.egg-info/top_level.txt +0 -0
  22. {seqcounter-1.0.0 → seqcounter-1.0.2}/setup.cfg +0 -0
  23. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/__init__.py +0 -0
  24. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/conftest.py +0 -0
  25. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_cli.py +0 -0
  26. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_integration.py +0 -0
  27. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_references.py +0 -0
  28. {seqcounter-1.0.0 → seqcounter-1.0.2}/tests/test_samples.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqcounter
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Count the occurrences of reference sequences in sequencing reads or genome assemblies
5
5
  Author-email: Marc-Olivier Duceppe <marc-olivier.duceppe@inspection.gc.ca>
6
6
  License-Expression: MIT
@@ -31,8 +31,8 @@ Dynamic: license-file
31
31
 
32
32
  <p align="center">
33
33
  <picture>
34
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo_dark.svg">
35
- <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
34
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo_dark.svg">
35
+ <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
36
36
  </picture>
37
37
  </p>
38
38
 
@@ -44,6 +44,7 @@ Dynamic: license-file
44
44
  <img src="https://img.shields.io/badge/python-3.10%E2%80%933.14-blue" alt="Python 3.10–3.14">
45
45
  <a href="LICENSE"><img src="https://img.shields.io/github/license/duceppemo/seqcounter" alt="License"></a>
46
46
  <a href="https://github.com/duceppemo/seqcounter/wiki"><img src="https://img.shields.io/badge/docs-wiki-informational" alt="Documentation"></a>
47
+ <a href="https://doi.org/10.5281/zenodo.23826933"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23826933.svg" alt="DOI"></a>
47
48
  </p>
48
49
 
49
50
  Count how many sequencing reads (fastq) or contigs (fasta) contain each sequence of a reference file, with
@@ -77,6 +78,9 @@ twice 1 0 154 65
77
78
  * **Nothing to configure**: no database, no index, no reference genome. Only the Python standard library, plus
78
79
  BBTools.
79
80
  * **Tested**: 105 tests, 100% coverage, end to end on synthetic references, genomes and reads with known counts.
81
+ * **Validated** on real data: counts identical to an independent k-mer scan, read for read; 16.1 copies recovered
82
+ for a 16-copy element in simulated reads; a negative control at zero throughout
83
+ ([Validation](https://github.com/duceppemo/seqcounter/wiki/Validation)).
80
84
 
81
85
  ## Installation
82
86
  ```
@@ -105,10 +109,12 @@ reads match it. See [How it works](https://github.com/duceppemo/seqcounter/wiki/
105
109
 
106
110
  ## Documentation
107
111
  The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
108
- [installation](https://github.com/duceppemo/seqcounter/wiki/Installation),
109
- [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
112
+ [installation](https://github.com/duceppemo/seqcounter/wiki/Installation), a
113
+ [tutorial](https://github.com/duceppemo/seqcounter/wiki/Tutorial) that counts IS6110 in three public samples in ten
114
+ minutes, [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
110
115
  [output files](https://github.com/duceppemo/seqcounter/wiki/Output-files),
111
116
  [how it works](https://github.com/duceppemo/seqcounter/wiki/How-it-works),
117
+ [validation](https://github.com/duceppemo/seqcounter/wiki/Validation),
112
118
  [troubleshooting](https://github.com/duceppemo/seqcounter/wiki/Troubleshooting) and the
113
119
  [FAQ](https://github.com/duceppemo/seqcounter/wiki/FAQ).
114
120
 
@@ -116,7 +122,10 @@ The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
116
122
  If you use seqcounter, please cite it and BBTools, which does the counting:
117
123
 
118
124
  > Duceppe M-O. seqcounter: count the occurrences of reference sequences in sequencing reads or genome assemblies.
119
- > https://github.com/duceppemo/seqcounter
125
+ > Zenodo. https://doi.org/10.5281/zenodo.23826933
126
+
127
+ This DOI always points to the latest version; each release also has its own DOI, listed on
128
+ [Zenodo](https://doi.org/10.5281/zenodo.23826933).
120
129
 
121
130
  > Bushnell B. BBTools. https://sourceforge.net/projects/bbmap/
122
131
 
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo_dark.svg">
4
- <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo_dark.svg">
4
+ <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
5
5
  </picture>
6
6
  </p>
7
7
 
@@ -13,6 +13,7 @@
13
13
  <img src="https://img.shields.io/badge/python-3.10%E2%80%933.14-blue" alt="Python 3.10–3.14">
14
14
  <a href="LICENSE"><img src="https://img.shields.io/github/license/duceppemo/seqcounter" alt="License"></a>
15
15
  <a href="https://github.com/duceppemo/seqcounter/wiki"><img src="https://img.shields.io/badge/docs-wiki-informational" alt="Documentation"></a>
16
+ <a href="https://doi.org/10.5281/zenodo.23826933"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23826933.svg" alt="DOI"></a>
16
17
  </p>
17
18
 
18
19
  Count how many sequencing reads (fastq) or contigs (fasta) contain each sequence of a reference file, with
@@ -46,6 +47,9 @@ twice 1 0 154 65
46
47
  * **Nothing to configure**: no database, no index, no reference genome. Only the Python standard library, plus
47
48
  BBTools.
48
49
  * **Tested**: 105 tests, 100% coverage, end to end on synthetic references, genomes and reads with known counts.
50
+ * **Validated** on real data: counts identical to an independent k-mer scan, read for read; 16.1 copies recovered
51
+ for a 16-copy element in simulated reads; a negative control at zero throughout
52
+ ([Validation](https://github.com/duceppemo/seqcounter/wiki/Validation)).
49
53
 
50
54
  ## Installation
51
55
  ```
@@ -74,10 +78,12 @@ reads match it. See [How it works](https://github.com/duceppemo/seqcounter/wiki/
74
78
 
75
79
  ## Documentation
76
80
  The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
77
- [installation](https://github.com/duceppemo/seqcounter/wiki/Installation),
78
- [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
81
+ [installation](https://github.com/duceppemo/seqcounter/wiki/Installation), a
82
+ [tutorial](https://github.com/duceppemo/seqcounter/wiki/Tutorial) that counts IS6110 in three public samples in ten
83
+ minutes, [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
79
84
  [output files](https://github.com/duceppemo/seqcounter/wiki/Output-files),
80
85
  [how it works](https://github.com/duceppemo/seqcounter/wiki/How-it-works),
86
+ [validation](https://github.com/duceppemo/seqcounter/wiki/Validation),
81
87
  [troubleshooting](https://github.com/duceppemo/seqcounter/wiki/Troubleshooting) and the
82
88
  [FAQ](https://github.com/duceppemo/seqcounter/wiki/FAQ).
83
89
 
@@ -85,7 +91,10 @@ The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
85
91
  If you use seqcounter, please cite it and BBTools, which does the counting:
86
92
 
87
93
  > Duceppe M-O. seqcounter: count the occurrences of reference sequences in sequencing reads or genome assemblies.
88
- > https://github.com/duceppemo/seqcounter
94
+ > Zenodo. https://doi.org/10.5281/zenodo.23826933
95
+
96
+ This DOI always points to the latest version; each release also has its own DOI, listed on
97
+ [Zenodo](https://doi.org/10.5281/zenodo.23826933).
89
98
 
90
99
  > Bushnell B. BBTools. https://sourceforge.net/projects/bbmap/
91
100
 
@@ -64,6 +64,11 @@ show_missing = true
64
64
  line-length = 120
65
65
  target-version = "py310"
66
66
 
67
+ [tool.ruff.format]
68
+ # The code is hand-wrapped and quoted with '', and "ruff format" is not part of the workflow (CONTRIBUTING.md
69
+ # asks for "ruff check" only). This at least keeps an editor that formats on save from requoting every line.
70
+ quote-style = "single"
71
+
67
72
  [tool.ruff.lint]
68
73
  select = ["E", "F", "W", "I", "UP", "B", "SIM"]
69
74
  ignore = ["UP031", "UP032"] # str.format() is used throughout
@@ -1,3 +1,4 @@
1
1
  """Count occurrences of reference sequences in sequencing reads or genome assemblies."""
2
2
 
3
- __version__ = '1.0.0'
3
+ __version__ = '1.0.2'
4
+ DOI = '10.5281/zenodo.23826933' # Zenodo concept DOI: all versions
@@ -3,6 +3,7 @@
3
3
  import json
4
4
  import logging
5
5
  import platform
6
+ import re
6
7
  import sys
7
8
  from concurrent import futures
8
9
  from dataclasses import dataclass, field
@@ -109,6 +110,16 @@ def write_counts(results, references, path, command=None):
109
110
  SAMPLES_HEADER = ['Sample', 'Type', 'Reads', 'Bases', 'Matched', 'MatchedPct', 'Files', 'Error']
110
111
 
111
112
 
113
+ def one_cell(text):
114
+ """
115
+ Text that cannot break the table it is written into: no tab, no line break.
116
+
117
+ Seal's error output is tab aligned and several lines long, and a file name may hold anything, so neither can
118
+ be written to a tab separated file as it comes.
119
+ """
120
+ return re.sub(r'[\t\r\n]+', ' ', text).strip()
121
+
122
+
112
123
  def write_samples(results, path):
113
124
  """One row per sample: how much was read, how much matched, and the error if it failed."""
114
125
  with open(path, 'w') as f:
@@ -120,8 +131,8 @@ def write_samples(results, path):
120
131
  '' if r.bases is None else str(r.bases),
121
132
  '' if r.matched is None else str(r.matched),
122
133
  '' if percent is None else '{:.4f}'.format(percent),
123
- ';'.join(r.files),
124
- (r.error or '').replace('\n', ' ')]) + '\n')
134
+ one_cell(';'.join(r.files)),
135
+ one_cell(r.error or '')]) + '\n')
125
136
 
126
137
 
127
138
  def write_json(results, references, path, run=None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqcounter
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Count the occurrences of reference sequences in sequencing reads or genome assemblies
5
5
  Author-email: Marc-Olivier Duceppe <marc-olivier.duceppe@inspection.gc.ca>
6
6
  License-Expression: MIT
@@ -31,8 +31,8 @@ Dynamic: license-file
31
31
 
32
32
  <p align="center">
33
33
  <picture>
34
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo_dark.svg">
35
- <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/master/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
34
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo_dark.svg">
35
+ <img src="https://raw.githubusercontent.com/duceppemo/seqcounter/main/assets/logo.svg" width="520" alt="seqcounter: count reference sequences in reads or assemblies">
36
36
  </picture>
37
37
  </p>
38
38
 
@@ -44,6 +44,7 @@ Dynamic: license-file
44
44
  <img src="https://img.shields.io/badge/python-3.10%E2%80%933.14-blue" alt="Python 3.10–3.14">
45
45
  <a href="LICENSE"><img src="https://img.shields.io/github/license/duceppemo/seqcounter" alt="License"></a>
46
46
  <a href="https://github.com/duceppemo/seqcounter/wiki"><img src="https://img.shields.io/badge/docs-wiki-informational" alt="Documentation"></a>
47
+ <a href="https://doi.org/10.5281/zenodo.23826933"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.23826933.svg" alt="DOI"></a>
47
48
  </p>
48
49
 
49
50
  Count how many sequencing reads (fastq) or contigs (fasta) contain each sequence of a reference file, with
@@ -77,6 +78,9 @@ twice 1 0 154 65
77
78
  * **Nothing to configure**: no database, no index, no reference genome. Only the Python standard library, plus
78
79
  BBTools.
79
80
  * **Tested**: 105 tests, 100% coverage, end to end on synthetic references, genomes and reads with known counts.
81
+ * **Validated** on real data: counts identical to an independent k-mer scan, read for read; 16.1 copies recovered
82
+ for a 16-copy element in simulated reads; a negative control at zero throughout
83
+ ([Validation](https://github.com/duceppemo/seqcounter/wiki/Validation)).
80
84
 
81
85
  ## Installation
82
86
  ```
@@ -105,10 +109,12 @@ reads match it. See [How it works](https://github.com/duceppemo/seqcounter/wiki/
105
109
 
106
110
  ## Documentation
107
111
  The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
108
- [installation](https://github.com/duceppemo/seqcounter/wiki/Installation),
109
- [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
112
+ [installation](https://github.com/duceppemo/seqcounter/wiki/Installation), a
113
+ [tutorial](https://github.com/duceppemo/seqcounter/wiki/Tutorial) that counts IS6110 in three public samples in ten
114
+ minutes, [usage and options](https://github.com/duceppemo/seqcounter/wiki/Usage),
110
115
  [output files](https://github.com/duceppemo/seqcounter/wiki/Output-files),
111
116
  [how it works](https://github.com/duceppemo/seqcounter/wiki/How-it-works),
117
+ [validation](https://github.com/duceppemo/seqcounter/wiki/Validation),
112
118
  [troubleshooting](https://github.com/duceppemo/seqcounter/wiki/Troubleshooting) and the
113
119
  [FAQ](https://github.com/duceppemo/seqcounter/wiki/FAQ).
114
120
 
@@ -116,7 +122,10 @@ The **[wiki](https://github.com/duceppemo/seqcounter/wiki)** covers
116
122
  If you use seqcounter, please cite it and BBTools, which does the counting:
117
123
 
118
124
  > Duceppe M-O. seqcounter: count the occurrences of reference sequences in sequencing reads or genome assemblies.
119
- > https://github.com/duceppemo/seqcounter
125
+ > Zenodo. https://doi.org/10.5281/zenodo.23826933
126
+
127
+ This DOI always points to the latest version; each release also has its own DOI, listed on
128
+ [Zenodo](https://doi.org/10.5281/zenodo.23826933).
120
129
 
121
130
  > Bushnell B. BBTools. https://sourceforge.net/projects/bbmap/
122
131
 
@@ -4,7 +4,15 @@ from datetime import datetime
4
4
  import pytest
5
5
 
6
6
  from seqcounter import counts
7
- from seqcounter.counts import Result, count_sample, count_samples, counts_table, run_info, write_reports
7
+ from seqcounter.counts import (
8
+ SAMPLES_HEADER,
9
+ Result,
10
+ count_sample,
11
+ count_samples,
12
+ counts_table,
13
+ run_info,
14
+ write_reports,
15
+ )
8
16
  from seqcounter.references import Reference
9
17
  from seqcounter.samples import Sample
10
18
  from seqcounter.seal import SealError, SealStats
@@ -91,7 +99,7 @@ def test_count_samples_never_asks_for_more_jobs_than_samples(monkeypatch):
91
99
  def test_write_reports(tmp_path, monkeypatch):
92
100
  monkeypatch.setattr(counts, 'versions', lambda: {'BBTools': '39.01', 'Java': 'openjdk 17'})
93
101
  results = [result('S1', {'present': 7, 'absent': 0}, reads=200, bases=20000, matched=7),
94
- result('S2', {}, error='Seal failed (exit code 1): boom\nCommand: seal.sh')]
102
+ result('S2', {}, error='Seal failed (exit code 1): boom\nReads:\t100\tTime:\t1.2s')]
95
103
  run = run_info('seqcounter -ref refs.fasta -i reads/ -o out/', {'K-mer size': 25},
96
104
  datetime(2026, 10, 30, 12, 0).astimezone())
97
105
  rows, reports = write_reports(results, REFERENCES, tmp_path / 'out', command=run['command'], run=run)
@@ -107,7 +115,9 @@ def test_write_reports(tmp_path, monkeypatch):
107
115
  assert samples[0] == ['Sample', 'Type', 'Reads', 'Bases', 'Matched', 'MatchedPct', 'Files', 'Error']
108
116
  assert samples[1] == ['S1', 'fastq', '200', '20000', '7', '3.5000', 'S1.fastq.gz', '']
109
117
  assert samples[2][:6] == ['S2', 'fastq', '', '', '', '']
110
- assert '\n' not in samples[2][7] and 'boom' in samples[2][7] # The error stays on one line
118
+ # Seal's output is tab aligned and several lines long: it must not add columns to a tab separated file
119
+ assert len(samples[2]) == len(SAMPLES_HEADER)
120
+ assert samples[2][7] == 'Seal failed (exit code 1): boom Reads: 100 Time: 1.2s'
111
121
 
112
122
  report = json.loads(reports[2].read_text())
113
123
  assert report['tool'] == 'seqcounter' and report['command'] == run['command']
@@ -125,7 +125,7 @@ def test_safe_paths_keeps_right_extension(tmp_path):
125
125
  [str(tmp_path / 'reads.fastq')]
126
126
 
127
127
 
128
- def test_safe_temporary_folder(monkeypatch):
128
+ def test_safe_temporary_folder(tmp_path, monkeypatch):
129
129
  folder = seal.safe_temporary_folder()
130
130
  assert os.path.isdir(folder) and not seal.UNSAFE.search(folder)
131
131
  os.rmdir(folder)
@@ -133,7 +133,7 @@ def test_safe_temporary_folder(monkeypatch):
133
133
  made = []
134
134
 
135
135
  def unsafe_mkdtemp(prefix):
136
- folder = '/tmp/{}xmx{}'.format(prefix, len(made))
136
+ folder = str(tmp_path / '{}xmx{}'.format(prefix, len(made)))
137
137
  os.mkdir(folder)
138
138
  made.append(folder)
139
139
  return folder
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes