millwork-solver 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.
@@ -0,0 +1,24 @@
1
+ """Public Millwork Solver Python client facade."""
2
+
3
+ from .client import AsyncSolver, Solver
4
+ from .errors import (
5
+ ClientClosedError,
6
+ FieldError,
7
+ NetworkError,
8
+ OperationBoundaryError,
9
+ ProblemError,
10
+ ProtocolError,
11
+ )
12
+ from .types import Page
13
+
14
+ __all__ = (
15
+ "AsyncSolver",
16
+ "ClientClosedError",
17
+ "FieldError",
18
+ "NetworkError",
19
+ "OperationBoundaryError",
20
+ "Page",
21
+ "ProblemError",
22
+ "ProtocolError",
23
+ "Solver",
24
+ )
@@ -0,0 +1,292 @@
1
+ {
2
+ "schema_version": 1,
3
+ "generated_from": {
4
+ "python_operation_view": "contracts/developer-experience/python-operation-view.json",
5
+ "operation_manifest_version": "1.0.0",
6
+ "openapi_sha256": "ad959a037922a16bda51221ed989d00ea0ae6b70eb86b5f2aef07eccee91d3c6",
7
+ "retry_fixture": "contracts/developer-experience/fixtures/retry-error-idempotency.json"
8
+ },
9
+ "operations": [
10
+ {
11
+ "operation_id": "deleteArmsByArmId",
12
+ "method": "DELETE",
13
+ "path": "/v1/arms/{armId}"
14
+ },
15
+ {
16
+ "operation_id": "deleteSourceConnectionsByConnectionId",
17
+ "method": "DELETE",
18
+ "path": "/v1/source-connections/{connectionId}"
19
+ },
20
+ {
21
+ "operation_id": "deleteVerifiersByVerifierId",
22
+ "method": "DELETE",
23
+ "path": "/v1/verifiers/{verifierId}"
24
+ },
25
+ {
26
+ "operation_id": "getAccount",
27
+ "method": "GET",
28
+ "path": "/v1/account"
29
+ },
30
+ {
31
+ "operation_id": "getApiKeys",
32
+ "method": "GET",
33
+ "path": "/v1/api-keys"
34
+ },
35
+ {
36
+ "operation_id": "getArms",
37
+ "method": "GET",
38
+ "path": "/v1/arms"
39
+ },
40
+ {
41
+ "operation_id": "getArmsByArmId",
42
+ "method": "GET",
43
+ "path": "/v1/arms/{armId}"
44
+ },
45
+ {
46
+ "operation_id": "getComplianceExport",
47
+ "method": "GET",
48
+ "path": "/v1/compliance-export"
49
+ },
50
+ {
51
+ "operation_id": "getCredentials",
52
+ "method": "GET",
53
+ "path": "/v1/credentials"
54
+ },
55
+ {
56
+ "operation_id": "getEvalSummary",
57
+ "method": "GET",
58
+ "path": "/v1/eval-summary"
59
+ },
60
+ {
61
+ "operation_id": "getExecutions",
62
+ "method": "GET",
63
+ "path": "/v1/executions"
64
+ },
65
+ {
66
+ "operation_id": "getExecutionsByExecutionId",
67
+ "method": "GET",
68
+ "path": "/v1/executions/{executionId}"
69
+ },
70
+ {
71
+ "operation_id": "getExecutionsByExecutionIdEvents",
72
+ "method": "GET",
73
+ "path": "/v1/executions/{executionId}/events"
74
+ },
75
+ {
76
+ "operation_id": "getExecutionsByExecutionIdResult",
77
+ "method": "GET",
78
+ "path": "/v1/executions/{executionId}/result"
79
+ },
80
+ {
81
+ "operation_id": "getModelCatalog",
82
+ "method": "GET",
83
+ "path": "/v1/model-catalog"
84
+ },
85
+ {
86
+ "operation_id": "getModelDefinitions",
87
+ "method": "GET",
88
+ "path": "/v1/model-definitions"
89
+ },
90
+ {
91
+ "operation_id": "getModelDeployments",
92
+ "method": "GET",
93
+ "path": "/v1/model-deployments"
94
+ },
95
+ {
96
+ "operation_id": "getModelDeploymentsByModelDeploymentId",
97
+ "method": "GET",
98
+ "path": "/v1/model-deployments/{modelDeploymentId}"
99
+ },
100
+ {
101
+ "operation_id": "getModelSourceProfiles",
102
+ "method": "GET",
103
+ "path": "/v1/model-source-profiles"
104
+ },
105
+ {
106
+ "operation_id": "getProposals",
107
+ "method": "GET",
108
+ "path": "/v1/proposals"
109
+ },
110
+ {
111
+ "operation_id": "getProposalsByProposalId",
112
+ "method": "GET",
113
+ "path": "/v1/proposals/{proposalId}"
114
+ },
115
+ {
116
+ "operation_id": "getReceipts",
117
+ "method": "GET",
118
+ "path": "/v1/receipts"
119
+ },
120
+ {
121
+ "operation_id": "getReceiptsByExecutionId",
122
+ "method": "GET",
123
+ "path": "/v1/receipts/{executionId}"
124
+ },
125
+ {
126
+ "operation_id": "getSourceConnections",
127
+ "method": "GET",
128
+ "path": "/v1/source-connections"
129
+ },
130
+ {
131
+ "operation_id": "getSourceConnectionsByConnectionId",
132
+ "method": "GET",
133
+ "path": "/v1/source-connections/{connectionId}"
134
+ },
135
+ {
136
+ "operation_id": "getSourceCredentialHandoffsByHandoffIntentId",
137
+ "method": "GET",
138
+ "path": "/v1/source-credential-handoffs/{handoffIntentId}"
139
+ },
140
+ {
141
+ "operation_id": "getUsage",
142
+ "method": "GET",
143
+ "path": "/v1/usage"
144
+ },
145
+ {
146
+ "operation_id": "getVerifiers",
147
+ "method": "GET",
148
+ "path": "/v1/verifiers"
149
+ },
150
+ {
151
+ "operation_id": "getVerifiersByVerifierId",
152
+ "method": "GET",
153
+ "path": "/v1/verifiers/{verifierId}"
154
+ },
155
+ {
156
+ "operation_id": "getWebhooks",
157
+ "method": "GET",
158
+ "path": "/v1/webhooks"
159
+ },
160
+ {
161
+ "operation_id": "getWebhooksByWebhookIdDeliveries",
162
+ "method": "GET",
163
+ "path": "/v1/webhooks/{webhookId}/deliveries"
164
+ },
165
+ {
166
+ "operation_id": "patchApiKeysByApiKeyId",
167
+ "method": "PATCH",
168
+ "path": "/v1/api-keys/{apiKeyId}"
169
+ },
170
+ {
171
+ "operation_id": "patchArmsByArmId",
172
+ "method": "PATCH",
173
+ "path": "/v1/arms/{armId}"
174
+ },
175
+ {
176
+ "operation_id": "patchTenantsByTenantId",
177
+ "method": "PATCH",
178
+ "path": "/v1/tenants/{tenantId}"
179
+ },
180
+ {
181
+ "operation_id": "patchVerifiersByVerifierId",
182
+ "method": "PATCH",
183
+ "path": "/v1/verifiers/{verifierId}"
184
+ },
185
+ {
186
+ "operation_id": "postApiKeys",
187
+ "method": "POST",
188
+ "path": "/v1/api-keys"
189
+ },
190
+ {
191
+ "operation_id": "postApiKeysByApiKeyIdRevoke",
192
+ "method": "POST",
193
+ "path": "/v1/api-keys/{apiKeyId}/revoke"
194
+ },
195
+ {
196
+ "operation_id": "postArms",
197
+ "method": "POST",
198
+ "path": "/v1/arms"
199
+ },
200
+ {
201
+ "operation_id": "postCredentials",
202
+ "method": "POST",
203
+ "path": "/v1/credentials"
204
+ },
205
+ {
206
+ "operation_id": "postExecutions",
207
+ "method": "POST",
208
+ "path": "/v1/executions"
209
+ },
210
+ {
211
+ "operation_id": "postExecutionsByExecutionIdCancel",
212
+ "method": "POST",
213
+ "path": "/v1/executions/{executionId}/cancel"
214
+ },
215
+ {
216
+ "operation_id": "postProposalsByProposalIdApprove",
217
+ "method": "POST",
218
+ "path": "/v1/proposals/{proposalId}/approve"
219
+ },
220
+ {
221
+ "operation_id": "postProposalsByProposalIdReject",
222
+ "method": "POST",
223
+ "path": "/v1/proposals/{proposalId}/reject"
224
+ },
225
+ {
226
+ "operation_id": "postSourceConnections",
227
+ "method": "POST",
228
+ "path": "/v1/source-connections"
229
+ },
230
+ {
231
+ "operation_id": "postSourceConnectionsByConnectionIdDeploymentsSync",
232
+ "method": "POST",
233
+ "path": "/v1/source-connections/{connectionId}/deployments/sync"
234
+ },
235
+ {
236
+ "operation_id": "postSourceConnectionsByConnectionIdRotate",
237
+ "method": "POST",
238
+ "path": "/v1/source-connections/{connectionId}/rotate"
239
+ },
240
+ {
241
+ "operation_id": "postSourceConnectionsByConnectionIdTest",
242
+ "method": "POST",
243
+ "path": "/v1/source-connections/{connectionId}/test"
244
+ },
245
+ {
246
+ "operation_id": "postSourceCredentialHandoffs",
247
+ "method": "POST",
248
+ "path": "/v1/source-credential-handoffs"
249
+ },
250
+ {
251
+ "operation_id": "postTenants",
252
+ "method": "POST",
253
+ "path": "/v1/tenants"
254
+ },
255
+ {
256
+ "operation_id": "postVerifiers",
257
+ "method": "POST",
258
+ "path": "/v1/verifiers"
259
+ },
260
+ {
261
+ "operation_id": "postVerifiersByVerifierIdTest",
262
+ "method": "POST",
263
+ "path": "/v1/verifiers/{verifierId}/test"
264
+ },
265
+ {
266
+ "operation_id": "postWebhooks",
267
+ "method": "POST",
268
+ "path": "/v1/webhooks"
269
+ },
270
+ {
271
+ "operation_id": "postWebhooksByWebhookIdRotateSecret",
272
+ "method": "POST",
273
+ "path": "/v1/webhooks/{webhookId}/rotate-secret"
274
+ },
275
+ {
276
+ "operation_id": "postWebhooksDeliveriesByDeliveryIdReplay",
277
+ "method": "POST",
278
+ "path": "/v1/webhooks/deliveries/{deliveryId}/replay"
279
+ }
280
+ ],
281
+ "retry_policy": {
282
+ "safe_methods": [
283
+ "GET",
284
+ "HEAD"
285
+ ],
286
+ "retryable_failures": [
287
+ "network_error",
288
+ "http_5xx"
289
+ ],
290
+ "maximum_retries": 2
291
+ }
292
+ }
@@ -0,0 +1,3 @@
1
+ """Repository-internal PY2 core. No public import surface is selected here."""
2
+
3
+ __all__: tuple[str, ...] = ()
@@ -0,0 +1,110 @@
1
+ """The sole name-neutral seam for a future PY0-selected public API.
2
+
3
+ This repository-internal module deliberately exposes no distribution, import,
4
+ public client-class, environment-variable, runtime-support, or lifecycle name.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import time
11
+ from collections.abc import Awaitable, Callable
12
+ from pathlib import Path
13
+
14
+ import httpx
15
+
16
+ from .catalog import _OperationCatalog
17
+ from .clients import _AsyncClient, _SyncClient
18
+ from .transport import _AsyncTransport, _SyncTransport, _TransportPolicy
19
+
20
+
21
+ def _create_sync_client(
22
+ *,
23
+ repository_root: Path | None = None,
24
+ snapshot_path: Path | None = None,
25
+ credential: str,
26
+ base_url: str,
27
+ timeout: float | httpx.Timeout,
28
+ maximum_retries: int | None = None,
29
+ backoff_seconds: float = 0.0,
30
+ transport: httpx.BaseTransport | None = None,
31
+ sleep: Callable[[float], None] = time.sleep,
32
+ ) -> _SyncClient:
33
+ if (repository_root is None) == (snapshot_path is None):
34
+ raise ValueError("exactly one of repository_root or snapshot_path is required")
35
+ if repository_root is not None:
36
+ catalog = _OperationCatalog.from_repository(repository_root)
37
+ policy = _TransportPolicy.from_repository(
38
+ repository_root,
39
+ backoff_seconds=backoff_seconds,
40
+ maximum_retries=maximum_retries,
41
+ )
42
+ else:
43
+ assert snapshot_path is not None
44
+ catalog = _OperationCatalog.from_snapshot(snapshot_path)
45
+ policy = _TransportPolicy.from_snapshot(
46
+ snapshot_path,
47
+ backoff_seconds=backoff_seconds,
48
+ maximum_retries=maximum_retries,
49
+ )
50
+ http_client = httpx.Client(
51
+ base_url=base_url,
52
+ headers={"authorization": f"Bearer {credential}"},
53
+ timeout=timeout,
54
+ transport=transport,
55
+ follow_redirects=False,
56
+ trust_env=False,
57
+ )
58
+ return _SyncClient(
59
+ catalog,
60
+ policy,
61
+ _SyncTransport(http_client, policy, sleep),
62
+ close_transport=http_client.close,
63
+ sleep=sleep,
64
+ )
65
+
66
+
67
+ def _create_async_client(
68
+ *,
69
+ repository_root: Path | None = None,
70
+ snapshot_path: Path | None = None,
71
+ credential: str,
72
+ base_url: str,
73
+ timeout: float | httpx.Timeout,
74
+ maximum_retries: int | None = None,
75
+ backoff_seconds: float = 0.0,
76
+ transport: httpx.AsyncBaseTransport | None = None,
77
+ sleep: Callable[[float], Awaitable[None]] = asyncio.sleep,
78
+ ) -> _AsyncClient:
79
+ if (repository_root is None) == (snapshot_path is None):
80
+ raise ValueError("exactly one of repository_root or snapshot_path is required")
81
+ if repository_root is not None:
82
+ catalog = _OperationCatalog.from_repository(repository_root)
83
+ policy = _TransportPolicy.from_repository(
84
+ repository_root,
85
+ backoff_seconds=backoff_seconds,
86
+ maximum_retries=maximum_retries,
87
+ )
88
+ else:
89
+ assert snapshot_path is not None
90
+ catalog = _OperationCatalog.from_snapshot(snapshot_path)
91
+ policy = _TransportPolicy.from_snapshot(
92
+ snapshot_path,
93
+ backoff_seconds=backoff_seconds,
94
+ maximum_retries=maximum_retries,
95
+ )
96
+ http_client = httpx.AsyncClient(
97
+ base_url=base_url,
98
+ headers={"authorization": f"Bearer {credential}"},
99
+ timeout=timeout,
100
+ transport=transport,
101
+ follow_redirects=False,
102
+ trust_env=False,
103
+ )
104
+ return _AsyncClient(
105
+ catalog,
106
+ policy,
107
+ _AsyncTransport(http_client, policy, sleep),
108
+ close_transport=http_client.aclose,
109
+ sleep=sleep,
110
+ )
@@ -0,0 +1,218 @@
1
+ """Resolve the reachable wire operations through PY1's pointers into F1."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from types import MappingProxyType
10
+ from typing import Any, Mapping
11
+ from urllib.parse import quote, unquote
12
+
13
+ from .errors import _OperationBoundaryError
14
+
15
+
16
+ _HTTP_METHODS = frozenset({"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"})
17
+ _PATH_PARAMETER = re.compile(r"\{([^{}]+)\}")
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class _Operation:
22
+ operation_id: str
23
+ method: str
24
+ path_template: str
25
+
26
+ def bind_path(self, path_parameters: Mapping[str, object] | None) -> str:
27
+ parameters = dict(path_parameters or {})
28
+ expected = set(_PATH_PARAMETER.findall(self.path_template))
29
+ if set(parameters) != expected:
30
+ raise _OperationBoundaryError(
31
+ f"{self.operation_id} path parameters must be exactly {sorted(expected)}"
32
+ )
33
+ return _PATH_PARAMETER.sub(
34
+ lambda match: quote(str(parameters[match.group(1)]), safe=""),
35
+ self.path_template,
36
+ )
37
+
38
+ def match_path(self, concrete_path: str) -> Mapping[str, str] | None:
39
+ names: list[str] = []
40
+ pieces: list[str] = []
41
+ position = 0
42
+ for parameter in _PATH_PARAMETER.finditer(self.path_template):
43
+ pieces.append(re.escape(self.path_template[position : parameter.start()]))
44
+ pieces.append(r"([^/]+)")
45
+ names.append(parameter.group(1))
46
+ position = parameter.end()
47
+ pieces.append(re.escape(self.path_template[position:]))
48
+ expression = "^" + "".join(pieces) + "$"
49
+ matched = re.match(expression, concrete_path)
50
+ if matched is None:
51
+ return None
52
+ return MappingProxyType({name: unquote(value) for name, value in zip(names, matched.groups())})
53
+
54
+
55
+ def _load_json(path: Path) -> Any:
56
+ with path.open("r", encoding="utf-8") as file:
57
+ return json.load(file)
58
+
59
+
60
+ def _resolve_pointer(repository_root: Path, source: str) -> Any:
61
+ relative_file, separator, fragment = source.partition("#")
62
+ if separator != "#" or not fragment.startswith("/"):
63
+ raise _OperationBoundaryError(f"PY1 source is not a JSON pointer: {source}")
64
+ absolute_file = (repository_root / relative_file).resolve()
65
+ try:
66
+ absolute_file.relative_to(repository_root.resolve())
67
+ except ValueError as error:
68
+ raise _OperationBoundaryError("PY1 source escapes the repository root") from error
69
+ value = _load_json(absolute_file)
70
+ for encoded_token in fragment.removeprefix("/").split("/"):
71
+ token = encoded_token.replace("~1", "/").replace("~0", "~")
72
+ if isinstance(value, list):
73
+ value = value[int(token)]
74
+ else:
75
+ value = value[token]
76
+ return value
77
+
78
+
79
+ class _OperationCatalog:
80
+ def __init__(self, operations: Mapping[str, _Operation], event_operation_id: str) -> None:
81
+ self._operations = MappingProxyType(dict(operations))
82
+ self.event_operation_id = event_operation_id
83
+
84
+ @classmethod
85
+ def from_repository(cls, repository_root: Path) -> "_OperationCatalog":
86
+ repository_root = repository_root.resolve()
87
+ view_path = repository_root / "contracts/developer-experience/python-operation-view.json"
88
+ view = _load_json(view_path)
89
+ sources = view.get("sources")
90
+ expected_counts = view.get("expected_counts")
91
+ if not isinstance(sources, dict) or expected_counts != {
92
+ "operations": 70,
93
+ "included": 54,
94
+ "excluded": 16,
95
+ }:
96
+ raise _OperationBoundaryError("PY1 pointer/count contract drifted")
97
+
98
+ included_ids = _resolve_pointer(repository_root, sources["included_operation_ids"])
99
+ classifications = _resolve_pointer(repository_root, sources["operation_classifications"])
100
+ wire_paths = _resolve_pointer(repository_root, sources["wire_contract"])
101
+ if not isinstance(included_ids, list) or not isinstance(classifications, list):
102
+ raise _OperationBoundaryError("PY1/F1 operation pointers must resolve to arrays")
103
+
104
+ wire_operations: dict[str, tuple[str, str]] = {}
105
+ for path, path_item in wire_paths.items():
106
+ for method, operation in path_item.items():
107
+ normalized_method = method.upper()
108
+ if normalized_method not in _HTTP_METHODS:
109
+ continue
110
+ operation_id = operation.get("operationId")
111
+ if not isinstance(operation_id, str) or operation_id in wire_operations:
112
+ raise _OperationBoundaryError("OpenAPI operation IDs must be present and unique")
113
+ wire_operations[operation_id] = (normalized_method, path)
114
+
115
+ classification_by_id: dict[str, Mapping[str, Any]] = {}
116
+ for classification in classifications:
117
+ operation_id = classification.get("operation_id")
118
+ if not isinstance(operation_id, str) or operation_id in classification_by_id:
119
+ raise _OperationBoundaryError("F1 operation IDs must be present and unique")
120
+ classification_by_id[operation_id] = classification
121
+
122
+ included_set = set(included_ids)
123
+ f1_included = {
124
+ operation_id
125
+ for operation_id, classification in classification_by_id.items()
126
+ if classification.get("disposition") == "include"
127
+ }
128
+ f1_excluded = {
129
+ operation_id
130
+ for operation_id, classification in classification_by_id.items()
131
+ if classification.get("disposition") == "exclude"
132
+ }
133
+ if len(wire_operations) != 70 or len(f1_included) != 54 or len(f1_excluded) != 16:
134
+ raise _OperationBoundaryError("F1 54/16 operation partition drifted")
135
+ if included_set != f1_included or len(included_ids) != len(included_set):
136
+ raise _OperationBoundaryError("PY1 pointer does not resolve exactly to F1's 54 includes")
137
+ if set(classification_by_id) != set(wire_operations):
138
+ raise _OperationBoundaryError("F1 classifications do not cover OpenAPI exactly")
139
+
140
+ allowed: dict[str, _Operation] = {}
141
+ for operation_id in included_ids:
142
+ classification = classification_by_id[operation_id]
143
+ method_and_path = wire_operations.get(operation_id)
144
+ if method_and_path != (classification.get("method"), classification.get("path")):
145
+ raise _OperationBoundaryError(f"{operation_id} F1/OpenAPI mapping drifted")
146
+ allowed[operation_id] = _Operation(operation_id, *method_and_path)
147
+
148
+ event_operations = [
149
+ operation.operation_id
150
+ for operation in allowed.values()
151
+ if operation.method == "GET" and operation.path_template.endswith("/events")
152
+ ]
153
+ if len(event_operations) != 1:
154
+ raise _OperationBoundaryError("F1 must expose exactly one execution-events operation")
155
+ return cls(allowed, event_operations[0])
156
+
157
+ @classmethod
158
+ def from_snapshot(cls, snapshot_path: Path) -> "_OperationCatalog":
159
+ """Load the generated, F1-derived catalog bundled in an installed artifact."""
160
+
161
+ snapshot = _load_json(snapshot_path)
162
+ if snapshot.get("schema_version") != 1:
163
+ raise _OperationBoundaryError("installed Python wire snapshot version drifted")
164
+ operations = snapshot.get("operations")
165
+ if not isinstance(operations, list) or len(operations) != 54:
166
+ raise _OperationBoundaryError("installed Python wire snapshot must contain 54 operations")
167
+
168
+ allowed: dict[str, _Operation] = {}
169
+ for item in operations:
170
+ if not isinstance(item, dict) or set(item) != {"operation_id", "method", "path"}:
171
+ raise _OperationBoundaryError("installed Python wire snapshot operation shape drifted")
172
+ operation_id = item.get("operation_id")
173
+ method = item.get("method")
174
+ path = item.get("path")
175
+ if (
176
+ not isinstance(operation_id, str)
177
+ or not isinstance(method, str)
178
+ or method not in _HTTP_METHODS
179
+ or not isinstance(path, str)
180
+ or operation_id in allowed
181
+ ):
182
+ raise _OperationBoundaryError("installed Python wire snapshot operation is invalid")
183
+ allowed[operation_id] = _Operation(operation_id, method, path)
184
+
185
+ event_operations = [
186
+ operation.operation_id
187
+ for operation in allowed.values()
188
+ if operation.method == "GET" and operation.path_template.endswith("/events")
189
+ ]
190
+ if len(event_operations) != 1:
191
+ raise _OperationBoundaryError(
192
+ "installed Python wire snapshot must expose one execution-events operation"
193
+ )
194
+ return cls(allowed, event_operations[0])
195
+
196
+ @property
197
+ def operation_ids(self) -> frozenset[str]:
198
+ return frozenset(self._operations)
199
+
200
+ def operation(self, operation_id: str) -> _Operation:
201
+ operation = self._operations.get(operation_id)
202
+ if operation is None:
203
+ raise _OperationBoundaryError(f"operation is outside the F1 Python boundary: {operation_id}")
204
+ return operation
205
+
206
+ def match_fixture_request(self, method: str, concrete_path: str) -> tuple[str, Mapping[str, str]]:
207
+ matches: list[tuple[str, Mapping[str, str]]] = []
208
+ for operation in self._operations.values():
209
+ if operation.method != method:
210
+ continue
211
+ path_parameters = operation.match_path(concrete_path)
212
+ if path_parameters is not None:
213
+ matches.append((operation.operation_id, path_parameters))
214
+ if len(matches) != 1:
215
+ raise _OperationBoundaryError(
216
+ f"fixture request must match exactly one included operation: {method} {concrete_path}"
217
+ )
218
+ return matches[0]