adagio-cli 0.1.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.
Files changed (57) hide show
  1. adagio/__init__.py +15 -0
  2. adagio/app/parsers/pipeline.py +89 -0
  3. adagio/check.py +16 -0
  4. adagio/cli/__init__.py +1 -0
  5. adagio/cli/args.py +62 -0
  6. adagio/cli/cache.py +82 -0
  7. adagio/cli/config.py +183 -0
  8. adagio/cli/dynamic.py +656 -0
  9. adagio/cli/main.py +313 -0
  10. adagio/cli/pipeline.py +49 -0
  11. adagio/cli/pipeline_sources.py +322 -0
  12. adagio/cli/qapi.py +300 -0
  13. adagio/cli/runner.py +310 -0
  14. adagio/cli/runtime.py +530 -0
  15. adagio/cli/task_exec.py +474 -0
  16. adagio/convert.py +0 -0
  17. adagio/describe.py +487 -0
  18. adagio/executors/__init__.py +34 -0
  19. adagio/executors/apptainer.py +275 -0
  20. adagio/executors/base.py +92 -0
  21. adagio/executors/cache_support.py +82 -0
  22. adagio/executors/common.py +124 -0
  23. adagio/executors/conda.py +314 -0
  24. adagio/executors/container_support.py +260 -0
  25. adagio/executors/defaults.py +99 -0
  26. adagio/executors/docker.py +404 -0
  27. adagio/executors/path_utils.py +60 -0
  28. adagio/executors/serial_runner.py +318 -0
  29. adagio/executors/signature.py +140 -0
  30. adagio/executors/task_contract.py +125 -0
  31. adagio/executors/task_environments.py +760 -0
  32. adagio/model/__init__.py +0 -0
  33. adagio/model/arguments.py +43 -0
  34. adagio/model/ast.py +67 -0
  35. adagio/model/pipeline.py +85 -0
  36. adagio/model/task.py +149 -0
  37. adagio/monitor/__init__.py +0 -0
  38. adagio/monitor/api.py +102 -0
  39. adagio/monitor/base.py +0 -0
  40. adagio/monitor/composite.py +99 -0
  41. adagio/monitor/connected.py +212 -0
  42. adagio/monitor/log.py +83 -0
  43. adagio/monitor/tty.py +301 -0
  44. adagio/protocol/__init__.py +0 -0
  45. adagio/protocol/base.py +0 -0
  46. adagio/protocol/file.py +0 -0
  47. adagio/protocol/http.py +0 -0
  48. adagio/protocol/s3.py +0 -0
  49. adagio/qapi/__init__.py +13 -0
  50. adagio/qapi/build.py +418 -0
  51. adagio/qapi/client.py +56 -0
  52. adagio/type_format.py +149 -0
  53. adagio_cli-0.1.0.dist-info/METADATA +136 -0
  54. adagio_cli-0.1.0.dist-info/RECORD +57 -0
  55. adagio_cli-0.1.0.dist-info/WHEEL +4 -0
  56. adagio_cli-0.1.0.dist-info/entry_points.txt +3 -0
  57. adagio_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
adagio/cli/dynamic.py ADDED
@@ -0,0 +1,656 @@
1
+ import inspect
2
+ import math
3
+ import re
4
+ import types
5
+ from pathlib import Path
6
+ from typing import Any, Annotated, Callable, Union, get_args, get_origin
7
+
8
+ from cyclopts import Group
9
+ from cyclopts import Parameter as CliParameter
10
+
11
+ from ..app.parsers.pipeline import Input as InputSpec
12
+ from ..app.parsers.pipeline import Output as OutputSpec
13
+ from ..app.parsers.pipeline import Parameter as ParamSpec
14
+ from ..executors.cache_support import (
15
+ CACHE_DIR_HELP,
16
+ RECYCLE_POOL_HELP,
17
+ REUSE_HELP,
18
+ )
19
+ from ..type_format import (
20
+ compact_type_text,
21
+ path_type_label,
22
+ render_type_text,
23
+ type_label_display_width,
24
+ )
25
+ from .args import ParamType, ShowParamsMode, dynamic_opt, to_identifier
26
+
27
+
28
+ class _PipelineGroupFormatter:
29
+ """Render pipeline options in one aligned table."""
30
+
31
+ def __init__(self, entry_metadata: dict[str, dict[str, Any]]):
32
+ self.entry_metadata = entry_metadata
33
+
34
+ def __call__(self, console: Any, options: Any, panel: Any) -> None:
35
+ from rich.console import Group as RichGroup
36
+ from rich.console import NewLine
37
+
38
+ from cyclopts.help.specs import PanelSpec, TableSpec
39
+
40
+ renderables: list[Any] = []
41
+
42
+ if panel.description:
43
+ renderables.append(panel.description)
44
+ if not panel.entries:
45
+ return
46
+
47
+ if renderables:
48
+ renderables.append(NewLine())
49
+ columns = _get_pipeline_parameter_columns(
50
+ console, panel.entries, self.entry_metadata
51
+ )
52
+ renderables.append(TableSpec().build(columns, panel.entries))
53
+ console.print(PanelSpec().build(RichGroup(*renderables), title=panel.title))
54
+
55
+
56
+ def _entry_key(entry: Any) -> str:
57
+ options = entry.all_options if hasattr(entry, "all_options") else ()
58
+ return next((name for name in options if name.startswith("--")), "")
59
+
60
+
61
+ def _unwrap_optional_type(type_hint: Any) -> Any:
62
+ origin = get_origin(type_hint)
63
+ if origin not in (types.UnionType, Union):
64
+ return type_hint
65
+
66
+ args = [arg for arg in get_args(type_hint) if arg is not type(None)]
67
+ return args[0] if len(args) == 1 else type_hint
68
+
69
+
70
+ def _pipeline_type_label(type_hint: Any) -> str:
71
+ type_hint = _unwrap_optional_type(type_hint)
72
+ if type_hint is bool:
73
+ return "BOOLEAN"
74
+ if type_hint is int:
75
+ return "INTEGER"
76
+ if type_hint is float:
77
+ return "NUMBER"
78
+ if type_hint is Path:
79
+ return "PATH"
80
+ return "TEXT"
81
+
82
+
83
+ def _display_type_label(*, spec_type: str | None, type_hint: Any, is_input: bool) -> str:
84
+ if is_input:
85
+ return path_type_label(spec_type)
86
+
87
+ if spec_type:
88
+ compact = compact_type_text(spec_type)
89
+ if compact.startswith("["):
90
+ return compact
91
+
92
+ return _pipeline_type_label(type_hint)
93
+
94
+
95
+ def _output_path_help(description: str | None) -> str:
96
+ cleaned = (description or "").strip()
97
+ if cleaned:
98
+ return f"{cleaned} Overrides --output-dir for this output."
99
+ return "Overrides --output-dir for this output."
100
+
101
+
102
+ def _render_pipeline_type(
103
+ entry: Any, entry_metadata: dict[str, dict[str, Any]], width: int
104
+ ) -> Any:
105
+ label = entry_metadata.get(_entry_key(entry), {}).get("type_label", "TEXT")
106
+ return render_type_text(label, width)
107
+
108
+
109
+ def _render_pipeline_description(
110
+ entry: Any, entry_metadata: dict[str, dict[str, Any]]
111
+ ) -> Any:
112
+ from rich.text import Text
113
+
114
+ from cyclopts.help.inline_text import InlineText
115
+
116
+ metadata = entry_metadata.get(_entry_key(entry), {})
117
+ description = entry.description
118
+ if description is None:
119
+ description = InlineText(Text())
120
+ elif not isinstance(description, InlineText):
121
+ if hasattr(description, "__rich_console__"):
122
+ description = InlineText(description)
123
+ else:
124
+ description = InlineText(Text(str(description)))
125
+
126
+ default = metadata.get("default")
127
+ if default is not None:
128
+ description.append(Text(f"[default: {default}]", "dim"))
129
+
130
+ if metadata.get("required"):
131
+ description.append(Text("[required]", "dim red"))
132
+
133
+ return description
134
+
135
+
136
+ def _get_pipeline_parameter_columns(
137
+ console: Any,
138
+ entries: list[Any],
139
+ entry_metadata: dict[str, dict[str, Any]],
140
+ ) -> tuple[Any, ...]:
141
+ from cyclopts.help.specs import (
142
+ ColumnSpec,
143
+ NameRenderer,
144
+ )
145
+
146
+ max_width = math.ceil(console.width * 0.35)
147
+ type_width = max(
148
+ 8,
149
+ min(
150
+ max(
151
+ type_label_display_width(
152
+ entry_metadata.get(_entry_key(entry), {}).get("type_label", "TEXT")
153
+ )
154
+ for entry in entries
155
+ ),
156
+ max(28, min(70, math.ceil(console.width * 0.35))),
157
+ ),
158
+ )
159
+ name_column = ColumnSpec(
160
+ renderer=NameRenderer(max_width=max_width),
161
+ header="Option",
162
+ justify="left",
163
+ style="cyan",
164
+ max_width=max_width,
165
+ )
166
+ type_column = ColumnSpec(
167
+ renderer=lambda entry: _render_pipeline_type(entry, entry_metadata, type_width),
168
+ header="Type",
169
+ justify="left",
170
+ no_wrap=True,
171
+ width=type_width,
172
+ min_width=type_width,
173
+ max_width=type_width,
174
+ )
175
+ description_column = ColumnSpec(
176
+ renderer=lambda entry: _render_pipeline_description(entry, entry_metadata),
177
+ header="Description",
178
+ justify="left",
179
+ overflow="fold",
180
+ )
181
+
182
+ return (name_column, type_column, description_column)
183
+
184
+
185
+ def _spec_py_type(type_name: str) -> type:
186
+ """Map pipeline type text to a Python type."""
187
+ normalized = re.sub(r"[^a-z0-9]+", " ", (type_name or "").lower()).strip()
188
+ tokens = set(normalized.split())
189
+
190
+ if {"bool", "boolean"} & tokens or "bool" in normalized:
191
+ return bool
192
+ if {"int", "integer"} & tokens or "int" in normalized:
193
+ return int
194
+ if {"float", "double", "number", "numeric", "real"} & tokens:
195
+ return float
196
+ if {"str", "string", "text"} & tokens:
197
+ return str
198
+ return str
199
+
200
+
201
+ def _default_py_type(default: Any) -> type | None:
202
+ """Infer a Python type from a default value."""
203
+ if isinstance(default, bool):
204
+ return bool
205
+ if isinstance(default, int):
206
+ return int
207
+ if isinstance(default, float):
208
+ return float
209
+ if isinstance(default, str):
210
+ return str
211
+ return None
212
+
213
+
214
+ def _resolve_param_type(type_name: str, default: Any) -> type:
215
+ """Resolve the CLI parameter type from type text and default."""
216
+ declared = _spec_py_type(type_name)
217
+ inferred = _default_py_type(default)
218
+ if inferred is None:
219
+ return declared
220
+ if declared is str and inferred is not str:
221
+ return inferred
222
+ return declared
223
+
224
+
225
+ def _format_help_text(
226
+ *,
227
+ description: str | None = None,
228
+ ) -> str:
229
+ """Return plain description text for pipeline help rows."""
230
+ return (description or "").strip()
231
+
232
+
233
+ def _is_required_param(spec: ParamSpec) -> bool:
234
+ return bool(spec.required and spec.default is None)
235
+
236
+
237
+ def _is_collection_type(type_name: str) -> bool:
238
+ return type_name.startswith("List[") or type_name.startswith("Collection[")
239
+
240
+
241
+ def build_dynamic_run(
242
+ *,
243
+ input_specs: list[InputSpec],
244
+ param_specs: list[ParamSpec],
245
+ output_specs: list[OutputSpec],
246
+ visible_input_names: set[str] | None = None,
247
+ visible_param_names: set[str] | None = None,
248
+ visible_output_names: set[str] | None = None,
249
+ argument_inputs: dict[str, Any] | None = None,
250
+ argument_params: dict[str, Any] | None = None,
251
+ run_handler: Callable[
252
+ [
253
+ Path,
254
+ Path | None,
255
+ Path | None,
256
+ dict[str, Any],
257
+ list[tuple[str, str]],
258
+ list[tuple[str, str]],
259
+ list[tuple[str, str]],
260
+ str,
261
+ list[str],
262
+ list[str],
263
+ ],
264
+ None,
265
+ ],
266
+ ):
267
+ """Build a dynamic run command from pipeline input, parameter, and output specs."""
268
+ visible_input_names = (
269
+ set(visible_input_names) if visible_input_names is not None else None
270
+ )
271
+ visible_param_names = set(visible_param_names) if visible_param_names is not None else None
272
+ visible_output_names = (
273
+ set(visible_output_names) if visible_output_names is not None else None
274
+ )
275
+ input_bindings: list[tuple[str, str]] = []
276
+ param_bindings: list[tuple[str, str]] = []
277
+ output_bindings: list[tuple[str, str]] = []
278
+ required_inputs: list[str] = []
279
+ required_params: list[str] = []
280
+ seen_idents: set[str] = set()
281
+ entry_metadata: dict[str, dict[str, Any]] = {}
282
+ seen_opts: set[str] = {
283
+ "--pipeline",
284
+ "-p",
285
+ "--arguments",
286
+ "--config",
287
+ "--show-params",
288
+ "--cache-dir",
289
+ "--reuse",
290
+ "--no-reuse",
291
+ "--output-dir",
292
+ "--recycle-pool",
293
+ "--log-dir",
294
+ "--targets",
295
+ }
296
+ argument_inputs = argument_inputs or {}
297
+ argument_params = argument_params or {}
298
+ command_group = Group("Command Options", sort_key=0)
299
+ pipeline_group = Group(
300
+ "Pipeline",
301
+ sort_key=1,
302
+ help_formatter=_PipelineGroupFormatter(entry_metadata),
303
+ )
304
+
305
+ annotations: dict[str, Any] = {
306
+ "pipeline": Annotated[
307
+ Path,
308
+ CliParameter(
309
+ name=("--pipeline", "-p"),
310
+ group=command_group,
311
+ help="Path to the pipeline file or a catalog reference like @adagio/slug.",
312
+ ),
313
+ ]
314
+ }
315
+
316
+ annotations["arguments_file"] = Annotated[
317
+ Path | None,
318
+ CliParameter(
319
+ name=("--arguments",),
320
+ group=command_group,
321
+ help="Path to a JSON arguments file. Values are applied before CLI overrides.",
322
+ ),
323
+ ]
324
+ annotations["show_params"] = Annotated[
325
+ ShowParamsMode,
326
+ CliParameter(
327
+ name=("--show-params",),
328
+ group=command_group,
329
+ help="Parameter display mode: all, missing, or required.",
330
+ ),
331
+ ]
332
+ annotations["config_file"] = Annotated[
333
+ Path | None,
334
+ CliParameter(
335
+ name=("--config",),
336
+ group=command_group,
337
+ help="Path to a TOML runtime config file.",
338
+ ),
339
+ ]
340
+ annotations["cache_dir"] = Annotated[
341
+ Path,
342
+ CliParameter(
343
+ name=("--cache-dir",),
344
+ group=command_group,
345
+ help=CACHE_DIR_HELP,
346
+ ),
347
+ ]
348
+ annotations["reuse"] = Annotated[
349
+ bool,
350
+ CliParameter(
351
+ name=("--reuse",),
352
+ negative=("--no-reuse",),
353
+ group=command_group,
354
+ help=REUSE_HELP,
355
+ ),
356
+ ]
357
+ annotations["output_dir"] = Annotated[
358
+ Path | None,
359
+ CliParameter(
360
+ name=("--output-dir",),
361
+ group=command_group,
362
+ help="Directory for all pipeline outputs.",
363
+ ),
364
+ ]
365
+ annotations["recycle_pool"] = Annotated[
366
+ str | None,
367
+ CliParameter(
368
+ name=("--recycle-pool",),
369
+ group=command_group,
370
+ help=RECYCLE_POOL_HELP,
371
+ ),
372
+ ]
373
+ annotations["log_dir"] = Annotated[
374
+ Path | None,
375
+ CliParameter(
376
+ name=("--log-dir",),
377
+ group=command_group,
378
+ help=(
379
+ "Directory to copy each task's container log into before the "
380
+ "temp work dir is torn down."
381
+ ),
382
+ ),
383
+ ]
384
+ annotations["targets"] = Annotated[
385
+ str | None,
386
+ CliParameter(
387
+ name=("--targets",),
388
+ group=command_group,
389
+ help=(
390
+ "Comma-separated node ids to produce; the plan is pruned to "
391
+ "their upstream closure. Defaults to all nodes."
392
+ ),
393
+ ),
394
+ ]
395
+
396
+ parameters: list[inspect.Parameter] = [
397
+ inspect.Parameter(
398
+ name="pipeline",
399
+ kind=inspect.Parameter.KEYWORD_ONLY,
400
+ annotation=annotations["pipeline"],
401
+ ),
402
+ inspect.Parameter(
403
+ name="cache_dir",
404
+ kind=inspect.Parameter.KEYWORD_ONLY,
405
+ annotation=annotations["cache_dir"],
406
+ ),
407
+ inspect.Parameter(
408
+ name="arguments_file",
409
+ kind=inspect.Parameter.KEYWORD_ONLY,
410
+ default=None,
411
+ annotation=annotations["arguments_file"],
412
+ ),
413
+ inspect.Parameter(
414
+ name="show_params",
415
+ kind=inspect.Parameter.KEYWORD_ONLY,
416
+ default=ShowParamsMode.REQUIRED,
417
+ annotation=annotations["show_params"],
418
+ ),
419
+ inspect.Parameter(
420
+ name="config_file",
421
+ kind=inspect.Parameter.KEYWORD_ONLY,
422
+ default=None,
423
+ annotation=annotations["config_file"],
424
+ ),
425
+ inspect.Parameter(
426
+ name="reuse",
427
+ kind=inspect.Parameter.KEYWORD_ONLY,
428
+ default=True,
429
+ annotation=annotations["reuse"],
430
+ ),
431
+ inspect.Parameter(
432
+ name="output_dir",
433
+ kind=inspect.Parameter.KEYWORD_ONLY,
434
+ default=None,
435
+ annotation=annotations["output_dir"],
436
+ ),
437
+ inspect.Parameter(
438
+ name="recycle_pool",
439
+ kind=inspect.Parameter.KEYWORD_ONLY,
440
+ default=None,
441
+ annotation=annotations["recycle_pool"],
442
+ ),
443
+ inspect.Parameter(
444
+ name="log_dir",
445
+ kind=inspect.Parameter.KEYWORD_ONLY,
446
+ default=None,
447
+ annotation=annotations["log_dir"],
448
+ ),
449
+ inspect.Parameter(
450
+ name="targets",
451
+ kind=inspect.Parameter.KEYWORD_ONLY,
452
+ default=None,
453
+ annotation=annotations["targets"],
454
+ ),
455
+ ]
456
+
457
+ def add_dynamic_option(
458
+ *,
459
+ ident: str,
460
+ opt: str,
461
+ required: bool,
462
+ py_type: Any,
463
+ help_text: str,
464
+ default: Any,
465
+ group: Group | tuple[Group, ...],
466
+ show: bool = True,
467
+ ) -> None:
468
+ if opt in seen_opts:
469
+ raise ValueError(f"Conflicting CLI option generated: {opt!r}.")
470
+ seen_opts.add(opt)
471
+
472
+ annotation_type = py_type | None if default is None else py_type
473
+ annotations[ident] = Annotated[
474
+ annotation_type,
475
+ CliParameter(
476
+ name=(opt,),
477
+ group=group,
478
+ help=help_text,
479
+ required=required,
480
+ show=show,
481
+ ),
482
+ ]
483
+ parameters.append(
484
+ inspect.Parameter(
485
+ name=ident,
486
+ kind=inspect.Parameter.KEYWORD_ONLY,
487
+ default=default,
488
+ annotation=annotations[ident],
489
+ )
490
+ )
491
+
492
+ required_input_specs = [spec for spec in input_specs if spec.required]
493
+ optional_input_specs = [spec for spec in input_specs if not spec.required]
494
+ required_param_specs = [spec for spec in param_specs if _is_required_param(spec)]
495
+ optional_param_specs = [spec for spec in param_specs if not _is_required_param(spec)]
496
+
497
+ def add_input_spec(spec: InputSpec) -> None:
498
+ original = spec.name
499
+ ident = to_identifier(original, "input")
500
+ if ident in seen_idents:
501
+ raise ValueError(
502
+ f"Duplicate pipeline input name after normalization: {original!r}."
503
+ )
504
+ seen_idents.add(ident)
505
+ input_bindings.append((ident, original))
506
+ argument_value = argument_inputs.get(original)
507
+ display_required = bool(spec.required and _is_missing(argument_value))
508
+ if spec.required:
509
+ required_inputs.append(original)
510
+
511
+ type_text = spec.type
512
+ opt = dynamic_opt(original, ParamType.INPUT)
513
+ show = visible_input_names is None or original in visible_input_names
514
+ entry_metadata[opt] = {
515
+ "type_label": _display_type_label(
516
+ spec_type=type_text, type_hint=str, is_input=True
517
+ ),
518
+ "default": None,
519
+ "required": display_required,
520
+ }
521
+ add_dynamic_option(
522
+ ident=ident,
523
+ opt=opt,
524
+ required=False,
525
+ py_type=list[str] if _is_collection_type(spec.type) else str,
526
+ help_text=_format_help_text(
527
+ description=spec.description,
528
+ ),
529
+ default=None,
530
+ group=pipeline_group,
531
+ show=show,
532
+ )
533
+
534
+ def add_param_spec(spec: ParamSpec) -> None:
535
+ original = spec.name
536
+ ident = to_identifier(original, "param")
537
+ if ident in seen_idents:
538
+ raise ValueError(
539
+ f"Duplicate pipeline parameter name after normalization: {original!r}."
540
+ )
541
+ seen_idents.add(ident)
542
+ param_bindings.append((ident, original))
543
+
544
+ default = spec.default
545
+ is_required = _is_required_param(spec)
546
+ argument_value = argument_params.get(original)
547
+ has_argument_default = not _is_missing(argument_value)
548
+ display_default = (
549
+ default if default is not None else (argument_value if has_argument_default else None)
550
+ )
551
+ display_required = is_required and display_default is None
552
+ param_default = None
553
+ param_type: Any = _resolve_param_type(spec.type, default)
554
+ opt = dynamic_opt(original, ParamType.PARAM)
555
+ show = visible_param_names is None or original in visible_param_names
556
+ if is_required:
557
+ required_params.append(original)
558
+ entry_metadata[opt] = {
559
+ "type_label": _display_type_label(
560
+ spec_type=spec.type, type_hint=param_type, is_input=False
561
+ ),
562
+ "default": display_default,
563
+ "required": display_required,
564
+ }
565
+ add_dynamic_option(
566
+ ident=ident,
567
+ opt=opt,
568
+ required=False,
569
+ py_type=param_type,
570
+ help_text=_format_help_text(
571
+ description=spec.description,
572
+ ),
573
+ default=param_default,
574
+ group=pipeline_group,
575
+ show=show,
576
+ )
577
+
578
+ for spec in required_input_specs:
579
+ add_input_spec(spec)
580
+ for spec in required_param_specs:
581
+ add_param_spec(spec)
582
+ for spec in optional_input_specs:
583
+ add_input_spec(spec)
584
+ for spec in optional_param_specs:
585
+ add_param_spec(spec)
586
+
587
+ for spec in output_specs:
588
+ original = spec.name
589
+ ident = to_identifier(original, "output")
590
+ if ident in seen_idents:
591
+ raise ValueError(
592
+ f"Duplicate pipeline output name after normalization: {original!r}."
593
+ )
594
+ seen_idents.add(ident)
595
+ output_bindings.append((ident, original))
596
+ opt = dynamic_opt(original, ParamType.OUTPUT)
597
+ show = visible_output_names is None or original in visible_output_names
598
+ entry_metadata[opt] = {
599
+ "type_label": path_type_label(spec.type),
600
+ "default": None,
601
+ "required": False,
602
+ }
603
+ add_dynamic_option(
604
+ ident=ident,
605
+ opt=opt,
606
+ required=False,
607
+ py_type=str,
608
+ help_text=_format_help_text(
609
+ description=_output_path_help(spec.description),
610
+ ),
611
+ default=None,
612
+ group=pipeline_group,
613
+ show=show,
614
+ )
615
+
616
+ def run(
617
+ pipeline: Path,
618
+ arguments_file: Path | None = None,
619
+ show_params: ShowParamsMode = ShowParamsMode.REQUIRED,
620
+ config_file: Path | None = None,
621
+ output_dir: Path | None = None,
622
+ recycle_pool: str | None = None,
623
+ log_dir: Path | None = None,
624
+ targets: str | None = None,
625
+ **kwargs: Any,
626
+ ) -> None:
627
+ _ = show_params
628
+ kwargs["output_dir"] = output_dir
629
+ kwargs["recycle_pool"] = recycle_pool
630
+ kwargs["log_dir"] = log_dir
631
+ kwargs["targets"] = targets
632
+ run_handler(
633
+ pipeline,
634
+ arguments_file,
635
+ config_file,
636
+ kwargs,
637
+ input_bindings,
638
+ param_bindings,
639
+ output_bindings,
640
+ "output_dir",
641
+ required_inputs,
642
+ required_params,
643
+ )
644
+
645
+ run.__annotations__ = annotations
646
+ run.__signature__ = inspect.Signature(parameters)
647
+ run.__doc__ = (
648
+ "Run an Adagio pipeline.\n\n"
649
+ "Dynamic inputs, parameters, and outputs are loaded from the pipeline file and exposed as CLI options.\n"
650
+ "Use: adagio run --pipeline PATH-OR-@ADAGIO/SLUG --help"
651
+ )
652
+ return run
653
+
654
+
655
+ def _is_missing(value: Any) -> bool:
656
+ return value is None or value == "" or value == "<fill me>" or value == [] or value == {}