airbyte-cdk 6.28.0__py3-none-any.whl → 6.28.0.dev0__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.
@@ -22,6 +22,9 @@ from airbyte_cdk.sources.streams.checkpoint.per_partition_key_serializer import
22
22
  )
23
23
  from airbyte_cdk.sources.streams.concurrent.cursor import ConcurrentCursor, Cursor, CursorField
24
24
  from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
25
+ from airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter import (
26
+ AbstractStreamStateConverter,
27
+ )
25
28
  from airbyte_cdk.sources.types import Record, StreamSlice, StreamState
26
29
 
27
30
  logger = logging.getLogger("airbyte")
@@ -72,6 +75,7 @@ class ConcurrentPerPartitionCursor(Cursor):
72
75
  stream_state: Any,
73
76
  message_repository: MessageRepository,
74
77
  connector_state_manager: ConnectorStateManager,
78
+ connector_state_converter: AbstractStreamStateConverter,
75
79
  cursor_field: CursorField,
76
80
  ) -> None:
77
81
  self._global_cursor: Optional[StreamState] = {}
@@ -79,6 +83,7 @@ class ConcurrentPerPartitionCursor(Cursor):
79
83
  self._stream_namespace = stream_namespace
80
84
  self._message_repository = message_repository
81
85
  self._connector_state_manager = connector_state_manager
86
+ self._connector_state_converter = connector_state_converter
82
87
  self._cursor_field = cursor_field
83
88
 
84
89
  self._cursor_factory = cursor_factory
@@ -301,8 +306,7 @@ class ConcurrentPerPartitionCursor(Cursor):
301
306
  ):
302
307
  # We assume that `stream_state` is in a global format that can be applied to all partitions.
303
308
  # Example: {"global_state_format_key": "global_state_format_value"}
304
- self._global_cursor = deepcopy(stream_state)
305
- self._new_global_cursor = deepcopy(stream_state)
309
+ self._set_global_state(stream_state)
306
310
 
307
311
  else:
308
312
  self._use_global_cursor = stream_state.get("use_global_cursor", False)
@@ -319,8 +323,7 @@ class ConcurrentPerPartitionCursor(Cursor):
319
323
 
320
324
  # set default state for missing partitions if it is per partition with fallback to global
321
325
  if self._GLOBAL_STATE_KEY in stream_state:
322
- self._global_cursor = deepcopy(stream_state[self._GLOBAL_STATE_KEY])
323
- self._new_global_cursor = deepcopy(stream_state[self._GLOBAL_STATE_KEY])
326
+ self._set_global_state(stream_state[self._GLOBAL_STATE_KEY])
324
327
 
325
328
  # Set initial parent state
326
329
  if stream_state.get("parent_state"):
@@ -329,6 +332,27 @@ class ConcurrentPerPartitionCursor(Cursor):
329
332
  # Set parent state for partition routers based on parent streams
330
333
  self._partition_router.set_initial_state(stream_state)
331
334
 
335
+ def _set_global_state(self, stream_state: Mapping[str, Any]) -> None:
336
+ """
337
+ Initializes the global cursor state from the provided stream state.
338
+
339
+ If the cursor field key is present in the stream state, its value is parsed,
340
+ formatted, and stored as the global cursor. This ensures consistency in state
341
+ representation across partitions.
342
+ """
343
+ if self.cursor_field.cursor_field_key in stream_state:
344
+ global_state_value = stream_state[self.cursor_field.cursor_field_key]
345
+ final_format_global_state_value = self._connector_state_converter.output_format(
346
+ self._connector_state_converter.parse_value(global_state_value)
347
+ )
348
+
349
+ fixed_global_state = {
350
+ self.cursor_field.cursor_field_key: final_format_global_state_value
351
+ }
352
+
353
+ self._global_cursor = deepcopy(fixed_global_state)
354
+ self._new_global_cursor = deepcopy(fixed_global_state)
355
+
332
356
  def observe(self, record: Record) -> None:
333
357
  if not self._use_global_cursor and self.limit_reached():
334
358
  self._use_global_cursor = True
@@ -372,5 +396,10 @@ class ConcurrentPerPartitionCursor(Cursor):
372
396
  cursor = self._cursor_per_partition[partition_key]
373
397
  return cursor
374
398
 
399
+ def _extract_cursor_value_from_state(self, state: StreamState) -> Any:
400
+ return self._connector_state_converter.parse_value(
401
+ state[self.cursor_field.cursor_field_key]
402
+ )
403
+
375
404
  def limit_reached(self) -> bool:
376
405
  return self._over_limit > self.DEFAULT_MAX_PARTITIONS_NUMBER
@@ -1202,6 +1202,22 @@ class ModelToComponentFactory:
1202
1202
  )
1203
1203
  cursor_field = CursorField(interpolated_cursor_field.eval(config=config))
1204
1204
 
1205
+ datetime_format = datetime_based_cursor_model.datetime_format
1206
+
1207
+ cursor_granularity = (
1208
+ parse_duration(datetime_based_cursor_model.cursor_granularity)
1209
+ if datetime_based_cursor_model.cursor_granularity
1210
+ else None
1211
+ )
1212
+
1213
+ connector_state_converter: DateTimeStreamStateConverter
1214
+ connector_state_converter = CustomFormatConcurrentStreamStateConverter(
1215
+ datetime_format=datetime_format,
1216
+ input_datetime_formats=datetime_based_cursor_model.cursor_datetime_formats,
1217
+ is_sequential_state=True, # ConcurrentPerPartitionCursor only works with sequential state
1218
+ cursor_granularity=cursor_granularity,
1219
+ )
1220
+
1205
1221
  # Create the cursor factory
1206
1222
  cursor_factory = ConcurrentCursorFactory(
1207
1223
  partial(
@@ -1225,6 +1241,7 @@ class ModelToComponentFactory:
1225
1241
  stream_state=stream_state,
1226
1242
  message_repository=self._message_repository, # type: ignore
1227
1243
  connector_state_manager=state_manager,
1244
+ connector_state_converter=connector_state_converter,
1228
1245
  cursor_field=cursor_field,
1229
1246
  )
1230
1247
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.28.0
3
+ Version: 6.28.0.dev0
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -88,7 +88,7 @@ airbyte_cdk/sources/declarative/extractors/record_selector.py,sha256=tjNwcURmlyD
88
88
  airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py,sha256=LhqGDfX06_dDYLKsIVnwQ_nAWCln-v8PV7Wgt_QVeTI,6533
89
89
  airbyte_cdk/sources/declarative/extractors/type_transformer.py,sha256=d6Y2Rfg8pMVEEnHllfVksWZdNVOU55yk34O03dP9muY,1626
90
90
  airbyte_cdk/sources/declarative/incremental/__init__.py,sha256=U1oZKtBaEC6IACmvziY9Wzg7Z8EgF4ZuR7NwvjlB_Sk,1255
91
- airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py,sha256=ndUZaPW0MsN4_3cGpB-0Uu0otSPnmPmHgbixoJeDmjA,16660
91
+ airbyte_cdk/sources/declarative/incremental/concurrent_partition_cursor.py,sha256=maTJX48fXXbm3qZoX6DzRVujUTeF48eeyC1ii96BXj0,17951
92
92
  airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py,sha256=_UzUnSIUsDbRgbFTXgSyZEFb4ws-KdhdQPWO8mFbV7U,22028
93
93
  airbyte_cdk/sources/declarative/incremental/declarative_cursor.py,sha256=5Bhw9VRPyIuCaD0wmmq_L3DZsa-rJgtKSEUzSd8YYD0,536
94
94
  airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py,sha256=9HO-QbL9akvjq2NP7l498RwLA4iQZlBMQW1tZbt34I8,15943
@@ -115,7 +115,7 @@ airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py,sha256=958MMX6_Z
115
115
  airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=Rir9_z3Kcd5Es0-LChrzk-0qubAsiK_RSEnLmK2OXm8,553
116
116
  airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=CXwTfD3wSQq3okcqwigpprbHhSURUokh4GK2OmOyKC8,9132
117
117
  airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=IWUOdF03o-aQn0Occo1BJCxU0Pz-QILk5L67nzw2thw,6803
118
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=zo1FbRgLL-wA7O0rpyJ7IaRkQ6OhyP6Lq4KZGqU4ijw,125265
118
+ airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=x3QC5ECRX3gn9DeWwKR8ropQXPGbDN6inQueqpDkK-k,126044
119
119
  airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=HJ-Syp3p7RpyR_OK0X_a2kSyISfu3W-PKrRI16iY0a8,957
120
120
  airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=n82J15S8bjeMZ5uROu--P3hnbQoxkY5v7RPHYx7g7ro,2929
121
121
  airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
@@ -351,9 +351,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
351
351
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
352
352
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
353
353
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
354
- airbyte_cdk-6.28.0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
355
- airbyte_cdk-6.28.0.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
356
- airbyte_cdk-6.28.0.dist-info/METADATA,sha256=EYHArwfUWLBPhnr-8IYPz0Vhh8cA3Nx5BKznR26QB2g,6010
357
- airbyte_cdk-6.28.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
358
- airbyte_cdk-6.28.0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
359
- airbyte_cdk-6.28.0.dist-info/RECORD,,
354
+ airbyte_cdk-6.28.0.dev0.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
355
+ airbyte_cdk-6.28.0.dev0.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
356
+ airbyte_cdk-6.28.0.dev0.dist-info/METADATA,sha256=lOUTgiqxemt8MFagRsnNAwvwURmnoOV8WbRqSB97Kb8,6015
357
+ airbyte_cdk-6.28.0.dev0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
358
+ airbyte_cdk-6.28.0.dev0.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
359
+ airbyte_cdk-6.28.0.dev0.dist-info/RECORD,,