denseclass 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DenseCall2 contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include README.md
2
+ include requirements.txt
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: denseclass
3
+ Version: 0.1.0
4
+ Author: linlian
5
+ Author-email: 21620151153308@stu.xmu.edu.cn
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Operating System :: POSIX :: Linux
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENCE.txt
15
+ Requires-Dist: numpy>=1.23
16
+ Requires-Dist: scipy>=1.9
17
+ Requires-Dist: pandas>=1.5
18
+ Requires-Dist: h5py>=3.7
19
+ Requires-Dist: torch>=2.0
20
+ Requires-Dist: tqdm>=4.64
21
+ Requires-Dist: toml>=0.10
22
+ Requires-Dist: scikit-learn>=1.1
23
+ Requires-Dist: matplotlib>=3.6
24
+ Requires-Dist: seaborn>=0.12
25
+ Requires-Dist: biopython>=1.80
26
+ Requires-Dist: numba>=0.57
27
+ Requires-Dist: plotnine>=0.12
28
+ Requires-Dist: ont-fast5-api>=4.1
29
+ Requires-Dist: ont-bonito>=0.7
30
+ Dynamic: author
31
+ Dynamic: author-email
32
+ Dynamic: classifier
33
+ Dynamic: description
34
+ Dynamic: description-content-type
35
+ Dynamic: license
36
+ Dynamic: license-file
37
+ Dynamic: requires-dist
38
+ Dynamic: requires-python
39
+
40
+ <div align="center">
41
+
42
+ # 🦠 **denseclass**
43
+
44
+ **Nanopore signal classification** — label every raw read by species, barcode or taxon,
45
+ straight from the current, with no basecalling step.
46
+
47
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
48
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENCE.txt)
49
+
50
+ </div>
51
+
52
+ ---
53
+
54
+ ## 📖 Table of Contents
55
+
56
+ - [Overview](#-overview)
57
+ - [Installation](#-installation)
58
+ - [Quick start](#-quick-start)
59
+ - [Subcommands](#-subcommands)
60
+ - [Training modes](#-training-modes)
61
+ - [Examples](#-examples)
62
+ - [Relationship to densecall](#-relationship-to-densecall)
63
+ - [Development](#-development)
64
+
65
+ ---
66
+
67
+ ## 🧬 Overview
68
+
69
+ Reads are trimmed to their informative region (adapter/polyA detection for DNA,
70
+ poly(A)-tail trimming for RNA), encoded, and passed through a small 1-D CNN that
71
+ predicts one class per read. Training works either on **real** labelled reads or on
72
+ **signal simulated** from reference genomes, so you can build a classifier without
73
+ per-species reads for every class.
74
+
75
+ This package was split out of [DenseCall](https://github.com/n-damo/densecall) and
76
+ now stands alone — see [Relationship to densecall](#-relationship-to-densecall).
77
+
78
+ ---
79
+
80
+ ## 📦 Installation
81
+
82
+ ```bash
83
+ git clone <this-repo>
84
+ cd denseclass
85
+ pip install -e .
86
+ ```
87
+
88
+ `torch` is deliberately unpinned so the resolver picks a build matching your local
89
+ CUDA toolkit. For GPU basecalling-adjacent work, install the CUDA toolkit first and
90
+ verify `nvcc` is on `$PATH`:
91
+
92
+ ```bash
93
+ conda install -c nvidia cuda-toolkit=12.8
94
+ ```
95
+
96
+ ---
97
+
98
+ ## 🚀 Quick start
99
+
100
+ ```bash
101
+ denseclass --help
102
+ denseclass label --help
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 🎯 Subcommands
108
+
109
+ | Subcommand | Purpose |
110
+ |:--|:--|
111
+ | `label` | Build `label_map.json` / `inverse_label_map.json` from a FASTA whose headers are the class names |
112
+ | `split` | Split a read→label pairs file into train / test sets |
113
+ | `gendata` | Convert POD5/FAST5 + labels into `train.hdf5` / `valid.hdf5` |
114
+ | `train` | Train the classification CNN (real or simulated data) |
115
+ | `predict` | Classify reads with a trained model |
116
+ | `precision` | One-class precision / recall / F1 / ROC evaluation |
117
+ | `evaluation` | Full multiclass panel (confusion matrix, PR/ROC, ...) |
118
+ | `comparison` | Side-by-side comparison of two models |
119
+
120
+ ---
121
+
122
+ ## 🧪 Training modes
123
+
124
+ | Mode | Description |
125
+ |:--|:--|
126
+ | **Real-data** | `gendata` extracts signal chunks from reads already labelled by species, then `train` (no `--simu`) fits the CNN directly. |
127
+ | **Simulation** | `train --simu <refs.fa> --preset <name>` synthesises chunks on the fly from a FASTA of reference genomes. Presets: `dna-r9-min`, `dna-r10-min`, `rna-r9-min`, `rna004-min`, ... |
128
+
129
+ The CNN architecture is set by a small TOML (`dna.toml` with `strides=[2,2,2,2,2]` for
130
+ DNA, `rna.toml` with `strides=[5,2,2,2,2]` for RNA) passed via `-config`. The
131
+ `label_map.json` / `inverse_label_map.json` pair must be produced first with
132
+ `denseclass label`.
133
+
134
+ ---
135
+
136
+ ## 🧪 Examples
137
+
138
+ ### Barcode / species classification on real reads
139
+
140
+ ```bash
141
+ # 1. build the label map
142
+ denseclass label species.fa
143
+
144
+ # 2. split read→label pairs
145
+ denseclass split readid_species.txt --train_file train_labels.txt --test_file test_labels.txt
146
+
147
+ # 3. extract training chunks
148
+ denseclass gendata train.pod5 \
149
+ --label_path train_labels.txt --save_path ./ --seqlen 6000 --do-trim
150
+
151
+ # 4. train
152
+ denseclass train model_real \
153
+ -config dna.toml --directory ./ --label-map label_map.json \
154
+ --chunksize 6000 -f --new -y
155
+
156
+ # 5. predict
157
+ denseclass predict model_real test.pod5 inverse_label_map.json \
158
+ --chunksize 6000 > result_real.txt
159
+
160
+ # 6. evaluate
161
+ denseclass evaluation result_real.txt test_labels.txt \
162
+ -m label_map.json -o ./real
163
+ ```
164
+
165
+ ### Simulation-based training (no real per-species data)
166
+
167
+ ```bash
168
+ denseclass label species.fa
169
+
170
+ denseclass train model_simu \
171
+ -config dna.toml --directory ./ --label-map label_map.json \
172
+ --chunksize 25000 -f --new --simu species.fa --preset dna-r10-min -y
173
+
174
+ denseclass predict model_simu reads_dir/ inverse_label_map.json \
175
+ --chunksize 25000 > result_simu.txt
176
+
177
+ denseclass precision result_simu.txt test_labels.txt -m label_map.json
178
+ ```
179
+
180
+ ### RNA direct-read classification
181
+
182
+ RNA reads are reverse-complement mapped and sequenced after the poly(A) tail, so
183
+ `gendata` / `predict` take `--rna --tail`, and RNA models use `rna.toml`:
184
+
185
+ ```bash
186
+ denseclass gendata train.pod5 \
187
+ --label_path train_labels.txt --save_path ./ --rna --seqlen 50000 --tail
188
+
189
+ denseclass train model \
190
+ -config rna.toml --directory ./ --label-map label_map.json \
191
+ -f --new -y --chunksize 50000 --rna
192
+
193
+ denseclass predict model test.pod5 inverse_label_map.json \
194
+ --rna --tail --chunksize 50000 > result_real.txt
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 🔗 Relationship to densecall
200
+
201
+ This package was previously `densecall classify`, wrapping `densecall/microbiome/`.
202
+ It is now standalone:
203
+
204
+ - **No `densecall` import anywhere.** The one helper that used to be shared
205
+ (`convert_base_name`) is vendored in `denseclass/base_util.py`.
206
+ - **Separate dependency set and release cycle.** Install and upgrade it on its own.
207
+ - **The trained `model/` directory is a normal model directory** and can be used with
208
+ the DenseCall basecalling `train` / `predict` tooling where applicable.
209
+
210
+ Migration is a command rename:
211
+
212
+ | Before | After |
213
+ |:--|:--|
214
+ | `densecall classify label` | `denseclass label` |
215
+ | `densecall classify gendata` | `denseclass gendata` |
216
+ | `densecall classify train` | `denseclass train` |
217
+ | `densecall classify predict` | `denseclass predict` |
218
+
219
+ All flags are unchanged.
220
+
221
+ ---
222
+
223
+ ## 🛠 Development
224
+
225
+ ```bash
226
+ pytest tests/ # structural checks: imports, CLI tree, packaging
227
+ ```
228
+
229
+ The test suite is purely structural — it verifies that every module imports, the
230
+ command tree builds and formats, and the packaging metadata is consistent. It needs
231
+ no signal data or checkpoints. End-to-end behaviour is exercised through the examples
232
+ above.
233
+
234
+ ### Publishing
235
+
236
+ ```bash
237
+ # 1. bump __version__ in denseclass/cli.py
238
+ python -m build
239
+ twine check dist/* && twine upload dist/*
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 📄 License
245
+
246
+ MIT — see [LICENCE.txt](LICENCE.txt).
@@ -0,0 +1,207 @@
1
+ <div align="center">
2
+
3
+ # 🦠 **denseclass**
4
+
5
+ **Nanopore signal classification** — label every raw read by species, barcode or taxon,
6
+ straight from the current, with no basecalling step.
7
+
8
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
9
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENCE.txt)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ ## 📖 Table of Contents
16
+
17
+ - [Overview](#-overview)
18
+ - [Installation](#-installation)
19
+ - [Quick start](#-quick-start)
20
+ - [Subcommands](#-subcommands)
21
+ - [Training modes](#-training-modes)
22
+ - [Examples](#-examples)
23
+ - [Relationship to densecall](#-relationship-to-densecall)
24
+ - [Development](#-development)
25
+
26
+ ---
27
+
28
+ ## 🧬 Overview
29
+
30
+ Reads are trimmed to their informative region (adapter/polyA detection for DNA,
31
+ poly(A)-tail trimming for RNA), encoded, and passed through a small 1-D CNN that
32
+ predicts one class per read. Training works either on **real** labelled reads or on
33
+ **signal simulated** from reference genomes, so you can build a classifier without
34
+ per-species reads for every class.
35
+
36
+ This package was split out of [DenseCall](https://github.com/n-damo/densecall) and
37
+ now stands alone — see [Relationship to densecall](#-relationship-to-densecall).
38
+
39
+ ---
40
+
41
+ ## 📦 Installation
42
+
43
+ ```bash
44
+ git clone <this-repo>
45
+ cd denseclass
46
+ pip install -e .
47
+ ```
48
+
49
+ `torch` is deliberately unpinned so the resolver picks a build matching your local
50
+ CUDA toolkit. For GPU basecalling-adjacent work, install the CUDA toolkit first and
51
+ verify `nvcc` is on `$PATH`:
52
+
53
+ ```bash
54
+ conda install -c nvidia cuda-toolkit=12.8
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 🚀 Quick start
60
+
61
+ ```bash
62
+ denseclass --help
63
+ denseclass label --help
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🎯 Subcommands
69
+
70
+ | Subcommand | Purpose |
71
+ |:--|:--|
72
+ | `label` | Build `label_map.json` / `inverse_label_map.json` from a FASTA whose headers are the class names |
73
+ | `split` | Split a read→label pairs file into train / test sets |
74
+ | `gendata` | Convert POD5/FAST5 + labels into `train.hdf5` / `valid.hdf5` |
75
+ | `train` | Train the classification CNN (real or simulated data) |
76
+ | `predict` | Classify reads with a trained model |
77
+ | `precision` | One-class precision / recall / F1 / ROC evaluation |
78
+ | `evaluation` | Full multiclass panel (confusion matrix, PR/ROC, ...) |
79
+ | `comparison` | Side-by-side comparison of two models |
80
+
81
+ ---
82
+
83
+ ## 🧪 Training modes
84
+
85
+ | Mode | Description |
86
+ |:--|:--|
87
+ | **Real-data** | `gendata` extracts signal chunks from reads already labelled by species, then `train` (no `--simu`) fits the CNN directly. |
88
+ | **Simulation** | `train --simu <refs.fa> --preset <name>` synthesises chunks on the fly from a FASTA of reference genomes. Presets: `dna-r9-min`, `dna-r10-min`, `rna-r9-min`, `rna004-min`, ... |
89
+
90
+ The CNN architecture is set by a small TOML (`dna.toml` with `strides=[2,2,2,2,2]` for
91
+ DNA, `rna.toml` with `strides=[5,2,2,2,2]` for RNA) passed via `-config`. The
92
+ `label_map.json` / `inverse_label_map.json` pair must be produced first with
93
+ `denseclass label`.
94
+
95
+ ---
96
+
97
+ ## 🧪 Examples
98
+
99
+ ### Barcode / species classification on real reads
100
+
101
+ ```bash
102
+ # 1. build the label map
103
+ denseclass label species.fa
104
+
105
+ # 2. split read→label pairs
106
+ denseclass split readid_species.txt --train_file train_labels.txt --test_file test_labels.txt
107
+
108
+ # 3. extract training chunks
109
+ denseclass gendata train.pod5 \
110
+ --label_path train_labels.txt --save_path ./ --seqlen 6000 --do-trim
111
+
112
+ # 4. train
113
+ denseclass train model_real \
114
+ -config dna.toml --directory ./ --label-map label_map.json \
115
+ --chunksize 6000 -f --new -y
116
+
117
+ # 5. predict
118
+ denseclass predict model_real test.pod5 inverse_label_map.json \
119
+ --chunksize 6000 > result_real.txt
120
+
121
+ # 6. evaluate
122
+ denseclass evaluation result_real.txt test_labels.txt \
123
+ -m label_map.json -o ./real
124
+ ```
125
+
126
+ ### Simulation-based training (no real per-species data)
127
+
128
+ ```bash
129
+ denseclass label species.fa
130
+
131
+ denseclass train model_simu \
132
+ -config dna.toml --directory ./ --label-map label_map.json \
133
+ --chunksize 25000 -f --new --simu species.fa --preset dna-r10-min -y
134
+
135
+ denseclass predict model_simu reads_dir/ inverse_label_map.json \
136
+ --chunksize 25000 > result_simu.txt
137
+
138
+ denseclass precision result_simu.txt test_labels.txt -m label_map.json
139
+ ```
140
+
141
+ ### RNA direct-read classification
142
+
143
+ RNA reads are reverse-complement mapped and sequenced after the poly(A) tail, so
144
+ `gendata` / `predict` take `--rna --tail`, and RNA models use `rna.toml`:
145
+
146
+ ```bash
147
+ denseclass gendata train.pod5 \
148
+ --label_path train_labels.txt --save_path ./ --rna --seqlen 50000 --tail
149
+
150
+ denseclass train model \
151
+ -config rna.toml --directory ./ --label-map label_map.json \
152
+ -f --new -y --chunksize 50000 --rna
153
+
154
+ denseclass predict model test.pod5 inverse_label_map.json \
155
+ --rna --tail --chunksize 50000 > result_real.txt
156
+ ```
157
+
158
+ ---
159
+
160
+ ## 🔗 Relationship to densecall
161
+
162
+ This package was previously `densecall classify`, wrapping `densecall/microbiome/`.
163
+ It is now standalone:
164
+
165
+ - **No `densecall` import anywhere.** The one helper that used to be shared
166
+ (`convert_base_name`) is vendored in `denseclass/base_util.py`.
167
+ - **Separate dependency set and release cycle.** Install and upgrade it on its own.
168
+ - **The trained `model/` directory is a normal model directory** and can be used with
169
+ the DenseCall basecalling `train` / `predict` tooling where applicable.
170
+
171
+ Migration is a command rename:
172
+
173
+ | Before | After |
174
+ |:--|:--|
175
+ | `densecall classify label` | `denseclass label` |
176
+ | `densecall classify gendata` | `denseclass gendata` |
177
+ | `densecall classify train` | `denseclass train` |
178
+ | `densecall classify predict` | `denseclass predict` |
179
+
180
+ All flags are unchanged.
181
+
182
+ ---
183
+
184
+ ## 🛠 Development
185
+
186
+ ```bash
187
+ pytest tests/ # structural checks: imports, CLI tree, packaging
188
+ ```
189
+
190
+ The test suite is purely structural — it verifies that every module imports, the
191
+ command tree builds and formats, and the packaging metadata is consistent. It needs
192
+ no signal data or checkpoints. End-to-end behaviour is exercised through the examples
193
+ above.
194
+
195
+ ### Publishing
196
+
197
+ ```bash
198
+ # 1. bump __version__ in denseclass/cli.py
199
+ python -m build
200
+ twine check dist/* && twine upload dist/*
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 📄 License
206
+
207
+ MIT — see [LICENCE.txt](LICENCE.txt).
@@ -0,0 +1,12 @@
1
+ """Signal classification for nanopore reads.
2
+
3
+ Split out of the ``densecall`` basecaller into this standalone package. Nothing
4
+ here imports ``densecall``: the only helper that was shared
5
+ (``convert_base_name``) is vendored in ``base_util.py``.
6
+
7
+ Pipeline: label -> split -> gendata -> train -> predict -> evaluate
8
+ """
9
+
10
+ from denseclass.cli import __version__, argparser, main, subcommands
11
+
12
+ __all__ = ['main', 'argparser', 'subcommands', '__version__']
@@ -0,0 +1,4 @@
1
+ from denseclass.cli import main
2
+
3
+ if __name__ == '__main__':
4
+ main()
@@ -0,0 +1,41 @@
1
+ """Base-name to IUPAC regular-expression helpers.
2
+
3
+ Vendored from ``densecall.mod_util`` so this package has no dependency on the
4
+ basecaller. Only ``convert_base_name`` was ever used here; the rest of
5
+ ``mod_util`` pulls in remora/mappy, which this package does not need.
6
+ """
7
+
8
+ # IUPAC degenerate codes expanded into character classes.
9
+ _MERGE_BASES = {
10
+ "A": "A",
11
+ "C": "C",
12
+ "G": "G",
13
+ "T": "T",
14
+ "M": "[AC]",
15
+ "V": "[ACG]",
16
+ "R": "[AG]",
17
+ "H": "[ACT]",
18
+ "W": "[AT]",
19
+ "D": "[AGT]",
20
+ "S": "[CG]",
21
+ "B": "[CGT]",
22
+ "Y": "[CT]",
23
+ "N": "[ACGT]",
24
+ "K": "[GT]",
25
+ }
26
+
27
+
28
+ def convert_base_name(base_name):
29
+ """Expand a base string into a regular expression pattern.
30
+
31
+ Args:
32
+ base_name (str): Input base string to be converted, e.g. ``"NAY"``.
33
+
34
+ Returns:
35
+ str: Regular expression pattern equivalent to the input, e.g.
36
+ ``"[ACGT]A[CT]"``.
37
+ """
38
+ pattern = ""
39
+ for base in base_name:
40
+ pattern += _MERGE_BASES.get(base, base)
41
+ return pattern
@@ -0,0 +1,76 @@
1
+ """Command-line namespace for signal classification.
2
+
3
+ ``argparser()`` builds the subcommand tree for introspection and library use;
4
+ ``main()`` is the console entry point. Both share the same leaf registration,
5
+ and ``main`` mirrors DenseCall's two-level layout (leaves hang directly off the
6
+ top parser) so that ``denseclass <cmd> --help`` shows the leaf's help.
7
+ """
8
+
9
+ from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
10
+
11
+ __version__ = '0.1.0'
12
+
13
+ subcommands = [
14
+ 'label', 'split', 'gendata', 'train', 'predict',
15
+ 'precision', 'evaluation', 'comparison'
16
+ ]
17
+
18
+ __all__ = ['argparser', 'main', 'subcommands', '__version__']
19
+
20
+ _DESCRIPTION = ('Signal classification: label map -> split -> gendata -> '
21
+ 'train -> predict -> evaluate')
22
+
23
+
24
+ def _leaves():
25
+ """Yield (name, module) for each subcommand."""
26
+ for name in subcommands:
27
+ mod = __import__(f"denseclass.{name}", fromlist=['argparser', 'main'])
28
+ yield name, mod
29
+
30
+
31
+ def _add_subparsers(parser):
32
+ """Attach every subcommand to ``parser`` as a real subparser."""
33
+ sub = parser.add_subparsers(
34
+ title='subcommands', description='valid commands',
35
+ help='additional help', dest='subcommand'
36
+ )
37
+ sub.required = True
38
+ for name, mod in _leaves():
39
+ p = sub.add_parser(name, parents=[mod.argparser()])
40
+ p.set_defaults(func=mod.main)
41
+ return sub
42
+
43
+
44
+ def argparser():
45
+ """The subcommand namespace on its own (``add_help=False``, since each leaf
46
+ carries its own ``-h``)."""
47
+ parser = ArgumentParser(
48
+ "denseclass",
49
+ formatter_class=ArgumentDefaultsHelpFormatter,
50
+ description=_DESCRIPTION,
51
+ add_help=False,
52
+ )
53
+ parser.add_argument(
54
+ '-v', '--version', action='version',
55
+ version='%(prog)s {}'.format(__version__)
56
+ )
57
+ _add_subparsers(parser)
58
+ return parser
59
+
60
+
61
+ def main(args=None):
62
+ """Console entry point: leaves hang off the top parser so
63
+ ``denseclass <cmd> --help`` reaches the leaf."""
64
+ parser = ArgumentParser(
65
+ 'denseclass',
66
+ formatter_class=ArgumentDefaultsHelpFormatter,
67
+ description=_DESCRIPTION,
68
+ )
69
+ parser.add_argument(
70
+ '-v', '--version', action='version',
71
+ version='%(prog)s {}'.format(__version__)
72
+ )
73
+ _add_subparsers(parser)
74
+
75
+ parsed = parser.parse_args(args)
76
+ parsed.func(parsed)