reader-workbench 1.0.0__py3-none-any.whl
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.
- reader_workbench/__init__.py +22 -0
- reader_workbench/__main__.py +4 -0
- reader_workbench/_version.py +17 -0
- reader_workbench/api/__init__.py +74 -0
- reader_workbench/api/_record_reads.py +75 -0
- reader_workbench/api/artifacts.py +79 -0
- reader_workbench/api/facade.py +538 -0
- reader_workbench/api/models.py +285 -0
- reader_workbench/api/notebooks.py +63 -0
- reader_workbench/contracts/__init__.py +18 -0
- reader_workbench/contracts/builtins/__init__.py +36 -0
- reader_workbench/contracts/builtins/cytometry.py +140 -0
- reader_workbench/contracts/builtins/four_state_event_window.py +214 -0
- reader_workbench/contracts/builtins/generic.py +21 -0
- reader_workbench/contracts/builtins/logic.py +149 -0
- reader_workbench/contracts/builtins/plate_reader.py +47 -0
- reader_workbench/contracts/catalog.py +257 -0
- reader_workbench/contracts/model.py +109 -0
- reader_workbench/domains/__init__.py +1 -0
- reader_workbench/domains/cytometry/__init__.py +3 -0
- reader_workbench/domains/cytometry/analysis/__init__.py +29 -0
- reader_workbench/domains/cytometry/analysis/events.py +182 -0
- reader_workbench/domains/cytometry/analysis/gating.py +175 -0
- reader_workbench/domains/cytometry/analysis/workflow.py +248 -0
- reader_workbench/domains/cytometry/io/__init__.py +3 -0
- reader_workbench/domains/cytometry/io/fcs.py +135 -0
- reader_workbench/domains/cytometry/plots/__init__.py +5 -0
- reader_workbench/domains/cytometry/plots/diagnostic.py +155 -0
- reader_workbench/domains/logic/__init__.py +3 -0
- reader_workbench/domains/logic/crosstalk/__init__.py +3 -0
- reader_workbench/domains/logic/crosstalk/pairs.py +661 -0
- reader_workbench/domains/logic/four_state_vector/__init__.py +7 -0
- reader_workbench/domains/logic/four_state_vector/builder.py +321 -0
- reader_workbench/domains/logic/four_state_vector/collection/__init__.py +20 -0
- reader_workbench/domains/logic/four_state_vector/collection/checks.py +49 -0
- reader_workbench/domains/logic/four_state_vector/collection/constants.py +29 -0
- reader_workbench/domains/logic/four_state_vector/collection/model.py +19 -0
- reader_workbench/domains/logic/four_state_vector/collection/render.py +383 -0
- reader_workbench/domains/logic/four_state_vector/collection/sources.py +185 -0
- reader_workbench/domains/logic/four_state_vector/config.py +214 -0
- reader_workbench/domains/logic/four_state_vector/diagnostic.py +361 -0
- reader_workbench/domains/logic/four_state_vector/heatmap.py +86 -0
- reader_workbench/domains/logic/four_state_vector/math.py +191 -0
- reader_workbench/domains/logic/four_state_vector/reference.py +85 -0
- reader_workbench/domains/logic/four_state_vector/selection.py +228 -0
- reader_workbench/domains/logic/four_state_vector/treatment_semantics.py +51 -0
- reader_workbench/domains/logic/four_state_vector/validation.py +19 -0
- reader_workbench/domains/logic/logic_symmetry/__init__.py +3 -0
- reader_workbench/domains/logic/logic_symmetry/encodings.py +93 -0
- reader_workbench/domains/logic/logic_symmetry/extract_corners.py +192 -0
- reader_workbench/domains/logic/logic_symmetry/main.py +236 -0
- reader_workbench/domains/logic/logic_symmetry/metrics.py +96 -0
- reader_workbench/domains/logic/logic_symmetry/overlay.py +129 -0
- reader_workbench/domains/logic/logic_symmetry/prep.py +138 -0
- reader_workbench/domains/logic/logic_symmetry/render.py +356 -0
- reader_workbench/domains/logic/treatment_columns.py +42 -0
- reader_workbench/domains/plate_reader/__init__.py +1 -0
- reader_workbench/domains/plate_reader/analysis/__init__.py +14 -0
- reader_workbench/domains/plate_reader/analysis/fold_change.py +474 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/__init__.py +21 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/aggregation.py +191 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/contract_fields.py +50 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/contracts.py +320 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/design_dispositions.py +54 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/disposition_records.py +140 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/event_sensitivity.py +27 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/materialize.py +274 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/observation_resampling.py +97 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/reduction.py +62 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/seeds.py +15 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/sources.py +308 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusion_validation.py +94 -0
- reader_workbench/domains/plate_reader/analysis/four_state_event_window/well_exclusions.py +54 -0
- reader_workbench/domains/plate_reader/analysis/timepoints.py +76 -0
- reader_workbench/domains/plate_reader/io/__init__.py +6 -0
- reader_workbench/domains/plate_reader/io/sample_map.py +65 -0
- reader_workbench/domains/plate_reader/io/synergy_h1/__init__.py +6 -0
- reader_workbench/domains/plate_reader/io/synergy_h1/_kinetic.py +141 -0
- reader_workbench/domains/plate_reader/io/synergy_h1/_parser.py +295 -0
- reader_workbench/domains/plate_reader/io/synergy_h1/_shared.py +195 -0
- reader_workbench/domains/plate_reader/io/synergy_h1/_snapshot.py +130 -0
- reader_workbench/domains/plate_reader/ordering.py +59 -0
- reader_workbench/domains/plate_reader/plots/__init__.py +15 -0
- reader_workbench/domains/plate_reader/plots/_data.py +29 -0
- reader_workbench/domains/plate_reader/plots/common.py +346 -0
- reader_workbench/domains/plate_reader/plots/distributions.py +324 -0
- reader_workbench/domains/plate_reader/plots/dual_reporter_triptych.py +525 -0
- reader_workbench/domains/plate_reader/plots/dual_reporter_triptych_render.py +195 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/__init__.py +26 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic.py +295 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_components.py +149 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_render.py +308 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/diagnostic_style.py +51 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/schema.py +8 -0
- reader_workbench/domains/plate_reader/plots/four_state_event_window/summary.py +140 -0
- reader_workbench/domains/plate_reader/plots/grouping.py +53 -0
- reader_workbench/domains/plate_reader/plots/panels/__init__.py +12 -0
- reader_workbench/domains/plate_reader/plots/panels/snapshot.py +161 -0
- reader_workbench/domains/plate_reader/plots/panels/snapshot_data.py +91 -0
- reader_workbench/domains/plate_reader/plots/panels/time_series.py +296 -0
- reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic.py +435 -0
- reader_workbench/domains/plate_reader/plots/single_reporter_diagnostic_render.py +300 -0
- reader_workbench/domains/plate_reader/plots/snapshot_barplot/__init__.py +315 -0
- reader_workbench/domains/plate_reader/plots/snapshot_barplot/planning.py +168 -0
- reader_workbench/domains/plate_reader/plots/snapshot_heatmap/__init__.py +205 -0
- reader_workbench/domains/plate_reader/plots/snapshot_heatmap/inputs.py +103 -0
- reader_workbench/domains/plate_reader/plots/time_series.py +317 -0
- reader_workbench/domains/plate_reader/plots/ts_and_snap/__init__.py +479 -0
- reader_workbench/domains/plate_reader/plots/ts_and_snap/planning.py +283 -0
- reader_workbench/domains/time_series/__init__.py +29 -0
- reader_workbench/domains/time_series/aggregation.py +60 -0
- reader_workbench/domains/time_series/contracts.py +368 -0
- reader_workbench/domains/time_series/reduction.py +395 -0
- reader_workbench/errors.py +55 -0
- reader_workbench/maintenance/__init__.py +6 -0
- reader_workbench/maintenance/docs.py +335 -0
- reader_workbench/maintenance/model.py +28 -0
- reader_workbench/maintenance/release.py +39 -0
- reader_workbench/maintenance/skills.py +124 -0
- reader_workbench/plotting/__init__.py +20 -0
- reader_workbench/plotting/mpl.py +56 -0
- reader_workbench/plotting/sinks.py +69 -0
- reader_workbench/plotting/style.py +175 -0
- reader_workbench/plotting/utils.py +27 -0
- reader_workbench/plugins/__init__.py +1 -0
- reader_workbench/plugins/catalog.py +33 -0
- reader_workbench/plugins/export/__init__.py +0 -0
- reader_workbench/plugins/export/_paths.py +21 -0
- reader_workbench/plugins/export/csv.py +41 -0
- reader_workbench/plugins/export/xlsx.py +44 -0
- reader_workbench/plugins/ingest/__init__.py +0 -0
- reader_workbench/plugins/ingest/_discovery.py +58 -0
- reader_workbench/plugins/ingest/discovery_policy.py +66 -0
- reader_workbench/plugins/ingest/flow_cytometer.py +139 -0
- reader_workbench/plugins/ingest/synergy_h1.py +234 -0
- reader_workbench/plugins/manifests/__init__.py +1 -0
- reader_workbench/plugins/manifests/export.py +29 -0
- reader_workbench/plugins/manifests/ingest.py +29 -0
- reader_workbench/plugins/manifests/plot.py +161 -0
- reader_workbench/plugins/manifests/transform.py +172 -0
- reader_workbench/plugins/manifests/validator.py +18 -0
- reader_workbench/plugins/plot/__init__.py +0 -0
- reader_workbench/plugins/plot/_shared.py +55 -0
- reader_workbench/plugins/plot/cytometry_diagnostic.py +54 -0
- reader_workbench/plugins/plot/distributions.py +58 -0
- reader_workbench/plugins/plot/dual_reporter_triptych.py +224 -0
- reader_workbench/plugins/plot/four_state_event_window_diagnostic.py +133 -0
- reader_workbench/plugins/plot/four_state_event_window_summary.py +53 -0
- reader_workbench/plugins/plot/four_state_vector_collection.py +45 -0
- reader_workbench/plugins/plot/four_state_vector_diagnostic.py +105 -0
- reader_workbench/plugins/plot/four_state_vector_heatmap.py +63 -0
- reader_workbench/plugins/plot/logic_symmetry.py +56 -0
- reader_workbench/plugins/plot/single_reporter_diagnostic.py +279 -0
- reader_workbench/plugins/plot/snapshot_barplot.py +65 -0
- reader_workbench/plugins/plot/snapshot_heatmap.py +104 -0
- reader_workbench/plugins/plot/time_series.py +114 -0
- reader_workbench/plugins/plot/ts_and_snap.py +210 -0
- reader_workbench/plugins/transform/__init__.py +0 -0
- reader_workbench/plugins/transform/_four_state_vector.py +204 -0
- reader_workbench/plugins/transform/_labeling.py +109 -0
- reader_workbench/plugins/transform/alias.py +70 -0
- reader_workbench/plugins/transform/assay_labels.py +62 -0
- reader_workbench/plugins/transform/blank.py +79 -0
- reader_workbench/plugins/transform/crosstalk_pairs.py +180 -0
- reader_workbench/plugins/transform/cytometry_gating.py +120 -0
- reader_workbench/plugins/transform/fold_change.py +79 -0
- reader_workbench/plugins/transform/four_state_event_window.py +93 -0
- reader_workbench/plugins/transform/four_state_vector.py +62 -0
- reader_workbench/plugins/transform/four_state_vector_collection.py +41 -0
- reader_workbench/plugins/transform/logic_symmetry.py +67 -0
- reader_workbench/plugins/transform/outlier_filter.py +60 -0
- reader_workbench/plugins/transform/overflow.py +197 -0
- reader_workbench/plugins/transform/ratio.py +237 -0
- reader_workbench/plugins/transform/sample_map.py +170 -0
- reader_workbench/plugins/transform/sample_metadata.py +94 -0
- reader_workbench/plugins/validator/__init__.py +1 -0
- reader_workbench/plugins/validator/to_tidy_plus_map.py +155 -0
- reader_workbench/protocols/__init__.py +80 -0
- reader_workbench/protocols/_builtins_plate_reader_growth.py +179 -0
- reader_workbench/protocols/_builtins_plate_reader_variants.py +274 -0
- reader_workbench/protocols/builtins.py +1656 -0
- reader_workbench/protocols/compiler.py +22 -0
- reader_workbench/protocols/compilers/__init__.py +1 -0
- reader_workbench/protocols/compilers/common.py +100 -0
- reader_workbench/protocols/compilers/cytometry.py +87 -0
- reader_workbench/protocols/compilers/generic.py +14 -0
- reader_workbench/protocols/compilers/logic.py +245 -0
- reader_workbench/protocols/compilers/plate_reader.py +937 -0
- reader_workbench/protocols/compilers/plate_reader_pipeline.py +197 -0
- reader_workbench/protocols/model.py +1486 -0
- reader_workbench/protocols/semantic_coverage.py +234 -0
- reader_workbench/runtime/__init__.py +12 -0
- reader_workbench/runtime/builtin.py +23 -0
- reader_workbench/runtime/model.py +42 -0
- reader_workbench/workbench/__init__.py +60 -0
- reader_workbench/workbench/assets/__init__.py +22 -0
- reader_workbench/workbench/assets/types.py +118 -0
- reader_workbench/workbench/audit/__init__.py +5 -0
- reader_workbench/workbench/audit/experiments.py +307 -0
- reader_workbench/workbench/audit/staging.py +187 -0
- reader_workbench/workbench/cli/__init__.py +51 -0
- reader_workbench/workbench/cli/_lazy.py +9 -0
- reader_workbench/workbench/cli/_records_view.py +150 -0
- reader_workbench/workbench/cli/_surface_execution.py +443 -0
- reader_workbench/workbench/cli/audit.py +95 -0
- reader_workbench/workbench/cli/automation.py +229 -0
- reader_workbench/workbench/cli/demo.py +46 -0
- reader_workbench/workbench/cli/dop.py +91 -0
- reader_workbench/workbench/cli/experiments.py +635 -0
- reader_workbench/workbench/cli/helpers.py +232 -0
- reader_workbench/workbench/cli/main.py +59 -0
- reader_workbench/workbench/cli/maintenance.py +82 -0
- reader_workbench/workbench/cli/notebooks.py +260 -0
- reader_workbench/workbench/cli/pagination.py +117 -0
- reader_workbench/workbench/cli/protocols.py +336 -0
- reader_workbench/workbench/cli/shared.py +309 -0
- reader_workbench/workbench/cli/surfaces.py +534 -0
- reader_workbench/workbench/cli/verification.py +128 -0
- reader_workbench/workbench/commands.py +10 -0
- reader_workbench/workbench/config/__init__.py +47 -0
- reader_workbench/workbench/config/identity.py +13 -0
- reader_workbench/workbench/config/load.py +405 -0
- reader_workbench/workbench/config/model.py +274 -0
- reader_workbench/workbench/context.py +26 -0
- reader_workbench/workbench/decl/__init__.py +31 -0
- reader_workbench/workbench/decl/build.py +190 -0
- reader_workbench/workbench/decl/model.py +81 -0
- reader_workbench/workbench/dop/__init__.py +12 -0
- reader_workbench/workbench/dop/builtins.py +261 -0
- reader_workbench/workbench/dop/model.py +209 -0
- reader_workbench/workbench/engine/__init__.py +42 -0
- reader_workbench/workbench/engine/_shared.py +76 -0
- reader_workbench/workbench/engine/contracts.py +283 -0
- reader_workbench/workbench/engine/execution.py +326 -0
- reader_workbench/workbench/engine/file_outputs.py +260 -0
- reader_workbench/workbench/engine/inputs.py +161 -0
- reader_workbench/workbench/engine/invocations.py +507 -0
- reader_workbench/workbench/engine/planning.py +72 -0
- reader_workbench/workbench/engine/runtime.py +464 -0
- reader_workbench/workbench/engine/setup.py +149 -0
- reader_workbench/workbench/engine/validation.py +684 -0
- reader_workbench/workbench/experiment/__init__.py +47 -0
- reader_workbench/workbench/experiment/model.py +381 -0
- reader_workbench/workbench/experiments.py +133 -0
- reader_workbench/workbench/graph/__init__.py +47 -0
- reader_workbench/workbench/graph/nodes.py +102 -0
- reader_workbench/workbench/graph/normalize.py +177 -0
- reader_workbench/workbench/graph/refs.py +148 -0
- reader_workbench/workbench/input_discovery.py +19 -0
- reader_workbench/workbench/inspection/__init__.py +3 -0
- reader_workbench/workbench/inspection/catalogs.py +128 -0
- reader_workbench/workbench/inspection/common.py +92 -0
- reader_workbench/workbench/inspection/dop.py +64 -0
- reader_workbench/workbench/inspection/experiments.py +449 -0
- reader_workbench/workbench/inspection/inventory.py +68 -0
- reader_workbench/workbench/inspection/protocols.py +368 -0
- reader_workbench/workbench/inspection/readiness.py +333 -0
- reader_workbench/workbench/inspection/reports.py +367 -0
- reader_workbench/workbench/inspection/results.py +166 -0
- reader_workbench/workbench/inspection/runtime.py +287 -0
- reader_workbench/workbench/inspection/semantics.py +192 -0
- reader_workbench/workbench/inspection/validation.py +30 -0
- reader_workbench/workbench/notebooks/__init__.py +17 -0
- reader_workbench/workbench/notebooks/_launch_registry.py +112 -0
- reader_workbench/workbench/notebooks/_launch_runtime.py +104 -0
- reader_workbench/workbench/notebooks/components/__init__.py +21 -0
- reader_workbench/workbench/notebooks/components/deliverables.py +403 -0
- reader_workbench/workbench/notebooks/components/overview.py +119 -0
- reader_workbench/workbench/notebooks/eda.marimo.py.txt +153 -0
- reader_workbench/workbench/notebooks/launch.py +274 -0
- reader_workbench/workbench/notebooks/presentation.py +136 -0
- reader_workbench/workbench/notebooks/scaffold.py +60 -0
- reader_workbench/workbench/ontology.py +78 -0
- reader_workbench/workbench/paths.py +44 -0
- reader_workbench/workbench/ports/__init__.py +31 -0
- reader_workbench/workbench/ports/model.py +168 -0
- reader_workbench/workbench/records/__init__.py +44 -0
- reader_workbench/workbench/records/epoch.py +329 -0
- reader_workbench/workbench/records/evidence.py +247 -0
- reader_workbench/workbench/records/identity.py +87 -0
- reader_workbench/workbench/records/locking.py +185 -0
- reader_workbench/workbench/records/model.py +711 -0
- reader_workbench/workbench/records/sources.py +73 -0
- reader_workbench/workbench/records/store.py +1022 -0
- reader_workbench/workbench/records/verification.py +998 -0
- reader_workbench/workbench/registry.py +333 -0
- reader_workbench/workbench/spec_overrides.py +215 -0
- reader_workbench-1.0.0.dist-info/METADATA +91 -0
- reader_workbench-1.0.0.dist-info/RECORD +293 -0
- reader_workbench-1.0.0.dist-info/WHEEL +5 -0
- reader_workbench-1.0.0.dist-info/entry_points.txt +2 -0
- reader_workbench-1.0.0.dist-info/licenses/LICENSE +21 -0
- reader_workbench-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess # noqa: F401
|
|
4
|
+
import sys # noqa: F401
|
|
5
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich import box
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
from rich.theme import Theme
|
|
14
|
+
from rich.traceback import install as rich_tracebacks
|
|
15
|
+
|
|
16
|
+
from reader_workbench._version import package_version
|
|
17
|
+
from reader_workbench.errors import ReaderError
|
|
18
|
+
|
|
19
|
+
from .automation import (
|
|
20
|
+
emit_document,
|
|
21
|
+
error_envelope,
|
|
22
|
+
json_requested,
|
|
23
|
+
reader_error_details,
|
|
24
|
+
success_envelope,
|
|
25
|
+
)
|
|
26
|
+
from .pagination import Page, PageRequestError, page_collection
|
|
27
|
+
|
|
28
|
+
THEME = Theme(
|
|
29
|
+
{
|
|
30
|
+
"title": "bold cyan",
|
|
31
|
+
"accent": "cyan",
|
|
32
|
+
"ok": "bold green",
|
|
33
|
+
"warn": "bold yellow",
|
|
34
|
+
"error": "bold red",
|
|
35
|
+
"muted": "dim",
|
|
36
|
+
"path": "magenta",
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
app = typer.Typer(
|
|
41
|
+
add_completion=False,
|
|
42
|
+
invoke_without_command=True,
|
|
43
|
+
help=(
|
|
44
|
+
"reader — experimental workbench.\n\n"
|
|
45
|
+
"Discover assays and experiments, inspect workflow plans, validate YAML, "
|
|
46
|
+
"run pipelines, and produce plots, exports, or notebooks. "
|
|
47
|
+
"Start with 'reader demo', 'reader ls', or 'reader protocols'."
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
console = Console(theme=THEME)
|
|
51
|
+
rich_tracebacks(show_locals=False)
|
|
52
|
+
|
|
53
|
+
JOB_INDEX_SCOPE_NOTE = "resolved against the nearest experiments/ root from the current working directory"
|
|
54
|
+
JOB_ARG_HELP = (
|
|
55
|
+
f"Path to config.yaml • experiment directory • or numeric index from the default 'reader ls' inventory, "
|
|
56
|
+
f"{JOB_INDEX_SCOPE_NOTE}."
|
|
57
|
+
)
|
|
58
|
+
JOB_ARG_HELP_WITH_DEFAULT = f"{JOB_ARG_HELP[:-1]} (defaults to nearest ./config.yaml)"
|
|
59
|
+
JOB_ARG_HELP_SHORT = (
|
|
60
|
+
f"Experiment config path, directory, or index from the default 'reader ls' inventory, {JOB_INDEX_SCOPE_NOTE}."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
PLOT_ONLY_OPTION = typer.Option(None, "--only", help="Run only the specified plot id (repeatable).")
|
|
64
|
+
PLOT_EXCLUDE_OPTION = typer.Option(None, "--exclude", help="Exclude the specified plot id (repeatable).")
|
|
65
|
+
PLOT_INPUT_OPTION = typer.Option(
|
|
66
|
+
None,
|
|
67
|
+
"--input",
|
|
68
|
+
metavar="KEY=VALUE",
|
|
69
|
+
help="Override reads bindings for selected plot specs (repeatable).",
|
|
70
|
+
)
|
|
71
|
+
PLOT_SET_OPTION = typer.Option(
|
|
72
|
+
None,
|
|
73
|
+
"--set",
|
|
74
|
+
metavar="PATH=VALUE",
|
|
75
|
+
help="Patch spec fields for selected plots (reads.*, with.*, writes.*). Repeatable.",
|
|
76
|
+
)
|
|
77
|
+
EXPORT_ONLY_OPTION = typer.Option(None, "--only", help="Run only the specified export id (repeatable).")
|
|
78
|
+
EXPORT_EXCLUDE_OPTION = typer.Option(None, "--exclude", help="Exclude the specified export id (repeatable).")
|
|
79
|
+
EXPORT_INPUT_OPTION = typer.Option(
|
|
80
|
+
None,
|
|
81
|
+
"--input",
|
|
82
|
+
metavar="KEY=VALUE",
|
|
83
|
+
help="Override reads bindings for selected export specs (repeatable).",
|
|
84
|
+
)
|
|
85
|
+
EXPORT_SET_OPTION = typer.Option(
|
|
86
|
+
None,
|
|
87
|
+
"--set",
|
|
88
|
+
metavar="PATH=VALUE",
|
|
89
|
+
help="Patch spec fields for selected exports (reads.*, with.*, writes.*). Repeatable.",
|
|
90
|
+
)
|
|
91
|
+
NOTEBOOK_MODE_OPTION = typer.Option(
|
|
92
|
+
"edit",
|
|
93
|
+
"--mode",
|
|
94
|
+
help="Launch mode: edit | run | none (default: edit).",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.callback(invoke_without_command=True)
|
|
99
|
+
def _main(
|
|
100
|
+
ctx: typer.Context,
|
|
101
|
+
version: bool = typer.Option(False, "--version", is_eager=True, help="Show the installed Reader version."),
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Show help when no command is provided."""
|
|
104
|
+
if version:
|
|
105
|
+
typer.echo(package_version())
|
|
106
|
+
raise typer.Exit()
|
|
107
|
+
if ctx.invoked_subcommand is None:
|
|
108
|
+
typer.echo(ctx.get_help())
|
|
109
|
+
raise typer.Exit()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def checkmark(cond: bool) -> str:
|
|
113
|
+
return "[ok]✓[/ok]" if cond else "[muted]—[/muted]"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def table(title: str) -> Table:
|
|
117
|
+
return Table(
|
|
118
|
+
title=f"[title]{title}[/title]",
|
|
119
|
+
title_justify="left",
|
|
120
|
+
header_style="bold",
|
|
121
|
+
box=box.ROUNDED,
|
|
122
|
+
expand=True,
|
|
123
|
+
show_lines=False,
|
|
124
|
+
show_edge=True,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def abort(msg: str, *, code: int = 1) -> None:
|
|
129
|
+
if json_requested():
|
|
130
|
+
emit_document(
|
|
131
|
+
error_envelope(
|
|
132
|
+
code="command_rejected",
|
|
133
|
+
field="command",
|
|
134
|
+
reason=msg,
|
|
135
|
+
remediation="Correct the reported command state, then retry.",
|
|
136
|
+
retryable=False,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
raise typer.Exit(code=code)
|
|
140
|
+
console.print(Panel.fit(f"[error]✗ {msg}[/error]", border_style="error", box=box.ROUNDED))
|
|
141
|
+
raise typer.Exit(code=code)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def handle_reader_error(err: ReaderError) -> None:
|
|
145
|
+
if json_requested():
|
|
146
|
+
emit_document(error_envelope(**reader_error_details(err)))
|
|
147
|
+
raise typer.Exit(code=1)
|
|
148
|
+
abort(str(err))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def normalize_output_format(
|
|
152
|
+
value: str | None,
|
|
153
|
+
*,
|
|
154
|
+
default: str = "table",
|
|
155
|
+
allowed: tuple[str, ...] = ("table", "json"),
|
|
156
|
+
) -> str:
|
|
157
|
+
if not isinstance(value, str):
|
|
158
|
+
return default
|
|
159
|
+
fmt = value.strip().lower() or default
|
|
160
|
+
if fmt not in allowed:
|
|
161
|
+
raise typer.BadParameter(f"format must be one of: {', '.join(allowed)}")
|
|
162
|
+
return fmt
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def normalize_semantic_section(
|
|
166
|
+
value: str | None,
|
|
167
|
+
*,
|
|
168
|
+
allowed: Sequence[str],
|
|
169
|
+
format: str,
|
|
170
|
+
) -> str | None:
|
|
171
|
+
if not isinstance(value, str):
|
|
172
|
+
return None
|
|
173
|
+
section = value.strip().lower()
|
|
174
|
+
if format != "json":
|
|
175
|
+
raise typer.BadParameter("--section requires --format json", param_hint="--section")
|
|
176
|
+
if section not in allowed:
|
|
177
|
+
raise typer.BadParameter(
|
|
178
|
+
f"section must be one of: {', '.join(allowed)}",
|
|
179
|
+
param_hint="--section",
|
|
180
|
+
)
|
|
181
|
+
return section
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def normalize_paging_options(
|
|
185
|
+
limit: int | object,
|
|
186
|
+
continuation: str | object,
|
|
187
|
+
) -> tuple[int | None, str | None]:
|
|
188
|
+
normalized_limit = limit if type(limit) is int else None
|
|
189
|
+
normalized_continuation = continuation.strip() if isinstance(continuation, str) else None
|
|
190
|
+
return normalized_limit, normalized_continuation or None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def page_json_collection[T](
|
|
194
|
+
items: Sequence[T],
|
|
195
|
+
*,
|
|
196
|
+
key: Callable[[T], str],
|
|
197
|
+
surface: str,
|
|
198
|
+
selection: Mapping[str, object],
|
|
199
|
+
limit: int | None,
|
|
200
|
+
continuation: str | None,
|
|
201
|
+
) -> Page[T]:
|
|
202
|
+
try:
|
|
203
|
+
return page_collection(
|
|
204
|
+
items,
|
|
205
|
+
key=key,
|
|
206
|
+
surface=surface,
|
|
207
|
+
selection=selection,
|
|
208
|
+
limit=limit,
|
|
209
|
+
continuation=continuation,
|
|
210
|
+
)
|
|
211
|
+
except PageRequestError as exc:
|
|
212
|
+
option = f"--{exc.field.replace('_', '-')}"
|
|
213
|
+
raise typer.BadParameter(str(exc), param_hint=option) from exc
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def require_json_paging(*, format: str, limit: int | None, continuation: str | None) -> None:
|
|
217
|
+
if format == "json" or (limit is None and continuation is None):
|
|
218
|
+
return
|
|
219
|
+
option = "--continuation" if continuation is not None else "--limit"
|
|
220
|
+
raise typer.BadParameter(f"{option} requires --format json", param_hint=option)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def normalize_flag(value: bool | object, *, default: bool = False) -> bool:
|
|
224
|
+
return value if isinstance(value, bool) else default
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def normalize_status_filter(value: str | None) -> str | None:
|
|
228
|
+
if not isinstance(value, str):
|
|
229
|
+
return None
|
|
230
|
+
normalized = value.strip().lower()
|
|
231
|
+
if not normalized:
|
|
232
|
+
return None
|
|
233
|
+
allowed = {"ok", "config_error"}
|
|
234
|
+
if normalized not in allowed:
|
|
235
|
+
raise typer.BadParameter(f"status must be one of: {', '.join(sorted(allowed))}")
|
|
236
|
+
return normalized
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def normalize_lifecycle_filter(value: str | None) -> str | None:
|
|
240
|
+
if not isinstance(value, str):
|
|
241
|
+
return None
|
|
242
|
+
normalized = value.strip().lower()
|
|
243
|
+
if not normalized:
|
|
244
|
+
return None
|
|
245
|
+
allowed = {"active", "draft", "template"}
|
|
246
|
+
if normalized not in allowed:
|
|
247
|
+
raise typer.BadParameter(f"lifecycle must be one of: {', '.join(sorted(allowed))}")
|
|
248
|
+
return normalized
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _is_protocol_binding_value_ref(value: object) -> bool:
|
|
252
|
+
value_type = type(value)
|
|
253
|
+
# Keep plain CLI imports off the protocol bootstrap path.
|
|
254
|
+
return (
|
|
255
|
+
value_type.__module__ == "reader_workbench.protocols.model" and value_type.__name__ == "ProtocolBindingValueRef"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def json_friendly(value):
|
|
260
|
+
if _is_protocol_binding_value_ref(value):
|
|
261
|
+
payload = {"binding_value": value.key}
|
|
262
|
+
if value.has_default:
|
|
263
|
+
payload["default"] = json_friendly(value.default)
|
|
264
|
+
return payload
|
|
265
|
+
if isinstance(value, Path):
|
|
266
|
+
return str(value)
|
|
267
|
+
if isinstance(value, dict):
|
|
268
|
+
return {str(key): json_friendly(item) for key, item in value.items()}
|
|
269
|
+
if isinstance(value, tuple):
|
|
270
|
+
return [json_friendly(item) for item in value]
|
|
271
|
+
if isinstance(value, list):
|
|
272
|
+
return [json_friendly(item) for item in value]
|
|
273
|
+
return value
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def emit_json(
|
|
277
|
+
payload: object,
|
|
278
|
+
*,
|
|
279
|
+
projection: str = "full",
|
|
280
|
+
truncated: bool = False,
|
|
281
|
+
continuation: str | None = None,
|
|
282
|
+
) -> None:
|
|
283
|
+
emit_document(
|
|
284
|
+
success_envelope(
|
|
285
|
+
json_friendly(payload),
|
|
286
|
+
projection=projection,
|
|
287
|
+
truncated=truncated,
|
|
288
|
+
continuation=continuation,
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def emit_json_error(
|
|
294
|
+
*,
|
|
295
|
+
code: str,
|
|
296
|
+
field: str,
|
|
297
|
+
reason: str,
|
|
298
|
+
remediation: str,
|
|
299
|
+
retryable: bool = False,
|
|
300
|
+
) -> None:
|
|
301
|
+
emit_document(
|
|
302
|
+
error_envelope(
|
|
303
|
+
code=code,
|
|
304
|
+
field=field,
|
|
305
|
+
reason=reason,
|
|
306
|
+
remediation=remediation,
|
|
307
|
+
retryable=retryable,
|
|
308
|
+
)
|
|
309
|
+
)
|