dirigent-testing 0.16.6__tar.gz → 0.17.0__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.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-testing
3
- Version: 0.16.6
3
+ Version: 0.17.0
4
4
  Summary: Test doubles and pytest fixtures for writing and testing dirigent blocks.
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Programming Language :: Python :: 3.13
9
- Requires-Dist: dirigent-common==0.16.6
10
- Requires-Dist: dirigent-plugin==0.16.6
9
+ Requires-Dist: dirigent-common==0.17.0
10
+ Requires-Dist: dirigent-plugin==0.17.0
11
11
  Requires-Dist: httpx2>=2.12.0
12
12
  Requires-Dist: pydantic>=2.13.5
13
13
  Requires-Dist: pytest>=9.1.1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-testing"
3
- version = "0.16.6"
3
+ version = "0.17.0"
4
4
  description = "Test doubles and pytest fixtures for writing and testing dirigent blocks."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -11,8 +11,8 @@ classifiers = [
11
11
  "Programming Language :: Python :: 3.13",
12
12
  ]
13
13
  dependencies = [
14
- "dirigent-common==0.16.6",
15
- "dirigent-plugin==0.16.6",
14
+ "dirigent-common==0.17.0",
15
+ "dirigent-plugin==0.17.0",
16
16
  "httpx2>=2.12.0",
17
17
  "pydantic>=2.13.5",
18
18
  "pytest>=9.1.1",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-testing"
3
- version = "0.16.6"
3
+ version = "0.17.0"
4
4
  description = "Test doubles and pytest fixtures for writing and testing dirigent blocks."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -11,8 +11,8 @@ classifiers = [
11
11
  "Programming Language :: Python :: 3.13",
12
12
  ]
13
13
  dependencies = [
14
- "dirigent-common==0.16.6",
15
- "dirigent-plugin==0.16.6",
14
+ "dirigent-common==0.17.0",
15
+ "dirigent-plugin==0.17.0",
16
16
  "httpx2>=2.12.0",
17
17
  "pydantic>=2.13.5",
18
18
  "pytest>=9.1.1",
@@ -23,6 +23,7 @@ from dirigent_plugin import (
23
23
  StatResult,
24
24
  StepContext,
25
25
  )
26
+ from dirigent_testing.messages import TEST_REFUSAL
26
27
 
27
28
 
28
29
  class RecordingLogger:
@@ -76,6 +77,8 @@ class FakeStorage:
76
77
  def __init__(self, root: Path) -> None:
77
78
  """Root the facade at a throwaway directory."""
78
79
  self.root = root
80
+ #: What each written URI was declared to be, since a file has nowhere to keep it.
81
+ self.content_types: dict[str, str] = {}
79
82
 
80
83
  def path_for(self, uri: str) -> Path:
81
84
  """Map a URI onto a local path: absolute as given, relative under the root."""
@@ -91,13 +94,17 @@ class FakeStorage:
91
94
  if payload[middle:]:
92
95
  yield payload[middle:]
93
96
 
94
- def open_write(self, uri: str) -> AbstractAsyncContextManager[ByteSink]:
95
- """Open a writer for a URI, creating the directories it needs."""
97
+ def open_write(self, uri: str, *, content_type: str | None = None) -> AbstractAsyncContextManager[ByteSink]:
98
+ """Open a writer for a URI, creating the directories it needs and recording its type."""
96
99
 
97
100
  @asynccontextmanager
98
101
  async def writer() -> AsyncGenerator[ByteSink]:
99
102
  path = self.path_for(uri)
100
103
  path.parent.mkdir(parents=True, exist_ok=True)
104
+ if content_type is None:
105
+ self.content_types.pop(uri, None)
106
+ else:
107
+ self.content_types[uri] = content_type
101
108
  sink = FakeSink(path)
102
109
  try:
103
110
  yield sink
@@ -112,7 +119,12 @@ class FakeStorage:
112
119
  if not path.is_file():
113
120
  return None
114
121
  info = path.stat()
115
- return StatResult(uri=uri, size=info.st_size, modified_at=datetime.fromtimestamp(info.st_mtime, tz=UTC))
122
+ return StatResult(
123
+ uri=uri,
124
+ size=info.st_size,
125
+ modified_at=datetime.fromtimestamp(info.st_mtime, tz=UTC),
126
+ content_type=self.content_types.get(uri),
127
+ )
116
128
 
117
129
  async def list(self, uri: str) -> AsyncIterator[StatResult]:
118
130
  """List the objects matching a pattern, in a stable order."""
@@ -168,7 +180,7 @@ class FakeRuns:
168
180
  """Record the start and answer with a run, a skip, or the refusal the test installed."""
169
181
  self.started.append((pipeline, dict(params), max_depth))
170
182
  if self.refusal is not None:
171
- raise RunRefused(self.refusal)
183
+ raise RunRefused(TEST_REFUSAL, detail=self.refusal)
172
184
  if pipeline in self.skip:
173
185
  return StartedRun(pipeline=pipeline)
174
186
  run_id = self.hold(pipeline)
@@ -0,0 +1,7 @@
1
+ """Every refusal a test double makes, catalogued under the ``testing`` prefix."""
2
+
3
+ from dirigent_common import Catalogue
4
+
5
+ TESTING = Catalogue("testing")
6
+
7
+ TEST_REFUSAL = TESTING.define("refusal", "{detail}")