atlan-application-sdk-api 3.51.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.
Files changed (78) hide show
  1. application_sdk/__init__.py +7 -0
  2. application_sdk/_build_info.py +62 -0
  3. application_sdk/_context_errors.py +65 -0
  4. application_sdk/_install.py +130 -0
  5. application_sdk/_logging.py +171 -0
  6. application_sdk/_runtime/__init__.py +29 -0
  7. application_sdk/_runtime/offload.py +1020 -0
  8. application_sdk/_runtime/progress.py +757 -0
  9. application_sdk/clients/__init__.py +63 -0
  10. application_sdk/clients/_interface.py +58 -0
  11. application_sdk/clients/_sql_failure_classifier.py +112 -0
  12. application_sdk/clients/base.py +565 -0
  13. application_sdk/clients/http_errors.py +28 -0
  14. application_sdk/clients/models.py +49 -0
  15. application_sdk/clients/sql.py +1218 -0
  16. application_sdk/clients/sql_errors.py +192 -0
  17. application_sdk/clients/sql_typecasters.py +431 -0
  18. application_sdk/clients/ssl_utils.py +247 -0
  19. application_sdk/common/__init__.py +53 -0
  20. application_sdk/common/_generated_tree.py +321 -0
  21. application_sdk/common/_listing.py +192 -0
  22. application_sdk/common/aws_utils.py +522 -0
  23. application_sdk/common/aws_utils_errors.py +88 -0
  24. application_sdk/common/build_identity.py +120 -0
  25. application_sdk/common/concurrency.py +45 -0
  26. application_sdk/common/errors.py +99 -0
  27. application_sdk/common/filter_matching.py +261 -0
  28. application_sdk/common/logger.py +11 -0
  29. application_sdk/common/models.py +31 -0
  30. application_sdk/common/sql_filters.py +719 -0
  31. application_sdk/common/sql_filters_errors.py +17 -0
  32. application_sdk/common/task_queue.py +431 -0
  33. application_sdk/common/types.py +51 -0
  34. application_sdk/common/utils.py +87 -0
  35. application_sdk/constants.py +1120 -0
  36. application_sdk/contracts/__init__.py +158 -0
  37. application_sdk/contracts/base.py +1178 -0
  38. application_sdk/contracts/events.py +251 -0
  39. application_sdk/contracts/types.py +801 -0
  40. application_sdk/contracts/types_errors.py +17 -0
  41. application_sdk/credentials/__init__.py +326 -0
  42. application_sdk/credentials/errors.py +226 -0
  43. application_sdk/credentials/extra.py +114 -0
  44. application_sdk/credentials/ingress.py +283 -0
  45. application_sdk/credentials/ref.py +376 -0
  46. application_sdk/credentials/spec.py +182 -0
  47. application_sdk/credentials/types.py +339 -0
  48. application_sdk/credentials/utils.py +257 -0
  49. application_sdk/errors/__init__.py +224 -0
  50. application_sdk/errors/base.py +504 -0
  51. application_sdk/errors/categories.py +118 -0
  52. application_sdk/errors/http.py +81 -0
  53. application_sdk/errors/leaves.py +526 -0
  54. application_sdk/errors/wire.py +124 -0
  55. application_sdk/handler/__init__.py +97 -0
  56. application_sdk/handler/_warmup.py +198 -0
  57. application_sdk/handler/asgi.py +252 -0
  58. application_sdk/handler/base.py +281 -0
  59. application_sdk/handler/context.py +212 -0
  60. application_sdk/handler/contracts.py +1166 -0
  61. application_sdk/handler/manifest.py +81 -0
  62. application_sdk/handler/metadata_routes.py +699 -0
  63. application_sdk/handler/object_routes.py +418 -0
  64. application_sdk/handler/request_contract.py +91 -0
  65. application_sdk/handler/routes.py +1118 -0
  66. application_sdk/handler/service_errors.py +37 -0
  67. application_sdk/server/__init__.py +15 -0
  68. application_sdk/server/fastapi/models.py +338 -0
  69. application_sdk/templates/__init__.py +52 -0
  70. application_sdk/templates/contracts/__init__.py +111 -0
  71. application_sdk/templates/contracts/base_metadata_extraction.py +32 -0
  72. application_sdk/templates/contracts/incremental_sql.py +563 -0
  73. application_sdk/templates/contracts/sql_metadata.py +824 -0
  74. application_sdk/templates/contracts/sql_query.py +105 -0
  75. application_sdk/version.py +40 -0
  76. atlan_application_sdk_api-3.51.0.dist-info/METADATA +230 -0
  77. atlan_application_sdk_api-3.51.0.dist-info/RECORD +78 -0
  78. atlan_application_sdk_api-3.51.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,563 @@
1
+ """Typed contracts for incremental SQL metadata extraction.
2
+
3
+ Provides the full contract hierarchy for the v3 incremental extraction template,
4
+ including the ``IncrementalRunContext`` local accumulator and all per-task
5
+ input/output types.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import dataclasses
11
+ import re
12
+
13
+ from pydantic import Field, field_serializer, field_validator
14
+
15
+ from application_sdk.contracts.base import Input, Output, SerializableEnum
16
+ from application_sdk.templates.contracts.sql_metadata import (
17
+ ExtractionInput,
18
+ ExtractionOutput,
19
+ ExtractionTaskInput,
20
+ FetchColumnsOutput,
21
+ FetchDatabasesOutput,
22
+ FetchSchemasOutput,
23
+ FetchTablesOutput,
24
+ )
25
+
26
+ # BLDX-518: marker_timestamp is substituted into incremental SQL templates
27
+ # via ``str.replace``, so untrusted input could carry SQL escape sequences
28
+ # (the marker is sourced from a persistent file but can also be overridden
29
+ # from the workflow input — see ``FetchIncrementalMarkerInput.existing_marker``).
30
+ #
31
+ # Accept either:
32
+ # * Empty string — signals "no marker, perform a full extraction".
33
+ # * ISO-8601 / RFC-3339 timestamp — date + time, optional fractional
34
+ # seconds, optional timezone offset. ``T`` or space between date and
35
+ # time is allowed to match the variants different sources emit.
36
+ #
37
+ # Anything else (including SQL escape chars) is rejected by the validator
38
+ # regardless of whether the value came from object storage or a workflow
39
+ # override.
40
+ _ISO_TIMESTAMP_PATTERN = re.compile(
41
+ r"^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$"
42
+ )
43
+
44
+
45
+ def _validate_marker_timestamp(value: str | None) -> str | None:
46
+ """Reject marker timestamps that don't match ISO-8601 / empty.
47
+
48
+ Empty / None passes through (no marker = full-extraction signal).
49
+ Anything else must match :data:`_ISO_TIMESTAMP_PATTERN`. Raises
50
+ ``ValueError`` so Pydantic surfaces it as ``ValidationError``.
51
+
52
+ Assumption: all SDK-produced markers are ISO-8601 timestamps or empty
53
+ strings, so adding this validator to Output contracts
54
+ (``FetchIncrementalMarkerOutput``, ``UpdateMarkerOutput``) is safe for
55
+ Temporal workflow replay — no well-formed history entry would be
56
+ rejected by this constraint.
57
+ """
58
+ if value is None or value == "":
59
+ return value
60
+ if not _ISO_TIMESTAMP_PATTERN.match(value):
61
+ # conformance: ignore[E012] Pydantic field_validator requires ValueError to surface as ValidationError; replacing with AppError would break Pydantic validation contract
62
+ raise ValueError(
63
+ f"marker_timestamp must be empty or an ISO-8601 / RFC-3339 timestamp "
64
+ f"(got {value!r}). Reject reason: prevents SQL injection via "
65
+ "templates that substitute the marker into a quoted literal."
66
+ )
67
+ return value
68
+
69
+
70
+ def _validate_wire_marker(value: str) -> str:
71
+ """Validate a wire-form marker field (a ``str``, ``""`` for none)."""
72
+ _validate_marker_timestamp(value)
73
+ return value
74
+
75
+
76
+ # =============================================================================
77
+ # Marker sentinel boundary
78
+ # =============================================================================
79
+ #
80
+ # "No marker" has one Python spelling, ``None`` (``IncrementalRunContext`` and
81
+ # ``MarkerPair``), and one wire spelling, ``""`` (every task contract field, so
82
+ # existing Temporal histories and app payloads keep their shape). These two
83
+ # functions are the only place one becomes the other.
84
+
85
+
86
+ def marker_to_wire(marker: str | None) -> str:
87
+ """Encode a Python marker for a task contract: ``None`` becomes ``""``."""
88
+ return "" if marker is None else marker
89
+
90
+
91
+ def marker_from_wire(value: str) -> str | None:
92
+ """Decode a task contract's marker field: ``""`` becomes ``None``."""
93
+ return value or None
94
+
95
+
96
+ # =============================================================================
97
+ # IncrementalRunContext — local accumulator, NOT a Temporal payload
98
+ # =============================================================================
99
+
100
+
101
+ @dataclasses.dataclass
102
+ class IncrementalRunContext:
103
+ """Incremental extraction state accumulated across tasks within a single run().
104
+
105
+ This context is a local variable in run() — NOT stored in app_state, NOT a
106
+ Temporal payload. It is built progressively as task outputs are collected.
107
+ Temporal's workflow replay reconstructs it deterministically from recorded
108
+ activity outputs, so it is crash-safe.
109
+ """
110
+
111
+ workflow_id: str = ""
112
+ workflow_run_id: str = ""
113
+ output_prefix: str = ""
114
+ output_path: str = ""
115
+ connection_qualified_name: str = ""
116
+ connection_name: str = ""
117
+ application_name: str = ""
118
+ incremental_extraction: bool = False
119
+ column_batch_size: int = 25000
120
+ column_chunk_size: int = 100000
121
+ copy_workers: int = 3
122
+ upload_concurrency: int = 4
123
+ prepone_marker_timestamp: bool = True
124
+ prepone_marker_hours: int = 3
125
+ # Set by fetch_incremental_marker
126
+ marker_timestamp: str | None = None
127
+ next_marker_timestamp: str = ""
128
+ # Set by read_current_state
129
+ current_state_available: bool = False
130
+ current_state_path: str = ""
131
+ current_state_s3_prefix: str = ""
132
+ current_state_json_count: int = 0
133
+ # Set by write_current_state
134
+ current_state_files: int = 0
135
+ incremental_diff_path: str = ""
136
+ incremental_diff_s3_prefix: str = ""
137
+ incremental_diff_files: int = 0
138
+ # Set by prepare_column_extraction_queries
139
+ total_column_batches: int = 0
140
+ changed_tables: int = 0
141
+ backfill_tables: int = 0
142
+
143
+ def is_incremental_ready(self) -> bool:
144
+ """Return True when all prerequisites for incremental mode are satisfied.
145
+
146
+ Requires: incremental_extraction=True AND a marker from a previous run AND
147
+ a current-state snapshot from that run. On the very first run, marker_timestamp
148
+ is None and current_state_available is False, so this returns False and the
149
+ run performs a full extraction — building the initial state for future runs.
150
+ """
151
+ return bool(
152
+ self.incremental_extraction
153
+ and self.marker_timestamp
154
+ and self.current_state_available
155
+ )
156
+
157
+
158
+ # =============================================================================
159
+ # Top-level input/output for IncrementalSqlMetadataExtractor.run()
160
+ # =============================================================================
161
+
162
+
163
+ class IncrementalExtractionInput(ExtractionInput):
164
+ """Top-level input for an incremental SQL metadata extraction run.
165
+
166
+ Extends :class:`ExtractionInput` with incremental-specific configuration.
167
+ """
168
+
169
+ incremental_extraction: bool = False
170
+ """Enable incremental extraction mode."""
171
+
172
+ column_batch_size: int = 25000
173
+ """Number of tables per batch for incremental column extraction."""
174
+
175
+ column_chunk_size: int = 100000
176
+ """Number of column records per output chunk file."""
177
+
178
+ copy_workers: int = 3
179
+ """Parallel workers for file copy operations during state snapshot."""
180
+
181
+ upload_concurrency: int = Field(default=4, gt=0)
182
+ """Max concurrent object-store requests for the current-state and
183
+ incremental-diff uploads. Connections with very large column counts
184
+ (100K+) can raise this to shrink wall-clock time on the upload step."""
185
+
186
+ prepone_marker_timestamp: bool = True
187
+ """Whether to move the marker back by ``prepone_marker_hours``."""
188
+
189
+ prepone_marker_hours: int = 3
190
+ """Hours to subtract from the marker when preponing is enabled."""
191
+
192
+
193
+ class IncrementalExtractionOutput(ExtractionOutput):
194
+ """Top-level output from an incremental SQL metadata extraction run.
195
+
196
+ Extends :class:`ExtractionOutput` with incremental-specific statistics.
197
+ """
198
+
199
+ current_state_files: int = 0
200
+ """Number of files written to the current-state snapshot."""
201
+
202
+ incremental_diff_files: int = 0
203
+ """Number of files in the incremental diff (0 on first run)."""
204
+
205
+ column_batches_executed: int = 0
206
+ """Number of incremental column batches executed."""
207
+
208
+ changed_tables: int = 0
209
+ """Tables detected as changed since last run."""
210
+
211
+ backfill_tables: int = 0
212
+ """Tables detected as needing backfill (new tables)."""
213
+
214
+ marker_updated: bool = False
215
+ """Whether the incremental marker was updated after this run."""
216
+
217
+
218
+ # =============================================================================
219
+ # Shared incremental task input base
220
+ # =============================================================================
221
+
222
+
223
+ class IncrementalTaskInput(ExtractionTaskInput):
224
+ """Base task input with incremental runtime state.
225
+
226
+ Extends :class:`ExtractionTaskInput` with the incremental fields that
227
+ every incremental task needs to decide between full and incremental paths.
228
+ """
229
+
230
+ incremental_extraction: bool = False
231
+ """Whether incremental extraction is enabled for this run."""
232
+
233
+ marker_timestamp: str = ""
234
+ """Marker timestamp from previous run; empty string means full extraction."""
235
+
236
+ current_state_available: bool = False
237
+ """Whether a current-state snapshot from a previous run is available."""
238
+
239
+ column_chunk_size: int = 100000
240
+ """Number of column records per output chunk file."""
241
+
242
+ @field_validator("marker_timestamp", mode="after")
243
+ @classmethod
244
+ def _validate_marker(cls, v: str) -> str:
245
+ return _validate_wire_marker(v)
246
+
247
+
248
+ # =============================================================================
249
+ # Per-task incremental input types (inherit IncrementalTaskInput)
250
+ # =============================================================================
251
+
252
+
253
+ class FetchTablesIncrementalInput(IncrementalTaskInput):
254
+ """Input for the incremental fetch_tables task.
255
+
256
+ Carries all fields from :class:`IncrementalTaskInput`. The marker and
257
+ current_state_available fields together indicate whether to use the
258
+ incremental or full-extraction SQL path — see the ``fetch_tables``
259
+ docstring in :class:`IncrementalSqlMetadataExtractor` for details.
260
+ """
261
+
262
+
263
+ class FetchColumnsIncrementalInput(IncrementalTaskInput):
264
+ """Input for the incremental fetch_columns task.
265
+
266
+ When both ``marker_timestamp`` is non-empty and ``current_state_available``
267
+ is True, the base ``fetch_columns`` implementation returns immediately with
268
+ zero counts — column extraction is delegated to batch tasks instead.
269
+ """
270
+
271
+
272
+ # =============================================================================
273
+ # fetch_incremental_marker task
274
+ # =============================================================================
275
+
276
+
277
+ class FetchIncrementalMarkerInput(Input):
278
+ """Input for the fetch_incremental_marker task."""
279
+
280
+ connection_qualified_name: str = ""
281
+ """Connection qualified name used to locate the persistent marker file."""
282
+
283
+ application_name: str = ""
284
+ """Application name for S3 path resolution."""
285
+
286
+ existing_marker: str | None = None
287
+ """Pre-existing marker value (e.g., from a manual workflow override)."""
288
+
289
+ prepone_enabled: bool = True
290
+ """Whether to move the marker back by ``prepone_hours``."""
291
+
292
+ prepone_hours: float = 3.0
293
+ """Hours to subtract from the marker when preponing is enabled."""
294
+
295
+ @field_validator("existing_marker", mode="after")
296
+ @classmethod
297
+ def _validate_existing_marker(cls, v: str | None) -> str | None:
298
+ return _validate_marker_timestamp(v)
299
+
300
+
301
+ class FetchIncrementalMarkerOutput(Output):
302
+ """Output from the fetch_incremental_marker task."""
303
+
304
+ marker_timestamp: str = ""
305
+ """Processed marker from the previous run; empty on the first run."""
306
+
307
+ next_marker_timestamp: str = ""
308
+ """New marker timestamp generated for the current run."""
309
+
310
+ @field_validator("marker_timestamp", "next_marker_timestamp", mode="after")
311
+ @classmethod
312
+ def _validate_marker(cls, v: str) -> str:
313
+ return _validate_wire_marker(v)
314
+
315
+
316
+ # =============================================================================
317
+ # read_current_state task
318
+ # =============================================================================
319
+
320
+
321
+ class ReadCurrentStateInput(Input):
322
+ """Input for the read_current_state task."""
323
+
324
+ connection_qualified_name: str = ""
325
+ application_name: str = ""
326
+ output_path: str = ""
327
+ """The run's output path. Only read when the snapshot is materialized on
328
+ read (an ``after_current_state_read`` or legacy ``read_current_state``
329
+ override), which puts it under ``{output_path}/incremental/previous-state``."""
330
+
331
+
332
+ class ReadCurrentStateOutput(Output):
333
+ """Output from the read_current_state task."""
334
+
335
+ current_state_path: str = ""
336
+ """Local path the snapshot was materialized into, or ``""`` when the read
337
+ only probed (the default: nothing is downloaded until a later task needs
338
+ the files)."""
339
+
340
+ current_state_s3_prefix: str = ""
341
+ """S3 prefix for the current-state folder."""
342
+
343
+ current_state_available: bool = False
344
+ """Whether a non-empty current-state snapshot was found."""
345
+
346
+ current_state_json_count: int = 0
347
+ """Number of JSON files in the committed current state."""
348
+
349
+
350
+ # =============================================================================
351
+ # prepare_column_extraction_queries task
352
+ # =============================================================================
353
+
354
+
355
+ class PrepareColumnQueriesInput(IncrementalTaskInput):
356
+ """Input for the prepare_column_extraction_queries task."""
357
+
358
+ connection_qualified_name: str = ""
359
+ """Connection qualified name for persistent artifact path resolution."""
360
+
361
+ current_state_s3_prefix: str = ""
362
+ """S3 prefix of the current-state snapshot for backfill comparison."""
363
+
364
+ column_batch_size: int = 25000
365
+ """Number of tables per batch file."""
366
+
367
+ application_name: str = ""
368
+
369
+
370
+ class PrepareColumnQueriesOutput(Output):
371
+ """Output from the prepare_column_extraction_queries task."""
372
+
373
+ total_batches: int = 0
374
+ changed_tables: int = 0
375
+ backfill_tables: int = 0
376
+ total_tables: int = 0
377
+ batches_s3_prefix: str = ""
378
+ batches_local_dir: str = ""
379
+
380
+
381
+ # =============================================================================
382
+ # execute_single_column_batch task
383
+ # =============================================================================
384
+
385
+
386
+ class ExecuteColumnBatchInput(IncrementalTaskInput):
387
+ """Input for executing a single incremental column batch."""
388
+
389
+ batch_index: int = 0
390
+ """Zero-based index of this batch within the total."""
391
+
392
+ total_batches: int = 1
393
+ """Total number of batches."""
394
+
395
+ batches_s3_prefix: str = ""
396
+ """S3 prefix where the batch JSON files are stored."""
397
+
398
+ application_name: str = ""
399
+
400
+
401
+ class ColumnBatchStatus(SerializableEnum):
402
+ """Outcome of one ``execute_single_column_batch`` task.
403
+
404
+ A ``StrEnum``: members compare equal to, and serialize as, the plain
405
+ strings the field carried before it was typed.
406
+
407
+ Members:
408
+ SUCCESS: The batch file was found and its SQL ran.
409
+ NOT_FOUND: The batch file was missing, so nothing ran.
410
+ """
411
+
412
+ SUCCESS = "success"
413
+ NOT_FOUND = "not_found"
414
+
415
+
416
+ class ExecuteColumnBatchOutput(Output):
417
+ """Output from executing a single incremental column batch."""
418
+
419
+ batch_index: int = 0
420
+ records: int = 0
421
+ # Pre-dates BLDX-1244's standard Output.status (``OutputStatus`` enum)
422
+ # and uses domain-specific values ("not_found", "success") that aren't
423
+ # part of that vocabulary, hence its own enum; the ignore acknowledges
424
+ # the deliberate field-type narrowing. ``None`` is "not set" in Python;
425
+ # on the wire it stays ``""``, the old default, in both directions.
426
+ status: ColumnBatchStatus | None = None # type: ignore[assignment]
427
+
428
+ @field_validator("status", mode="before")
429
+ @classmethod
430
+ def _legacy_unset_status(cls, v: object) -> object:
431
+ # A payload recorded before the field was typed carries "" for "not
432
+ # set"; it must still deserialize on replay.
433
+ return None if v == "" else v
434
+
435
+ @field_serializer("status")
436
+ def _unset_status_as_empty_string(self, v: ColumnBatchStatus | None) -> str:
437
+ # The field was a plain str defaulting to "": a consumer reading the
438
+ # payload expects a string, never null.
439
+ return "" if v is None else v.value
440
+
441
+
442
+ # =============================================================================
443
+ # write_current_state task
444
+ # =============================================================================
445
+
446
+
447
+ class WriteCurrentStateInput(IncrementalTaskInput):
448
+ """Input for the write_current_state task."""
449
+
450
+ workflow_run_id: str = Field(
451
+ default="",
452
+ deprecated=(
453
+ "WriteCurrentStateInput.workflow_run_id is deprecated and ignored; "
454
+ "write_current_state reads the run ID from its own Temporal context, "
455
+ "so stop passing it — will be removed in v4.0.0."
456
+ ),
457
+ )
458
+ """Deprecated and ignored; the task uses its own Temporal run ID instead.
459
+
460
+ That run ID stamps the current-state snapshot and keys the incremental
461
+ diff. A value here that disagrees with it is logged at WARNING. Will be
462
+ removed in v4.0.0."""
463
+
464
+ current_state_s3_prefix: str = ""
465
+ """S3 prefix for the existing current-state (for previous-state download)."""
466
+
467
+ copy_workers: int = 3
468
+ """Parallel workers for file copy operations."""
469
+
470
+ upload_concurrency: int = Field(default=4, gt=0)
471
+ """Max concurrent object-store requests when uploading the current-state
472
+ snapshot and incremental diff. Connections with very large column counts
473
+ (100K+) can raise this to shrink wall-clock time on the upload step,
474
+ which otherwise runs at this same fixed default regardless of scale."""
475
+
476
+ application_name: str = ""
477
+
478
+
479
+ class WriteCurrentStateOutput(Output):
480
+ """Output from the write_current_state task."""
481
+
482
+ current_state_path: str = ""
483
+ current_state_s3_prefix: str = ""
484
+ current_state_files: int = 0
485
+ incremental_diff_path: str = ""
486
+ incremental_diff_s3_prefix: str = ""
487
+ incremental_diff_files: int = 0
488
+
489
+
490
+ # =============================================================================
491
+ # update_incremental_marker task
492
+ # =============================================================================
493
+
494
+
495
+ class UpdateMarkerInput(Input):
496
+ """Input for the update_incremental_marker task."""
497
+
498
+ connection_qualified_name: str = ""
499
+ next_marker_timestamp: str = ""
500
+ application_name: str = ""
501
+
502
+ @field_validator("next_marker_timestamp", mode="after")
503
+ @classmethod
504
+ def _validate_marker(cls, v: str) -> str:
505
+ return _validate_wire_marker(v)
506
+
507
+
508
+ class UpdateMarkerOutput(Output):
509
+ """Output from the update_incremental_marker task."""
510
+
511
+ marker_written: bool = False
512
+ marker_timestamp: str = ""
513
+ s3_key: str = ""
514
+
515
+ @field_validator("marker_timestamp", mode="after")
516
+ @classmethod
517
+ def _validate_marker(cls, v: str) -> str:
518
+ return _validate_wire_marker(v)
519
+
520
+
521
+ # =============================================================================
522
+ # Re-export base types used in incremental contracts for convenience
523
+ # =============================================================================
524
+
525
+ __all__ = [
526
+ # Context (not a Temporal payload)
527
+ "IncrementalRunContext",
528
+ # Marker sentinel boundary
529
+ "marker_from_wire",
530
+ "marker_to_wire",
531
+ # Top-level run() contracts
532
+ "IncrementalExtractionInput",
533
+ "IncrementalExtractionOutput",
534
+ # Shared incremental task base
535
+ "IncrementalTaskInput",
536
+ # Per-task incremental inputs (override of base extraction tasks)
537
+ "FetchTablesIncrementalInput",
538
+ "FetchColumnsIncrementalInput",
539
+ # fetch_incremental_marker
540
+ "FetchIncrementalMarkerInput",
541
+ "FetchIncrementalMarkerOutput",
542
+ # read_current_state
543
+ "ReadCurrentStateInput",
544
+ "ReadCurrentStateOutput",
545
+ # prepare_column_extraction_queries
546
+ "PrepareColumnQueriesInput",
547
+ "PrepareColumnQueriesOutput",
548
+ # execute_single_column_batch
549
+ "ColumnBatchStatus",
550
+ "ExecuteColumnBatchInput",
551
+ "ExecuteColumnBatchOutput",
552
+ # write_current_state
553
+ "WriteCurrentStateInput",
554
+ "WriteCurrentStateOutput",
555
+ # update_incremental_marker
556
+ "UpdateMarkerInput",
557
+ "UpdateMarkerOutput",
558
+ # Re-exported base output types (used in incremental extractor signatures)
559
+ "FetchDatabasesOutput",
560
+ "FetchSchemasOutput",
561
+ "FetchTablesOutput",
562
+ "FetchColumnsOutput",
563
+ ]