pyadata-cli 0.7.1__tar.gz → 0.9.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 (57) hide show
  1. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/PKG-INFO +4 -3
  2. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/README.md +3 -2
  3. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/pyproject.toml +1 -1
  4. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/cli.py +40 -11
  5. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/import_data.py +3 -1
  6. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/conversion/__init__.py +46 -5
  7. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/conversion/base.py +4 -0
  8. pyadata_cli-0.9.0/src/adata/conversion/cellbender.py +657 -0
  9. pyadata_cli-0.9.0/src/adata/conversion/features.py +380 -0
  10. pyadata_cli-0.9.0/src/adata/conversion/mtx.py +605 -0
  11. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/conversion/tenx.py +73 -294
  12. pyadata_cli-0.9.0/src/adata/core/csr_build.py +377 -0
  13. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/recast.py +19 -102
  14. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/subset.py +6 -2
  15. pyadata_cli-0.9.0/src/adata/formats/mtx.py +481 -0
  16. pyadata_cli-0.9.0/src/adata/formats/sparse.py +152 -0
  17. pyadata_cli-0.9.0/src/adata/formats/textio.py +236 -0
  18. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/storage/__init__.py +2 -10
  19. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/util/path.py +12 -0
  20. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/util/staging.py +5 -2
  21. pyadata_cli-0.7.1/src/adata/formats/sparse.py +0 -253
  22. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/LICENSE +0 -0
  23. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/__init__.py +0 -0
  24. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/__init__.py +0 -0
  25. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/concat.py +0 -0
  26. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/convert.py +0 -0
  27. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/create.py +0 -0
  28. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/export.py +0 -0
  29. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/info.py +0 -0
  30. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/ls.py +0 -0
  31. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/recast.py +0 -0
  32. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/split.py +0 -0
  33. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/commands/subset.py +0 -0
  34. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/conversion/anndata_copy.py +0 -0
  35. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/__init__.py +0 -0
  36. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/concat.py +0 -0
  37. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/info.py +0 -0
  38. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/query.py +0 -0
  39. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/read.py +0 -0
  40. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/core/select.py +0 -0
  41. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/elements/__init__.py +0 -0
  42. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/elements/read.py +0 -0
  43. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/elements/spec.py +0 -0
  44. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/elements/strings.py +0 -0
  45. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/elements/write.py +0 -0
  46. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/__init__.py +0 -0
  47. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/array.py +0 -0
  48. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/common.py +0 -0
  49. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/dataframe.py +0 -0
  50. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/image.py +0 -0
  51. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/json_data.py +0 -0
  52. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/formats/validate.py +0 -0
  53. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/info.py +0 -0
  54. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/py.typed +0 -0
  55. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/read.py +0 -0
  56. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/util/__init__.py +0 -0
  57. {pyadata_cli-0.7.1 → pyadata_cli-0.9.0}/src/adata/util/names.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyadata-cli
3
- Version: 0.7.1
3
+ Version: 0.9.0
4
4
  Summary: Streaming CLI for exploring and editing large AnnData .h5ad and .zarr stores
5
5
  Keywords: anndata,h5ad,zarr,bioinformatics,single-cell,cli
6
6
  Author: Aljes Binkevich
@@ -48,7 +48,7 @@ A command-line tool for exploring huge AnnData stores (`.h5ad` and `.zarr`) with
48
48
  - Auto-detects `.h5ad` files vs `.zarr` directories
49
49
  - Chunked processing for dense and sparse matrices (CSR/CSC)
50
50
  - Reads every AnnData on-disk layout, from 0.7.x through the current spec, and always writes the current one
51
- - Converts between HDF5 and Zarr (v2 and v3) in either direction with `adata convert`, and to and from 10x Genomics HDF5 matrices
51
+ - Converts between HDF5 and Zarr (v2 and v3) in either direction with `adata convert`, and to and from 10x Genomics HDF5 matrices and 10x / STARsolo MTX folders, and from CellBender output
52
52
  - Rich terminal output with progress indicators, kept on stderr so results pipe cleanly
53
53
 
54
54
  **Documentation: [cellgeni.github.io/adata-cli](https://cellgeni.github.io/adata-cli/)**
@@ -96,7 +96,7 @@ Run help at any level (e.g. `adata --help`, `adata export --help`).
96
96
  - `subset` – stream and write a filtered copy, selected by obs/var name lists (`--obs`/`--var`) or by expression (`--obs-query`/`--var-query`).
97
97
  - `split` – write one store per distinct value of an annotation column, with a CSV manifest.
98
98
  - `concat` – concatenate stores along the obs axis, with `--join inner|outer` and merge strategies for var and uns.
99
- - `convert` – change a store's format: h5ad ↔ Zarr v2/v3, and 10x Genomics HDF5 (Cell Ranger's `*_feature_bc_matrix.h5`) in and out, streaming, with the output written beside its destination and moved into place only once complete.
99
+ - `convert` – change a store's format: h5ad ↔ Zarr v2/v3, 10x Genomics HDF5 (Cell Ranger's `*_feature_bc_matrix.h5`) and 10x / STARsolo MTX folders in and out, CellBender output in, streaming, with the output written beside its destination and moved into place only once complete.
100
100
  - `recast` – change a matrix's dtype, layout (CSR/CSC/dense) or density, streaming; refuses a lossy cast or a large size increase unless forced. Called `convert` until 0.7.0.
101
101
  - `export` – extract data from a store; subcommands: `dataframe` (any dataframe group to CSV), `array` (dense to `.npy`), `sparse` (CSR/CSC to `.mtx`), `dict` (JSON), `image` (PNG). Results go to stdout when no `--output` is given.
102
102
  - `import` – write new data into a store at any path; subcommands: `dataframe` (CSV), `array` (`.npy`), `sparse` (`.mtx`), `dict` (JSON), `image` (PNG/JPEG/TIFF).
@@ -140,6 +140,7 @@ adata convert data.h5ad -o data.zarr
140
140
  adata convert data.zarr -o data.h5ad --compression gzip
141
141
  adata convert filtered_feature_bc_matrix.h5 -o counts.h5ad --feature-type "Gene Expression"
142
142
  adata convert counts.h5ad -o for_cellbender.h5 --layer counts
143
+ adata convert Solo.out/Velocyto/raw -o velocyto.h5ad
143
144
  ```
144
145
 
145
146
  ## Documentation
@@ -8,7 +8,7 @@ A command-line tool for exploring huge AnnData stores (`.h5ad` and `.zarr`) with
8
8
  - Auto-detects `.h5ad` files vs `.zarr` directories
9
9
  - Chunked processing for dense and sparse matrices (CSR/CSC)
10
10
  - Reads every AnnData on-disk layout, from 0.7.x through the current spec, and always writes the current one
11
- - Converts between HDF5 and Zarr (v2 and v3) in either direction with `adata convert`, and to and from 10x Genomics HDF5 matrices
11
+ - Converts between HDF5 and Zarr (v2 and v3) in either direction with `adata convert`, and to and from 10x Genomics HDF5 matrices and 10x / STARsolo MTX folders, and from CellBender output
12
12
  - Rich terminal output with progress indicators, kept on stderr so results pipe cleanly
13
13
 
14
14
  **Documentation: [cellgeni.github.io/adata-cli](https://cellgeni.github.io/adata-cli/)**
@@ -56,7 +56,7 @@ Run help at any level (e.g. `adata --help`, `adata export --help`).
56
56
  - `subset` – stream and write a filtered copy, selected by obs/var name lists (`--obs`/`--var`) or by expression (`--obs-query`/`--var-query`).
57
57
  - `split` – write one store per distinct value of an annotation column, with a CSV manifest.
58
58
  - `concat` – concatenate stores along the obs axis, with `--join inner|outer` and merge strategies for var and uns.
59
- - `convert` – change a store's format: h5ad ↔ Zarr v2/v3, and 10x Genomics HDF5 (Cell Ranger's `*_feature_bc_matrix.h5`) in and out, streaming, with the output written beside its destination and moved into place only once complete.
59
+ - `convert` – change a store's format: h5ad ↔ Zarr v2/v3, 10x Genomics HDF5 (Cell Ranger's `*_feature_bc_matrix.h5`) and 10x / STARsolo MTX folders in and out, CellBender output in, streaming, with the output written beside its destination and moved into place only once complete.
60
60
  - `recast` – change a matrix's dtype, layout (CSR/CSC/dense) or density, streaming; refuses a lossy cast or a large size increase unless forced. Called `convert` until 0.7.0.
61
61
  - `export` – extract data from a store; subcommands: `dataframe` (any dataframe group to CSV), `array` (dense to `.npy`), `sparse` (CSR/CSC to `.mtx`), `dict` (JSON), `image` (PNG). Results go to stdout when no `--output` is given.
62
62
  - `import` – write new data into a store at any path; subcommands: `dataframe` (CSV), `array` (`.npy`), `sparse` (`.mtx`), `dict` (JSON), `image` (PNG/JPEG/TIFF).
@@ -100,6 +100,7 @@ adata convert data.h5ad -o data.zarr
100
100
  adata convert data.zarr -o data.h5ad --compression gzip
101
101
  adata convert filtered_feature_bc_matrix.h5 -o counts.h5ad --feature-type "Gene Expression"
102
102
  adata convert counts.h5ad -o for_cellbender.h5 --layer counts
103
+ adata convert Solo.out/Velocyto/raw -o velocyto.h5ad
103
104
  ```
104
105
 
105
106
  ## Documentation
@@ -3,7 +3,7 @@
3
3
  # so the distribution is published as `pyadata-cli`. The import package
4
4
  # and the command are both still `adata`.
5
5
  name = "pyadata-cli"
6
- version = "0.7.1"
6
+ version = "0.9.0"
7
7
  description = "Streaming CLI for exploring and editing large AnnData .h5ad and .zarr stores"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.12"
@@ -522,6 +522,8 @@ _CONVERT_FLAGS = {
522
522
  "gene_ids_column": "--gene-ids-column",
523
523
  "gene_names_column": "--gene-names-column",
524
524
  "feature_types_column": "--feature-types-column",
525
+ "raw": "--raw",
526
+ "min_cell_probability": "--min-cell-probability",
525
527
  }
526
528
 
527
529
  #: Flags that only make sense for format conversion; with recast's flags,
@@ -553,7 +555,7 @@ def convert(
553
555
  ctx: typer.Context,
554
556
  file: Path = typer.Argument(
555
557
  ...,
556
- help="Input file or store",
558
+ help="Input file, store, or 10x/STARsolo matrix folder",
557
559
  exists=True,
558
560
  readable=True,
559
561
  dir_okay=True,
@@ -563,7 +565,8 @@ def convert(
563
565
  None,
564
566
  "--output",
565
567
  "-o",
566
- help="Output path; its extension picks the format (.h5ad, .zarr, .h5)",
568
+ help="Output path; its extension picks the format (.h5ad, .zarr, .h5), "
569
+ "or a folder with --to 10x-mtx",
567
570
  dir_okay=True,
568
571
  file_okay=True,
569
572
  ),
@@ -586,7 +589,7 @@ def convert(
586
589
  None,
587
590
  "--compression",
588
591
  help="none, gzip or lzf for h5ad; none, gzip, zstd or blosc for zarr "
589
- "(defaults to the source's)",
592
+ "(defaults to the source's); gzip or none for a 10x-mtx folder",
590
593
  ),
591
594
  force: bool = typer.Option(
592
595
  False,
@@ -601,7 +604,8 @@ def convert(
601
604
  genome: Optional[str] = typer.Option(
602
605
  None,
603
606
  "--genome",
604
- help="10x input: the genome to keep (picks the group in a Cell Ranger 2 file)",
607
+ help="10x input: the genome to keep (picks the group or subfolder of a "
608
+ "Cell Ranger 2 matrix)",
605
609
  ),
606
610
  var_names: str = typer.Option(
607
611
  "gene_symbols",
@@ -643,6 +647,21 @@ def convert(
643
647
  "--feature-types-column",
644
648
  help="10x output: var column holding feature types (default feature_types)",
645
649
  ),
650
+ raw: Optional[Path] = typer.Option(
651
+ None,
652
+ "--raw",
653
+ help="CellBender input: the raw Cell Ranger .h5 it ran on, added as "
654
+ "layers['cellranger']",
655
+ exists=True,
656
+ dir_okay=False,
657
+ readable=True,
658
+ ),
659
+ min_cell_probability: Optional[float] = typer.Option(
660
+ None,
661
+ "--min-cell-probability",
662
+ help="CellBender input: keep droplets with cell_probability at least "
663
+ "this (CellBender's own cutoff is 0.5)",
664
+ ),
646
665
  # Deprecated: recast's flags, so `convert` keeps working as it did until
647
666
  # 1.0.0. Hidden from --help; the old matrix paths arrive in ctx.args.
648
667
  inplace: bool = typer.Option(False, "--inplace", hidden=True),
@@ -657,11 +676,14 @@ def convert(
657
676
  Convert a store to another format.
658
677
 
659
678
  The output's extension picks the format: .h5ad, .zarr, or .h5 for a 10x
660
- Genomics matrix (Cell Ranger's *_feature_bc_matrix.h5). Between h5ad and
661
- zarr every element is copied, streamed, keeping the source's chunking and
662
- compression where the target can express them. A 10x matrix is read as
663
- scanpy.read_10x_h5(gex_only=False) reads it, keeping integer counts, and
664
- written as Cell Ranger writes it.
679
+ Genomics matrix (Cell Ranger's *_feature_bc_matrix.h5); --to 10x-mtx
680
+ writes a Matrix Market folder. Between h5ad and zarr every element is
681
+ copied, streamed, keeping the source's chunking and compression where the
682
+ target can express them. CellBender output keeps its latent variables, as
683
+ CellBender's own loader names them. A 10x matrix is read as scanpy's read_10x_h5 or
684
+ read_10x_mtx (gex_only=False) reads it, keeping integer counts, and
685
+ written as Cell Ranger writes it. MTX folders from Cell Ranger and
686
+ STARsolo, velocyto's included, are read in two streaming passes.
665
687
 
666
688
  Nothing is written until every check has passed, and the output is moved
667
689
  into place only once complete: a failed conversion leaves nothing
@@ -677,6 +699,11 @@ def convert(
677
699
  adata convert filtered_feature_bc_matrix.h5 -o counts.h5ad
678
700
  adata convert raw_feature_bc_matrix.h5 -o rna.h5ad --feature-type "Gene Expression"
679
701
  adata convert counts.h5ad -o for_cellbender.h5 --layer counts
702
+ adata convert Solo.out/Gene/filtered -o counts.h5ad
703
+ adata convert Solo.out/Velocyto/raw -o velocyto.h5ad
704
+ adata convert counts.h5ad -o counts_mtx --to 10x-mtx --layer counts
705
+ adata convert cellbender_output.h5 -o cb.h5ad --raw raw_feature_bc_matrix.h5
706
+ adata convert cellbender_output.h5 -o cells.h5ad --min-cell-probability 0.5
680
707
  """
681
708
  entries = list(ctx.args)
682
709
  recast_flags = _given(ctx, _RECAST_FLAGS)
@@ -760,6 +787,8 @@ def convert(
760
787
  gene_ids_column=gene_ids_column,
761
788
  gene_names_column=gene_names_column,
762
789
  feature_types_column=feature_types_column,
790
+ raw=raw,
791
+ min_cell_probability=min_cell_probability,
763
792
  ),
764
793
  )
765
794
  except Exception as e:
@@ -1117,7 +1146,7 @@ def export_sparse(
1117
1146
  "--output",
1118
1147
  "-o",
1119
1148
  writable=True,
1120
- help="Output .mtx file path (defaults to stdout)",
1149
+ help="Output .mtx file path; .mtx.gz gzips it (defaults to stdout)",
1121
1150
  ),
1122
1151
  head: Optional[int] = typer.Option(
1123
1152
  None, "--head", "-n", help="Output only the first n entries of mtx file"
@@ -1407,7 +1436,7 @@ def import_sparse(
1407
1436
  ..., help="Object path to create/replace (e.g., 'X', 'layers/counts')"
1408
1437
  ),
1409
1438
  input_file: Path = typer.Argument(
1410
- ..., help="Input .mtx file", exists=True, readable=True
1439
+ ..., help="Input .mtx or .mtx.gz file", exists=True, readable=True
1411
1440
  ),
1412
1441
  output: Optional[Path] = typer.Option(
1413
1442
  None,
@@ -60,7 +60,9 @@ def import_object(
60
60
  console: Console,
61
61
  ) -> None:
62
62
  target_file = _prepare_target_path(file, output_file, inplace, console)
63
- ext = input_file.suffix.lower()
63
+ suffixes = [x.lower() for x in input_file.suffixes]
64
+ # A gzipped matrix is still a matrix: `counts.mtx.gz` imports as `.mtx`.
65
+ ext = suffixes[-2] if len(suffixes) > 1 and suffixes[-1] == ".gz" else input_file.suffix.lower()
64
66
 
65
67
  if ext not in EXTENSION_FORMAT:
66
68
  raise ValueError(
@@ -17,7 +17,7 @@ from typing import Dict, FrozenSet, List, Optional
17
17
 
18
18
  import h5py
19
19
 
20
- from adata.conversion import tenx
20
+ from adata.conversion import cellbender, mtx, tenx
21
21
  from adata.conversion.anndata_copy import AnnDataCopy
22
22
  from adata.conversion.base import ConversionPlan, Converter, Format, Options
23
23
  from adata.storage import has_valid_anndata_root_attrs, is_zarr_path
@@ -66,6 +66,15 @@ FORMATS: Dict[str, Format] = {
66
66
  anndata=True,
67
67
  sniff=is_zarr_path,
68
68
  ),
69
+ # Before 10x-h5: a CellBender file is a 10x matrix too, and the first
70
+ # sniffer to match wins. `--from 10x-h5` still reads one as plain 10x.
71
+ "cellbender": Format(
72
+ name="cellbender",
73
+ description="CellBender remove-background output (0.2 and later)",
74
+ extensions=(".h5",),
75
+ sniff=cellbender.sniff,
76
+ importer=cellbender.CellbenderImport(),
77
+ ),
69
78
  "10x-h5": Format(
70
79
  name="10x-h5",
71
80
  description="10x Genomics feature-barcode matrix (Cell Ranger v2 or v3)",
@@ -74,6 +83,15 @@ FORMATS: Dict[str, Format] = {
74
83
  importer=tenx.TenxImport(),
75
84
  exporter=tenx.TenxExport(),
76
85
  ),
86
+ # A folder, so no extension: chosen as output with `--to 10x-mtx`.
87
+ "10x-mtx": Format(
88
+ name="10x-mtx",
89
+ description="10x Genomics / STARsolo Matrix Market folder",
90
+ extensions=(),
91
+ sniff=mtx.sniff,
92
+ importer=mtx.MtxImport(),
93
+ exporter=mtx.MtxExport(),
94
+ ),
77
95
  }
78
96
 
79
97
  _ANNDATA_COPY = AnnDataCopy()
@@ -106,6 +124,12 @@ def detect_format(path: Path) -> str:
106
124
  for fmt in FORMATS.values():
107
125
  if (fmt.anndata or fmt.sniff is None) and suffix in fmt.extensions:
108
126
  return fmt.name
127
+ if suffix in (".mtx", ".gz") and path.name.lower().endswith((".mtx", ".mtx.gz")):
128
+ raise ValueError(
129
+ f"'{path.name}' is a single Matrix Market file, which `convert` "
130
+ "does not read on its own: give the folder holding it with its "
131
+ "barcodes and features, or use `adata import sparse`."
132
+ )
109
133
  raise ValueError(
110
134
  f"Cannot tell what format '{path}' is. Pass --from, one of: {_names()}."
111
135
  )
@@ -121,24 +145,41 @@ def output_format(path: Path, requested: Optional[str] = None) -> str:
121
145
  path = Path(path)
122
146
  if requested is not None and requested not in FORMATS:
123
147
  raise ValueError(f"Unknown format {requested!r}; use one of: {_names()}.")
148
+ if requested is not None and not (
149
+ FORMATS[requested].anndata or FORMATS[requested].exporter is not None
150
+ ):
151
+ raise ValueError(f"{requested} can be read but not written.")
124
152
 
125
153
  suffix = path.suffix.lower()
154
+ # Only formats that can be written: `.h5` names CellBender output too,
155
+ # but an `.h5` output is a 10x matrix.
126
156
  by_extension = next(
127
- (fmt.name for fmt in FORMATS.values() if suffix in fmt.extensions), None
157
+ (
158
+ fmt.name
159
+ for fmt in FORMATS.values()
160
+ if suffix in fmt.extensions and (fmt.anndata or fmt.exporter is not None)
161
+ ),
162
+ None,
128
163
  )
129
164
  if requested is None:
130
165
  if by_extension is None:
131
166
  raise ValueError(
132
167
  f"Cannot tell the output format from '{path.name}'. "
133
- f"Use a known extension or pass --to, one of: {_names()}."
168
+ f"Use a known extension or pass --to, one of: {_names()} "
169
+ "(--to 10x-mtx for a folder)."
134
170
  )
135
171
  return by_extension
136
172
 
137
173
  if by_extension is not None and by_extension != requested:
174
+ extensions = FORMATS[requested].extensions
138
175
  raise ValueError(
139
176
  f"--to {requested} contradicts the output's extension, which means "
140
- f"{by_extension}. Name the output with one of: "
141
- f"{', '.join(FORMATS[requested].extensions)}."
177
+ f"{by_extension}. Name the output "
178
+ + (
179
+ f"with one of: {', '.join(extensions)}."
180
+ if extensions
181
+ else "as a folder, without an extension."
182
+ )
142
183
  )
143
184
  if (requested == "zarr") != (suffix == ".zarr"):
144
185
  raise ValueError(
@@ -35,6 +35,10 @@ class Options:
35
35
  gene_names_column: Optional[str] = None
36
36
  feature_types_column: Optional[str] = None
37
37
 
38
+ # Reading CellBender output.
39
+ raw: Optional[Path] = None
40
+ min_cell_probability: Optional[float] = None
41
+
38
42
 
39
43
  @dataclass
40
44
  class ConversionPlan: