dirigent-core 0.16.0__tar.gz → 0.16.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 (56) hide show
  1. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/PKG-INFO +4 -4
  2. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/pyproject.toml +4 -4
  3. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/pyproject.toml.orig +4 -4
  4. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/pipelines.py +19 -5
  5. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/protocol.py +11 -1
  6. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/secrets.py +6 -2
  7. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/LICENSE +0 -0
  8. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/README.md +0 -0
  9. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/__init__.py +0 -0
  10. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/alembic/env.py +0 -0
  11. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/alembic/script.py.mako +0 -0
  12. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/alembic/versions/0001_baseline_schema.py +0 -0
  13. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/alerting.py +0 -0
  14. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/artifacts.py +0 -0
  15. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/auth.py +0 -0
  16. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/blockdocs.py +0 -0
  17. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/config.py +0 -0
  18. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/configdocs.py +0 -0
  19. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/database.py +0 -0
  20. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/directory.py +0 -0
  21. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/documents.py +0 -0
  22. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/documentschema.py +0 -0
  23. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/__init__.py +0 -0
  24. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/claim.py +0 -0
  25. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/context.py +0 -0
  26. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/definition.py +0 -0
  27. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/executor.py +0 -0
  28. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/failure.py +0 -0
  29. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/recovery.py +0 -0
  30. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/references.py +0 -0
  31. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/runs.py +0 -0
  32. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/services.py +0 -0
  33. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/engine/state.py +0 -0
  34. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/examples.py +0 -0
  35. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/ids.py +0 -0
  36. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/logging.py +0 -0
  37. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/migrations.py +0 -0
  38. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/models.py +0 -0
  39. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/plugins.py +0 -0
  40. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/py.typed +0 -0
  41. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/ratelimit.py +0 -0
  42. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/registry.py +0 -0
  43. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/reporting.py +0 -0
  44. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/retention.py +0 -0
  45. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/scheduler.py +0 -0
  46. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/schemas.py +0 -0
  47. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/storage.py +0 -0
  48. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/telemetry.py +0 -0
  49. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/trigger_documents.py +0 -0
  50. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/triggers/__init__.py +0 -0
  51. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/triggers/backfill.py +0 -0
  52. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/triggers/materialize.py +0 -0
  53. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/triggers/schedules.py +0 -0
  54. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/triggers/webhooks.py +0 -0
  55. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/types.py +0 -0
  56. {dirigent_core-0.16.0 → dirigent_core-0.16.2}/src/dirigent_core/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-core
3
- Version: 0.16.0
3
+ Version: 0.16.2
4
4
  Summary: Dirigent engine core: schema, configuration, and plugin host.
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
@@ -10,9 +10,9 @@ Requires-Dist: argon2-cffi>=25.1.0
10
10
  Requires-Dist: asyncpg>=0.31.0
11
11
  Requires-Dist: cronsim>=2.7
12
12
  Requires-Dist: cryptography>=46.0.5
13
- Requires-Dist: dirigent-client
14
- Requires-Dist: dirigent-common
15
- Requires-Dist: dirigent-plugin
13
+ Requires-Dist: dirigent-client==0.16.2
14
+ Requires-Dist: dirigent-common==0.16.2
15
+ Requires-Dist: dirigent-plugin==0.16.2
16
16
  Requires-Dist: jsonschema>=4.26.0
17
17
  Requires-Dist: opentelemetry-api>=1.44.0
18
18
  Requires-Dist: pydantic-settings>=2.15.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-core"
3
- version = "0.16.0"
3
+ version = "0.16.2"
4
4
  description = "Dirigent engine core: schema, configuration, and plugin host."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -13,9 +13,9 @@ dependencies = [
13
13
  "asyncpg>=0.31.0",
14
14
  "cronsim>=2.7",
15
15
  "cryptography>=46.0.5",
16
- "dirigent-client",
17
- "dirigent-common",
18
- "dirigent-plugin",
16
+ "dirigent-client==0.16.2",
17
+ "dirigent-common==0.16.2",
18
+ "dirigent-plugin==0.16.2",
19
19
  "jsonschema>=4.26.0",
20
20
  "opentelemetry-api>=1.44.0",
21
21
  "pydantic-settings>=2.15.0",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-core"
3
- version = "0.16.0"
3
+ version = "0.16.2"
4
4
  description = "Dirigent engine core: schema, configuration, and plugin host."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -13,9 +13,9 @@ dependencies = [
13
13
  "asyncpg>=0.31.0",
14
14
  "cronsim>=2.7",
15
15
  "cryptography>=46.0.5",
16
- "dirigent-client",
17
- "dirigent-common",
18
- "dirigent-plugin",
16
+ "dirigent-client==0.16.2",
17
+ "dirigent-common==0.16.2",
18
+ "dirigent-plugin==0.16.2",
19
19
  "jsonschema>=4.26.0",
20
20
  "opentelemetry-api>=1.44.0",
21
21
  "pydantic-settings>=2.15.0",
@@ -471,24 +471,38 @@ async def last_runs(session: AsyncSession, pipeline_ids: Sequence[UUID]) -> dict
471
471
  status=row.status,
472
472
  started_at=row.started_at,
473
473
  finished_at=row.finished_at,
474
- failed_step=failing.get(row.id),
474
+ failed_step=failing[row.id].step if row.id in failing else None,
475
475
  )
476
476
  for row in rows
477
477
  }
478
478
 
479
479
 
480
- async def failing_steps(session: AsyncSession, run_ids: Sequence[UUID]) -> dict[UUID, str]:
481
- """Name the step each run's first failed attempt was of."""
480
+ class FailedStep(NamedTuple):
481
+ """A run's first failed attempt, as a listing names it: which step, and what it said."""
482
+
483
+ step: str
484
+ error: str | None
485
+
486
+
487
+ async def failing_steps(session: AsyncSession, run_ids: Sequence[UUID]) -> dict[UUID, FailedStep]:
488
+ """Name the step each run's first failed attempt was of, and what that attempt said.
489
+
490
+ A run row carries no error of its own unless it was cancelled: what went wrong is written
491
+ on the attempt, so a listing that names the step reads its message from the same row.
492
+ """
482
493
  if not run_ids:
483
494
  return {}
484
495
  ranked = (
485
496
  sa.select(
486
497
  StepAttempt.run_id,
487
498
  StepAttempt.step_name,
499
+ StepAttempt.error,
488
500
  sa.func.row_number().over(partition_by=StepAttempt.run_id, order_by=StepAttempt.id).label("rank"),
489
501
  )
490
502
  .where(StepAttempt.run_id.in_(run_ids), StepAttempt.status == AttemptStatus.FAILED)
491
503
  .subquery()
492
504
  )
493
- rows = await session.execute(sa.select(ranked.c.run_id, ranked.c.step_name).where(ranked.c.rank == 1))
494
- return {run_id: step for run_id, step in rows.all()}
505
+ rows = await session.execute(
506
+ sa.select(ranked.c.run_id, ranked.c.step_name, ranked.c.error).where(ranked.c.rank == 1)
507
+ )
508
+ return {run_id: FailedStep(step, error) for run_id, step, error in rows.all()}
@@ -33,6 +33,10 @@ ABSENT: Final = "-"
33
33
  LEVEL_WIDTH: Final = 8
34
34
  MESSAGE_WIDTH: Final = 30
35
35
 
36
+ #: The first year a zone's offset is a whole-minute standard one everywhere. Before it, a
37
+ #: conversion into the reader's zone spells local mean time rather than a time.
38
+ STANDARD_TIME_YEAR: Final = 1900
39
+
36
40
  #: Nested values are spread into ``prefix.key`` fields for the flat spellings.
37
41
  NESTED_PREFIXES: Final[Mapping[str, str]] = {"output": "output.", "fields": ""}
38
42
 
@@ -173,13 +177,19 @@ def _pad(text: str, width: int) -> str:
173
177
 
174
178
 
175
179
  def _stamp(at: str, *, local: bool) -> str:
176
- """Render an instant for a person: the same moment, in the reader's own offset."""
180
+ """Render an instant for a person: the same moment, in the reader's own offset.
181
+
182
+ A moment from before standard time is left in the spelling it arrived in: a zone's offset
183
+ that far back is its local mean time, and a line reading ``+00:53:28`` says nothing.
184
+ """
177
185
  if not local:
178
186
  return at
179
187
  try:
180
188
  parsed = datetime.fromisoformat(at)
181
189
  except ValueError:
182
190
  return at
191
+ if parsed.year < STANDARD_TIME_YEAR:
192
+ return at
183
193
  return parsed.astimezone().isoformat(timespec="milliseconds")
184
194
 
185
195
 
@@ -33,8 +33,7 @@ class SecretKeyMissing(SecretError):
33
33
  """Say exactly which variable to set, and how to generate a key."""
34
34
  super().__init__(
35
35
  "no secret key is configured: set DIRIGENT_SECRET_KEY before storing or reading "
36
- "connection secrets (generate one with `python -c "
37
- '"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"`)'
36
+ "connection secrets (generate one with `dg secret-key`)"
38
37
  )
39
38
 
40
39
 
@@ -51,6 +50,11 @@ class SecretKeyMismatch(SecretError):
51
50
  self.key_id = key_id
52
51
 
53
52
 
53
+ def generate_key() -> str:
54
+ """Mint a key for DIRIGENT_SECRET_KEY."""
55
+ return Fernet.generate_key().decode()
56
+
57
+
54
58
  def derive_key(material: str) -> bytes:
55
59
  """Turn configured key material into a Fernet key, accepting a passphrase as well.
56
60
 
File without changes
File without changes