calc-flow-python 4.0.0__cp313-abi3-win_amd64.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,775 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Mapping, Sequence
5
+ from dataclasses import dataclass
6
+ from typing import Any, Literal
7
+
8
+ type BatchKind = Literal["table", "array"]
9
+ type OptionValueType = Literal["string", "integer", "number", "boolean"]
10
+ type ExecutionMode = Literal["batch", "stream"]
11
+ type OutputFinality = Literal["per_row_final", "group_final_append_only", "unproven"]
12
+ type CheckpointSupport = Literal["stateless", "checkpointed_stateful", "unproven"]
13
+ type PartitionContract = Literal["none", "row_axis_independent"]
14
+
15
+ CAPABILITY_SCHEMA_VERSION = 3
16
+
17
+ _EXECUTION_MODES = frozenset(("batch", "stream"))
18
+ _OUTPUT_FINALITIES = frozenset(("per_row_final", "group_final_append_only", "unproven"))
19
+ _CHECKPOINT_SUPPORTS = frozenset(("stateless", "checkpointed_stateful", "unproven"))
20
+ _PARTITION_CONTRACTS = frozenset(("none", "row_axis_independent"))
21
+ _CAPABILITY_RULES = frozenset(
22
+ (
23
+ ("array_api_safe_dtype", "1"),
24
+ ("elementwise_broadcast", "1"),
25
+ ("feature_axis_reduction", "1"),
26
+ ("table_matmul_static_rhs", "1"),
27
+ )
28
+ )
29
+
30
+ PORTABLE_ARROW_TYPES = (
31
+ "bool",
32
+ "date32",
33
+ "date64",
34
+ "float32",
35
+ "float64",
36
+ "int8",
37
+ "int16",
38
+ "int32",
39
+ "int64",
40
+ "large_string",
41
+ "string",
42
+ "time32[s]",
43
+ "time64[us]",
44
+ "timestamp[ms]",
45
+ "timestamp[us]",
46
+ "uint8",
47
+ "uint16",
48
+ "uint32",
49
+ "uint64",
50
+ )
51
+
52
+
53
+ @dataclass(frozen=True, slots=True)
54
+ class ProviderOption:
55
+ name: str
56
+ value_type: OptionValueType
57
+ required: bool = False
58
+
59
+ def __post_init__(self) -> None:
60
+ if type(self.name) is not str:
61
+ raise TypeError(
62
+ "provider options_schema field name must contain strict data; "
63
+ f"found {type(self.name).__name__}"
64
+ )
65
+ if type(self.value_type) is not str:
66
+ raise TypeError(
67
+ f"provider options_schema field {self.name!r}.value_type must "
68
+ "contain strict data; "
69
+ f"found {type(self.value_type).__name__}"
70
+ )
71
+ if self.value_type not in {"string", "integer", "number", "boolean"}:
72
+ raise ValueError(
73
+ f"provider options_schema field {self.name!r}.value_type must be "
74
+ "string, integer, number, or boolean; "
75
+ f"found {self.value_type}"
76
+ )
77
+ if type(self.required) is not bool:
78
+ raise TypeError(
79
+ f"provider options_schema at {self.name!r}.required must contain "
80
+ f"strict data; found {type(self.required).__name__}"
81
+ )
82
+
83
+
84
+ @dataclass(frozen=True, slots=True)
85
+ class ProviderOptionsSchema:
86
+ fields: tuple[ProviderOption, ...] = ()
87
+ additional_properties: Literal[False] = False
88
+
89
+ def __post_init__(self) -> None:
90
+ if type(self.fields) is not tuple or any(
91
+ not isinstance(field, ProviderOption) for field in self.fields
92
+ ):
93
+ raise TypeError(
94
+ "provider options_schema fields must be a tuple of "
95
+ "ProviderOption values"
96
+ )
97
+ if self.additional_properties is not False:
98
+ raise ValueError(
99
+ "provider options_schema additional_properties must be False"
100
+ )
101
+ names = tuple(field.name for field in self.fields)
102
+ duplicate = next(
103
+ (name for index, name in enumerate(names) if name in names[:index]),
104
+ None,
105
+ )
106
+ if duplicate is not None:
107
+ raise ValueError(
108
+ f"provider options_schema contains duplicate field {duplicate!r}"
109
+ )
110
+ object.__setattr__(
111
+ self,
112
+ "fields",
113
+ tuple(sorted(self.fields, key=lambda field: field.name)),
114
+ )
115
+
116
+
117
+ @dataclass(frozen=True, slots=True)
118
+ class RuntimeSessionScope:
119
+ kind: Literal["runtime_session"]
120
+ session_id: str
121
+ revision: int
122
+
123
+
124
+ def _require_exact_str(owner: str, field: str, value: object) -> None:
125
+ if type(value) is not str:
126
+ raise TypeError(
127
+ f"{owner} {field} must be an exact str; found {type(value).__name__}"
128
+ )
129
+
130
+
131
+ def _require_exact_bool(owner: str, field: str, value: object) -> None:
132
+ if type(value) is not bool:
133
+ raise TypeError(
134
+ f"{owner} {field} must be an exact bool; found {type(value).__name__}"
135
+ )
136
+
137
+
138
+ def _require_port_tuple(owner: str, field: str, value: object) -> None:
139
+ if type(value) is not tuple or any(
140
+ not isinstance(port, ProviderPort) for port in value
141
+ ):
142
+ raise TypeError(
143
+ f"{owner} {field} must be a tuple of ProviderPort values; "
144
+ f"found {type(value).__name__}"
145
+ )
146
+
147
+
148
+ def _require_modes(owner: str, value: object) -> tuple[ExecutionMode, ...]:
149
+ if type(value) is not tuple or any(type(mode) is not str for mode in value):
150
+ raise TypeError(f"{owner} modes must be a tuple of execution modes")
151
+ if not value:
152
+ raise ValueError(f"{owner} modes must declare at least one execution mode")
153
+ unknown = next((mode for mode in value if mode not in _EXECUTION_MODES), None)
154
+ if unknown is not None:
155
+ raise ValueError(
156
+ f"{owner} execution modes are batch and stream; found {unknown!r}"
157
+ )
158
+ return value # type: ignore[return-value]
159
+
160
+
161
+ def _require_finality(owner: str, value: object) -> OutputFinality:
162
+ if type(value) is not str:
163
+ raise TypeError(
164
+ f"{owner} finality must be an exact str; found {type(value).__name__}"
165
+ )
166
+ if value not in _OUTPUT_FINALITIES:
167
+ raise ValueError(
168
+ "output finality must be per_row_final, group_final_append_only, or "
169
+ f"unproven; found {value!r}"
170
+ )
171
+ return value # type: ignore[return-value]
172
+
173
+
174
+ def _require_state_version(owner: str, support: str, state_version: object) -> None:
175
+ if support != "checkpointed_stateful":
176
+ if state_version is not None:
177
+ raise ValueError(
178
+ f"{owner} state_version must be None unless checkpointed_stateful"
179
+ )
180
+ return
181
+ if state_version is None or (type(state_version) is int and state_version <= 0):
182
+ raise ValueError(
183
+ f"{owner} checkpointed_stateful requires a positive state_version"
184
+ )
185
+ if type(state_version) is not int:
186
+ raise TypeError(
187
+ f"{owner} state_version must be an exact int when "
188
+ f"checkpointed_stateful; found {type(state_version).__name__}"
189
+ )
190
+
191
+
192
+ def _require_checkpoint_support(
193
+ owner: str, support: object, state_version: object, stateful: bool
194
+ ) -> None:
195
+ if type(support) is not str:
196
+ raise TypeError(
197
+ f"{owner} checkpoint_support must be an exact str; "
198
+ f"found {type(support).__name__}"
199
+ )
200
+ if support not in _CHECKPOINT_SUPPORTS:
201
+ raise ValueError(
202
+ "checkpoint support must be stateless, checkpointed_stateful, or "
203
+ f"unproven; found {support!r}"
204
+ )
205
+ _require_state_version(owner, support, state_version)
206
+ if support == "stateless" and stateful:
207
+ raise ValueError(f"{owner} stateless capability must set stateful=False")
208
+
209
+
210
+ def _require_state_layouts(
211
+ owner: str, support: str, state_version: int | None, state_layouts: object
212
+ ) -> None:
213
+ """Validate the durable layout inventory behind a state contract.
214
+
215
+ ``state_layouts`` enumerates every checkpoint layout the operator
216
+ version may install; a single ``state_version`` cannot express a
217
+ per-declaration layout split such as rolling's EWMA layout v2.
218
+ """
219
+
220
+ if type(state_layouts) is not tuple:
221
+ raise TypeError(
222
+ f"{owner} state_layouts must be an exact tuple; "
223
+ f"found {type(state_layouts).__name__}"
224
+ )
225
+ if support != "checkpointed_stateful":
226
+ if state_layouts:
227
+ raise ValueError(
228
+ f"{owner} state_layouts must be empty unless checkpointed_stateful"
229
+ )
230
+ return
231
+ _require_stateful_layout_inventory(owner, state_version, state_layouts)
232
+
233
+
234
+ def _require_stateful_layout_inventory(
235
+ owner: str, state_version: int | None, state_layouts: tuple[int, ...]
236
+ ) -> None:
237
+ if not state_layouts:
238
+ raise ValueError(
239
+ f"{owner} checkpointed_stateful requires at least one state layout"
240
+ )
241
+ for layout in state_layouts:
242
+ if type(layout) is not int:
243
+ raise TypeError(
244
+ f"{owner} state layouts must be exact positive integers; "
245
+ f"found {type(layout).__name__}"
246
+ )
247
+ if layout <= 0:
248
+ raise ValueError(f"{owner} state layouts must be positive integers")
249
+ _require_ascending_layouts(owner, state_layouts)
250
+ if state_version not in state_layouts:
251
+ raise ValueError(f"{owner} state_layouts must contain state_version")
252
+
253
+
254
+ def _require_ascending_layouts(owner: str, state_layouts: tuple[int, ...]) -> None:
255
+ if any(
256
+ left >= right
257
+ for left, right in zip(state_layouts[:-1], state_layouts[1:], strict=True)
258
+ ):
259
+ raise ValueError(
260
+ f"{owner} state_layouts must be strictly ascending without duplicates"
261
+ )
262
+
263
+
264
+ @dataclass(frozen=True, slots=True)
265
+ class CapabilityRule:
266
+ name: str
267
+ version: str
268
+
269
+ def __post_init__(self) -> None:
270
+ if type(self.name) is not str or type(self.version) is not str:
271
+ raise TypeError(
272
+ "capability rule name and version must be exact strings; found "
273
+ f"{type(self.name).__name__} and {type(self.version).__name__}"
274
+ )
275
+ if (self.name, self.version) not in _CAPABILITY_RULES:
276
+ raise ValueError(f"unknown capability rule {self.name}@{self.version}")
277
+
278
+
279
+ @dataclass(frozen=True, slots=True)
280
+ class ProviderArrayRules:
281
+ supported_dtypes: tuple[str, ...]
282
+ safe_dtype_rule: CapabilityRule
283
+ shape_rules: tuple[CapabilityRule, ...]
284
+
285
+ def __post_init__(self) -> None:
286
+ if type(self.supported_dtypes) is not tuple or any(
287
+ type(dtype) is not str for dtype in self.supported_dtypes
288
+ ):
289
+ raise TypeError(
290
+ "provider array rules supported_dtypes must be a tuple of str; "
291
+ f"found {type(self.supported_dtypes).__name__}"
292
+ )
293
+ if not isinstance(self.safe_dtype_rule, CapabilityRule):
294
+ raise TypeError(
295
+ "provider array rules safe_dtype_rule must be a CapabilityRule; "
296
+ f"found {type(self.safe_dtype_rule).__name__}"
297
+ )
298
+ if type(self.shape_rules) is not tuple or any(
299
+ not isinstance(rule, CapabilityRule) for rule in self.shape_rules
300
+ ):
301
+ raise TypeError(
302
+ "provider array rules shape_rules must be a tuple of "
303
+ f"CapabilityRule values; found {type(self.shape_rules).__name__}"
304
+ )
305
+ object.__setattr__(
306
+ self, "supported_dtypes", tuple(sorted(self.supported_dtypes))
307
+ )
308
+ object.__setattr__(
309
+ self,
310
+ "shape_rules",
311
+ tuple(
312
+ sorted(
313
+ self.shape_rules,
314
+ key=lambda rule: (rule.name, rule.version),
315
+ )
316
+ ),
317
+ )
318
+
319
+
320
+ @dataclass(frozen=True, slots=True)
321
+ class _StatelessProviderLifecycle:
322
+ """Trusted process-local proof attached by the native stream seam."""
323
+
324
+ deterministic: bool
325
+ replay_safe: bool
326
+ supports_static_inputs: bool
327
+ array_rules: ProviderArrayRules
328
+
329
+
330
+ @dataclass(frozen=True, slots=True)
331
+ class OperatorCapability:
332
+ kind: str
333
+ version: str
334
+ input_ports: tuple[ProviderPort, ...]
335
+ output_ports: tuple[ProviderPort, ...]
336
+ modes: tuple[ExecutionMode, ...]
337
+ finality: OutputFinality
338
+ requires_datafusion: bool
339
+ stateful: bool
340
+ microbatch_invariant: bool
341
+ requires_watermark: bool
342
+ checkpoint_support: CheckpointSupport
343
+ state_version: int | None
344
+ deterministic: bool
345
+ replay_safe: bool
346
+ state_layouts: tuple[int, ...] = ()
347
+
348
+ def __post_init__(self) -> None:
349
+ _require_exact_str("operator capability", "kind", self.kind)
350
+ _require_exact_str("operator capability", "version", self.version)
351
+ _require_port_tuple("operator capability", "input_ports", self.input_ports)
352
+ _require_port_tuple("operator capability", "output_ports", self.output_ports)
353
+ _require_modes("operator capability", self.modes)
354
+ _require_finality("operator capability", self.finality)
355
+ _require_exact_bool(
356
+ "operator capability", "requires_datafusion", self.requires_datafusion
357
+ )
358
+ _require_exact_bool("operator capability", "stateful", self.stateful)
359
+ _require_exact_bool(
360
+ "operator capability", "microbatch_invariant", self.microbatch_invariant
361
+ )
362
+ _require_exact_bool(
363
+ "operator capability", "requires_watermark", self.requires_watermark
364
+ )
365
+ _require_exact_bool("operator capability", "deterministic", self.deterministic)
366
+ _require_exact_bool("operator capability", "replay_safe", self.replay_safe)
367
+ _require_checkpoint_support(
368
+ "operator capability",
369
+ self.checkpoint_support,
370
+ self.state_version,
371
+ self.stateful,
372
+ )
373
+ _require_state_layouts(
374
+ "operator capability",
375
+ self.checkpoint_support,
376
+ self.state_version,
377
+ self.state_layouts,
378
+ )
379
+
380
+
381
+ @dataclass(frozen=True, slots=True)
382
+ class UdfCapability:
383
+ provider: str
384
+ name: str
385
+ version: str
386
+ kind: Literal["data_fusion_scalar"]
387
+ input_types: tuple[str, ...]
388
+ return_type: str
389
+ volatility: str
390
+
391
+
392
+ @dataclass(frozen=True, slots=True)
393
+ class ProviderPort:
394
+ name: str
395
+ kind: BatchKind
396
+ required: bool
397
+
398
+
399
+ @dataclass(frozen=True, slots=True)
400
+ class ProviderCapability:
401
+ provider: str
402
+ name: str
403
+ version: str
404
+ input_ports: tuple[ProviderPort, ...]
405
+ output_ports: tuple[ProviderPort, ...]
406
+ options_schema: ProviderOptionsSchema | None
407
+ modes: tuple[ExecutionMode, ...]
408
+ finality: OutputFinality
409
+ stateful: bool
410
+ microbatch_invariant: bool
411
+ requires_watermark: bool
412
+ checkpoint_support: CheckpointSupport
413
+ state_version: int | None
414
+ deterministic: bool
415
+ replay_safe: bool
416
+ supports_static_inputs: bool
417
+ partition_contract: PartitionContract
418
+ array_rules: ProviderArrayRules | None
419
+
420
+ def __post_init__(self) -> None:
421
+ _require_exact_str("provider capability", "provider", self.provider)
422
+ _require_exact_str("provider capability", "version", self.version)
423
+ _require_port_tuple("provider capability", "input_ports", self.input_ports)
424
+ _require_port_tuple("provider capability", "output_ports", self.output_ports)
425
+ _require_modes("provider capability", self.modes)
426
+ _require_finality("provider capability", self.finality)
427
+ _require_exact_bool("provider capability", "stateful", self.stateful)
428
+ _require_exact_bool(
429
+ "provider capability", "microbatch_invariant", self.microbatch_invariant
430
+ )
431
+ _require_exact_bool(
432
+ "provider capability", "requires_watermark", self.requires_watermark
433
+ )
434
+ _require_exact_bool("provider capability", "deterministic", self.deterministic)
435
+ _require_exact_bool("provider capability", "replay_safe", self.replay_safe)
436
+ _require_exact_bool(
437
+ "provider capability", "supports_static_inputs", self.supports_static_inputs
438
+ )
439
+ if type(self.partition_contract) is not str:
440
+ raise TypeError(
441
+ "provider capability partition_contract must be an exact str; "
442
+ f"found {type(self.partition_contract).__name__}"
443
+ )
444
+ if self.partition_contract not in _PARTITION_CONTRACTS:
445
+ raise ValueError(
446
+ "provider capability partition_contract must be none or "
447
+ f"row_axis_independent; found {self.partition_contract!r}"
448
+ )
449
+ if self.array_rules is not None and not isinstance(
450
+ self.array_rules, ProviderArrayRules
451
+ ):
452
+ raise TypeError(
453
+ "provider capability array_rules must be a ProviderArrayRules or "
454
+ f"None; found {type(self.array_rules).__name__}"
455
+ )
456
+ _require_checkpoint_support(
457
+ "provider capability",
458
+ self.checkpoint_support,
459
+ self.state_version,
460
+ self.stateful,
461
+ )
462
+
463
+
464
+ @dataclass(frozen=True, slots=True)
465
+ class RuntimeCapabilities:
466
+ schema_version: Literal[3]
467
+ scope: RuntimeSessionScope
468
+ package_version: str
469
+ project_format_versions: tuple[int, ...]
470
+ batch_kinds: tuple[BatchKind, ...]
471
+ portable_arrow_types: tuple[str, ...]
472
+ operators: tuple[OperatorCapability, ...]
473
+ udfs: tuple[UdfCapability, ...]
474
+ providers: tuple[ProviderCapability, ...]
475
+ connectors: tuple[ConnectorCapability, ...]
476
+
477
+
478
+ def runtime_capabilities(
479
+ *,
480
+ session_id: str,
481
+ revision: int,
482
+ package_version: str,
483
+ registrations: Sequence[Mapping[str, Any]],
484
+ ) -> RuntimeCapabilities:
485
+ from calc_flow import _native
486
+
487
+ udfs = tuple(
488
+ sorted(
489
+ (
490
+ UdfCapability(
491
+ provider=str(registration["provider"]),
492
+ name=str(registration["name"]),
493
+ version=str(registration["version"]),
494
+ kind="data_fusion_scalar",
495
+ input_types=tuple(registration["input_types"]),
496
+ return_type=str(registration["return_type"]),
497
+ volatility=str(registration["volatility"]),
498
+ )
499
+ for registration in registrations
500
+ if registration["kind"] == "scalar_udf"
501
+ ),
502
+ key=lambda item: (item.provider, item.name, item.version),
503
+ )
504
+ )
505
+
506
+ def provider_capability(registration: Mapping[str, Any]) -> ProviderCapability:
507
+ lifecycle = registration.get("stream_lifecycle")
508
+ if isinstance(lifecycle, _StatelessProviderLifecycle):
509
+ modes: tuple[ExecutionMode, ...] = ("batch", "stream")
510
+ finality: OutputFinality = "per_row_final"
511
+ microbatch_invariant = True
512
+ deterministic = lifecycle.deterministic
513
+ replay_safe = lifecycle.replay_safe
514
+ supports_static_inputs = lifecycle.supports_static_inputs
515
+ partition_contract: PartitionContract = "row_axis_independent"
516
+ array_rules: ProviderArrayRules | None = lifecycle.array_rules
517
+ else:
518
+ modes = ("batch",)
519
+ finality = "unproven"
520
+ microbatch_invariant = False
521
+ deterministic = False
522
+ replay_safe = False
523
+ supports_static_inputs = False
524
+ partition_contract = "none"
525
+ array_rules = None
526
+ return ProviderCapability(
527
+ provider=str(registration["provider"]),
528
+ name=str(registration["name"]),
529
+ version=str(registration["version"]),
530
+ input_ports=tuple(
531
+ ProviderPort(str(name), kind, required=True)
532
+ for name, kind in registration.get("input_ports", (("input", "array"),))
533
+ ),
534
+ output_ports=tuple(
535
+ ProviderPort(str(name), kind, required=True)
536
+ for name, kind in registration.get(
537
+ "output_ports", (("output", "array"),)
538
+ )
539
+ ),
540
+ options_schema=registration.get("options_schema"),
541
+ modes=modes,
542
+ finality=finality,
543
+ stateful=False,
544
+ microbatch_invariant=microbatch_invariant,
545
+ requires_watermark=False,
546
+ checkpoint_support="stateless",
547
+ state_version=None,
548
+ deterministic=deterministic,
549
+ replay_safe=replay_safe,
550
+ supports_static_inputs=supports_static_inputs,
551
+ partition_contract=partition_contract,
552
+ array_rules=array_rules,
553
+ )
554
+
555
+ providers = tuple(
556
+ sorted(
557
+ (
558
+ provider_capability(registration)
559
+ for registration in registrations
560
+ if registration["kind"] == "provider"
561
+ ),
562
+ key=lambda item: (item.provider, item.name, item.version),
563
+ )
564
+ )
565
+ return RuntimeCapabilities(
566
+ schema_version=CAPABILITY_SCHEMA_VERSION,
567
+ scope=RuntimeSessionScope(
568
+ kind="runtime_session",
569
+ session_id=session_id,
570
+ revision=revision,
571
+ ),
572
+ package_version=package_version,
573
+ project_format_versions=(3,),
574
+ batch_kinds=("array", "table"),
575
+ portable_arrow_types=PORTABLE_ARROW_TYPES,
576
+ operators=(
577
+ OperatorCapability(
578
+ kind="cross_section",
579
+ version="1",
580
+ input_ports=(ProviderPort("input", "table", required=True),),
581
+ output_ports=(ProviderPort("output", "table", required=True),),
582
+ modes=("batch", "stream"),
583
+ finality="group_final_append_only",
584
+ requires_datafusion=False,
585
+ stateful=True,
586
+ microbatch_invariant=True,
587
+ requires_watermark=True,
588
+ checkpoint_support="checkpointed_stateful",
589
+ state_version=1,
590
+ state_layouts=(1,),
591
+ deterministic=True,
592
+ replay_safe=True,
593
+ ),
594
+ OperatorCapability(
595
+ kind="expression",
596
+ version="1",
597
+ input_ports=(ProviderPort("input", "table", required=True),),
598
+ output_ports=(ProviderPort("output", "table", required=True),),
599
+ modes=("batch", "stream"),
600
+ finality="per_row_final",
601
+ requires_datafusion=True,
602
+ stateful=False,
603
+ microbatch_invariant=True,
604
+ requires_watermark=False,
605
+ checkpoint_support="stateless",
606
+ state_version=None,
607
+ deterministic=True,
608
+ replay_safe=True,
609
+ ),
610
+ OperatorCapability(
611
+ kind="rolling",
612
+ version="1",
613
+ input_ports=(ProviderPort("input", "table", required=True),),
614
+ output_ports=(ProviderPort("output", "table", required=True),),
615
+ modes=("batch", "stream"),
616
+ finality="per_row_final",
617
+ requires_datafusion=False,
618
+ stateful=True,
619
+ microbatch_invariant=True,
620
+ requires_watermark=True,
621
+ checkpoint_support="checkpointed_stateful",
622
+ state_version=1,
623
+ state_layouts=(1, 2),
624
+ deterministic=True,
625
+ replay_safe=True,
626
+ ),
627
+ OperatorCapability(
628
+ kind="sql",
629
+ version="1",
630
+ input_ports=(ProviderPort("input", "table", required=True),),
631
+ output_ports=(ProviderPort("output", "table", required=True),),
632
+ modes=("batch", "stream"),
633
+ finality="unproven",
634
+ requires_datafusion=True,
635
+ stateful=False,
636
+ microbatch_invariant=False,
637
+ requires_watermark=False,
638
+ checkpoint_support="stateless",
639
+ state_version=None,
640
+ deterministic=True,
641
+ replay_safe=True,
642
+ ),
643
+ OperatorCapability(
644
+ kind="stream_join",
645
+ version="1",
646
+ input_ports=(
647
+ ProviderPort("left", "table", required=True),
648
+ ProviderPort("right", "table", required=True),
649
+ ),
650
+ output_ports=(ProviderPort("output", "table", required=True),),
651
+ modes=("stream",),
652
+ finality="unproven",
653
+ requires_datafusion=True,
654
+ stateful=True,
655
+ microbatch_invariant=False,
656
+ requires_watermark=True,
657
+ checkpoint_support="checkpointed_stateful",
658
+ state_version=1,
659
+ state_layouts=(1,),
660
+ deterministic=True,
661
+ replay_safe=True,
662
+ ),
663
+ ),
664
+ udfs=udfs,
665
+ providers=providers,
666
+ connectors=connector_capabilities(_native.registered_connectors()),
667
+ )
668
+
669
+
670
+ # ---------------------------------------------------------------------------
671
+ # Connector capability surface (M6-08)
672
+ # ---------------------------------------------------------------------------
673
+
674
+ type DeliveryCapabilityKind = Literal["best_effort", "at_least_once", "exactly_once"]
675
+ type ReplayCapabilityKind = Literal["replayable_exact", "unreplayable"]
676
+ type WatermarkSupportKind = Literal["native", "generated_only"]
677
+ type TransactionSupportKind = Literal[
678
+ "none",
679
+ "pre_commit_commit",
680
+ "ledger_idempotent",
681
+ "retry_deduplicated",
682
+ ]
683
+
684
+
685
+ @dataclass(frozen=True, slots=True)
686
+ class ConnectorCapabilities:
687
+ delivery: DeliveryCapabilityKind
688
+ replay: ReplayCapabilityKind
689
+ watermark: WatermarkSupportKind
690
+ transaction: TransactionSupportKind
691
+ snapshot: bool
692
+ polling: bool
693
+ cdc: bool
694
+ lookup: bool
695
+
696
+
697
+ @dataclass(frozen=True, slots=True)
698
+ class ConnectorCapability:
699
+ provider: str
700
+ name: str
701
+ version: str
702
+ kind: Literal["source", "sink", "both"]
703
+ capabilities: ConnectorCapabilities
704
+ formats: tuple[str, ...]
705
+ options_schema: Mapping[str, object]
706
+
707
+ def __post_init__(self) -> None:
708
+ if type(self.provider) is not str or not self.provider:
709
+ raise ValueError("connector capability provider must be a non-empty string")
710
+ if type(self.name) is not str or not self.name:
711
+ raise ValueError("connector capability name must be a non-empty string")
712
+ if type(self.version) is not str or not self.version:
713
+ raise ValueError("connector capability version must be a non-empty string")
714
+ if self.kind not in {"source", "sink", "both"}:
715
+ raise ValueError(
716
+ f"connector capability kind must be source, sink, or both; "
717
+ f"found {self.kind}"
718
+ )
719
+ if not isinstance(self.capabilities, ConnectorCapabilities):
720
+ raise TypeError(
721
+ "connector capability capabilities must be a ConnectorCapabilities; "
722
+ f"found {type(self.capabilities).__name__}"
723
+ )
724
+ if not isinstance(self.formats, tuple):
725
+ raise TypeError(
726
+ "connector capability formats must be a tuple of strings; "
727
+ f"found {type(self.formats).__name__}"
728
+ )
729
+ if not isinstance(self.options_schema, Mapping):
730
+ raise TypeError(
731
+ "connector capability options_schema must be a Mapping; "
732
+ f"found {type(self.options_schema).__name__}"
733
+ )
734
+
735
+
736
+ def _parse_options_schema(raw: object) -> dict[str, object]:
737
+ """Parses the JSON string the native layer serializes into a dict."""
738
+ if isinstance(raw, dict):
739
+ return dict(raw)
740
+ if isinstance(raw, str):
741
+ return dict(json.loads(raw))
742
+ return {}
743
+
744
+
745
+ def connector_capabilities(
746
+ registrations: Sequence[Mapping[str, Any]],
747
+ ) -> tuple[ConnectorCapability, ...]:
748
+ """Builds the sorted tuple of connector capabilities from native data."""
749
+ parsed: list[ConnectorCapability] = []
750
+ for registration in registrations:
751
+ caps_data = registration.get("capabilities", {})
752
+ caps = ConnectorCapabilities(
753
+ delivery=str(caps_data.get("delivery", "at_least_once")),
754
+ replay=str(caps_data.get("replay", "unreplayable")),
755
+ watermark=str(caps_data.get("watermark", "generated_only")),
756
+ transaction=str(caps_data.get("transaction", "none")),
757
+ snapshot=bool(caps_data.get("snapshot", False)),
758
+ polling=bool(caps_data.get("polling", False)),
759
+ cdc=bool(caps_data.get("cdc", False)),
760
+ lookup=bool(caps_data.get("lookup", False)),
761
+ )
762
+ parsed.append(
763
+ ConnectorCapability(
764
+ provider=str(registration["provider"]),
765
+ name=str(registration["name"]),
766
+ version=str(registration["version"]),
767
+ kind=str(registration.get("kind", "both")),
768
+ capabilities=caps,
769
+ formats=tuple(str(f) for f in registration.get("formats", ())),
770
+ options_schema=_parse_options_schema(
771
+ registration.get("options_schema", "{}")
772
+ ),
773
+ )
774
+ )
775
+ return tuple(sorted(parsed, key=lambda c: (c.provider, c.name, c.version)))