oligraph 0.2.0__tar.gz → 0.2.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.
@@ -1138,7 +1138,7 @@ dependencies = [
1138
1138
 
1139
1139
  [[package]]
1140
1140
  name = "oligraph-py"
1141
- version = "0.2.0"
1141
+ version = "0.2.2"
1142
1142
  dependencies = [
1143
1143
  "needletail",
1144
1144
  "oligraph-rs",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oligraph
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -36,6 +36,8 @@ Available as a CLI tool, a Python package, and a browser-based web app (all comp
36
36
  oligraph-rs -i <input.fasta> -o <output_prefix> [-l <min_overlap>] [-m <all|pca>]
37
37
  ```
38
38
 
39
+ (There is also an `oligraph` command in the [Python package](#command-line), if you would rather not build the Rust binary.)
40
+
39
41
  | Flag | Description |
40
42
  |---|---|
41
43
  | `-i, --input` | Input FASTA file of oligonucleotide sequences |
@@ -100,6 +102,25 @@ To build from source instead (needs Rust 1.85 or later):
100
102
  pip install git+https://github.com/AgentK9/oligraph-rs
101
103
  ```
102
104
 
105
+ ### Command line
106
+
107
+ Installing the package also provides an `oligraph` command (and `python -m oligraph`), so the tool is usable without a Rust toolchain:
108
+
109
+ ```sh
110
+ oligraph -i pool.fasta.gz -o results -l 20 -m pca
111
+ ```
112
+
113
+ Flags match the Rust CLI above, with these additions:
114
+
115
+ | Flag | Description |
116
+ |---|---|
117
+ | `-o, --output` | Optional here — omit it to write the GFA to stdout |
118
+ | `--on-invalid` | `skip` (default) drops unusable records and names them on stderr; `raise` fails |
119
+ | `--positional-ids` | Label output with indices, reproducing the Rust CLI's output exactly |
120
+ | `-q, --quiet` | Suppress progress on stderr |
121
+
122
+ The one behavioural difference from `oligraph-rs`: output is labelled with FASTA record names by default rather than positional indices.
123
+
103
124
  ### Quick start
104
125
 
105
126
  Pass a dict of sequences, and get back a graph whose edges and contigs are labelled with your own names:
@@ -10,6 +10,8 @@ Available as a CLI tool, a Python package, and a browser-based web app (all comp
10
10
  oligraph-rs -i <input.fasta> -o <output_prefix> [-l <min_overlap>] [-m <all|pca>]
11
11
  ```
12
12
 
13
+ (There is also an `oligraph` command in the [Python package](#command-line), if you would rather not build the Rust binary.)
14
+
13
15
  | Flag | Description |
14
16
  |---|---|
15
17
  | `-i, --input` | Input FASTA file of oligonucleotide sequences |
@@ -74,6 +76,25 @@ To build from source instead (needs Rust 1.85 or later):
74
76
  pip install git+https://github.com/AgentK9/oligraph-rs
75
77
  ```
76
78
 
79
+ ### Command line
80
+
81
+ Installing the package also provides an `oligraph` command (and `python -m oligraph`), so the tool is usable without a Rust toolchain:
82
+
83
+ ```sh
84
+ oligraph -i pool.fasta.gz -o results -l 20 -m pca
85
+ ```
86
+
87
+ Flags match the Rust CLI above, with these additions:
88
+
89
+ | Flag | Description |
90
+ |---|---|
91
+ | `-o, --output` | Optional here — omit it to write the GFA to stdout |
92
+ | `--on-invalid` | `skip` (default) drops unusable records and names them on stderr; `raise` fails |
93
+ | `--positional-ids` | Label output with indices, reproducing the Rust CLI's output exactly |
94
+ | `-q, --quiet` | Suppress progress on stderr |
95
+
96
+ The one behavioural difference from `oligraph-rs`: output is labelled with FASTA record names by default rather than positional indices.
97
+
77
98
  ### Quick start
78
99
 
79
100
  Pass a dict of sequences, and get back a graph whose edges and contigs are labelled with your own names:
@@ -28,6 +28,11 @@ classifiers = [
28
28
  "Topic :: Scientific/Engineering :: Bio-Informatics",
29
29
  ]
30
30
 
31
+ # Console entry point. The Rust CLI binary cannot ship inside a pyo3 extension
32
+ # wheel, so this is a Python front-end over the same bindings.
33
+ [project.scripts]
34
+ oligraph = "oligraph.__main__:main"
35
+
31
36
  [project.urls]
32
37
  Homepage = "https://github.com/AgentK9/oligraph-rs"
33
38
  Repository = "https://github.com/AgentK9/oligraph-rs"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "oligraph-py"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  edition = "2024"
5
5
  description = "Python bindings for OliGraph"
6
6
  repository = "https://github.com/AgentK9/oligraph-rs"
@@ -0,0 +1,146 @@
1
+ """Command-line interface for OliGraph.
2
+
3
+ Installed as the ``oligraph`` command, and runnable as ``python -m oligraph``.
4
+
5
+ Flags mirror the Rust CLI (``oligraph-rs``) so the two are interchangeable, with
6
+ one deliberate difference: this one labels its output with the FASTA record names
7
+ rather than positional indices. Pass ``--positional-ids`` for byte-identical
8
+ output to the Rust CLI.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import List, Optional
17
+
18
+ from . import __version__, build_from_fasta
19
+
20
+
21
+ def _build_parser() -> argparse.ArgumentParser:
22
+ p = argparse.ArgumentParser(
23
+ prog="oligraph",
24
+ description=(
25
+ "Overlap graph builder and contig assembler for oligonucleotide pools."
26
+ ),
27
+ )
28
+ p.add_argument(
29
+ "-i",
30
+ "--input",
31
+ required=True,
32
+ type=Path,
33
+ metavar="FASTA",
34
+ help="input FASTA/FASTQ file; plain or gzipped",
35
+ )
36
+ p.add_argument(
37
+ "-o",
38
+ "--output",
39
+ type=Path,
40
+ metavar="PREFIX",
41
+ help=(
42
+ "output prefix; writes PREFIX.gfa, PREFIX.fasta and "
43
+ "PREFIX.contigs.fasta. Omit to write the GFA to stdout"
44
+ ),
45
+ )
46
+ # No 1-64 bound is declared here on purpose: the range lives in the Rust core
47
+ # (validate_min_overlap) and is reported through the ValueError below, so it
48
+ # is never restated in two places.
49
+ p.add_argument(
50
+ "-l",
51
+ "--min-overlap",
52
+ type=int,
53
+ default=20,
54
+ metavar="BP",
55
+ help="minimum overlap length in bases (default: 20, range: 1-64)",
56
+ )
57
+ p.add_argument(
58
+ "-m",
59
+ "--method",
60
+ choices=("all", "pca"),
61
+ default="all",
62
+ help="'all' keeps every edge type; 'pca' keeps only 3'-end annealing",
63
+ )
64
+ p.add_argument(
65
+ "--on-invalid",
66
+ choices=("skip", "raise"),
67
+ default="skip",
68
+ help=(
69
+ "what to do with an empty or non-ACGT record (default: skip, which "
70
+ "drops it and reports the name on stderr)"
71
+ ),
72
+ )
73
+ p.add_argument(
74
+ "--positional-ids",
75
+ action="store_true",
76
+ help="label output with positional indices instead of record names",
77
+ )
78
+ p.add_argument(
79
+ "-q", "--quiet", action="store_true", help="suppress progress on stderr"
80
+ )
81
+ p.add_argument("--version", action="version", version=f"oligraph {__version__}")
82
+ return p
83
+
84
+
85
+ def main(argv: Optional[List[str]] = None) -> int:
86
+ args = _build_parser().parse_args(argv)
87
+
88
+ def log(msg: str) -> None:
89
+ if not args.quiet:
90
+ print(msg, file=sys.stderr)
91
+
92
+ # The core reports bad input as typed errors; surface them as a one-line
93
+ # message rather than a traceback.
94
+ try:
95
+ g = build_from_fasta(
96
+ args.input,
97
+ min_overlap=args.min_overlap,
98
+ method=args.method,
99
+ on_invalid=args.on_invalid,
100
+ )
101
+ except (OSError, ValueError) as e:
102
+ print(f"error: {e}", file=sys.stderr)
103
+ return 1
104
+
105
+ names = not args.positional_ids
106
+ stats = g.stats
107
+ log(
108
+ f"loaded {stats.n_sequences} sequences "
109
+ f"(lengths {stats.len_min}-{stats.len_max}) with min_overlap={g.min_overlap}"
110
+ )
111
+ if g.skipped:
112
+ log(f"skipped {len(g.skipped)} unusable records: {', '.join(g.skipped)}")
113
+ log(f"found {stats.n_edges} edges, {stats.n_isolated} isolated sequences")
114
+
115
+ if args.output is None:
116
+ sys.stdout.write(g.to_gfa(names=names))
117
+ return 0
118
+
119
+ prefix = args.output
120
+ if prefix.parent != Path(""):
121
+ prefix.parent.mkdir(parents=True, exist_ok=True)
122
+
123
+ try:
124
+ gfa = prefix.with_suffix(".gfa")
125
+ g.write_gfa(gfa, names=names)
126
+ log(f"wrote {gfa}")
127
+
128
+ fasta = prefix.with_suffix(".fasta")
129
+ g.write_fasta(fasta, names=names)
130
+ log(f"wrote {fasta}")
131
+
132
+ if g.contigs:
133
+ contigs = prefix.with_suffix(".contigs.fasta")
134
+ g.write_contigs_fasta(contigs, names=names)
135
+ log(f"wrote {contigs} ({stats.n_contigs} contigs)")
136
+ else:
137
+ log("no contigs assembled (no connected components with edges)")
138
+ except OSError as e:
139
+ print(f"error: {e}", file=sys.stderr)
140
+ return 1
141
+
142
+ return 0
143
+
144
+
145
+ if __name__ == "__main__":
146
+ raise SystemExit(main())
File without changes
File without changes
File without changes
File without changes