airbyte-cdk 6.60.7__py3-none-any.whl → 6.60.9__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.
@@ -3,8 +3,8 @@
3
3
  #
4
4
 
5
5
 
6
- from dataclasses import asdict
7
- from typing import Any, Dict, List, Mapping, Optional
6
+ from dataclasses import asdict, dataclass, field
7
+ from typing import Any, ClassVar, Dict, List, Mapping
8
8
 
9
9
  from airbyte_cdk.connector_builder.test_reader import TestReader
10
10
  from airbyte_cdk.models import (
@@ -15,32 +15,45 @@ from airbyte_cdk.models import (
15
15
  Type,
16
16
  )
17
17
  from airbyte_cdk.models import Type as MessageType
18
- from airbyte_cdk.sources.declarative.concurrent_declarative_source import (
19
- ConcurrentDeclarativeSource,
20
- TestLimits,
21
- )
22
18
  from airbyte_cdk.sources.declarative.declarative_source import DeclarativeSource
23
19
  from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
20
+ from airbyte_cdk.sources.declarative.parsers.model_to_component_factory import (
21
+ ModelToComponentFactory,
22
+ )
24
23
  from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets
25
24
  from airbyte_cdk.utils.datetime_helpers import ab_datetime_now
26
25
  from airbyte_cdk.utils.traced_exception import AirbyteTracedException
27
26
 
27
+ DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE = 5
28
+ DEFAULT_MAXIMUM_NUMBER_OF_SLICES = 5
29
+ DEFAULT_MAXIMUM_RECORDS = 100
30
+ DEFAULT_MAXIMUM_STREAMS = 100
31
+
28
32
  MAX_PAGES_PER_SLICE_KEY = "max_pages_per_slice"
29
33
  MAX_SLICES_KEY = "max_slices"
30
34
  MAX_RECORDS_KEY = "max_records"
31
35
  MAX_STREAMS_KEY = "max_streams"
32
36
 
33
37
 
38
+ @dataclass
39
+ class TestLimits:
40
+ __test__: ClassVar[bool] = False # Tell Pytest this is not a Pytest class, despite its name
41
+
42
+ max_records: int = field(default=DEFAULT_MAXIMUM_RECORDS)
43
+ max_pages_per_slice: int = field(default=DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE)
44
+ max_slices: int = field(default=DEFAULT_MAXIMUM_NUMBER_OF_SLICES)
45
+ max_streams: int = field(default=DEFAULT_MAXIMUM_STREAMS)
46
+
47
+
34
48
  def get_limits(config: Mapping[str, Any]) -> TestLimits:
35
49
  command_config = config.get("__test_read_config", {})
36
- return TestLimits(
37
- max_records=command_config.get(MAX_RECORDS_KEY, TestLimits.DEFAULT_MAX_RECORDS),
38
- max_pages_per_slice=command_config.get(
39
- MAX_PAGES_PER_SLICE_KEY, TestLimits.DEFAULT_MAX_PAGES_PER_SLICE
40
- ),
41
- max_slices=command_config.get(MAX_SLICES_KEY, TestLimits.DEFAULT_MAX_SLICES),
42
- max_streams=command_config.get(MAX_STREAMS_KEY, TestLimits.DEFAULT_MAX_STREAMS),
50
+ max_pages_per_slice = (
51
+ command_config.get(MAX_PAGES_PER_SLICE_KEY) or DEFAULT_MAXIMUM_NUMBER_OF_PAGES_PER_SLICE
43
52
  )
53
+ max_slices = command_config.get(MAX_SLICES_KEY) or DEFAULT_MAXIMUM_NUMBER_OF_SLICES
54
+ max_records = command_config.get(MAX_RECORDS_KEY) or DEFAULT_MAXIMUM_RECORDS
55
+ max_streams = command_config.get(MAX_STREAMS_KEY) or DEFAULT_MAXIMUM_STREAMS
56
+ return TestLimits(max_records, max_pages_per_slice, max_slices, max_streams)
44
57
 
45
58
 
46
59
  def should_migrate_manifest(config: Mapping[str, Any]) -> bool:
@@ -62,30 +75,21 @@ def should_normalize_manifest(config: Mapping[str, Any]) -> bool:
62
75
  return config.get("__should_normalize", False)
63
76
 
64
77
 
65
- def create_source(
66
- config: Mapping[str, Any],
67
- limits: TestLimits,
68
- catalog: Optional[ConfiguredAirbyteCatalog],
69
- state: Optional[List[AirbyteStateMessage]],
70
- ) -> ConcurrentDeclarativeSource[Optional[List[AirbyteStateMessage]]]:
78
+ def create_source(config: Mapping[str, Any], limits: TestLimits) -> ManifestDeclarativeSource:
71
79
  manifest = config["__injected_declarative_manifest"]
72
-
73
- # We enforce a concurrency level of 1 so that the stream is processed on a single thread
74
- # to retain ordering for the grouping of the builder message responses.
75
- if "concurrency_level" in manifest:
76
- manifest["concurrency_level"]["default_concurrency"] = 1
77
- else:
78
- manifest["concurrency_level"] = {"type": "ConcurrencyLevel", "default_concurrency": 1}
79
-
80
- return ConcurrentDeclarativeSource(
81
- catalog=catalog,
80
+ return ManifestDeclarativeSource(
82
81
  config=config,
83
- state=state,
84
- source_config=manifest,
85
82
  emit_connector_builder_messages=True,
83
+ source_config=manifest,
86
84
  migrate_manifest=should_migrate_manifest(config),
87
85
  normalize_manifest=should_normalize_manifest(config),
88
- limits=limits,
86
+ component_factory=ModelToComponentFactory(
87
+ emit_connector_builder_messages=True,
88
+ limit_pages_fetched_per_slice=limits.max_pages_per_slice,
89
+ limit_slices_fetched=limits.max_slices,
90
+ disable_retries=True,
91
+ disable_cache=True,
92
+ ),
89
93
  )
90
94
 
91
95
 
@@ -91,12 +91,12 @@ def handle_connector_builder_request(
91
91
  def handle_request(args: List[str]) -> str:
92
92
  command, config, catalog, state = get_config_and_catalog_from_args(args)
93
93
  limits = get_limits(config)
94
- source = create_source(config=config, limits=limits, catalog=catalog, state=state)
95
- return orjson.dumps( # type: ignore[no-any-return] # Serializer.dump() always returns AirbyteMessage
94
+ source = create_source(config, limits)
95
+ return orjson.dumps(
96
96
  AirbyteMessageSerializer.dump(
97
97
  handle_connector_builder_request(source, command, config, catalog, state, limits)
98
98
  )
99
- ).decode()
99
+ ).decode() # type: ignore[no-any-return] # Serializer.dump() always returns AirbyteMessage
100
100
 
101
101
 
102
102
  if __name__ == "__main__":
@@ -5,7 +5,7 @@
5
5
  import json
6
6
  from copy import deepcopy
7
7
  from json import JSONDecodeError
8
- from typing import Any, Dict, List, Mapping, Optional, Union
8
+ from typing import Any, Dict, List, Mapping, Optional
9
9
 
10
10
  from airbyte_cdk.connector_builder.models import (
11
11
  AuxiliaryRequest,
@@ -17,8 +17,6 @@ from airbyte_cdk.connector_builder.models import (
17
17
  from airbyte_cdk.models import (
18
18
  AirbyteLogMessage,
19
19
  AirbyteMessage,
20
- AirbyteStateBlob,
21
- AirbyteStateMessage,
22
20
  OrchestratorType,
23
21
  TraceType,
24
22
  )
@@ -468,7 +466,7 @@ def handle_current_slice(
468
466
  return StreamReadSlices(
469
467
  pages=current_slice_pages,
470
468
  slice_descriptor=current_slice_descriptor,
471
- state=[convert_state_blob_to_mapping(latest_state_message)] if latest_state_message else [],
469
+ state=[latest_state_message] if latest_state_message else [],
472
470
  auxiliary_requests=auxiliary_requests if auxiliary_requests else [],
473
471
  )
474
472
 
@@ -720,23 +718,3 @@ def get_auxiliary_request_type(stream: dict, http: dict) -> str: # type: ignore
720
718
  Determines the type of the auxiliary request based on the stream and HTTP properties.
721
719
  """
722
720
  return "PARENT_STREAM" if stream.get("is_substream", False) else str(http.get("type", None))
723
-
724
-
725
- def convert_state_blob_to_mapping(
726
- state_message: Union[AirbyteStateMessage, Dict[str, Any]],
727
- ) -> Dict[str, Any]:
728
- """
729
- The AirbyteStreamState stores state as an AirbyteStateBlob which deceivingly is not
730
- a dictionary, but rather a list of kwargs fields. This in turn causes it to not be
731
- properly turned into a dictionary when translating this back into response output
732
- by the connector_builder_handler using asdict()
733
- """
734
-
735
- if isinstance(state_message, AirbyteStateMessage) and state_message.stream:
736
- state_value = state_message.stream.stream_state
737
- if isinstance(state_value, AirbyteStateBlob):
738
- state_value_mapping = {k: v for k, v in state_value.__dict__.items()}
739
- state_message.stream.stream_state = state_value_mapping # type: ignore # we intentionally set this as a Dict so that StreamReadSlices is translated properly in the resulting HTTP response
740
- return state_message # type: ignore # See above, but when this is an AirbyteStateMessage we must convert AirbyteStateBlob to a Dict
741
- else:
742
- return state_message # type: ignore # This is guaranteed to be a Dict since we check isinstance AirbyteStateMessage above
@@ -95,14 +95,11 @@ class ConcurrentReadProcessor:
95
95
  """
96
96
  stream_name = partition.stream_name()
97
97
  self._streams_to_running_partitions[stream_name].add(partition)
98
- cursor = self._stream_name_to_instance[stream_name].cursor
99
98
  if self._slice_logger.should_log_slice_message(self._logger):
100
99
  self._message_repository.emit_message(
101
100
  self._slice_logger.create_slice_log_message(partition.to_slice())
102
101
  )
103
- self._thread_pool_manager.submit(
104
- self._partition_reader.process_partition, partition, cursor
105
- )
102
+ self._thread_pool_manager.submit(self._partition_reader.process_partition, partition)
106
103
 
107
104
  def on_partition_complete_sentinel(
108
105
  self, sentinel: PartitionCompleteSentinel
@@ -115,16 +112,26 @@ class ConcurrentReadProcessor:
115
112
  """
116
113
  partition = sentinel.partition
117
114
 
118
- partitions_running = self._streams_to_running_partitions[partition.stream_name()]
119
- if partition in partitions_running:
120
- partitions_running.remove(partition)
121
- # If all partitions were generated and this was the last one, the stream is done
122
- if (
123
- partition.stream_name() not in self._streams_currently_generating_partitions
124
- and len(partitions_running) == 0
125
- ):
126
- yield from self._on_stream_is_done(partition.stream_name())
127
- yield from self._message_repository.consume_queue()
115
+ try:
116
+ if sentinel.is_successful:
117
+ stream = self._stream_name_to_instance[partition.stream_name()]
118
+ stream.cursor.close_partition(partition)
119
+ except Exception as exception:
120
+ self._flag_exception(partition.stream_name(), exception)
121
+ yield AirbyteTracedException.from_exception(
122
+ exception, stream_descriptor=StreamDescriptor(name=partition.stream_name())
123
+ ).as_sanitized_airbyte_message()
124
+ finally:
125
+ partitions_running = self._streams_to_running_partitions[partition.stream_name()]
126
+ if partition in partitions_running:
127
+ partitions_running.remove(partition)
128
+ # If all partitions were generated and this was the last one, the stream is done
129
+ if (
130
+ partition.stream_name() not in self._streams_currently_generating_partitions
131
+ and len(partitions_running) == 0
132
+ ):
133
+ yield from self._on_stream_is_done(partition.stream_name())
134
+ yield from self._message_repository.consume_queue()
128
135
 
129
136
  def on_record(self, record: Record) -> Iterable[AirbyteMessage]:
130
137
  """
@@ -4,7 +4,7 @@
4
4
  import concurrent
5
5
  import logging
6
6
  from queue import Queue
7
- from typing import Iterable, Iterator, List, Optional
7
+ from typing import Iterable, Iterator, List
8
8
 
9
9
  from airbyte_cdk.models import AirbyteMessage
10
10
  from airbyte_cdk.sources.concurrent_source.concurrent_read_processor import ConcurrentReadProcessor
@@ -16,7 +16,7 @@ from airbyte_cdk.sources.concurrent_source.thread_pool_manager import ThreadPool
16
16
  from airbyte_cdk.sources.message import InMemoryMessageRepository, MessageRepository
17
17
  from airbyte_cdk.sources.streams.concurrent.abstract_stream import AbstractStream
18
18
  from airbyte_cdk.sources.streams.concurrent.partition_enqueuer import PartitionEnqueuer
19
- from airbyte_cdk.sources.streams.concurrent.partition_reader import PartitionLogger, PartitionReader
19
+ from airbyte_cdk.sources.streams.concurrent.partition_reader import PartitionReader
20
20
  from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
21
21
  from airbyte_cdk.sources.streams.concurrent.partitions.types import (
22
22
  PartitionCompleteSentinel,
@@ -43,7 +43,6 @@ class ConcurrentSource:
43
43
  logger: logging.Logger,
44
44
  slice_logger: SliceLogger,
45
45
  message_repository: MessageRepository,
46
- queue: Optional[Queue[QueueItem]] = None,
47
46
  timeout_seconds: int = DEFAULT_TIMEOUT_SECONDS,
48
47
  ) -> "ConcurrentSource":
49
48
  is_single_threaded = initial_number_of_partitions_to_generate == 1 and num_workers == 1
@@ -60,13 +59,12 @@ class ConcurrentSource:
60
59
  logger,
61
60
  )
62
61
  return ConcurrentSource(
63
- threadpool=threadpool,
64
- logger=logger,
65
- slice_logger=slice_logger,
66
- queue=queue,
67
- message_repository=message_repository,
68
- initial_number_partitions_to_generate=initial_number_of_partitions_to_generate,
69
- timeout_seconds=timeout_seconds,
62
+ threadpool,
63
+ logger,
64
+ slice_logger,
65
+ message_repository,
66
+ initial_number_of_partitions_to_generate,
67
+ timeout_seconds,
70
68
  )
71
69
 
72
70
  def __init__(
@@ -74,7 +72,6 @@ class ConcurrentSource:
74
72
  threadpool: ThreadPoolManager,
75
73
  logger: logging.Logger,
76
74
  slice_logger: SliceLogger = DebugSliceLogger(),
77
- queue: Optional[Queue[QueueItem]] = None,
78
75
  message_repository: MessageRepository = InMemoryMessageRepository(),
79
76
  initial_number_partitions_to_generate: int = 1,
80
77
  timeout_seconds: int = DEFAULT_TIMEOUT_SECONDS,
@@ -94,28 +91,25 @@ class ConcurrentSource:
94
91
  self._initial_number_partitions_to_generate = initial_number_partitions_to_generate
95
92
  self._timeout_seconds = timeout_seconds
96
93
 
97
- # We set a maxsize to for the main thread to process record items when the queue size grows. This assumes that there are less
98
- # threads generating partitions that than are max number of workers. If it weren't the case, we could have threads only generating
99
- # partitions which would fill the queue. This number is arbitrarily set to 10_000 but will probably need to be changed given more
100
- # information and might even need to be configurable depending on the source
101
- self._queue = queue or Queue(maxsize=10_000)
102
-
103
94
  def read(
104
95
  self,
105
96
  streams: List[AbstractStream],
106
97
  ) -> Iterator[AirbyteMessage]:
107
98
  self._logger.info("Starting syncing")
99
+
100
+ # We set a maxsize to for the main thread to process record items when the queue size grows. This assumes that there are less
101
+ # threads generating partitions that than are max number of workers. If it weren't the case, we could have threads only generating
102
+ # partitions which would fill the queue. This number is arbitrarily set to 10_000 but will probably need to be changed given more
103
+ # information and might even need to be configurable depending on the source
104
+ queue: Queue[QueueItem] = Queue(maxsize=10_000)
108
105
  concurrent_stream_processor = ConcurrentReadProcessor(
109
106
  streams,
110
- PartitionEnqueuer(self._queue, self._threadpool),
107
+ PartitionEnqueuer(queue, self._threadpool),
111
108
  self._threadpool,
112
109
  self._logger,
113
110
  self._slice_logger,
114
111
  self._message_repository,
115
- PartitionReader(
116
- self._queue,
117
- PartitionLogger(self._slice_logger, self._logger, self._message_repository),
118
- ),
112
+ PartitionReader(queue),
119
113
  )
120
114
 
121
115
  # Enqueue initial partition generation tasks
@@ -123,7 +117,7 @@ class ConcurrentSource:
123
117
 
124
118
  # Read from the queue until all partitions were generated and read
125
119
  yield from self._consume_from_queue(
126
- self._queue,
120
+ queue,
127
121
  concurrent_stream_processor,
128
122
  )
129
123
  self._threadpool.check_for_errors_and_shutdown()
@@ -147,10 +141,7 @@ class ConcurrentSource:
147
141
  airbyte_message_or_record_or_exception,
148
142
  concurrent_stream_processor,
149
143
  )
150
- # In the event that a partition raises an exception, anything remaining in
151
- # the queue will be missed because is_done() can raise an exception and exit
152
- # out of this loop before remaining items are consumed
153
- if queue.empty() and concurrent_stream_processor.is_done():
144
+ if concurrent_stream_processor.is_done() and queue.empty():
154
145
  # all partitions were generated and processed. we're done here
155
146
  break
156
147
 
@@ -170,7 +161,5 @@ class ConcurrentSource:
170
161
  yield from concurrent_stream_processor.on_partition_complete_sentinel(queue_item)
171
162
  elif isinstance(queue_item, Record):
172
163
  yield from concurrent_stream_processor.on_record(queue_item)
173
- elif isinstance(queue_item, AirbyteMessage):
174
- yield queue_item
175
164
  else:
176
165
  raise ValueError(f"Unknown queue item type: {type(queue_item)}")
@@ -3,11 +3,7 @@
3
3
  #
4
4
 
5
5
  import logging
6
- from dataclasses import dataclass, field
7
- from queue import Queue
8
- from typing import Any, ClassVar, Generic, Iterator, List, Mapping, MutableMapping, Optional, Tuple
9
-
10
- from airbyte_protocol_dataclasses.models import Level
6
+ from typing import Any, Generic, Iterator, List, Mapping, MutableMapping, Optional, Tuple
11
7
 
12
8
  from airbyte_cdk.models import (
13
9
  AirbyteCatalog,
@@ -52,8 +48,6 @@ from airbyte_cdk.sources.declarative.stream_slicers.declarative_partition_genera
52
48
  StreamSlicerPartitionGenerator,
53
49
  )
54
50
  from airbyte_cdk.sources.declarative.types import ConnectionDefinition
55
- from airbyte_cdk.sources.message.concurrent_repository import ConcurrentMessageRepository
56
- from airbyte_cdk.sources.message.repository import InMemoryMessageRepository, MessageRepository
57
51
  from airbyte_cdk.sources.source import TState
58
52
  from airbyte_cdk.sources.streams import Stream
59
53
  from airbyte_cdk.sources.streams.concurrent.abstract_stream import AbstractStream
@@ -64,22 +58,6 @@ from airbyte_cdk.sources.streams.concurrent.availability_strategy import (
64
58
  from airbyte_cdk.sources.streams.concurrent.cursor import ConcurrentCursor, FinalStateCursor
65
59
  from airbyte_cdk.sources.streams.concurrent.default_stream import DefaultStream
66
60
  from airbyte_cdk.sources.streams.concurrent.helpers import get_primary_key_from_stream
67
- from airbyte_cdk.sources.streams.concurrent.partitions.types import QueueItem
68
-
69
-
70
- @dataclass
71
- class TestLimits:
72
- __test__: ClassVar[bool] = False # Tell Pytest this is not a Pytest class, despite its name
73
-
74
- DEFAULT_MAX_PAGES_PER_SLICE: ClassVar[int] = 5
75
- DEFAULT_MAX_SLICES: ClassVar[int] = 5
76
- DEFAULT_MAX_RECORDS: ClassVar[int] = 100
77
- DEFAULT_MAX_STREAMS: ClassVar[int] = 100
78
-
79
- max_records: int = field(default=DEFAULT_MAX_RECORDS)
80
- max_pages_per_slice: int = field(default=DEFAULT_MAX_PAGES_PER_SLICE)
81
- max_slices: int = field(default=DEFAULT_MAX_SLICES)
82
- max_streams: int = field(default=DEFAULT_MAX_STREAMS)
83
61
 
84
62
 
85
63
  class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
@@ -95,9 +73,7 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
95
73
  source_config: ConnectionDefinition,
96
74
  debug: bool = False,
97
75
  emit_connector_builder_messages: bool = False,
98
- migrate_manifest: bool = False,
99
- normalize_manifest: bool = False,
100
- limits: Optional[TestLimits] = None,
76
+ component_factory: Optional[ModelToComponentFactory] = None,
101
77
  config_path: Optional[str] = None,
102
78
  **kwargs: Any,
103
79
  ) -> None:
@@ -105,40 +81,22 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
105
81
  # no longer needs to store the original incoming state. But maybe there's an edge case?
106
82
  self._connector_state_manager = ConnectorStateManager(state=state) # type: ignore # state is always in the form of List[AirbyteStateMessage]. The ConnectorStateManager should use generics, but this can be done later
107
83
 
108
- # We set a maxsize to for the main thread to process record items when the queue size grows. This assumes that there are less
109
- # threads generating partitions that than are max number of workers. If it weren't the case, we could have threads only generating
110
- # partitions which would fill the queue. This number is arbitrarily set to 10_000 but will probably need to be changed given more
111
- # information and might even need to be configurable depending on the source
112
- queue: Queue[QueueItem] = Queue(maxsize=10_000)
113
- message_repository = InMemoryMessageRepository(
114
- Level.DEBUG if emit_connector_builder_messages else Level.INFO
115
- )
116
-
117
84
  # To reduce the complexity of the concurrent framework, we are not enabling RFR with synthetic
118
85
  # cursors. We do this by no longer automatically instantiating RFR cursors when converting
119
86
  # the declarative models into runtime components. Concurrent sources will continue to checkpoint
120
87
  # incremental streams running in full refresh.
121
- component_factory = ModelToComponentFactory(
88
+ component_factory = component_factory or ModelToComponentFactory(
122
89
  emit_connector_builder_messages=emit_connector_builder_messages,
123
90
  disable_resumable_full_refresh=True,
124
- message_repository=ConcurrentMessageRepository(queue, message_repository),
125
91
  connector_state_manager=self._connector_state_manager,
126
92
  max_concurrent_async_job_count=source_config.get("max_concurrent_async_job_count"),
127
- limit_pages_fetched_per_slice=limits.max_pages_per_slice if limits else None,
128
- limit_slices_fetched=limits.max_slices if limits else None,
129
- disable_retries=True if limits else False,
130
- disable_cache=True if limits else False,
131
93
  )
132
94
 
133
- self._limits = limits
134
-
135
95
  super().__init__(
136
96
  source_config=source_config,
137
97
  config=config,
138
98
  debug=debug,
139
99
  emit_connector_builder_messages=emit_connector_builder_messages,
140
- migrate_manifest=migrate_manifest,
141
- normalize_manifest=normalize_manifest,
142
100
  component_factory=component_factory,
143
101
  config_path=config_path,
144
102
  )
@@ -168,7 +126,6 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
168
126
  initial_number_of_partitions_to_generate=initial_number_of_partitions_to_generate,
169
127
  logger=self.logger,
170
128
  slice_logger=self._slice_logger,
171
- queue=queue,
172
129
  message_repository=self.message_repository,
173
130
  )
174
131
 
@@ -330,9 +287,6 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
330
287
  self.message_repository,
331
288
  ),
332
289
  stream_slicer=declarative_stream.retriever.stream_slicer,
333
- slice_limit=self._limits.max_slices
334
- if self._limits
335
- else None, # technically not needed because create_declarative_stream() -> create_simple_retriever() will apply the decorator. But for consistency and depending how we build create_default_stream, this may be needed later
336
290
  )
337
291
  else:
338
292
  if (
@@ -364,7 +318,6 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
364
318
  self.message_repository,
365
319
  ),
366
320
  stream_slicer=cursor,
367
- slice_limit=self._limits.max_slices if self._limits else None,
368
321
  )
369
322
 
370
323
  concurrent_streams.append(
@@ -396,9 +349,6 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
396
349
  self.message_repository,
397
350
  ),
398
351
  declarative_stream.retriever.stream_slicer,
399
- slice_limit=self._limits.max_slices
400
- if self._limits
401
- else None, # technically not needed because create_declarative_stream() -> create_simple_retriever() will apply the decorator. But for consistency and depending how we build create_default_stream, this may be needed later
402
352
  )
403
353
 
404
354
  final_state_cursor = FinalStateCursor(
@@ -460,7 +410,6 @@ class ConcurrentDeclarativeSource(ManifestDeclarativeSource, Generic[TState]):
460
410
  self.message_repository,
461
411
  ),
462
412
  perpartition_cursor,
463
- slice_limit=self._limits.max_slices if self._limits else None,
464
413
  )
465
414
 
466
415
  concurrent_streams.append(
@@ -622,10 +622,6 @@ SCHEMA_TRANSFORMER_TYPE_MAPPING = {
622
622
  SchemaNormalizationModel.Default: TransformConfig.DefaultSchemaNormalization,
623
623
  }
624
624
 
625
- # Ideally this should use the value defined in ConcurrentDeclarativeSource, but
626
- # this would be a circular import
627
- MAX_SLICES = 5
628
-
629
625
 
630
626
  class ModelToComponentFactory:
631
627
  EPOCH_DATETIME_FORMAT = "%s"
@@ -159,14 +159,7 @@ class DefaultPaginator(Paginator):
159
159
  ) -> Optional[str]:
160
160
  token = next_page_token.get("next_page_token") if next_page_token else None
161
161
  if token and self.page_token_option and isinstance(self.page_token_option, RequestPath):
162
- # make additional interpolation context
163
- interpolation_context = get_interpolation_context(
164
- stream_state=stream_state,
165
- stream_slice=stream_slice,
166
- next_page_token=next_page_token,
167
- )
168
- # Replace url base to only return the path
169
- return str(token).replace(self.url_base.eval(self.config, **interpolation_context), "") # type: ignore # url_base is casted to a InterpolatedString in __post_init__
162
+ return str(token)
170
163
  else:
171
164
  return None
172
165
 
@@ -1,11 +1,8 @@
1
- # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
1
+ # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
2
2
 
3
- from typing import Any, Iterable, Mapping, Optional, cast
3
+ from typing import Any, Iterable, Mapping, Optional
4
4
 
5
5
  from airbyte_cdk.sources.declarative.retrievers import Retriever
6
- from airbyte_cdk.sources.declarative.stream_slicers.stream_slicer_test_read_decorator import (
7
- StreamSlicerTestReadDecorator,
8
- )
9
6
  from airbyte_cdk.sources.message import MessageRepository
10
7
  from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
11
8
  from airbyte_cdk.sources.streams.concurrent.partitions.partition_generator import PartitionGenerator
@@ -86,23 +83,10 @@ class DeclarativePartition(Partition):
86
83
 
87
84
  class StreamSlicerPartitionGenerator(PartitionGenerator):
88
85
  def __init__(
89
- self,
90
- partition_factory: DeclarativePartitionFactory,
91
- stream_slicer: StreamSlicer,
92
- slice_limit: Optional[int] = None,
86
+ self, partition_factory: DeclarativePartitionFactory, stream_slicer: StreamSlicer
93
87
  ) -> None:
94
88
  self._partition_factory = partition_factory
95
-
96
- if slice_limit:
97
- self._stream_slicer = cast(
98
- StreamSlicer,
99
- StreamSlicerTestReadDecorator(
100
- wrapped_slicer=stream_slicer,
101
- maximum_number_of_slices=slice_limit,
102
- ),
103
- )
104
- else:
105
- self._stream_slicer = stream_slicer
89
+ self._stream_slicer = stream_slicer
106
90
 
107
91
  def generate(self) -> Iterable[Partition]:
108
92
  for stream_slice in self._stream_slicer.stream_slices():
@@ -4,10 +4,10 @@
4
4
 
5
5
  from dataclasses import dataclass
6
6
  from itertools import islice
7
- from typing import Any, Iterable
7
+ from typing import Any, Iterable, Mapping, Optional, Union
8
8
 
9
9
  from airbyte_cdk.sources.streams.concurrent.partitions.stream_slicer import StreamSlicer
10
- from airbyte_cdk.sources.types import StreamSlice
10
+ from airbyte_cdk.sources.types import StreamSlice, StreamState
11
11
 
12
12
 
13
13
  @dataclass
@@ -1,45 +1,14 @@
1
- # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
-
3
- import logging
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
4
  from queue import Queue
5
- from typing import Optional
6
5
 
7
6
  from airbyte_cdk.sources.concurrent_source.stream_thread_exception import StreamThreadException
8
- from airbyte_cdk.sources.message.repository import MessageRepository
9
- from airbyte_cdk.sources.streams.concurrent.cursor import Cursor
10
7
  from airbyte_cdk.sources.streams.concurrent.partitions.partition import Partition
11
8
  from airbyte_cdk.sources.streams.concurrent.partitions.types import (
12
9
  PartitionCompleteSentinel,
13
10
  QueueItem,
14
11
  )
15
- from airbyte_cdk.sources.utils.slice_logger import SliceLogger
16
-
17
-
18
- # Since moving all the connector builder workflow to the concurrent CDK which required correct ordering
19
- # of grouping log messages onto the main write thread using the ConcurrentMessageRepository, this
20
- # separate flow and class that was used to log slices onto this partition's message_repository
21
- # should just be replaced by emitting messages directly onto the repository instead of an intermediary.
22
- class PartitionLogger:
23
- """
24
- Helper class that provides a mechanism for passing a log message onto the current
25
- partitions message repository
26
- """
27
-
28
- def __init__(
29
- self,
30
- slice_logger: SliceLogger,
31
- logger: logging.Logger,
32
- message_repository: MessageRepository,
33
- ):
34
- self._slice_logger = slice_logger
35
- self._logger = logger
36
- self._message_repository = message_repository
37
-
38
- def log(self, partition: Partition) -> None:
39
- if self._slice_logger.should_log_slice_message(self._logger):
40
- self._message_repository.emit_message(
41
- self._slice_logger.create_slice_log_message(partition.to_slice())
42
- )
43
12
 
44
13
 
45
14
  class PartitionReader:
@@ -49,16 +18,13 @@ class PartitionReader:
49
18
 
50
19
  _IS_SUCCESSFUL = True
51
20
 
52
- def __init__(
53
- self, queue: Queue[QueueItem], partition_logger: Optional[PartitionLogger] = None
54
- ) -> None:
21
+ def __init__(self, queue: Queue[QueueItem]) -> None:
55
22
  """
56
23
  :param queue: The queue to put the records in.
57
24
  """
58
25
  self._queue = queue
59
- self._partition_logger = partition_logger
60
26
 
61
- def process_partition(self, partition: Partition, cursor: Cursor) -> None:
27
+ def process_partition(self, partition: Partition) -> None:
62
28
  """
63
29
  Process a partition and put the records in the output queue.
64
30
  When all the partitions are added to the queue, a sentinel is added to the queue to indicate that all the partitions have been generated.
@@ -71,13 +37,8 @@ class PartitionReader:
71
37
  :return: None
72
38
  """
73
39
  try:
74
- if self._partition_logger:
75
- self._partition_logger.log(partition)
76
-
77
40
  for record in partition.read():
78
41
  self._queue.put(record)
79
- cursor.observe(record)
80
- cursor.close_partition(partition)
81
42
  self._queue.put(PartitionCompleteSentinel(partition, self._IS_SUCCESSFUL))
82
43
  except Exception as e:
83
44
  self._queue.put(StreamThreadException(e, partition.stream_name()))
@@ -4,7 +4,6 @@
4
4
 
5
5
  from typing import Any, Union
6
6
 
7
- from airbyte_cdk.models import AirbyteMessage
8
7
  from airbyte_cdk.sources.concurrent_source.partition_generation_completed_sentinel import (
9
8
  PartitionGenerationCompletedSentinel,
10
9
  )
@@ -35,10 +34,5 @@ class PartitionCompleteSentinel:
35
34
  Typedef representing the items that can be added to the ThreadBasedConcurrentStream
36
35
  """
37
36
  QueueItem = Union[
38
- Record,
39
- Partition,
40
- PartitionCompleteSentinel,
41
- PartitionGenerationCompletedSentinel,
42
- Exception,
43
- AirbyteMessage,
37
+ Record, Partition, PartitionCompleteSentinel, PartitionGenerationCompletedSentinel, Exception
44
38
  ]
@@ -11,10 +11,6 @@ from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, Level
11
11
  from airbyte_cdk.models import Type as MessageType
12
12
 
13
13
 
14
- # Once everything runs on the concurrent CDK and we've cleaned up the legacy flows, we should try to remove
15
- # this class and write messages directly to the message_repository instead of through the logger because for
16
- # cases like the connector builder where ordering of messages is important, using the logger can cause
17
- # messages to be grouped out of order. Alas work for a different day.
18
14
  class SliceLogger(ABC):
19
15
  """
20
16
  SliceLogger is an interface that allows us to log slices of data in a uniform way.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.60.7
3
+ Version: 6.60.9
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -15,11 +15,11 @@ airbyte_cdk/config_observation.py,sha256=7SSPxtN0nXPkm4euGNcTTr1iLbwUL01jy-24V1H
15
15
  airbyte_cdk/connector.py,sha256=N6TUlrZOMjLAI85JrNAKkfyTqnO5xfBCw4oEfgjJd9o,4254
16
16
  airbyte_cdk/connector_builder/README.md,sha256=Hw3wvVewuHG9-QgsAq1jDiKuLlStDxKBz52ftyNRnBw,1665
17
17
  airbyte_cdk/connector_builder/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
18
- airbyte_cdk/connector_builder/connector_builder_handler.py,sha256=jRtSfj3aca006-01Hax-THJpuoysd8QR6JPGnr8q1Xg,6371
19
- airbyte_cdk/connector_builder/main.py,sha256=F9bmdz252pvGXAdDgPwIOPw3fl5fwTU41uG49BQyItI,3883
18
+ airbyte_cdk/connector_builder/connector_builder_handler.py,sha256=ySszXKleG7IGtxkwu2q9jczcwAAhZziLVzNAKtUvGY8,6664
19
+ airbyte_cdk/connector_builder/main.py,sha256=j1pP5N8RsnvQZ4iYxhLdLEHsJ5Ui7IVFBUi6wYMGBkM,3839
20
20
  airbyte_cdk/connector_builder/models.py,sha256=9pIZ98LW_d6fRS39VdnUOf3cxGt4TkC5MJ0_OrzcCRk,1578
21
21
  airbyte_cdk/connector_builder/test_reader/__init__.py,sha256=iTwBMoI9vaJotEgpqZbFjlxRcbxXYypSVJ9YxeHk7wc,120
22
- airbyte_cdk/connector_builder/test_reader/helpers.py,sha256=5GSrK9EVBDm5dwtudVbA-73EHh53-niRA-oj8eQVFHI,29236
22
+ airbyte_cdk/connector_builder/test_reader/helpers.py,sha256=vqoHpZeQ0BLIw2NiTNGXr0euA8gI_X0pcNRcHOv8sHM,27942
23
23
  airbyte_cdk/connector_builder/test_reader/message_grouper.py,sha256=LDNl-xFQwA4RsUpn7684KbWaVH-SWWBIwhHvIgduLTE,7090
24
24
  airbyte_cdk/connector_builder/test_reader/reader.py,sha256=3jLy3tUUHkG1rmGWrZuo4SmPYNVD9oiAqy8mdaUwzvo,21301
25
25
  airbyte_cdk/connector_builder/test_reader/types.py,sha256=hPZG3jO03kBaPyW94NI3JHRS1jxXGSNBcN1HFzOxo5Y,2528
@@ -57,8 +57,8 @@ airbyte_cdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  airbyte_cdk/sources/__init__.py,sha256=45J83QsFH3Wky3sVapZWg4C58R_i1thm61M06t2c1AQ,1156
58
58
  airbyte_cdk/sources/abstract_source.py,sha256=50vxEBRByiNhT4WJkiFvgM-C6PWqKSJgvuNC_aeg2cw,15547
59
59
  airbyte_cdk/sources/concurrent_source/__init__.py,sha256=3D_RJsxQfiLboSCDdNei1Iv-msRp3DXsas6E9kl7dXc,386
60
- airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py,sha256=iEm3st8ds4n6_Bn8J2hLK3bQuE5J6ihUitIexX4ADGQ,12240
61
- airbyte_cdk/sources/concurrent_source/concurrent_source.py,sha256=Npc18v1ERfQyTjBwIDErTtoBtY11GMml5o_jU3ppRA0,8457
60
+ airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py,sha256=P_GA5QayzehCf0ksUbEbGoNixBnauzsepv-0ICzhH4w,12691
61
+ airbyte_cdk/sources/concurrent_source/concurrent_source.py,sha256=P8B6EcLKaSstfAD9kDZsTJ0q8vRmdFrxLt-zOA5_By0,7737
62
62
  airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py,sha256=f9PIRPWn2tXu0-bxVeYHL2vYdqCzZ_kgpHg5_Ep-cfQ,6103
63
63
  airbyte_cdk/sources/concurrent_source/partition_generation_completed_sentinel.py,sha256=z1t-rAZBsqVidv2fpUlPHE9JgyXsITuGk4AMu96mXSQ,696
64
64
  airbyte_cdk/sources/concurrent_source/stream_thread_exception.py,sha256=-q6mG2145HKQ28rZGD1bUmjPlIZ1S7-Yhewl8Ntu6xI,764
@@ -86,7 +86,7 @@ airbyte_cdk/sources/declarative/checks/check_stream.py,sha256=QeExVmpSYjr_CnghHu
86
86
  airbyte_cdk/sources/declarative/checks/connection_checker.py,sha256=MBRJo6WJlZQHpIfOGaNOkkHUmgUl_4wDM6VPo41z5Ss,1383
87
87
  airbyte_cdk/sources/declarative/concurrency_level/__init__.py,sha256=5XUqrmlstYlMM0j6crktlKQwALek0uiz2D3WdM46MyA,191
88
88
  airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py,sha256=YIwCTCpOr_QSNW4ltQK0yUGWInI8PKNY216HOOegYLk,2101
89
- airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=ntVyaOmYt3b7rFUjmloDwE2Vwr9kAWMr1HryZicEnwY,30101
89
+ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=IwKlf20G5C4j-am9FrLhRN0qv61A5rU097xPnnFmt5U,27022
90
90
  airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
91
91
  airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=_zGNGq31RNy_0QBLt_EcTvgPyhj7urPdx6oA3M5-r3o,3150
92
92
  airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
@@ -141,7 +141,7 @@ airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=wnRUP0Xeru9R
141
141
  airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=2UdpCz3yi7ISZTyqkQXSSy3dMxeyOWqV7OlAS5b9GVg,11568
142
142
  airbyte_cdk/sources/declarative/parsers/manifest_normalizer.py,sha256=EtKjS9c94yNp3AwQC8KUCQaAYW5T3zvFYxoWYjc_buI,19729
143
143
  airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=pJmg78vqE5VfUrF_KJnWjucQ4k9IWFULeAxHCowrHXE,6806
144
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=ONwdHGA5j4g-Iv2LRzcpXXpHf1Zm9ofgLcXYDyDadYk,181582
144
+ airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=zr4oBy1MCbsJtOf4gffEQOFo5wX5oq9lhIpVUmyNJoE,181452
145
145
  airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=TBC9AkGaUqHm2IKHMPN6punBIcY5tWGULowcLoAVkfw,1109
146
146
  airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=VelO7zKqKtzMJ35jyFeg0ypJLQC0plqqIBNXoBW1G2E,3001
147
147
  airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
@@ -168,7 +168,7 @@ airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.p
168
168
  airbyte_cdk/sources/declarative/requesters/http_job_repository.py,sha256=pVzIDdfGs1eAZo9F6zeFYKlEmEqanhNvZLKFCHkdmNo,14348
169
169
  airbyte_cdk/sources/declarative/requesters/http_requester.py,sha256=cktdjnOu-o98smdCdrWC361iWNamPTZ-csT32OFh00c,18920
170
170
  airbyte_cdk/sources/declarative/requesters/paginators/__init__.py,sha256=uArbKs9JKNCt7t9tZoeWwjDpyI1HoPp29FNW0JzvaEM,644
171
- airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py,sha256=SB-Af3CRb4mJwhm4EKNxzl_PK2w5QS4tqrSNNMO2IV4,12760
171
+ airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py,sha256=kH_gwgULvEr8na-sST93gpr5pgqk8oT2aZNPebyMqlc,12293
172
172
  airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py,sha256=b1-zKxYOUMHn7ahdWpzKEzfG4A7s_WQWy-vzRqZWzME,2152
173
173
  airbyte_cdk/sources/declarative/requesters/paginators/paginator.py,sha256=TzJF1Q-CFlsHF9lMSfmnGCxRYm9_UQCmBcHYQpc7F30,2376
174
174
  airbyte_cdk/sources/declarative/requesters/paginators/strategies/__init__.py,sha256=2gly8fuZpDNwtu1Qg6oE2jBLGqQRdzSLJdnpk_iDV6I,767
@@ -220,9 +220,9 @@ airbyte_cdk/sources/declarative/schema/schema_loader.py,sha256=kjt8v0N5wWKA5zyLn
220
220
  airbyte_cdk/sources/declarative/spec/__init__.py,sha256=9FYO-fVOclrwjAW4qwRTbZRVopTc9rOaauAJfThdNCQ,177
221
221
  airbyte_cdk/sources/declarative/spec/spec.py,sha256=SwL_pfXZgcLYLJY-MAeFMHug9oYh2tOWjgG0C3DoLOY,3602
222
222
  airbyte_cdk/sources/declarative/stream_slicers/__init__.py,sha256=UX-cP_C-9FIFFPL9z8nuxu_rglssRsMOqQmQHN8FLB8,341
223
- airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py,sha256=GTG1nqXVxbT7wnU-Q9hftlcWAqj-y4zR2KCnLWNIcv4,4084
223
+ airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py,sha256=cjKGm4r438dd1GxrFHJ4aYrdzG2bkncnwaWxAwlXR3M,3585
224
224
  airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py,sha256=SOkIPBi2Wu7yxIvA15yFzUAB95a3IzA8LPq5DEqHQQc,725
225
- airbyte_cdk/sources/declarative/stream_slicers/stream_slicer_test_read_decorator.py,sha256=4vit5ADyhoZnd1psRVeM5jdySYzhjwspLVXxh8vt1M8,944
225
+ airbyte_cdk/sources/declarative/stream_slicers/stream_slicer_test_read_decorator.py,sha256=aUSleOw9elq3-5TaDUvp7H8W-2qUKqpr__kaJd8-ZFA,983
226
226
  airbyte_cdk/sources/declarative/transformations/__init__.py,sha256=CPJ8TlMpiUmvG3624VYu_NfTzxwKcfBjM2Q2wJ7fkSA,919
227
227
  airbyte_cdk/sources/declarative/transformations/add_fields.py,sha256=Eg1jQtRObgzxbtySTQs5uEZIjEklsoHFxYSPf78x6Ng,5420
228
228
  airbyte_cdk/sources/declarative/transformations/config_transformations/__init__.py,sha256=GaU3ezFa5opeDgdlNohX6TXsWJlOD2jOfJXQWeQCh7E,263
@@ -300,7 +300,6 @@ airbyte_cdk/sources/file_based/types.py,sha256=INxG7OPnkdUP69oYNKMAbwhvV1AGvLRHs
300
300
  airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZujIS0,730
301
301
  airbyte_cdk/sources/http_logger.py,sha256=H93kPAujHhPmXNX0JSFG3D-SL6yEFA5PtKot9Hu3TYA,1690
302
302
  airbyte_cdk/sources/message/__init__.py,sha256=y98fzHsQBwXwp2zEa4K5mxGFqjnx9lDn9O0pTk-VS4U,395
303
- airbyte_cdk/sources/message/concurrent_repository.py,sha256=axSiL2Rm1z6ENtx_ZZ7B_UfLNesGX-lBMQLJvIlmCN4,1999
304
303
  airbyte_cdk/sources/message/repository.py,sha256=SG7avgti_-dj8FcRHTTrhgLLGJbElv14_zIB0SH8AIc,4763
305
304
  airbyte_cdk/sources/source.py,sha256=KIBBH5VLEb8BZ8B9aROlfaI6OLoJqKDPMJ10jkAR7nk,3611
306
305
  airbyte_cdk/sources/specs/transfer_modes.py,sha256=sfSVO0yT6SaGKN5_TP0Nl_ftG0yPhecaBv0WkhAEXA8,932
@@ -326,12 +325,12 @@ airbyte_cdk/sources/streams/concurrent/default_stream.py,sha256=3SBjFa1z955pSE_2
326
325
  airbyte_cdk/sources/streams/concurrent/exceptions.py,sha256=JOZ446MCLpmF26r9KfS6OO_6rGjcjgJNZdcw6jccjEI,468
327
326
  airbyte_cdk/sources/streams/concurrent/helpers.py,sha256=S6AW8TgIASCZ2UuUcQLE8OzgYUHWt2-KPOvNPwnQf-Q,1596
328
327
  airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py,sha256=2t64b_z9cEPmlHZnjSiMTO8PEtEdiAJDG0JcYOtUqAE,3363
329
- airbyte_cdk/sources/streams/concurrent/partition_reader.py,sha256=0QKCJYBuCqiuHp0GqcOTvzsJS1Sp7qTNfjXESvs71Vk,3417
328
+ airbyte_cdk/sources/streams/concurrent/partition_reader.py,sha256=0TIrjbTzYJGdA0AZUzbeIKr0iHbawnoEKVl7bWxOFZY,1760
330
329
  airbyte_cdk/sources/streams/concurrent/partitions/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
331
330
  airbyte_cdk/sources/streams/concurrent/partitions/partition.py,sha256=CmaRcKn8y118No3qvbRV9DBeAUKv17lrVgloR4Y9TwU,1490
332
331
  airbyte_cdk/sources/streams/concurrent/partitions/partition_generator.py,sha256=_ymkkBr71_qt1fW0_MUqw96OfNBkeJngXQ09yolEDHw,441
333
332
  airbyte_cdk/sources/streams/concurrent/partitions/stream_slicer.py,sha256=zQPikLIt0yhP9EwZaPglRTIqFCauo4pSsJk_7kYq9Aw,1406
334
- airbyte_cdk/sources/streams/concurrent/partitions/types.py,sha256=EdPHmMcyZhRYtO2cniIQAQpzPfGCBpzmAJ3NTVS4qbo,1249
333
+ airbyte_cdk/sources/streams/concurrent/partitions/types.py,sha256=frPVvHtY7vLxpGEbMQzNvF1Y52ZVyct9f1DDhGoRjwY,1166
335
334
  airbyte_cdk/sources/streams/concurrent/state_converters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
336
335
  airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py,sha256=CCxCbgvUugxiWpHX8-dkkJHWKDjL5iwiIbOUj8KIJ9c,7079
337
336
  airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py,sha256=x8MLm1pTMfLNHvMF3P1ixYkYt_xjpbaIwnvhY_ofdBo,8076
@@ -365,7 +364,7 @@ airbyte_cdk/sources/utils/casing.py,sha256=QC-gV1O4e8DR4-bhdXieUPKm_JamzslVyfABL
365
364
  airbyte_cdk/sources/utils/files_directory.py,sha256=z8Dmr-wkL1sAqdwCST4MBUFAyMHPD2cJIzVdAuCynp8,391
366
365
  airbyte_cdk/sources/utils/record_helper.py,sha256=7wL-pDYrBpcmZHa8ORtiSOqBZJEZI5hdl2dA1RYiatk,2029
367
366
  airbyte_cdk/sources/utils/schema_helpers.py,sha256=bR3I70-e11S6B8r6VK-pthQXtcYrXojgXFvuK7lRrpg,8545
368
- airbyte_cdk/sources/utils/slice_logger.py,sha256=M1TvcYGMftXR841XdJmeEpKpQqrdOD5X-qsspfAMKMs,2168
367
+ airbyte_cdk/sources/utils/slice_logger.py,sha256=qWWeFLAvigFz0b4O1_O3QDM1cy8PqZAMMgVPR2hEeb8,1778
369
368
  airbyte_cdk/sources/utils/transform.py,sha256=0LOvIJg1vmg_70AiAVe-YHMr-LHrqEuxg9cm1BnYPDM,11725
370
369
  airbyte_cdk/sources/utils/types.py,sha256=41ZQR681t5TUnOScij58d088sb99klH_ZENFcaYro_g,175
371
370
  airbyte_cdk/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -425,9 +424,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
425
424
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
426
425
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
427
426
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
428
- airbyte_cdk-6.60.7.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
429
- airbyte_cdk-6.60.7.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
430
- airbyte_cdk-6.60.7.dist-info/METADATA,sha256=8UFDCTLKAEkz9_HGFQhGgKVW3I0w-oPneO4SHQIHU24,6477
431
- airbyte_cdk-6.60.7.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
432
- airbyte_cdk-6.60.7.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
433
- airbyte_cdk-6.60.7.dist-info/RECORD,,
427
+ airbyte_cdk-6.60.9.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
428
+ airbyte_cdk-6.60.9.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
429
+ airbyte_cdk-6.60.9.dist-info/METADATA,sha256=h5XYBhKyWQd7RWvXHD3cNlc2IozoRmWNwreYxK-dzc0,6477
430
+ airbyte_cdk-6.60.9.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
431
+ airbyte_cdk-6.60.9.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
432
+ airbyte_cdk-6.60.9.dist-info/RECORD,,
@@ -1,43 +0,0 @@
1
- # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
-
3
- from queue import Queue
4
- from typing import Callable, Iterable
5
-
6
- from airbyte_cdk.models import AirbyteMessage, Level
7
- from airbyte_cdk.sources.message.repository import LogMessage, MessageRepository
8
- from airbyte_cdk.sources.streams.concurrent.partitions.types import QueueItem
9
-
10
-
11
- class ConcurrentMessageRepository(MessageRepository):
12
- """
13
- Message repository that immediately loads messages onto the queue processed on the
14
- main thread. This ensures that messages are processed in the correct order they are
15
- received. The InMemoryMessageRepository implementation does not have guaranteed
16
- ordering since whether to process the main thread vs. partitions is non-deterministic
17
- and there can be a lag between reading the main-thread and consuming messages on the
18
- MessageRepository.
19
-
20
- This is particularly important for the connector builder which relies on grouping
21
- of messages to organize request/response, pages, and partitions.
22
- """
23
-
24
- def __init__(self, queue: Queue[QueueItem], message_repository: MessageRepository):
25
- self._queue = queue
26
- self._decorated_message_repository = message_repository
27
-
28
- def emit_message(self, message: AirbyteMessage) -> None:
29
- self._decorated_message_repository.emit_message(message)
30
- for message in self._decorated_message_repository.consume_queue():
31
- self._queue.put(message)
32
-
33
- def log_message(self, level: Level, message_provider: Callable[[], LogMessage]) -> None:
34
- self._decorated_message_repository.log_message(level, message_provider)
35
- for message in self._decorated_message_repository.consume_queue():
36
- self._queue.put(message)
37
-
38
- def consume_queue(self) -> Iterable[AirbyteMessage]:
39
- """
40
- This method shouldn't need to be called because as part of emit_message() we are already
41
- loading messages onto the queue processed on the main thread.
42
- """
43
- yield from []