dosho 0.1.0a1__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.
dosho/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """dosho -- a shinobi's tool bag. The native shinobi (stimela-ninja) cab
2
+ repository. See AGENTS.md for the design rationale.
3
+ """
4
+
5
+ from dosho import images
6
+ from dosho._builder import define_cab
7
+ from dosho.registry import get, list_cabs
8
+
9
+ __all__ = ["define_cab", "get", "images", "list_cabs"]
10
+
11
+ __version__ = "0.1.0a1"
dosho/_builder.py ADDED
@@ -0,0 +1,84 @@
1
+ """Thin builder for authoring a native `dosho` cab: wraps `shinobi.Cab` +
2
+ `shinobi.loaders.build_model` + `shinobi.loaders.sanitize_unique` to remove
3
+ the boilerplate of hand-transcribing a real tool's parameter list, without
4
+ inventing any new schema surface.
5
+
6
+ Generalises the `cab_from_defaults` helper first proven in
7
+ stimela-ninja's `examples/ninja_selfcal.py`. Deliberately not a
8
+ signature-introspecting decorator (`@cab`) -- that shape was tried once in
9
+ shinobi itself (`ba2bd48`) and removed (`605b6f8`): it can't express
10
+ `nom_de_guerre` (hyphenated CLI names aren't valid Python parameter
11
+ names), `input_patterns`, `Policies`, `positional`, or `implicit`. Raw
12
+ `Cab(...)` stays fully available for anything this helper doesn't cover --
13
+ it's sugar, not a wall.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any
19
+
20
+ from shinobi import Cab
21
+ from shinobi.loaders import build_model, sanitize_unique
22
+ from shinobi.steps.schema import ParamMeta, ParamPattern, Policies
23
+
24
+
25
+ def define_cab(
26
+ name: str,
27
+ command: str,
28
+ image: str,
29
+ fields: dict[str, tuple[str, bool, Any]],
30
+ *,
31
+ outputs: dict[str, tuple[str, bool, Any]] | None = None,
32
+ field_meta: dict[str, ParamMeta] | None = None,
33
+ policies: Policies | None = None,
34
+ input_patterns: list[ParamPattern] | None = None,
35
+ output_patterns: list[ParamPattern] | None = None,
36
+ flavour: str = "binary",
37
+ wranglers: dict[str, list[str]] | None = None,
38
+ info: str | None = None,
39
+ ) -> Cab:
40
+ """Build a `Cab` from a flat `{raw_param_name: (dtype, required,
41
+ default)}` dict -- the shape a tool's own `--help`/docs naturally give
42
+ you (e.g. `"data-ms": ("MS", True, None)`). Hyphenated/dotted raw
43
+ names are sanitised to valid pydantic field names, with the original
44
+ kept as a `nom_de_guerre` so the built cab's `build_argv` still emits
45
+ the tool's real flag.
46
+
47
+ `field_meta` entries the caller passes explicitly (keyed by the
48
+ *sanitised* field name -- e.g. an output field's `implicit` template,
49
+ or a `positional`/`repeat_as_tokens` override) are merged in on top of
50
+ the auto-derived `nom_de_guerre`s; an explicit entry that doesn't set
51
+ its own `nom_de_guerre` keeps the auto-derived one rather than losing
52
+ it.
53
+ """
54
+ resolved_fields: dict[str, tuple[str, bool, Any]] = {}
55
+ resolved_meta: dict[str, ParamMeta] = {}
56
+ seen: dict[str, str] = {}
57
+ for raw_name, spec in fields.items():
58
+ field = sanitize_unique(raw_name, seen)
59
+ resolved_fields[field] = spec
60
+ if field != raw_name:
61
+ resolved_meta[field] = ParamMeta(nom_de_guerre=raw_name)
62
+
63
+ for field, meta in (field_meta or {}).items():
64
+ auto = resolved_meta.get(field)
65
+ if auto is not None and meta.nom_de_guerre is None:
66
+ meta = meta.model_copy(update={"nom_de_guerre": auto.nom_de_guerre})
67
+ resolved_meta[field] = meta
68
+
69
+ return Cab(
70
+ name=name,
71
+ command=command,
72
+ image=image,
73
+ info=info,
74
+ flavour=flavour,
75
+ inputs_model=build_model(
76
+ f"{name}_Inputs", resolved_fields, allow_extra=bool(input_patterns)
77
+ ),
78
+ outputs_model=build_model(f"{name}_Outputs", outputs or {}),
79
+ field_meta=resolved_meta,
80
+ policies=policies or Policies(),
81
+ input_patterns=input_patterns or [],
82
+ output_patterns=output_patterns or [],
83
+ wranglers=wranglers or {},
84
+ )
dosho/cabs/__init__.py ADDED
@@ -0,0 +1,84 @@
1
+ """Every tool dosho ports, re-exported by name at package level, so a
2
+ pipeline author who knows what they want at write-time can do
3
+ `from dosho.cabs import wsclean` or `from dosho.cabs.casatasks import
4
+ listobs` directly -- no need to know or care whether a given tool is a
5
+ `Cab` (a real binary, argv-built and shelled out to) or a `StepRef` (a
6
+ Python-package tool with no standalone binary, run via `@shinobi.pystep`
7
+ and `ctx.import_func` inside a container instead -- CASA tasks are the
8
+ only example so far). Both shapes are first-class for `Recipe.add_step`.
9
+
10
+ For *runtime*/string-keyed lookup (the CLI, `shinobi.cabs` entry-point
11
+ discovery), use `dosho.get(name)`/`dosho.registry` instead -- that's a
12
+ second, parallel interface for a caller that doesn't know the name until
13
+ it runs, not a replacement for these direct imports.
14
+
15
+ Importing this module (or any of its submodules -- Python always
16
+ initializes a parent package first) eagerly constructs every `Cab`/
17
+ `StepRef` below. That's a deliberate, accepted tradeoff here: each one is
18
+ cheap (pydantic model + dict construction, no I/O), so the "don't pay for
19
+ what you don't use" laziness `dosho.registry`'s own submodule-path table
20
+ still gives a caller that only wants one specific tool is preserved
21
+ there, not here.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from dosho.cabs.aoflagger import aoflagger
27
+ from dosho.cabs.casaplotms import plotms
28
+ from dosho.cabs.casatasks import (
29
+ applycal,
30
+ bandpass,
31
+ clearcal,
32
+ fixvis,
33
+ flagdata,
34
+ flagmanager,
35
+ fluxscale,
36
+ gaincal,
37
+ initweights,
38
+ listobs,
39
+ mstransform,
40
+ polcal,
41
+ setjy,
42
+ )
43
+ from dosho.cabs.crystalball import crystalball
44
+ from dosho.cabs.cubical import cubical
45
+ from dosho.cabs.mosaic_queen import mosaic_queen
46
+ from dosho.cabs.owlcat_plotelev import owlcat_plotelev
47
+ from dosho.cabs.quartical import quartical
48
+ from dosho.cabs.ragavi import ragavi
49
+ from dosho.cabs.shadems import shadems
50
+ from dosho.cabs.simms import simms_classic, skysim, telsim
51
+ from dosho.cabs.sofia2 import sofia2
52
+ from dosho.cabs.tricolour import tricolour
53
+ from dosho.cabs.wsclean import wsclean
54
+
55
+ __all__ = [
56
+ "aoflagger",
57
+ "applycal",
58
+ "bandpass",
59
+ "clearcal",
60
+ "crystalball",
61
+ "cubical",
62
+ "fixvis",
63
+ "flagdata",
64
+ "flagmanager",
65
+ "fluxscale",
66
+ "gaincal",
67
+ "initweights",
68
+ "listobs",
69
+ "mosaic_queen",
70
+ "mstransform",
71
+ "owlcat_plotelev",
72
+ "plotms",
73
+ "polcal",
74
+ "quartical",
75
+ "ragavi",
76
+ "setjy",
77
+ "shadems",
78
+ "simms_classic",
79
+ "skysim",
80
+ "sofia2",
81
+ "telsim",
82
+ "tricolour",
83
+ "wsclean",
84
+ ]
@@ -0,0 +1,69 @@
1
+ """aoflagger -- automatic RFI flagger (https://aoflagger.readthedocs.io).
2
+
3
+ Ported field-by-field from cult-cargo's aoflagger.yml (a flat, static
4
+ schema -- no dynamic_schema, no package-scoped _include -- so this is a
5
+ mechanical transcription, not a structural fix like wsclean/cubical/
6
+ quartical).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from shinobi.steps.schema import ParamMeta, Policies
12
+
13
+ from dosho import images
14
+ from dosho._builder import define_cab
15
+
16
+ _FIELDS: dict[str, tuple[str, bool, object]] = {
17
+ "msname": ("MS", True, None),
18
+ "verbose": ("bool", False, False),
19
+ "threads": ("int", False, None),
20
+ "strategy": ("File", False, None),
21
+ "indirect_read": ("bool", False, True),
22
+ "memory_read": ("bool", False, False),
23
+ "auto_read_mode": ("bool", False, True),
24
+ "uvw": ("bool", False, False),
25
+ "column": ("str", False, "DATA"),
26
+ "skip_flagged": ("bool", False, False),
27
+ "bands": ("int", False, None),
28
+ "fields": ("int", False, None),
29
+ }
30
+
31
+ _FIELD_META: dict[str, ParamMeta] = {
32
+ "msname": ParamMeta(info="MS name(s) to be flagged"),
33
+ "verbose": ParamMeta(nom_de_guerre="v", info="Produce verbose output"),
34
+ "threads": ParamMeta(
35
+ nom_de_guerre="j",
36
+ info="overrides the number of threads specified in the strategy (default: one thread for each CPU core)",
37
+ ),
38
+ "strategy": ParamMeta(info="specifies a possible customized strategy"),
39
+ "indirect_read": ParamMeta(
40
+ nom_de_guerre="indirect-read",
41
+ info="will reorder the measurement set before starting, which is normally faster but requires free disk space to reorder the data to",
42
+ ),
43
+ "memory_read": ParamMeta(
44
+ nom_de_guerre="memory-read",
45
+ info="will read the entire measurement set in memory. This is the fastest, but requires much memory.",
46
+ ),
47
+ "auto_read_mode": ParamMeta(
48
+ nom_de_guerre="auto-read-mode",
49
+ info="will select either memory or direct mode based on available memory",
50
+ ),
51
+ "uvw": ParamMeta(info="reads uvw values (some exotic strategies require these)"),
52
+ "column": ParamMeta(info="specify column to flag"),
53
+ "skip_flagged": ParamMeta(
54
+ nom_de_guerre="skip-flagged",
55
+ info="will skip an ms if it has already been processed by AOFlagger according to its HISTORY table.",
56
+ ),
57
+ "bands": ParamMeta(info="comma separated list of (zero-indexed) band ids to process"),
58
+ "fields": ParamMeta(info="Field ID(s). Comma separated string if more than one field"),
59
+ }
60
+
61
+ aoflagger = define_cab(
62
+ "aoflagger",
63
+ "aoflagger",
64
+ images.AOFLAGGER,
65
+ _FIELDS,
66
+ field_meta=_FIELD_META,
67
+ policies=Policies(prefix="-"),
68
+ info="AOFlagger automatic RFI flagger (https://aoflagger.readthedocs.io)",
69
+ )
@@ -0,0 +1,62 @@
1
+ """`@shinobi.pystep` wrapper for CASA's `plotms` task
2
+ (`casaplotms.plotms`, a sibling package to `casatasks`, not part of it --
3
+ same `ctx.import_func`-inside-container mechanism as
4
+ `dosho.cabs.casatasks`, see that module's own docstring for the full
5
+ rationale).
6
+
7
+ One general wrapper covers both a minimal single-axis-pair plot (e.g. an
8
+ elevation-vs-hourangle track) and the fuller per-datacolumn/per-correlation
9
+ diagnostic-plot shape (iterating over correlations via `iteraxis`) --
10
+ callers omit whatever they don't need; the real task's own defaults apply.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from pathlib import Path
16
+
17
+ import shinobi
18
+ from pydantic import BaseModel
19
+
20
+ from dosho import images
21
+
22
+
23
+ class PlotmsOutputs(BaseModel):
24
+ plotfile: Path
25
+
26
+
27
+ @shinobi.pystep(image=images.CASA6)
28
+ def plotms(
29
+ ctx,
30
+ vis: Path,
31
+ xaxis: str,
32
+ yaxis: str,
33
+ plotfile: Path,
34
+ xdatacolumn: str = "",
35
+ ydatacolumn: str = "",
36
+ coloraxis: str = "",
37
+ field: str = "",
38
+ correlation: str = "",
39
+ iteraxis: str = "",
40
+ expformat: str = "png",
41
+ exprange: str = "all",
42
+ overwrite: bool = True,
43
+ showgui: bool = False,
44
+ ) -> PlotmsOutputs:
45
+ plotms_fn = ctx.import_func("plotms", "casaplotms")
46
+ plotms_fn(
47
+ vis=str(vis),
48
+ xaxis=xaxis,
49
+ yaxis=yaxis,
50
+ xdatacolumn=xdatacolumn,
51
+ ydatacolumn=ydatacolumn,
52
+ coloraxis=coloraxis,
53
+ field=field,
54
+ correlation=correlation,
55
+ iteraxis=iteraxis,
56
+ plotfile=str(plotfile),
57
+ expformat=expformat,
58
+ exprange=exprange,
59
+ overwrite=overwrite,
60
+ showgui=showgui,
61
+ )
62
+ return PlotmsOutputs(plotfile=plotfile)