mafutils 0.2.0__tar.gz → 0.3.0__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 (90) hide show
  1. mafutils-0.3.0/.github/workflows/tests.yml +28 -0
  2. {mafutils-0.2.0 → mafutils-0.3.0}/.gitignore +4 -1
  3. {mafutils-0.2.0 → mafutils-0.3.0}/DEVELOPMENT.md +51 -0
  4. {mafutils-0.2.0/mafutils.egg-info → mafutils-0.3.0}/PKG-INFO +24 -49
  5. {mafutils-0.2.0 → mafutils-0.3.0}/README.md +23 -48
  6. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/_version.py +3 -3
  7. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/fetch.py +61 -11
  8. {mafutils-0.2.0 → mafutils-0.3.0/mafutils.egg-info}/PKG-INFO +24 -49
  9. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/SOURCES.txt +6 -2
  10. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/scm_file_list.json +6 -2
  11. mafutils-0.3.0/mafutils.egg-info/scm_version.json +8 -0
  12. mafutils-0.3.0/tests/real-excerpt.maf +131 -0
  13. mafutils-0.3.0/tests/real-excerpt.maf.block.idx +9 -0
  14. mafutils-0.3.0/tests/real-excerpt.maf.scaffold.idx +2 -0
  15. {mafutils-0.2.0 → mafutils-0.3.0}/tests/test_fetch.py +61 -0
  16. mafutils-0.3.0/tests/test_real_data.py +115 -0
  17. mafutils-0.3.0/tests/test_stats.py +143 -0
  18. mafutils-0.2.0/benchmarks/maf_fetch.log +0 -516
  19. mafutils-0.2.0/maf_fetch.log +0 -5565
  20. mafutils-0.2.0/mafutils.egg-info/scm_version.json +0 -8
  21. {mafutils-0.2.0 → mafutils-0.3.0}/.codex +0 -0
  22. {mafutils-0.2.0 → mafutils-0.3.0}/.github/workflows/publish-pypi.yml +0 -0
  23. {mafutils-0.2.0 → mafutils-0.3.0}/AGENTS.md +0 -0
  24. {mafutils-0.2.0 → mafutils-0.3.0}/LICENSE +0 -0
  25. {mafutils-0.2.0 → mafutils-0.3.0}/THIRD_PARTY_LICENSES.md +0 -0
  26. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/README.md +0 -0
  27. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/Snakefile +0 -0
  28. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/baseline-manual-timings.txt +0 -0
  29. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/config.yaml +0 -0
  30. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/history.csv +0 -0
  31. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/notebook.ipynb +0 -0
  32. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/parse_benchmarks.py +0 -0
  33. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/profiles/slurm/config.yaml +0 -0
  34. {mafutils-0.2.0 → mafutils-0.3.0}/benchmarks/rulegraph.png +0 -0
  35. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/__init__.py +0 -0
  36. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/__main__.py +0 -0
  37. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/cli.py +0 -0
  38. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/gc.py +0 -0
  39. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/index.py +0 -0
  40. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/lib/__init__.py +0 -0
  41. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/lib/bgzf.py +0 -0
  42. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/lib/common.py +0 -0
  43. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/lib/loginit.py +0 -0
  44. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/stats.py +0 -0
  45. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils/validate.py +0 -0
  46. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/dependency_links.txt +0 -0
  47. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/entry_points.txt +0 -0
  48. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/requires.txt +0 -0
  49. {mafutils-0.2.0 → mafutils-0.3.0}/mafutils.egg-info/top_level.txt +0 -0
  50. {mafutils-0.2.0 → mafutils-0.3.0}/pyproject.toml +0 -0
  51. {mafutils-0.2.0 → mafutils-0.3.0}/setup.cfg +0 -0
  52. {mafutils-0.2.0 → mafutils-0.3.0}/tests/crossblocks-missing-species.bed +0 -0
  53. {mafutils-0.2.0 → mafutils-0.3.0}/tests/crossblocks.bed +0 -0
  54. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example-tabs.maf +0 -0
  55. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example-tabs.maf.block.idx +0 -0
  56. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example-tabs.maf.scaffold.idx +0 -0
  57. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.bed +0 -0
  58. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf +0 -0
  59. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.bgz +0 -0
  60. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.bgz.block.idx +0 -0
  61. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.bgz.scaffold.idx +0 -0
  62. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.block.idx +0 -0
  63. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.gz +0 -0
  64. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.gz.block.idx +0 -0
  65. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.gz.scaffold.idx +0 -0
  66. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.scaffold.idx +0 -0
  67. {mafutils-0.2.0 → mafutils-0.3.0}/tests/example.maf.scaffold.regenerated.idx +0 -0
  68. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/01-singleblock-gap.fa +0 -0
  69. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/02-truncatedblock.fa +0 -0
  70. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/03-crossblocks-missing.fa +0 -0
  71. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/04-singleblock.fa +0 -0
  72. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/05-crossblocks.fa +0 -0
  73. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/06-noalign.fa +0 -0
  74. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/07-negstrand.fa +0 -0
  75. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/08-span-multiple-gaps.fa +0 -0
  76. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/09-missingchrom.fa +0 -0
  77. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-fasta/10-crossblocks-missing-species.fa +0 -0
  78. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/01-singleblock-gap.maf +0 -0
  79. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/02-truncatedblock.maf +0 -0
  80. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/03-crossblocks-missing.maf +0 -0
  81. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/04-singleblock.maf +0 -0
  82. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/05-crossblocks.maf +0 -0
  83. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/06-noalign.maf +0 -0
  84. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/07-negstrand.maf +0 -0
  85. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/08-span-multiple-gaps.maf +0 -0
  86. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/09-missingchrom.maf +0 -0
  87. {mafutils-0.2.0 → mafutils-0.3.0}/tests/expected-maf/10-crossblocks-missing-species.maf +0 -0
  88. {mafutils-0.2.0 → mafutils-0.3.0}/tests/test_compression.py +0 -0
  89. {mafutils-0.2.0 → mafutils-0.3.0}/tests/test_gc.py +0 -0
  90. {mafutils-0.2.0 → mafutils-0.3.0}/tests/test_validate.py +0 -0
@@ -0,0 +1,28 @@
1
+ name: Tests
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: pytest
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Check out source
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.11"
21
+
22
+ - name: Install package and pytest
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install -e . pytest
26
+
27
+ - name: Run tests
28
+ run: pytest tests/ -v
@@ -9,4 +9,7 @@ data/
9
9
  # Regenerable benchmark pipeline output.
10
10
  benchmarks/results/
11
11
  benchmarks/summary.html
12
- benchmarks/.snakemake/
12
+ benchmarks/.snakemake/
13
+
14
+ # Stray runtime log mafutils fetch writes to its output dir by default.
15
+ maf_fetch.log
@@ -1,5 +1,9 @@
1
1
  # mafutils Development Notes
2
2
 
3
+ Internal implementation notes, gotchas, and maintainer workflows (testing,
4
+ releasing) for developing `mafutils` itself — see [`README.md`](README.md)
5
+ for user-facing installation/usage docs.
6
+
3
7
  - `tests/example.maf.scaffold.idx` is preserved as an older, headerless
4
8
  scaffold-index fixture for comparison — it deliberately sits at
5
9
  `example.maf`'s *default* scaffold-index path
@@ -155,6 +159,53 @@
155
159
  backward-seek risk entirely rather than relying on any per-region
156
160
  processing order.
157
161
 
162
+ ## Development setup
163
+
164
+ From a source checkout, an editable install picks up code changes without
165
+ reinstalling:
166
+
167
+ ```bash
168
+ pip install -e .
169
+ ```
170
+
171
+ Or run directly against the checkout without installing at all:
172
+
173
+ ```bash
174
+ python -m mafutils --help
175
+ ```
176
+
177
+ ## Testing
178
+
179
+ From inside this `mafutils/` directory:
180
+
181
+ ```bash
182
+ pytest tests/
183
+ ```
184
+
185
+ `tests/test_compression.py` covers cross-cutting compression behavior
186
+ (bgzip/gzip detection, index headers, index auto-derivation, and the
187
+ per-command parallel/sequential/fallback logic) across all three compression
188
+ types. `tests/test_validate.py` covers index integrity (size/mtime/hash
189
+ header fields, `mafutils validate`'s three-way verdict, and `--verify-hash`
190
+ on `fetch`/`stats`/`gc`). `tests/test_stats.py` checks `mafutils stats`'s
191
+ computed `overall.tsv`/`species.tsv` values against numbers hand-derived
192
+ directly from `tests/example.maf`'s raw content (not just "doesn't crash"),
193
+ including a regression guard that the sequential (`-p 1`, no
194
+ `ProcessPoolExecutor`) and real multi-worker (`-p 2`+) paths produce
195
+ identical output.
196
+
197
+ `tests/test_real_data.py` runs `stats`/`gc`/`fetch` against
198
+ `tests/real-excerpt.maf` -- 8 real, complete alignment blocks extracted
199
+ read-only from gwct's actual ~42GB production MAF
200
+ (`data/hamsters/uncompressed/...`), not hand-crafted. Unlike
201
+ `example.maf`-based tests (which verify exact hand-computed values),
202
+ these check plausibility/robustness on genuinely real data -- real
203
+ species-naming conventions, real gap patterns, real block-size
204
+ distribution -- since a hand-crafted fixture wouldn't think to include
205
+ whatever real data actually looks like. This is deliberately still a
206
+ tiny, committed fixture, not the real dataset itself: `data/hamsters/`
207
+ stays untouched and out of git (see the `.gitignore` `data/` entry).
208
+
158
209
  ## Releasing to PyPI
159
210
 
160
211
  - Publishing is triggered by pushing a Git tag that matches `v*` (for example
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mafutils
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Utilities for indexing, fetching, and summarizing MAFs.
5
5
  Author: Gregg Thomas
6
6
  License: MIT License
@@ -50,7 +50,7 @@ It currently provides five commands:
50
50
 
51
51
  ## Disclaimer
52
52
 
53
- This project was developed with significant assistance from a large language model (GPT-5 / Codex).
53
+ This project was developed with significant assistance from large language models (GPT-5 / Codex, Claude Sonnet).
54
54
 
55
55
  ## Third-Party Code
56
56
 
@@ -61,69 +61,62 @@ for full attribution and license text.
61
61
 
62
62
  ## Installation
63
63
 
64
- From this `mafutils/` directory:
65
-
66
64
  ```bash
67
- pip install -e .
65
+ pip install mafutils
68
66
  ```
69
67
 
70
- If you are using the current shared workflow environment instead of a separate
71
- package environment, you can also run it directly with:
72
-
73
- ```bash
74
- python -m mafutils --help
75
- ```
68
+ This installs the `mafutils` command.
76
69
 
77
70
  ## Quick Start
78
71
 
79
72
  Show top-level help:
80
73
 
81
74
  ```bash
82
- python -m mafutils --help
75
+ mafutils --help
83
76
  ```
84
77
 
85
78
  Create block and scaffold indexes for a MAF (defaults to
86
79
  `input.maf.block.idx` / `input.maf.scaffold.idx` if output paths are omitted):
87
80
 
88
81
  ```bash
89
- python -m mafutils index input.maf
82
+ mafutils index input.maf
90
83
  ```
91
84
 
92
85
  Fetch trimmed MAF regions from a BED file (index defaults to
93
86
  `input.maf.block.idx`):
94
87
 
95
88
  ```bash
96
- python -m mafutils fetch input.maf regions.bed -o outdir
89
+ mafutils fetch input.maf regions.bed -o outdir
97
90
  ```
98
91
 
99
92
  Fetch FASTA output instead of MAF:
100
93
 
101
94
  ```bash
102
- python -m mafutils fetch input.maf regions.bed -o outdir -f -fh species-coords-id
95
+ mafutils fetch input.maf regions.bed -o outdir -f -fh species-coords-id
103
96
  ```
104
97
 
105
98
  Extract full scaffolds using a scaffold index:
106
99
 
107
100
  ```bash
108
- python -m mafutils fetch input.maf scaffolds.bed -m scaffold -o outdir
101
+ mafutils fetch input.maf scaffolds.bed -m scaffold -o outdir
109
102
  ```
110
103
 
111
104
  Summarize an indexed MAF:
112
105
 
113
106
  ```bash
114
- python -m mafutils stats input.maf -o summary/example
107
+ mafutils stats input.maf -o summary/example
115
108
  ```
116
109
 
117
110
  Calculate per-species GC content:
118
111
 
119
112
  ```bash
120
- python -m mafutils gc input.maf -o summary/example
113
+ mafutils gc input.maf -o summary/example
121
114
  ```
122
115
 
123
116
  Check whether an index is still trustworthy (see Index Integrity below):
124
117
 
125
118
  ```bash
126
- python -m mafutils validate input.maf
119
+ mafutils validate input.maf
127
120
  ```
128
121
 
129
122
  ## Compression
@@ -139,13 +132,10 @@ python -m mafutils validate input.maf
139
132
  If you plan to use `--processes > 1` against compressed input, compress with
140
133
  `bgzip` rather than plain `gzip` to get real parallel speedup.
141
134
 
142
- **No extra dependency for bgzip:** BGZF support is provided by
143
- `mafutils/lib/bgzf.py`, vendored from [Biopython](https://biopython.org/)
144
- (see the notice at the top of that file and `THIRD_PARTY_LICENSES.md`) rather
145
- than by depending on the `biopython` package, which would otherwise pull in
146
- `numpy` for no other reason. This does mean upstream bug fixes to that file
147
- aren't picked up automatically — see `DEVELOPMENT.md` if you're debugging a
148
- bgzip-related issue.
135
+ **No extra dependency for bgzip:** BGZF support is vendored in
136
+ `mafutils/lib/bgzf.py` rather than depending on the `biopython` package —
137
+ see `DEVELOPMENT.md` for why, and [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md)
138
+ for attribution.
149
139
 
150
140
  ## Index Integrity
151
141
 
@@ -184,7 +174,7 @@ For a "check once, trust thereafter" workflow instead of passing
184
174
  Create block and scaffold indexes for a MAF file.
185
175
 
186
176
  ```bash
187
- python -m mafutils index MAF_FILE [BLOCK_INDEX] [SCAFFOLD_INDEX]
177
+ mafutils index MAF_FILE [BLOCK_INDEX] [SCAFFOLD_INDEX]
188
178
  ```
189
179
 
190
180
  Arguments:
@@ -200,7 +190,7 @@ Arguments:
200
190
  Fetch regions or scaffolds from a MAF using an existing index.
201
191
 
202
192
  ```bash
203
- python -m mafutils fetch [OPTIONS] MAF_FILE BED_FILE
193
+ mafutils fetch [OPTIONS] MAF_FILE BED_FILE
204
194
  ```
205
195
 
206
196
  Arguments:
@@ -224,6 +214,7 @@ Options:
224
214
  | `--fasta-dedupe` | FASTA duplicate handling: `none` or `most-seq` |
225
215
  | `--processes`, `-p` | Number of worker processes (see Compression above — plain gzip always runs single-process) |
226
216
  | `--mode`, `-m` | Fetch mode: `block` or `scaffold` |
217
+ | `--scaffold-subdirs` | Group output files into subfolders named by reference scaffold (`<output>/<scaffold>/<basename>`) instead of one flat directory |
227
218
  | `--verbose` | Emit warning lines from each completed batch |
228
219
  | `--profile` | Log internal timing breakdowns |
229
220
  | `--verify-hash` | Verify the index's stored content hash against the MAF file (see Index Integrity above) |
@@ -239,7 +230,7 @@ file) order, and decodes shared blocks only once even when regions overlap.
239
230
  Summarize an indexed MAF at overall, species, and block levels.
240
231
 
241
232
  ```bash
242
- python -m mafutils stats [OPTIONS] MAF_FILE [INDEX_FILE]
233
+ mafutils stats [OPTIONS] MAF_FILE [INDEX_FILE]
243
234
  ```
244
235
 
245
236
  Arguments:
@@ -276,7 +267,7 @@ GC is computed as `(G+C) / (A+C+G+T)`, case-insensitive; gaps, `N`, and other
276
267
  ambiguity codes are excluded from both the numerator and denominator.
277
268
 
278
269
  ```bash
279
- python -m mafutils gc MAF_FILE [INDEX_FILE] [OPTIONS]
270
+ mafutils gc MAF_FILE [INDEX_FILE] [OPTIONS]
280
271
  ```
281
272
 
282
273
  Arguments:
@@ -329,7 +320,7 @@ two indexes weren't built together (e.g. only one was rebuilt) and
329
320
  shouldn't be trusted as a matched pair.
330
321
 
331
322
  ```bash
332
- python -m mafutils validate MAF_FILE [INDEX_FILE]
323
+ mafutils validate MAF_FILE [INDEX_FILE]
333
324
  ```
334
325
 
335
326
  Arguments:
@@ -349,23 +340,7 @@ verdict. Exit codes:
349
340
  | `1` | MISMATCH | A conclusive difference was found (compression, size, or hash against the MAF file; or the block and scaffold index headers disagree) — rebuild the index(es). |
350
341
  | `2` | UNVERIFIABLE | Nothing contradicts, but there's no stored hash to be fully sure (the index predates this feature), or the scaffold index is missing/headerless so the pair couldn't be cross-checked. |
351
342
 
352
- ## Testing
353
-
354
- From inside this `mafutils/` directory:
355
-
356
- ```bash
357
- pytest tests/
358
- ```
359
-
360
- `tests/test_compression.py` covers cross-cutting compression behavior
361
- (bgzip/gzip detection, index headers, index auto-derivation, and the
362
- per-command parallel/sequential/fallback logic described above) across all
363
- three compression types. `tests/test_validate.py` covers index integrity
364
- (size/mtime/hash header fields, `mafutils validate`'s three-way verdict, and
365
- `--verify-hash` on `fetch`/`stats`/`gc`).
366
-
367
343
  ## Notes
368
344
 
369
- - `python -m mafutils ...` is the supported invocation from a source checkout.
370
- - The installed console entrypoint is `mafutils ...` once the package is
371
- installed.
345
+ For development setup (installing from a source checkout), running the test
346
+ suite, and internal implementation notes, see [`DEVELOPMENT.md`](DEVELOPMENT.md).
@@ -13,7 +13,7 @@ It currently provides five commands:
13
13
 
14
14
  ## Disclaimer
15
15
 
16
- This project was developed with significant assistance from a large language model (GPT-5 / Codex).
16
+ This project was developed with significant assistance from large language models (GPT-5 / Codex, Claude Sonnet).
17
17
 
18
18
  ## Third-Party Code
19
19
 
@@ -24,69 +24,62 @@ for full attribution and license text.
24
24
 
25
25
  ## Installation
26
26
 
27
- From this `mafutils/` directory:
28
-
29
27
  ```bash
30
- pip install -e .
28
+ pip install mafutils
31
29
  ```
32
30
 
33
- If you are using the current shared workflow environment instead of a separate
34
- package environment, you can also run it directly with:
35
-
36
- ```bash
37
- python -m mafutils --help
38
- ```
31
+ This installs the `mafutils` command.
39
32
 
40
33
  ## Quick Start
41
34
 
42
35
  Show top-level help:
43
36
 
44
37
  ```bash
45
- python -m mafutils --help
38
+ mafutils --help
46
39
  ```
47
40
 
48
41
  Create block and scaffold indexes for a MAF (defaults to
49
42
  `input.maf.block.idx` / `input.maf.scaffold.idx` if output paths are omitted):
50
43
 
51
44
  ```bash
52
- python -m mafutils index input.maf
45
+ mafutils index input.maf
53
46
  ```
54
47
 
55
48
  Fetch trimmed MAF regions from a BED file (index defaults to
56
49
  `input.maf.block.idx`):
57
50
 
58
51
  ```bash
59
- python -m mafutils fetch input.maf regions.bed -o outdir
52
+ mafutils fetch input.maf regions.bed -o outdir
60
53
  ```
61
54
 
62
55
  Fetch FASTA output instead of MAF:
63
56
 
64
57
  ```bash
65
- python -m mafutils fetch input.maf regions.bed -o outdir -f -fh species-coords-id
58
+ mafutils fetch input.maf regions.bed -o outdir -f -fh species-coords-id
66
59
  ```
67
60
 
68
61
  Extract full scaffolds using a scaffold index:
69
62
 
70
63
  ```bash
71
- python -m mafutils fetch input.maf scaffolds.bed -m scaffold -o outdir
64
+ mafutils fetch input.maf scaffolds.bed -m scaffold -o outdir
72
65
  ```
73
66
 
74
67
  Summarize an indexed MAF:
75
68
 
76
69
  ```bash
77
- python -m mafutils stats input.maf -o summary/example
70
+ mafutils stats input.maf -o summary/example
78
71
  ```
79
72
 
80
73
  Calculate per-species GC content:
81
74
 
82
75
  ```bash
83
- python -m mafutils gc input.maf -o summary/example
76
+ mafutils gc input.maf -o summary/example
84
77
  ```
85
78
 
86
79
  Check whether an index is still trustworthy (see Index Integrity below):
87
80
 
88
81
  ```bash
89
- python -m mafutils validate input.maf
82
+ mafutils validate input.maf
90
83
  ```
91
84
 
92
85
  ## Compression
@@ -102,13 +95,10 @@ python -m mafutils validate input.maf
102
95
  If you plan to use `--processes > 1` against compressed input, compress with
103
96
  `bgzip` rather than plain `gzip` to get real parallel speedup.
104
97
 
105
- **No extra dependency for bgzip:** BGZF support is provided by
106
- `mafutils/lib/bgzf.py`, vendored from [Biopython](https://biopython.org/)
107
- (see the notice at the top of that file and `THIRD_PARTY_LICENSES.md`) rather
108
- than by depending on the `biopython` package, which would otherwise pull in
109
- `numpy` for no other reason. This does mean upstream bug fixes to that file
110
- aren't picked up automatically — see `DEVELOPMENT.md` if you're debugging a
111
- bgzip-related issue.
98
+ **No extra dependency for bgzip:** BGZF support is vendored in
99
+ `mafutils/lib/bgzf.py` rather than depending on the `biopython` package —
100
+ see `DEVELOPMENT.md` for why, and [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md)
101
+ for attribution.
112
102
 
113
103
  ## Index Integrity
114
104
 
@@ -147,7 +137,7 @@ For a "check once, trust thereafter" workflow instead of passing
147
137
  Create block and scaffold indexes for a MAF file.
148
138
 
149
139
  ```bash
150
- python -m mafutils index MAF_FILE [BLOCK_INDEX] [SCAFFOLD_INDEX]
140
+ mafutils index MAF_FILE [BLOCK_INDEX] [SCAFFOLD_INDEX]
151
141
  ```
152
142
 
153
143
  Arguments:
@@ -163,7 +153,7 @@ Arguments:
163
153
  Fetch regions or scaffolds from a MAF using an existing index.
164
154
 
165
155
  ```bash
166
- python -m mafutils fetch [OPTIONS] MAF_FILE BED_FILE
156
+ mafutils fetch [OPTIONS] MAF_FILE BED_FILE
167
157
  ```
168
158
 
169
159
  Arguments:
@@ -187,6 +177,7 @@ Options:
187
177
  | `--fasta-dedupe` | FASTA duplicate handling: `none` or `most-seq` |
188
178
  | `--processes`, `-p` | Number of worker processes (see Compression above — plain gzip always runs single-process) |
189
179
  | `--mode`, `-m` | Fetch mode: `block` or `scaffold` |
180
+ | `--scaffold-subdirs` | Group output files into subfolders named by reference scaffold (`<output>/<scaffold>/<basename>`) instead of one flat directory |
190
181
  | `--verbose` | Emit warning lines from each completed batch |
191
182
  | `--profile` | Log internal timing breakdowns |
192
183
  | `--verify-hash` | Verify the index's stored content hash against the MAF file (see Index Integrity above) |
@@ -202,7 +193,7 @@ file) order, and decodes shared blocks only once even when regions overlap.
202
193
  Summarize an indexed MAF at overall, species, and block levels.
203
194
 
204
195
  ```bash
205
- python -m mafutils stats [OPTIONS] MAF_FILE [INDEX_FILE]
196
+ mafutils stats [OPTIONS] MAF_FILE [INDEX_FILE]
206
197
  ```
207
198
 
208
199
  Arguments:
@@ -239,7 +230,7 @@ GC is computed as `(G+C) / (A+C+G+T)`, case-insensitive; gaps, `N`, and other
239
230
  ambiguity codes are excluded from both the numerator and denominator.
240
231
 
241
232
  ```bash
242
- python -m mafutils gc MAF_FILE [INDEX_FILE] [OPTIONS]
233
+ mafutils gc MAF_FILE [INDEX_FILE] [OPTIONS]
243
234
  ```
244
235
 
245
236
  Arguments:
@@ -292,7 +283,7 @@ two indexes weren't built together (e.g. only one was rebuilt) and
292
283
  shouldn't be trusted as a matched pair.
293
284
 
294
285
  ```bash
295
- python -m mafutils validate MAF_FILE [INDEX_FILE]
286
+ mafutils validate MAF_FILE [INDEX_FILE]
296
287
  ```
297
288
 
298
289
  Arguments:
@@ -312,23 +303,7 @@ verdict. Exit codes:
312
303
  | `1` | MISMATCH | A conclusive difference was found (compression, size, or hash against the MAF file; or the block and scaffold index headers disagree) — rebuild the index(es). |
313
304
  | `2` | UNVERIFIABLE | Nothing contradicts, but there's no stored hash to be fully sure (the index predates this feature), or the scaffold index is missing/headerless so the pair couldn't be cross-checked. |
314
305
 
315
- ## Testing
316
-
317
- From inside this `mafutils/` directory:
318
-
319
- ```bash
320
- pytest tests/
321
- ```
322
-
323
- `tests/test_compression.py` covers cross-cutting compression behavior
324
- (bgzip/gzip detection, index headers, index auto-derivation, and the
325
- per-command parallel/sequential/fallback logic described above) across all
326
- three compression types. `tests/test_validate.py` covers index integrity
327
- (size/mtime/hash header fields, `mafutils validate`'s three-way verdict, and
328
- `--verify-hash` on `fetch`/`stats`/`gc`).
329
-
330
306
  ## Notes
331
307
 
332
- - `python -m mafutils ...` is the supported invocation from a source checkout.
333
- - The installed console entrypoint is `mafutils ...` once the package is
334
- installed.
308
+ For development setup (installing from a source checkout), running the test
309
+ suite, and internal implementation notes, see [`DEVELOPMENT.md`](DEVELOPMENT.md).
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.0'
22
- __version_tuple__ = version_tuple = (0, 2, 0)
21
+ __version__ = version = '0.3.0'
22
+ __version_tuple__ = version_tuple = (0, 3, 0)
23
23
 
24
- __commit_id__ = commit_id = 'g5d2163b7c'
24
+ __commit_id__ = commit_id = 'g694487dcd'