arkhai-kit-settlement-runtime 0.1.2__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.
- arkhai_kit_settlement_runtime-0.1.2.dist-info/METADATA +9 -0
- arkhai_kit_settlement_runtime-0.1.2.dist-info/RECORD +16 -0
- arkhai_kit_settlement_runtime-0.1.2.dist-info/WHEEL +4 -0
- market_settlement_runtime/__init__.py +191 -0
- market_settlement_runtime/clauses.py +318 -0
- market_settlement_runtime/configuration.py +1056 -0
- market_settlement_runtime/hosted_routes.py +387 -0
- market_settlement_runtime/jobs.py +168 -0
- market_settlement_runtime/models.py +238 -0
- market_settlement_runtime/policy.py +76 -0
- market_settlement_runtime/ports.py +145 -0
- market_settlement_runtime/publication.py +241 -0
- market_settlement_runtime/py.typed +0 -0
- market_settlement_runtime/runtime.py +958 -0
- market_settlement_runtime/servicing.py +334 -0
- market_settlement_runtime/sqlite_repository.py +1578 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arkhai-kit-settlement-runtime
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Mechanism-neutral commercial settlement lifecycle, durable servicing, and SQLite repository.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: <3.14,>=3.10
|
|
7
|
+
Requires-Dist: arkhai-core==0.2.0
|
|
8
|
+
Requires-Dist: arkhai-kit-identity==0.3.0
|
|
9
|
+
Requires-Dist: pydantic>=2.7
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
market_settlement_runtime/__init__.py,sha256=Ze__7QPyFReV6d-7zGUQ_jWnLs4r2AJtxl7v-2PlVWI,5028
|
|
2
|
+
market_settlement_runtime/clauses.py,sha256=fWjRB2gKuU_d4mlqPXCDuxJk_EhXaPbRCGKz-STbL74,10977
|
|
3
|
+
market_settlement_runtime/configuration.py,sha256=dLl6IvuNjVS7WiKW150nL5DmJnbduKvzZI1LCS2Emd0,42147
|
|
4
|
+
market_settlement_runtime/hosted_routes.py,sha256=-RuWBn23jsadcwOxFaFJrtyW6kS8zh6nxXet5CQ1OF0,13918
|
|
5
|
+
market_settlement_runtime/jobs.py,sha256=Ckjk7BwSn1-YNGzIfC-_v4BSi8h-0oCMlmzlD7Dq96g,5759
|
|
6
|
+
market_settlement_runtime/models.py,sha256=l04V4PIimNjA207TLWkgtlLUGGDG4I4CI8oVtUaAPKE,8411
|
|
7
|
+
market_settlement_runtime/policy.py,sha256=S8S1EsyWzF4FjnvkN5XGAbIexvAvogm0dZ751IlhL_A,2660
|
|
8
|
+
market_settlement_runtime/ports.py,sha256=vum4jPQ4Snjyp8q43JVRbaNep7TYdnwuDu0IlqBo6ps,4402
|
|
9
|
+
market_settlement_runtime/publication.py,sha256=xfq1RrvBYrYuwdUt7ZVhoDBnWDvTxOmupQx2-RVDPcY,8790
|
|
10
|
+
market_settlement_runtime/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
market_settlement_runtime/runtime.py,sha256=hUFSoMEiGspJZ1DSfkRAAKhLQtZLO_mVP3BZ7uF8HAg,35069
|
|
12
|
+
market_settlement_runtime/servicing.py,sha256=-c3m7i7ECslGH3YoHxFdL2flj4HzNzLd1Inum-5WJXY,12254
|
|
13
|
+
market_settlement_runtime/sqlite_repository.py,sha256=iw7DCjo6QBAm6P6Ien6VoDyr6f2WdPOiknCvI5j0juo,64954
|
|
14
|
+
arkhai_kit_settlement_runtime-0.1.2.dist-info/METADATA,sha256=KEnrvE9Y-fmT7PZF2x02v1EDUMKx7unjzI99BkLRL8Y,334
|
|
15
|
+
arkhai_kit_settlement_runtime-0.1.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
16
|
+
arkhai_kit_settlement_runtime-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""One durable, mechanism-neutral commercial settlement runtime."""
|
|
2
|
+
|
|
3
|
+
from market_core.query_dsl import (
|
|
4
|
+
ComparisonOperator,
|
|
5
|
+
FieldDescriptor,
|
|
6
|
+
MissingValueRule,
|
|
7
|
+
QueryValueType,
|
|
8
|
+
)
|
|
9
|
+
from .clauses import (
|
|
10
|
+
CompiledSettlementClause,
|
|
11
|
+
SettlementCandidate,
|
|
12
|
+
SettlementClauseResult,
|
|
13
|
+
compile_settlement_clause,
|
|
14
|
+
select_settlement_candidates,
|
|
15
|
+
settlement_clause_descriptors,
|
|
16
|
+
settlement_clause_matches,
|
|
17
|
+
)
|
|
18
|
+
from .configuration import (
|
|
19
|
+
SETTLEMENT_CONFIG_SCHEMA_VERSION,
|
|
20
|
+
AcceptedObligationArtifacts,
|
|
21
|
+
AcceptedObligationBuilder,
|
|
22
|
+
BuyerCompatibilityHook,
|
|
23
|
+
ClientFactory,
|
|
24
|
+
MechanismReadiness,
|
|
25
|
+
MechanismRegistration,
|
|
26
|
+
OptionBuilder,
|
|
27
|
+
PreflightCallback,
|
|
28
|
+
PublicationInputValidator,
|
|
29
|
+
ReadinessBlocker,
|
|
30
|
+
RegisteredSettlementClauseField,
|
|
31
|
+
SettlementClauseField,
|
|
32
|
+
SettlementClauseProjector,
|
|
33
|
+
SettlementConfig,
|
|
34
|
+
SettlementConfigurationError,
|
|
35
|
+
SettlementConfigurationRegistry,
|
|
36
|
+
SettlementRole,
|
|
37
|
+
)
|
|
38
|
+
from .jobs import (
|
|
39
|
+
FulfillmentOutcome,
|
|
40
|
+
PreparedSettlement,
|
|
41
|
+
SettlementJobCoordinator,
|
|
42
|
+
)
|
|
43
|
+
from .hosted_routes import (
|
|
44
|
+
AuthorizedSettlementRequest,
|
|
45
|
+
BeforeHostedReclaim,
|
|
46
|
+
HostedAcceptedAgreement,
|
|
47
|
+
HostedSettlementRouteCallbacks,
|
|
48
|
+
HostedSettlementRouteError,
|
|
49
|
+
HostedSettlementRouteService,
|
|
50
|
+
HostedSettlementStart,
|
|
51
|
+
)
|
|
52
|
+
from .models import (
|
|
53
|
+
ConditionDecision,
|
|
54
|
+
ConditionOutcome,
|
|
55
|
+
ConditionState,
|
|
56
|
+
EffectOutcome,
|
|
57
|
+
EscrowStatus,
|
|
58
|
+
MaterializationOutcome,
|
|
59
|
+
MaterializationState,
|
|
60
|
+
MaterializationStatus,
|
|
61
|
+
OperationKind,
|
|
62
|
+
OperationState,
|
|
63
|
+
Party,
|
|
64
|
+
SettlementObligationRecord,
|
|
65
|
+
SettlementOperationOutcome,
|
|
66
|
+
SettlementOperationRecord,
|
|
67
|
+
SettlementPlanStatus,
|
|
68
|
+
StatusOutcome,
|
|
69
|
+
TerminalEffectState,
|
|
70
|
+
aggregate_settlement_status,
|
|
71
|
+
canonical_json,
|
|
72
|
+
derive_obligation_ref,
|
|
73
|
+
obligation_payload_hash,
|
|
74
|
+
)
|
|
75
|
+
from .policy import (
|
|
76
|
+
FailureActionHandler,
|
|
77
|
+
FailurePolicy,
|
|
78
|
+
FailurePolicyResult,
|
|
79
|
+
)
|
|
80
|
+
from .ports import (
|
|
81
|
+
ConditionalEscrowClient,
|
|
82
|
+
SettlementRuntimeRepository,
|
|
83
|
+
SettlementServicingRepository,
|
|
84
|
+
)
|
|
85
|
+
from .runtime import (
|
|
86
|
+
MANUAL_REASON_KEY,
|
|
87
|
+
SettlementManualRequired,
|
|
88
|
+
SettlementRuntime,
|
|
89
|
+
settlement_operation_ref,
|
|
90
|
+
)
|
|
91
|
+
from .publication import (
|
|
92
|
+
SettlementPublicationClause,
|
|
93
|
+
compile_settlement_publication_clause,
|
|
94
|
+
)
|
|
95
|
+
from .servicing import (
|
|
96
|
+
EventCallback,
|
|
97
|
+
SettlementServicingWorker,
|
|
98
|
+
TerminalCallback,
|
|
99
|
+
)
|
|
100
|
+
from .sqlite_repository import (
|
|
101
|
+
SETTLEMENT_MIGRATION_ID,
|
|
102
|
+
SETTLEMENT_MECHANISM_PARAMS_MIGRATION_ID,
|
|
103
|
+
SETTLEMENT_PRINCIPAL_MIGRATION_ID,
|
|
104
|
+
SettlementMigration,
|
|
105
|
+
SettlementSQLiteRepository,
|
|
106
|
+
settlement_migrations,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
__all__ = [
|
|
110
|
+
"SETTLEMENT_CONFIG_SCHEMA_VERSION",
|
|
111
|
+
"AcceptedObligationArtifacts",
|
|
112
|
+
"AcceptedObligationBuilder",
|
|
113
|
+
"SETTLEMENT_MIGRATION_ID",
|
|
114
|
+
"SETTLEMENT_MECHANISM_PARAMS_MIGRATION_ID",
|
|
115
|
+
"SETTLEMENT_PRINCIPAL_MIGRATION_ID",
|
|
116
|
+
"CompiledSettlementClause",
|
|
117
|
+
"ComparisonOperator",
|
|
118
|
+
"BuyerCompatibilityHook",
|
|
119
|
+
"ClientFactory",
|
|
120
|
+
"SettlementCandidate",
|
|
121
|
+
"SettlementClauseField",
|
|
122
|
+
"SettlementClauseProjector",
|
|
123
|
+
"SettlementClauseResult",
|
|
124
|
+
"ConditionDecision",
|
|
125
|
+
"ConditionOutcome",
|
|
126
|
+
"ConditionState",
|
|
127
|
+
"ConditionalEscrowClient",
|
|
128
|
+
"AuthorizedSettlementRequest",
|
|
129
|
+
"BeforeHostedReclaim",
|
|
130
|
+
"HostedAcceptedAgreement",
|
|
131
|
+
"HostedSettlementRouteCallbacks",
|
|
132
|
+
"HostedSettlementRouteError",
|
|
133
|
+
"HostedSettlementRouteService",
|
|
134
|
+
"HostedSettlementStart",
|
|
135
|
+
"EffectOutcome",
|
|
136
|
+
"EscrowStatus",
|
|
137
|
+
"FieldDescriptor",
|
|
138
|
+
"EventCallback",
|
|
139
|
+
"FailureActionHandler",
|
|
140
|
+
"FailurePolicy",
|
|
141
|
+
"FailurePolicyResult",
|
|
142
|
+
"FulfillmentOutcome",
|
|
143
|
+
"MaterializationOutcome",
|
|
144
|
+
"MaterializationState",
|
|
145
|
+
"MaterializationStatus",
|
|
146
|
+
"MechanismReadiness",
|
|
147
|
+
"MechanismRegistration",
|
|
148
|
+
"OperationKind",
|
|
149
|
+
"OperationState",
|
|
150
|
+
"MissingValueRule",
|
|
151
|
+
"OptionBuilder",
|
|
152
|
+
"Party",
|
|
153
|
+
"PreflightCallback",
|
|
154
|
+
"PublicationInputValidator",
|
|
155
|
+
"PreparedSettlement",
|
|
156
|
+
"ReadinessBlocker",
|
|
157
|
+
"RegisteredSettlementClauseField",
|
|
158
|
+
"SettlementConfig",
|
|
159
|
+
"QueryValueType",
|
|
160
|
+
"SettlementConfigurationError",
|
|
161
|
+
"SettlementConfigurationRegistry",
|
|
162
|
+
"SettlementJobCoordinator",
|
|
163
|
+
"MANUAL_REASON_KEY",
|
|
164
|
+
"SettlementManualRequired",
|
|
165
|
+
"SettlementMigration",
|
|
166
|
+
"SettlementObligationRecord",
|
|
167
|
+
"SettlementOperationOutcome",
|
|
168
|
+
"SettlementOperationRecord",
|
|
169
|
+
"SettlementPlanStatus",
|
|
170
|
+
"SettlementPublicationClause",
|
|
171
|
+
"SettlementRole",
|
|
172
|
+
"SettlementRuntime",
|
|
173
|
+
"SettlementRuntimeRepository",
|
|
174
|
+
"SettlementSQLiteRepository",
|
|
175
|
+
"SettlementServicingRepository",
|
|
176
|
+
"SettlementServicingWorker",
|
|
177
|
+
"StatusOutcome",
|
|
178
|
+
"TerminalCallback",
|
|
179
|
+
"TerminalEffectState",
|
|
180
|
+
"aggregate_settlement_status",
|
|
181
|
+
"canonical_json",
|
|
182
|
+
"derive_obligation_ref",
|
|
183
|
+
"obligation_payload_hash",
|
|
184
|
+
"settlement_migrations",
|
|
185
|
+
"settlement_operation_ref",
|
|
186
|
+
"compile_settlement_publication_clause",
|
|
187
|
+
"compile_settlement_clause",
|
|
188
|
+
"select_settlement_candidates",
|
|
189
|
+
"settlement_clause_descriptors",
|
|
190
|
+
"settlement_clause_matches",
|
|
191
|
+
]
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""Typed settlement-clause compilation and same-option evaluation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from market_core.query_dsl import (
|
|
11
|
+
ComparisonOperator,
|
|
12
|
+
FieldDescriptor,
|
|
13
|
+
MissingValueRule,
|
|
14
|
+
QueryValidationError,
|
|
15
|
+
QueryValueType,
|
|
16
|
+
ScalarValue,
|
|
17
|
+
ValidatedComparison,
|
|
18
|
+
ValidatedQuery,
|
|
19
|
+
compile_query,
|
|
20
|
+
)
|
|
21
|
+
from market_core.schemas import SettlementOption
|
|
22
|
+
|
|
23
|
+
from .configuration import (
|
|
24
|
+
MechanismRegistration,
|
|
25
|
+
RegisteredSettlementClauseField,
|
|
26
|
+
SettlementConfig,
|
|
27
|
+
SettlementConfigurationError,
|
|
28
|
+
SettlementConfigurationRegistry,
|
|
29
|
+
SettlementRole,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_COMMON_OPERATORS = frozenset(
|
|
33
|
+
{
|
|
34
|
+
ComparisonOperator.EQUAL,
|
|
35
|
+
ComparisonOperator.NOT_EQUAL,
|
|
36
|
+
ComparisonOperator.IN,
|
|
37
|
+
ComparisonOperator.NOT_IN,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
_COMMON_FIELDS = (
|
|
41
|
+
FieldDescriptor(
|
|
42
|
+
name="mechanism",
|
|
43
|
+
value_type=QueryValueType.STRING,
|
|
44
|
+
operators=_COMMON_OPERATORS,
|
|
45
|
+
description="canonical settlement mechanism ID or configuration key",
|
|
46
|
+
),
|
|
47
|
+
FieldDescriptor(
|
|
48
|
+
name="asset",
|
|
49
|
+
value_type=QueryValueType.STRING,
|
|
50
|
+
operators=_COMMON_OPERATORS,
|
|
51
|
+
description="public settlement asset",
|
|
52
|
+
),
|
|
53
|
+
FieldDescriptor(
|
|
54
|
+
name="option_id",
|
|
55
|
+
value_type=QueryValueType.STRING,
|
|
56
|
+
operators=_COMMON_OPERATORS,
|
|
57
|
+
description="immutable settlement option ID",
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
_MISSING = object()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True, slots=True)
|
|
64
|
+
class CompiledSettlementClause:
|
|
65
|
+
"""One validated conjunction over one advertised settlement option."""
|
|
66
|
+
|
|
67
|
+
query: ValidatedQuery
|
|
68
|
+
|
|
69
|
+
def render(self) -> str:
|
|
70
|
+
return self.query.render()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True, slots=True)
|
|
74
|
+
class SettlementCandidate:
|
|
75
|
+
"""One installed, enabled, role-compatible advertised option."""
|
|
76
|
+
|
|
77
|
+
registration: MechanismRegistration
|
|
78
|
+
option: SettlementOption
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass(frozen=True, slots=True)
|
|
82
|
+
class SettlementClauseResult:
|
|
83
|
+
"""Priority-ordered survivors from the first accepted explicit clause."""
|
|
84
|
+
|
|
85
|
+
candidates: tuple[SettlementCandidate, ...]
|
|
86
|
+
matched_clause_index: int | None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def settlement_clause_descriptors(
|
|
90
|
+
registry: SettlementConfigurationRegistry,
|
|
91
|
+
*,
|
|
92
|
+
role: SettlementRole,
|
|
93
|
+
) -> tuple[FieldDescriptor, ...]:
|
|
94
|
+
"""Return common and role-applicable mechanism fields deterministically."""
|
|
95
|
+
|
|
96
|
+
contributed = tuple(
|
|
97
|
+
item.field.descriptor for item in registry.clause_fields(role=role)
|
|
98
|
+
)
|
|
99
|
+
return (*_COMMON_FIELDS, *contributed)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def compile_settlement_clause(
|
|
103
|
+
source: str,
|
|
104
|
+
registry: SettlementConfigurationRegistry,
|
|
105
|
+
*,
|
|
106
|
+
role: SettlementRole = "buyer",
|
|
107
|
+
) -> CompiledSettlementClause:
|
|
108
|
+
"""Compile one clause and normalize public mechanism aliases."""
|
|
109
|
+
|
|
110
|
+
query = compile_query(source, settlement_clause_descriptors(registry, role=role))
|
|
111
|
+
comparisons: list[ValidatedComparison] = []
|
|
112
|
+
for comparison in query.comparisons:
|
|
113
|
+
if comparison.field != "mechanism":
|
|
114
|
+
comparisons.append(comparison)
|
|
115
|
+
continue
|
|
116
|
+
value = comparison.value
|
|
117
|
+
values = value if isinstance(value, tuple) else (value,)
|
|
118
|
+
normalized: list[ScalarValue] = []
|
|
119
|
+
for item in values:
|
|
120
|
+
assert isinstance(item, str)
|
|
121
|
+
try:
|
|
122
|
+
normalized.append(registry.canonical_mechanism_id(item, role=role))
|
|
123
|
+
except SettlementConfigurationError as exc:
|
|
124
|
+
raise QueryValidationError(
|
|
125
|
+
"unknown_mechanism",
|
|
126
|
+
f"unknown or role-inapplicable settlement mechanism at position {comparison.span.start}",
|
|
127
|
+
position=comparison.span.start,
|
|
128
|
+
field="mechanism",
|
|
129
|
+
accepted_fields=("mechanism",),
|
|
130
|
+
) from exc
|
|
131
|
+
comparisons.append(
|
|
132
|
+
ValidatedComparison(
|
|
133
|
+
field=comparison.field,
|
|
134
|
+
operator=comparison.operator,
|
|
135
|
+
value=tuple(normalized) if isinstance(value, tuple) else normalized[0],
|
|
136
|
+
span=comparison.span,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
return CompiledSettlementClause(ValidatedQuery(tuple(comparisons)))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def settlement_clause_matches(
|
|
143
|
+
clause: CompiledSettlementClause,
|
|
144
|
+
option: SettlementOption,
|
|
145
|
+
registry: SettlementConfigurationRegistry,
|
|
146
|
+
*,
|
|
147
|
+
role: SettlementRole = "buyer",
|
|
148
|
+
) -> bool:
|
|
149
|
+
"""Evaluate every predicate against the same immutable option."""
|
|
150
|
+
|
|
151
|
+
contributed = _field_bindings(registry, role=role)
|
|
152
|
+
descriptors = {
|
|
153
|
+
descriptor.name: descriptor
|
|
154
|
+
for descriptor in settlement_clause_descriptors(registry, role=role)
|
|
155
|
+
}
|
|
156
|
+
for comparison in clause.query.comparisons:
|
|
157
|
+
descriptor = descriptors[comparison.field]
|
|
158
|
+
actual: Any
|
|
159
|
+
if comparison.field == "mechanism":
|
|
160
|
+
actual = option.mechanism
|
|
161
|
+
elif comparison.field == "asset":
|
|
162
|
+
actual = option.asset
|
|
163
|
+
elif comparison.field == "option_id":
|
|
164
|
+
actual = option.option_id
|
|
165
|
+
else:
|
|
166
|
+
binding = contributed[comparison.field]
|
|
167
|
+
if option.mechanism != binding.mechanism_id:
|
|
168
|
+
return False
|
|
169
|
+
actual = binding.field.projector(option)
|
|
170
|
+
if actual is None or (
|
|
171
|
+
isinstance(actual, Sequence)
|
|
172
|
+
and not isinstance(actual, (str, bytes))
|
|
173
|
+
and not actual
|
|
174
|
+
):
|
|
175
|
+
actual = _MISSING
|
|
176
|
+
if actual is _MISSING:
|
|
177
|
+
if descriptor.on_missing is MissingValueRule.PASS:
|
|
178
|
+
continue
|
|
179
|
+
return False
|
|
180
|
+
projected = _validated_projection(actual, descriptor)
|
|
181
|
+
if not _comparison_matches(comparison, projected):
|
|
182
|
+
return False
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def select_settlement_candidates(
|
|
187
|
+
advertised: Iterable[SettlementOption | Mapping[str, Any]],
|
|
188
|
+
*,
|
|
189
|
+
registry: SettlementConfigurationRegistry,
|
|
190
|
+
config: SettlementConfig,
|
|
191
|
+
clauses: Iterable[str | CompiledSettlementClause] = (),
|
|
192
|
+
public_context: Mapping[str, Any] | None = None,
|
|
193
|
+
) -> SettlementClauseResult:
|
|
194
|
+
"""Admit compatible options, then apply ordered clause alternatives."""
|
|
195
|
+
|
|
196
|
+
registry.validate(config, role="buyer")
|
|
197
|
+
decoded = tuple(SettlementOption.model_validate(option) for option in advertised)
|
|
198
|
+
by_mechanism: dict[str, list[SettlementOption]] = {}
|
|
199
|
+
for option in decoded:
|
|
200
|
+
by_mechanism.setdefault(option.mechanism, []).append(option)
|
|
201
|
+
|
|
202
|
+
candidates: list[SettlementCandidate] = []
|
|
203
|
+
for registration in registry.ordered_registrations(config, role="buyer"):
|
|
204
|
+
section = config.mechanism_config(registration.config_key)
|
|
205
|
+
if section is None or not bool(getattr(section, "enabled", False)):
|
|
206
|
+
continue
|
|
207
|
+
for option in sorted(
|
|
208
|
+
by_mechanism.get(registration.mechanism_id, ()),
|
|
209
|
+
key=lambda candidate: candidate.option_id,
|
|
210
|
+
):
|
|
211
|
+
if registry.buyer_compatible(
|
|
212
|
+
registration.mechanism_id,
|
|
213
|
+
option,
|
|
214
|
+
config,
|
|
215
|
+
public_context=public_context,
|
|
216
|
+
):
|
|
217
|
+
candidates.append(SettlementCandidate(registration, option))
|
|
218
|
+
|
|
219
|
+
compiled = tuple(
|
|
220
|
+
clause
|
|
221
|
+
if isinstance(clause, CompiledSettlementClause)
|
|
222
|
+
else compile_settlement_clause(clause, registry, role="buyer")
|
|
223
|
+
for clause in clauses
|
|
224
|
+
)
|
|
225
|
+
if not compiled:
|
|
226
|
+
return SettlementClauseResult(tuple(candidates), None)
|
|
227
|
+
for index, clause in enumerate(compiled):
|
|
228
|
+
survivors = tuple(
|
|
229
|
+
candidate
|
|
230
|
+
for candidate in candidates
|
|
231
|
+
if settlement_clause_matches(
|
|
232
|
+
clause,
|
|
233
|
+
candidate.option,
|
|
234
|
+
registry,
|
|
235
|
+
role="buyer",
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
if survivors:
|
|
239
|
+
return SettlementClauseResult(survivors, index)
|
|
240
|
+
return SettlementClauseResult((), None)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _field_bindings(
|
|
244
|
+
registry: SettlementConfigurationRegistry,
|
|
245
|
+
*,
|
|
246
|
+
role: SettlementRole,
|
|
247
|
+
) -> dict[str, RegisteredSettlementClauseField]:
|
|
248
|
+
bindings: dict[str, RegisteredSettlementClauseField] = {}
|
|
249
|
+
for item in registry.clause_fields(role=role):
|
|
250
|
+
bindings[item.field.descriptor.name] = item
|
|
251
|
+
for alias in item.field.descriptor.aliases:
|
|
252
|
+
bindings[alias] = item
|
|
253
|
+
return bindings
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _validated_projection(
|
|
257
|
+
actual: Any,
|
|
258
|
+
descriptor: FieldDescriptor,
|
|
259
|
+
) -> tuple[ScalarValue, ...]:
|
|
260
|
+
values = (
|
|
261
|
+
tuple(actual)
|
|
262
|
+
if isinstance(actual, Sequence) and not isinstance(actual, (str, bytes))
|
|
263
|
+
else (actual,)
|
|
264
|
+
)
|
|
265
|
+
if not values:
|
|
266
|
+
raise SettlementConfigurationError(
|
|
267
|
+
f"projector for {descriptor.name!r} returned an empty value"
|
|
268
|
+
)
|
|
269
|
+
normalized: list[ScalarValue] = []
|
|
270
|
+
for value in values:
|
|
271
|
+
if descriptor.value_type is QueryValueType.STRING:
|
|
272
|
+
valid = isinstance(value, str)
|
|
273
|
+
converted: ScalarValue = value
|
|
274
|
+
elif descriptor.value_type is QueryValueType.INTEGER:
|
|
275
|
+
valid = isinstance(value, int) and not isinstance(value, bool)
|
|
276
|
+
converted = value
|
|
277
|
+
elif descriptor.value_type is QueryValueType.DECIMAL:
|
|
278
|
+
valid = isinstance(value, (int, Decimal)) and not isinstance(value, bool)
|
|
279
|
+
converted = Decimal(value) if valid else value
|
|
280
|
+
else:
|
|
281
|
+
valid = isinstance(value, bool)
|
|
282
|
+
converted = value
|
|
283
|
+
if not valid:
|
|
284
|
+
raise SettlementConfigurationError(
|
|
285
|
+
f"projector for {descriptor.name!r} returned the wrong value type"
|
|
286
|
+
)
|
|
287
|
+
normalized.append(converted)
|
|
288
|
+
return tuple(normalized)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _comparison_matches(
|
|
292
|
+
comparison: ValidatedComparison,
|
|
293
|
+
actual: tuple[ScalarValue, ...],
|
|
294
|
+
) -> bool:
|
|
295
|
+
expected = comparison.value
|
|
296
|
+
operator = comparison.operator
|
|
297
|
+
if operator is ComparisonOperator.EQUAL:
|
|
298
|
+
assert not isinstance(expected, tuple)
|
|
299
|
+
return any(value == expected for value in actual)
|
|
300
|
+
if operator is ComparisonOperator.NOT_EQUAL:
|
|
301
|
+
assert not isinstance(expected, tuple)
|
|
302
|
+
return all(value != expected for value in actual)
|
|
303
|
+
if operator is ComparisonOperator.IN:
|
|
304
|
+
assert isinstance(expected, tuple)
|
|
305
|
+
return any(value in expected for value in actual)
|
|
306
|
+
if operator is ComparisonOperator.NOT_IN:
|
|
307
|
+
assert isinstance(expected, tuple)
|
|
308
|
+
return all(value not in expected for value in actual)
|
|
309
|
+
assert not isinstance(expected, tuple)
|
|
310
|
+
if operator is ComparisonOperator.LESS_THAN:
|
|
311
|
+
return any(value < expected for value in actual)
|
|
312
|
+
if operator is ComparisonOperator.LESS_THAN_OR_EQUAL:
|
|
313
|
+
return any(value <= expected for value in actual)
|
|
314
|
+
if operator is ComparisonOperator.GREATER_THAN:
|
|
315
|
+
return any(value > expected for value in actual)
|
|
316
|
+
if operator is ComparisonOperator.GREATER_THAN_OR_EQUAL:
|
|
317
|
+
return any(value >= expected for value in actual)
|
|
318
|
+
raise AssertionError(f"unsupported comparison operator {operator}")
|