dirigent-cli 0.15.0__tar.gz → 0.15.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.
Files changed (35) hide show
  1. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/PKG-INFO +1 -1
  2. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/pyproject.toml +1 -1
  3. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/pyproject.toml.orig +1 -1
  4. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/commands.py +38 -5
  5. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/sources.py +27 -1
  6. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/LICENSE +0 -0
  7. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/README.md +0 -0
  8. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/__init__.py +0 -0
  9. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/aliases.py +0 -0
  10. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/context.py +0 -0
  11. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/formatters.py +0 -0
  12. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/graph.py +0 -0
  13. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/health.py +0 -0
  14. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/init_form.py +0 -0
  15. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/local.py +0 -0
  16. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/main.py +0 -0
  17. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/output.py +0 -0
  18. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/params.py +0 -0
  19. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/profiles.py +0 -0
  20. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/project.py +0 -0
  21. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/py.typed +0 -0
  22. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/reaper.py +0 -0
  23. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/scaffold.py +0 -0
  24. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/schemas.py +0 -0
  25. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/seeding.py +0 -0
  26. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/starters.py +0 -0
  27. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/stream.py +0 -0
  28. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/summaries.py +0 -0
  29. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
  30. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
  31. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
  32. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
  33. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
  34. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/timing.py +0 -0
  35. {dirigent_cli-0.15.0 → dirigent_cli-0.15.2}/src/dirigent_cli/triggers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-cli
3
- Version: 0.15.0
3
+ Version: 0.15.2
4
4
  Summary: The dirigent command line interface (dirigent / dg).
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-cli"
3
- version = "0.15.0"
3
+ version = "0.15.2"
4
4
  description = "The dirigent command line interface (dirigent / dg)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-cli"
3
- version = "0.15.0"
3
+ version = "0.15.2"
4
4
  description = "The dirigent command line interface (dirigent / dg)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -65,7 +65,14 @@ from dirigent_cli.project import (
65
65
  write_token_env,
66
66
  )
67
67
  from dirigent_cli.scaffold import ScaffoldedRecord, ScaffoldError, ScaffoldRecord, scaffold_pack
68
- from dirigent_cli.sources import Document, SourceError, looks_like_a_document, read_document, read_path
68
+ from dirigent_cli.sources import (
69
+ Document,
70
+ SourceError,
71
+ looks_like_a_document,
72
+ parse_document,
73
+ read_document,
74
+ read_path,
75
+ )
69
76
  from dirigent_cli.stream import Sink, track_steps, use_scratch_prefix
70
77
  from dirigent_cli.timing import RunProfile, attempt_timing, by_step, profile, step_timing
71
78
  from dirigent_client import (
@@ -184,9 +191,13 @@ def _apply_one(
184
191
  source: ProvenanceSource | None = None,
185
192
  ) -> ApplyResult:
186
193
  """Apply one document and return the server's result."""
194
+ try:
195
+ body = parse_document(document)
196
+ except SourceError as error:
197
+ fail(str(error))
187
198
  return dg.call(
188
199
  dg.pipelines.apply(
189
- cast("dict[str, Any]", yaml.safe_load(document.text)),
200
+ body,
190
201
  code=code,
191
202
  source=source if source is not None else document.source,
192
203
  source_ref=document.ref,
@@ -374,12 +385,20 @@ def export_command(
374
385
  """Export a pipeline as canonical YAML."""
375
386
  with client_for(state_of(ctx)) as dg:
376
387
  text = dg.call(dg.pipelines.export(code, version=version))
388
+ if file is not None:
389
+ file.write_text(text)
377
390
  if state_of(ctx).json_output:
378
- return emit_fact("pipeline.exported", message="exported", code=code, version=version, document=text)
391
+ return emit_fact(
392
+ "pipeline.exported",
393
+ message="exported",
394
+ code=code,
395
+ version=version,
396
+ document=text,
397
+ path=None if file is None else str(file),
398
+ )
379
399
  if file is None:
380
400
  console.print(text, end="", highlight=False, markup=False)
381
401
  return
382
- file.write_text(text)
383
402
  console.print(f"Wrote [bold]{file}[/].")
384
403
 
385
404
 
@@ -2713,11 +2732,25 @@ def examples_list(
2713
2732
  def examples_show(
2714
2733
  ctx: typer.Context,
2715
2734
  code: Annotated[str, typer.Argument(help="The example to read.")],
2735
+ file: Annotated[
2736
+ Path | None, typer.Option("-f", "--file", help="Write the document to a file instead of stdout.")
2737
+ ] = None,
2716
2738
  local: Annotated[bool, typer.Option("--local", help=LOCAL_CATALOGUE)] = False,
2717
2739
  ) -> None:
2718
- """Print one example's document, verbatim, as the shelf holds it."""
2740
+ """Print one example's document, verbatim, as the shelf holds it, or write it to a file.
2741
+
2742
+ Stdout in a pipe carries records like every command, so a redirect keeps a record and
2743
+ not the document; `-f` writes the document itself.
2744
+ """
2719
2745
  state = state_of(ctx)
2720
2746
  entry = _example(state, code, local=local)
2747
+ if file is not None:
2748
+ file.parent.mkdir(parents=True, exist_ok=True)
2749
+ file.write_text(entry.source)
2750
+ if state.json_output:
2751
+ return emit_fact("example.written", message="written", code=entry.code, path=str(file))
2752
+ console.print(f"Wrote [bold]{file}[/].")
2753
+ return
2721
2754
  if state.json_output:
2722
2755
  return emit_fact("example.source", message="example", **entry.model_dump(mode="json"))
2723
2756
  console.print(entry.source, end="", highlight=False, markup=False)
@@ -2,9 +2,10 @@
2
2
 
3
3
  import sys
4
4
  from pathlib import Path
5
- from typing import Final
5
+ from typing import Any, Final, cast
6
6
 
7
7
  import httpx2
8
+ import yaml
8
9
  from pydantic import BaseModel, ConfigDict
9
10
 
10
11
  from dirigent_client.enums import ProvenanceSource
@@ -67,6 +68,31 @@ def read_path(path: Path) -> Document:
67
68
  return Document(text=path.read_text(), source=ProvenanceSource.FILE, ref=str(path))
68
69
 
69
70
 
71
+ def parse_document(document: Document) -> dict[str, Any]:
72
+ """Parse a document's text, refusing what is plainly not a pipeline document, by name.
73
+
74
+ The server checks the same things, but it never saw the file: an error that names the path
75
+ is the one a person can act on when a whole project was applied.
76
+ """
77
+ try:
78
+ parsed = yaml.safe_load(document.text)
79
+ except yaml.YAMLError as error:
80
+ raise SourceError(f"{document.label} is not readable YAML or JSON: {error}") from error
81
+ if not isinstance(parsed, dict):
82
+ kind = "empty" if parsed is None else type(parsed).__name__
83
+ raise SourceError(f"{document.label} is not a document: a document is a mapping, and this file is {kind}")
84
+ if "format" not in parsed:
85
+ if "kind" in parsed:
86
+ raise SourceError(
87
+ f"{document.label} holds a dg record (kind {parsed['kind']!r}), not a document: a command whose "
88
+ "stdout is a pipe writes records, so a redirect keeps the record and not the rendering. Write "
89
+ "the document with `dg examples show CODE -f FILE` or `dg export CODE -f FILE`, or copy a "
90
+ "starter with `dg pipeline new STARTER`."
91
+ )
92
+ raise SourceError(f"{document.label} declares no format; add `format: dirigent/v1`")
93
+ return cast("dict[str, Any]", parsed)
94
+
95
+
70
96
  def looks_like_a_document(reference: str) -> bool:
71
97
  """Report whether an argument names a document rather than a pipeline.
72
98
 
File without changes
File without changes