airbyte-cdk 0.67.0__py3-none-any.whl → 0.67.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airbyte_cdk/sources/abstract_source.py +30 -69
- airbyte_cdk/sources/connector_state_manager.py +12 -26
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +552 -524
- airbyte_cdk/sources/file_based/config/csv_format.py +2 -0
- airbyte_cdk/sources/file_based/file_types/parquet_parser.py +32 -14
- airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +3 -19
- airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +1 -3
- airbyte_cdk/sources/streams/__init__.py +2 -2
- airbyte_cdk/sources/streams/concurrent/adapters.py +3 -19
- airbyte_cdk/sources/streams/concurrent/cursor.py +1 -3
- airbyte_cdk/sources/streams/core.py +36 -34
- {airbyte_cdk-0.67.0.dist-info → airbyte_cdk-0.67.2.dist-info}/METADATA +3 -2
- {airbyte_cdk-0.67.0.dist-info → airbyte_cdk-0.67.2.dist-info}/RECORD +31 -31
- unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py +2 -1
- unit_tests/sources/file_based/config/test_csv_format.py +6 -1
- unit_tests/sources/file_based/file_types/test_parquet_parser.py +51 -6
- unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py +139 -199
- unit_tests/sources/file_based/scenarios/incremental_scenarios.py +91 -133
- unit_tests/sources/file_based/stream/concurrent/test_adapters.py +2 -13
- unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py +2 -2
- unit_tests/sources/file_based/test_scenarios.py +2 -2
- unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py +9 -9
- unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py +5 -5
- unit_tests/sources/streams/concurrent/test_adapters.py +2 -13
- unit_tests/sources/streams/test_stream_read.py +221 -11
- unit_tests/sources/test_abstract_source.py +142 -130
- unit_tests/sources/test_connector_state_manager.py +3 -124
- unit_tests/sources/test_source.py +18 -14
- {airbyte_cdk-0.67.0.dist-info → airbyte_cdk-0.67.2.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-0.67.0.dist-info → airbyte_cdk-0.67.2.dist-info}/WHEEL +0 -0
- {airbyte_cdk-0.67.0.dist-info → airbyte_cdk-0.67.2.dist-info}/top_level.txt +0 -0
| @@ -5,7 +5,7 @@ | |
| 5 5 | 
             
            import json
         | 
| 6 6 | 
             
            import logging
         | 
| 7 7 | 
             
            import os
         | 
| 8 | 
            -
            from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple
         | 
| 8 | 
            +
            from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, Union
         | 
| 9 9 | 
             
            from urllib.parse import unquote
         | 
| 10 10 |  | 
| 11 11 | 
             
            import pyarrow as pa
         | 
| @@ -16,7 +16,7 @@ from airbyte_cdk.sources.file_based.file_based_stream_reader import AbstractFile | |
| 16 16 | 
             
            from airbyte_cdk.sources.file_based.file_types.file_type_parser import FileTypeParser
         | 
| 17 17 | 
             
            from airbyte_cdk.sources.file_based.remote_file import RemoteFile
         | 
| 18 18 | 
             
            from airbyte_cdk.sources.file_based.schema_helpers import SchemaType
         | 
| 19 | 
            -
            from pyarrow import Scalar
         | 
| 19 | 
            +
            from pyarrow import DictionaryArray, Scalar
         | 
| 20 20 |  | 
| 21 21 |  | 
| 22 22 | 
             
            class ParquetParser(FileTypeParser):
         | 
| @@ -95,10 +95,23 @@ class ParquetParser(FileTypeParser): | |
| 95 95 | 
             
                    return FileReadMode.READ_BINARY
         | 
| 96 96 |  | 
| 97 97 | 
             
                @staticmethod
         | 
| 98 | 
            -
                def _to_output_value(parquet_value: Scalar, parquet_format: ParquetFormat) -> Any:
         | 
| 98 | 
            +
                def _to_output_value(parquet_value: Union[Scalar, DictionaryArray], parquet_format: ParquetFormat) -> Any:
         | 
| 99 | 
            +
                    """
         | 
| 100 | 
            +
                    Convert an entry in a pyarrow table to a value that can be output by the source.
         | 
| 101 | 
            +
                    """
         | 
| 102 | 
            +
                    if isinstance(parquet_value, DictionaryArray):
         | 
| 103 | 
            +
                        return ParquetParser._dictionary_array_to_python_value(parquet_value)
         | 
| 104 | 
            +
                    else:
         | 
| 105 | 
            +
                        return ParquetParser._scalar_to_python_value(parquet_value, parquet_format)
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                @staticmethod
         | 
| 108 | 
            +
                def _scalar_to_python_value(parquet_value: Scalar, parquet_format: ParquetFormat) -> Any:
         | 
| 99 109 | 
             
                    """
         | 
| 100 110 | 
             
                    Convert a pyarrow scalar to a value that can be output by the source.
         | 
| 101 111 | 
             
                    """
         | 
| 112 | 
            +
                    if parquet_value.as_py() is None:
         | 
| 113 | 
            +
                        return None
         | 
| 114 | 
            +
             | 
| 102 115 | 
             
                    # Convert date and datetime objects to isoformat strings
         | 
| 103 116 | 
             
                    if pa.types.is_time(parquet_value.type) or pa.types.is_timestamp(parquet_value.type) or pa.types.is_date(parquet_value.type):
         | 
| 104 117 | 
             
                        return parquet_value.as_py().isoformat()
         | 
| @@ -109,23 +122,14 @@ class ParquetParser(FileTypeParser): | |
| 109 122 |  | 
| 110 123 | 
             
                    # Decode binary strings to utf-8
         | 
| 111 124 | 
             
                    if ParquetParser._is_binary(parquet_value.type):
         | 
| 112 | 
            -
                         | 
| 113 | 
            -
             | 
| 114 | 
            -
                            return py_value
         | 
| 115 | 
            -
                        return py_value.decode("utf-8")
         | 
| 125 | 
            +
                        return parquet_value.as_py().decode("utf-8")
         | 
| 126 | 
            +
             | 
| 116 127 | 
             
                    if pa.types.is_decimal(parquet_value.type):
         | 
| 117 128 | 
             
                        if parquet_format.decimal_as_float:
         | 
| 118 129 | 
             
                            return parquet_value.as_py()
         | 
| 119 130 | 
             
                        else:
         | 
| 120 131 | 
             
                            return str(parquet_value.as_py())
         | 
| 121 132 |  | 
| 122 | 
            -
                    # Dictionaries are stored as two columns: indices and values
         | 
| 123 | 
            -
                    # The indices column is an array of integers that maps to the values column
         | 
| 124 | 
            -
                    if pa.types.is_dictionary(parquet_value.type):
         | 
| 125 | 
            -
                        return {
         | 
| 126 | 
            -
                            "indices": parquet_value.indices.tolist(),
         | 
| 127 | 
            -
                            "values": parquet_value.dictionary.tolist(),
         | 
| 128 | 
            -
                        }
         | 
| 129 133 | 
             
                    if pa.types.is_map(parquet_value.type):
         | 
| 130 134 | 
             
                        return {k: v for k, v in parquet_value.as_py()}
         | 
| 131 135 |  | 
| @@ -149,6 +153,20 @@ class ParquetParser(FileTypeParser): | |
| 149 153 | 
             
                    else:
         | 
| 150 154 | 
             
                        return parquet_value.as_py()
         | 
| 151 155 |  | 
| 156 | 
            +
                @staticmethod
         | 
| 157 | 
            +
                def _dictionary_array_to_python_value(parquet_value: DictionaryArray) -> Dict[str, Any]:
         | 
| 158 | 
            +
                    """
         | 
| 159 | 
            +
                    Convert a pyarrow dictionary array to a value that can be output by the source.
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    Dictionaries are stored as two columns: indices and values
         | 
| 162 | 
            +
                    The indices column is an array of integers that maps to the values column
         | 
| 163 | 
            +
                    """
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                    return {
         | 
| 166 | 
            +
                        "indices": parquet_value.indices.tolist(),
         | 
| 167 | 
            +
                        "values": parquet_value.dictionary.tolist(),
         | 
| 168 | 
            +
                    }
         | 
| 169 | 
            +
             | 
| 152 170 | 
             
                @staticmethod
         | 
| 153 171 | 
             
                def parquet_type_to_schema_type(parquet_type: pa.DataType, parquet_format: ParquetFormat) -> Mapping[str, str]:
         | 
| 154 172 | 
             
                    """
         | 
| @@ -7,7 +7,7 @@ import logging | |
| 7 7 | 
             
            from functools import lru_cache
         | 
| 8 8 | 
             
            from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, MutableMapping, Optional, Union
         | 
| 9 9 |  | 
| 10 | 
            -
            from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, Level, SyncMode, Type
         | 
| 10 | 
            +
            from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, ConfiguredAirbyteStream, Level, SyncMode, Type
         | 
| 11 11 | 
             
            from airbyte_cdk.sources import AbstractSource
         | 
| 12 12 | 
             
            from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
         | 
| 13 13 | 
             
            from airbyte_cdk.sources.file_based.availability_strategy import (
         | 
| @@ -156,29 +156,13 @@ class FileBasedStreamFacade(AbstractStreamFacade[DefaultStream], AbstractFileBas | |
| 156 156 | 
             
                def get_underlying_stream(self) -> DefaultStream:
         | 
| 157 157 | 
             
                    return self._abstract_stream
         | 
| 158 158 |  | 
| 159 | 
            -
                def  | 
| 159 | 
            +
                def read(
         | 
| 160 160 | 
             
                    self,
         | 
| 161 | 
            -
                     | 
| 162 | 
            -
                    logger: logging.Logger,
         | 
| 163 | 
            -
                    slice_logger: SliceLogger,
         | 
| 164 | 
            -
                ) -> Iterable[StreamData]:
         | 
| 165 | 
            -
                    """
         | 
| 166 | 
            -
                    Read full refresh. Delegate to the underlying AbstractStream, ignoring all the parameters
         | 
| 167 | 
            -
                    :param cursor_field: (ignored)
         | 
| 168 | 
            -
                    :param logger: (ignored)
         | 
| 169 | 
            -
                    :param slice_logger: (ignored)
         | 
| 170 | 
            -
                    :return: Iterable of StreamData
         | 
| 171 | 
            -
                    """
         | 
| 172 | 
            -
                    yield from self._read_records()
         | 
| 173 | 
            -
             | 
| 174 | 
            -
                def read_incremental(
         | 
| 175 | 
            -
                    self,
         | 
| 176 | 
            -
                    cursor_field: Optional[List[str]],
         | 
| 161 | 
            +
                    configured_stream: ConfiguredAirbyteStream,
         | 
| 177 162 | 
             
                    logger: logging.Logger,
         | 
| 178 163 | 
             
                    slice_logger: SliceLogger,
         | 
| 179 164 | 
             
                    stream_state: MutableMapping[str, Any],
         | 
| 180 165 | 
             
                    state_manager: ConnectorStateManager,
         | 
| 181 | 
            -
                    per_stream_state_enabled: bool,
         | 
| 182 166 | 
             
                    internal_config: InternalConfig,
         | 
| 183 167 | 
             
                ) -> Iterable[StreamData]:
         | 
| 184 168 | 
             
                    yield from self._read_records()
         | 
| @@ -155,9 +155,7 @@ class FileBasedConcurrentCursor(AbstractConcurrentFileBasedCursor): | |
| 155 155 | 
             
                            self._stream_namespace,
         | 
| 156 156 | 
             
                            new_state,
         | 
| 157 157 | 
             
                        )
         | 
| 158 | 
            -
                        state_message = self._connector_state_manager.create_state_message(
         | 
| 159 | 
            -
                            self._stream_name, self._stream_namespace, send_per_stream_state=True
         | 
| 160 | 
            -
                        )
         | 
| 158 | 
            +
                        state_message = self._connector_state_manager.create_state_message(self._stream_name, self._stream_namespace)
         | 
| 161 159 | 
             
                        self._message_repository.emit_message(state_message)
         | 
| 162 160 |  | 
| 163 161 | 
             
                def _get_new_cursor_value(self) -> str:
         | 
| @@ -3,6 +3,6 @@ | |
| 3 3 | 
             
            #
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Initialize Streams Package
         | 
| 6 | 
            -
            from .core import IncrementalMixin, Stream
         | 
| 6 | 
            +
            from .core import FULL_REFRESH_SENTINEL_STATE_KEY, IncrementalMixin, Stream
         | 
| 7 7 |  | 
| 8 | 
            -
            __all__ = ["IncrementalMixin", "Stream"]
         | 
| 8 | 
            +
            __all__ = ["FULL_REFRESH_SENTINEL_STATE_KEY", "IncrementalMixin", "Stream"]
         | 
| @@ -8,7 +8,7 @@ import logging | |
| 8 8 | 
             
            from functools import lru_cache
         | 
| 9 9 | 
             
            from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Union
         | 
| 10 10 |  | 
| 11 | 
            -
            from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, AirbyteStream, Level, SyncMode, Type
         | 
| 11 | 
            +
            from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, AirbyteStream, ConfiguredAirbyteStream, Level, SyncMode, Type
         | 
| 12 12 | 
             
            from airbyte_cdk.sources import AbstractSource, Source
         | 
| 13 13 | 
             
            from airbyte_cdk.sources.connector_state_manager import ConnectorStateManager
         | 
| 14 14 | 
             
            from airbyte_cdk.sources.message import MessageRepository
         | 
| @@ -116,29 +116,13 @@ class StreamFacade(AbstractStreamFacade[DefaultStream], Stream): | |
| 116 116 | 
             
                    self._slice_logger = slice_logger
         | 
| 117 117 | 
             
                    self._logger = logger
         | 
| 118 118 |  | 
| 119 | 
            -
                def  | 
| 119 | 
            +
                def read(
         | 
| 120 120 | 
             
                    self,
         | 
| 121 | 
            -
                     | 
| 122 | 
            -
                    logger: logging.Logger,
         | 
| 123 | 
            -
                    slice_logger: SliceLogger,
         | 
| 124 | 
            -
                ) -> Iterable[StreamData]:
         | 
| 125 | 
            -
                    """
         | 
| 126 | 
            -
                    Read full refresh. Delegate to the underlying AbstractStream, ignoring all the parameters
         | 
| 127 | 
            -
                    :param cursor_field: (ignored)
         | 
| 128 | 
            -
                    :param logger: (ignored)
         | 
| 129 | 
            -
                    :param slice_logger: (ignored)
         | 
| 130 | 
            -
                    :return: Iterable of StreamData
         | 
| 131 | 
            -
                    """
         | 
| 132 | 
            -
                    yield from self._read_records()
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                def read_incremental(
         | 
| 135 | 
            -
                    self,
         | 
| 136 | 
            -
                    cursor_field: Optional[List[str]],
         | 
| 121 | 
            +
                    configured_stream: ConfiguredAirbyteStream,
         | 
| 137 122 | 
             
                    logger: logging.Logger,
         | 
| 138 123 | 
             
                    slice_logger: SliceLogger,
         | 
| 139 124 | 
             
                    stream_state: MutableMapping[str, Any],
         | 
| 140 125 | 
             
                    state_manager: ConnectorStateManager,
         | 
| 141 | 
            -
                    per_stream_state_enabled: bool,
         | 
| 142 126 | 
             
                    internal_config: InternalConfig,
         | 
| 143 127 | 
             
                ) -> Iterable[StreamData]:
         | 
| 144 128 | 
             
                    yield from self._read_records()
         | 
| @@ -184,9 +184,7 @@ class ConcurrentCursor(Cursor): | |
| 184 184 | 
             
                    # TODO: if we migrate stored state to the concurrent state format
         | 
| 185 185 | 
             
                    #  (aka stop calling self._connector_state_converter.convert_to_sequential_state`), we'll need to cast datetimes to string or
         | 
| 186 186 | 
             
                    #  int before emitting state
         | 
| 187 | 
            -
                    state_message = self._connector_state_manager.create_state_message(
         | 
| 188 | 
            -
                        self._stream_name, self._stream_namespace, send_per_stream_state=True
         | 
| 189 | 
            -
                    )
         | 
| 187 | 
            +
                    state_message = self._connector_state_manager.create_state_message(self._stream_name, self._stream_namespace)
         | 
| 190 188 | 
             
                    self._message_repository.emit_message(state_message)
         | 
| 191 189 |  | 
| 192 190 | 
             
                def _merge_partitions(self) -> None:
         | 
| @@ -11,7 +11,7 @@ from functools import lru_cache | |
| 11 11 | 
             
            from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Union
         | 
| 12 12 |  | 
| 13 13 | 
             
            import airbyte_cdk.sources.utils.casing as casing
         | 
| 14 | 
            -
            from airbyte_cdk.models import AirbyteMessage, AirbyteStream, SyncMode
         | 
| 14 | 
            +
            from airbyte_cdk.models import AirbyteMessage, AirbyteStream, ConfiguredAirbyteStream, SyncMode
         | 
| 15 15 | 
             
            from airbyte_cdk.models import Type as MessageType
         | 
| 16 16 |  | 
| 17 17 | 
             
            # list of all possible HTTP methods which can be used for sending of request bodies
         | 
| @@ -31,6 +31,10 @@ StreamData = Union[Mapping[str, Any], AirbyteMessage] | |
| 31 31 |  | 
| 32 32 | 
             
            JsonSchema = Mapping[str, Any]
         | 
| 33 33 |  | 
| 34 | 
            +
            # Streams that only support full refresh don't have a suitable cursor so this sentinel
         | 
| 35 | 
            +
            # value is used to indicate that stream should not load the incoming state value
         | 
| 36 | 
            +
            FULL_REFRESH_SENTINEL_STATE_KEY = "__ab_full_refresh_state_message"
         | 
| 37 | 
            +
             | 
| 34 38 |  | 
| 35 39 | 
             
            def package_name_from_class(cls: object) -> str:
         | 
| 36 40 | 
             
                """Find the package name given a class name"""
         | 
| @@ -107,39 +111,24 @@ class Stream(ABC): | |
| 107 111 | 
             
                    """
         | 
| 108 112 | 
             
                    return None
         | 
| 109 113 |  | 
| 110 | 
            -
                def  | 
| 111 | 
            -
                    self,
         | 
| 112 | 
            -
                    cursor_field: Optional[List[str]],
         | 
| 113 | 
            -
                    logger: logging.Logger,
         | 
| 114 | 
            -
                    slice_logger: SliceLogger,
         | 
| 115 | 
            -
                ) -> Iterable[StreamData]:
         | 
| 116 | 
            -
                    slices = self.stream_slices(sync_mode=SyncMode.full_refresh, cursor_field=cursor_field)
         | 
| 117 | 
            -
                    logger.debug(f"Processing stream slices for {self.name} (sync_mode: full_refresh)", extra={"stream_slices": slices})
         | 
| 118 | 
            -
                    for _slice in slices:
         | 
| 119 | 
            -
                        if slice_logger.should_log_slice_message(logger):
         | 
| 120 | 
            -
                            yield slice_logger.create_slice_log_message(_slice)
         | 
| 121 | 
            -
                        yield from self.read_records(
         | 
| 122 | 
            -
                            stream_slice=_slice,
         | 
| 123 | 
            -
                            sync_mode=SyncMode.full_refresh,
         | 
| 124 | 
            -
                            cursor_field=cursor_field,
         | 
| 125 | 
            -
                        )
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                def read_incremental(  # type: ignore  # ignoring typing for ConnectorStateManager because of circular dependencies
         | 
| 114 | 
            +
                def read(  # type: ignore  # ignoring typing for ConnectorStateManager because of circular dependencies
         | 
| 128 115 | 
             
                    self,
         | 
| 129 | 
            -
                     | 
| 116 | 
            +
                    configured_stream: ConfiguredAirbyteStream,
         | 
| 130 117 | 
             
                    logger: logging.Logger,
         | 
| 131 118 | 
             
                    slice_logger: SliceLogger,
         | 
| 132 119 | 
             
                    stream_state: MutableMapping[str, Any],
         | 
| 133 120 | 
             
                    state_manager,
         | 
| 134 | 
            -
                    per_stream_state_enabled: bool,
         | 
| 135 121 | 
             
                    internal_config: InternalConfig,
         | 
| 136 122 | 
             
                ) -> Iterable[StreamData]:
         | 
| 123 | 
            +
                    sync_mode = configured_stream.sync_mode
         | 
| 124 | 
            +
                    cursor_field = configured_stream.cursor_field
         | 
| 125 | 
            +
             | 
| 137 126 | 
             
                    slices = self.stream_slices(
         | 
| 138 127 | 
             
                        cursor_field=cursor_field,
         | 
| 139 | 
            -
                        sync_mode= | 
| 128 | 
            +
                        sync_mode=sync_mode,  # todo: change this interface to no longer rely on sync_mode for behavior
         | 
| 140 129 | 
             
                        stream_state=stream_state,
         | 
| 141 130 | 
             
                    )
         | 
| 142 | 
            -
                    logger.debug(f"Processing stream slices for {self.name} (sync_mode:  | 
| 131 | 
            +
                    logger.debug(f"Processing stream slices for {self.name} (sync_mode: {sync_mode.name})", extra={"stream_slices": slices})
         | 
| 143 132 |  | 
| 144 133 | 
             
                    has_slices = False
         | 
| 145 134 | 
             
                    record_counter = 0
         | 
| @@ -148,7 +137,7 @@ class Stream(ABC): | |
| 148 137 | 
             
                        if slice_logger.should_log_slice_message(logger):
         | 
| 149 138 | 
             
                            yield slice_logger.create_slice_log_message(_slice)
         | 
| 150 139 | 
             
                        records = self.read_records(
         | 
| 151 | 
            -
                            sync_mode= | 
| 140 | 
            +
                            sync_mode=sync_mode,  # todo: change this interface to no longer rely on sync_mode for behavior
         | 
| 152 141 | 
             
                            stream_slice=_slice,
         | 
| 153 142 | 
             
                            stream_state=stream_state,
         | 
| 154 143 | 
             
                            cursor_field=cursor_field or None,
         | 
| @@ -160,20 +149,34 @@ class Stream(ABC): | |
| 160 149 | 
             
                            ):
         | 
| 161 150 | 
             
                                record_data = record_data_or_message if isinstance(record_data_or_message, Mapping) else record_data_or_message.record
         | 
| 162 151 | 
             
                                stream_state = self.get_updated_state(stream_state, record_data)
         | 
| 163 | 
            -
                                checkpoint_interval = self.state_checkpoint_interval
         | 
| 164 152 | 
             
                                record_counter += 1
         | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 153 | 
            +
             | 
| 154 | 
            +
                                if sync_mode == SyncMode.incremental:
         | 
| 155 | 
            +
                                    # Checkpoint intervals are a bit controversial, but see below comment about why we're gating it right now
         | 
| 156 | 
            +
                                    checkpoint_interval = self.state_checkpoint_interval
         | 
| 157 | 
            +
                                    if checkpoint_interval and record_counter % checkpoint_interval == 0:
         | 
| 158 | 
            +
                                        airbyte_state_message = self._checkpoint_state(stream_state, state_manager)
         | 
| 159 | 
            +
                                        yield airbyte_state_message
         | 
| 167 160 |  | 
| 168 161 | 
             
                                if internal_config.is_limit_reached(record_counter):
         | 
| 169 162 | 
             
                                    break
         | 
| 170 163 |  | 
| 171 | 
            -
                         | 
| 164 | 
            +
                        if sync_mode == SyncMode.incremental:
         | 
| 165 | 
            +
                            # Even though right now, only incremental streams running as incremental mode will emit periodic checkpoints. Rather than
         | 
| 166 | 
            +
                            # overhaul how refresh interacts with the platform, this positions the code so that once we want to start emitting
         | 
| 167 | 
            +
                            # periodic checkpoints in full refresh mode it can be done here
         | 
| 168 | 
            +
                            airbyte_state_message = self._checkpoint_state(stream_state, state_manager)
         | 
| 169 | 
            +
                            yield airbyte_state_message
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    if not has_slices or sync_mode == SyncMode.full_refresh:
         | 
| 172 | 
            +
                        if sync_mode == SyncMode.full_refresh:
         | 
| 173 | 
            +
                            # We use a dummy state if there is no suitable value provided by full_refresh streams that do not have a valid cursor.
         | 
| 174 | 
            +
                            # Incremental streams running full_refresh mode emit a meaningful state
         | 
| 175 | 
            +
                            stream_state = stream_state or {FULL_REFRESH_SENTINEL_STATE_KEY: True}
         | 
| 172 176 |  | 
| 173 | 
            -
             | 
| 174 | 
            -
                         | 
| 175 | 
            -
                         | 
| 176 | 
            -
                        yield checkpoint
         | 
| 177 | 
            +
                        # We should always emit a final state message for full refresh sync or streams that do not have any slices
         | 
| 178 | 
            +
                        airbyte_state_message = self._checkpoint_state(stream_state, state_manager)
         | 
| 179 | 
            +
                        yield airbyte_state_message
         | 
| 177 180 |  | 
| 178 181 | 
             
                @abstractmethod
         | 
| 179 182 | 
             
                def read_records(
         | 
| @@ -361,7 +364,6 @@ class Stream(ABC): | |
| 361 364 | 
             
                    self,
         | 
| 362 365 | 
             
                    stream_state: Mapping[str, Any],
         | 
| 363 366 | 
             
                    state_manager,
         | 
| 364 | 
            -
                    per_stream_state_enabled: bool,
         | 
| 365 367 | 
             
                ) -> AirbyteMessage:
         | 
| 366 368 | 
             
                    # First attempt to retrieve the current state using the stream's state property. We receive an AttributeError if the state
         | 
| 367 369 | 
             
                    # property is not implemented by the stream instance and as a fallback, use the stream_state retrieved from the stream
         | 
| @@ -373,4 +375,4 @@ class Stream(ABC): | |
| 373 375 |  | 
| 374 376 | 
             
                    except AttributeError:
         | 
| 375 377 | 
             
                        state_manager.update_state_for_stream(self.name, self.namespace, stream_state)
         | 
| 376 | 
            -
                    return state_manager.create_state_message(self.name, self.namespace | 
| 378 | 
            +
                    return state_manager.create_state_message(self.name, self.namespace)
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.1
         | 
| 2 2 | 
             
            Name: airbyte-cdk
         | 
| 3 | 
            -
            Version: 0.67. | 
| 3 | 
            +
            Version: 0.67.2
         | 
| 4 4 | 
             
            Summary: A framework for writing Airbyte Connectors.
         | 
| 5 5 | 
             
            Home-page: https://github.com/airbytehq/airbyte
         | 
| 6 6 | 
             
            Author: Airbyte
         | 
| @@ -147,7 +147,8 @@ pip install -e ".[dev]" # [dev] installs development-only dependencies | |
| 147 147 | 
             
            If the iteration you are working on includes changes to the models, you might want to regenerate them. In order to do that, you can run:
         | 
| 148 148 |  | 
| 149 149 | 
             
            ```bash
         | 
| 150 | 
            -
             | 
| 150 | 
            +
            cd airbyte-cdk/python
         | 
| 151 | 
            +
            ./gradlew build
         | 
| 151 152 | 
             
            ```
         | 
| 152 153 |  | 
| 153 154 | 
             
            This will generate the files based on the schemas, add the license information and format the code. If you want to only do the former and rely on
         | 
| @@ -24,9 +24,9 @@ airbyte_cdk/models/__init__.py,sha256=Kg8YHBqUsNWHlAw-u3ZGdG4dxLh7qBlHhqMRfamNCR | |
| 24 24 | 
             
            airbyte_cdk/models/airbyte_protocol.py,sha256=DoJvnmGM3xMAZFTwA6_RGMiKSFqfE3ib_Ru0KJ65Ag4,100
         | 
| 25 25 | 
             
            airbyte_cdk/models/well_known_types.py,sha256=KKfNbow2gdLoC1Z4hcXy_JR8m_acsB2ol7gQuEgjobw,117
         | 
| 26 26 | 
             
            airbyte_cdk/sources/__init__.py,sha256=Ov7Uf03KPSZUmMZqZfUAK3tQwsdKjDQUDvTb-H0JyfA,1141
         | 
| 27 | 
            -
            airbyte_cdk/sources/abstract_source.py,sha256= | 
| 27 | 
            +
            airbyte_cdk/sources/abstract_source.py,sha256=vcYtKYZkQnKQamj7lB11xU32yFkZSlCrN7Z1n2iGKXM,15033
         | 
| 28 28 | 
             
            airbyte_cdk/sources/config.py,sha256=PYsY7y2u3EUwxLiEb96JnuKwH_E8CuxKggsRO2ZPSRc,856
         | 
| 29 | 
            -
            airbyte_cdk/sources/connector_state_manager.py,sha256= | 
| 29 | 
            +
            airbyte_cdk/sources/connector_state_manager.py,sha256=rMb8roMcupsKtTXbGsufVl6bq-XVGBTxyTqTOuluMQs,10003
         | 
| 30 30 | 
             
            airbyte_cdk/sources/http_config.py,sha256=OBZeuyFilm6NlDlBhFQvHhTWabEvZww6OHDIlZujIS0,730
         | 
| 31 31 | 
             
            airbyte_cdk/sources/http_logger.py,sha256=v0kkpDtA0GUOgj6_3AayrYaBrSHBqG4t3MGbrtxaNmU,1437
         | 
| 32 32 | 
             
            airbyte_cdk/sources/source.py,sha256=dk50z8Roc28MJ8FxWe652B-GwItO__bTZqFm7WOtHnw,4412
         | 
| @@ -80,7 +80,7 @@ airbyte_cdk/sources/declarative/interpolation/interpolation.py,sha256=dyIM-bzh54 | |
| 80 80 | 
             
            airbyte_cdk/sources/declarative/interpolation/jinja.py,sha256=8bUH6xJRkao8BanwzBFj-1CDj7RR2xPO5u_-FfyRNks,5128
         | 
| 81 81 | 
             
            airbyte_cdk/sources/declarative/interpolation/macros.py,sha256=V6WGKJ9cXX1rjuM4bK3Cs9xEryMlkY2U3FMsSBhrgC8,3098
         | 
| 82 82 | 
             
            airbyte_cdk/sources/declarative/models/__init__.py,sha256=EiYnzwCHZV7EYqMJqcy6xKSeHvTKZBsQndjbEwmiTW4,93
         | 
| 83 | 
            -
            airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256= | 
| 83 | 
            +
            airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=YBz0FIS8crj3bJ8sErWtCRol-6he1fewoMgQtAWj2XM,60991
         | 
| 84 84 | 
             
            airbyte_cdk/sources/declarative/parsers/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
         | 
| 85 85 | 
             
            airbyte_cdk/sources/declarative/parsers/class_types_registry.py,sha256=5vOvMuyWlpALrOq2ehLxa7wO6tlFIlgUNtMYrMCKIjE,6092
         | 
| 86 86 | 
             
            airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=y7_G5mM07zxT5YG975kdC2PAja-Uc83pYp8WrV3GNdo,522
         | 
| @@ -164,7 +164,7 @@ airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availabi | |
| 164 164 | 
             
            airbyte_cdk/sources/file_based/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 165 165 | 
             
            airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py,sha256=dgOoQuoi7-7wdTMSP7wz4ENXIDT49Ew4FoAxnnplGGc,4956
         | 
| 166 166 | 
             
            airbyte_cdk/sources/file_based/config/avro_format.py,sha256=q1I2G9bGNy3ADds35PfWT7Mss6fjYzUtYDkUYvh5v7s,712
         | 
| 167 | 
            -
            airbyte_cdk/sources/file_based/config/csv_format.py,sha256= | 
| 167 | 
            +
            airbyte_cdk/sources/file_based/config/csv_format.py,sha256=XcKuM2xa0OZErSDdOdeAx79KZV52lKvVXXuRDYLKVuk,7526
         | 
| 168 168 | 
             
            airbyte_cdk/sources/file_based/config/file_based_stream_config.py,sha256=l9DFyttYbxY9exwy67WzRXySEk_yKV2G_THRA_Sq1I4,4229
         | 
| 169 169 | 
             
            airbyte_cdk/sources/file_based/config/jsonl_format.py,sha256=fAPzZnoghGgHjaDvx6Qo68C8j54mBxo1NTdpwSI0VZo,374
         | 
| 170 170 | 
             
            airbyte_cdk/sources/file_based/config/parquet_format.py,sha256=yKHgXYu3zJWrGfBlJ3JQZ3gVFPumF-K4rjVPNoYTUZ0,737
         | 
| @@ -177,7 +177,7 @@ airbyte_cdk/sources/file_based/file_types/avro_parser.py,sha256=FC3L6D32SzhAv4jy | |
| 177 177 | 
             
            airbyte_cdk/sources/file_based/file_types/csv_parser.py,sha256=biq2Fi7Nw5K1hIX_MelctEfpC5BEbGRqHL8rkCjS9ng,18414
         | 
| 178 178 | 
             
            airbyte_cdk/sources/file_based/file_types/file_type_parser.py,sha256=Gbn-8v1-jLhKpJXTNOOc5PZT1Jzah6G-INCZt4snLdQ,2819
         | 
| 179 179 | 
             
            airbyte_cdk/sources/file_based/file_types/jsonl_parser.py,sha256=MkjK_J2OqzqRPyGeQFQFADxgwqsRaNtoawB7dwKxWb0,5666
         | 
| 180 | 
            -
            airbyte_cdk/sources/file_based/file_types/parquet_parser.py,sha256= | 
| 180 | 
            +
            airbyte_cdk/sources/file_based/file_types/parquet_parser.py,sha256=xktrUFFzBD-mlnu0MMa0O6HIrZBmaQCAqDgi28tPzOc,9998
         | 
| 181 181 | 
             
            airbyte_cdk/sources/file_based/file_types/unstructured_parser.py,sha256=omYdo6daIHI-YWF9WsKFdFHRXTFWgJjJ3OqegiN345k,16736
         | 
| 182 182 | 
             
            airbyte_cdk/sources/file_based/schema_validation_policies/__init__.py,sha256=sEVnRhZ8x9f7PNjo6lewxid9z0PI8eSj7gSoFC3MH1Y,527
         | 
| 183 183 | 
             
            airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py,sha256=uwk6Ugf23xKG4PRPVVRVwpcNjTwPgxejl03vLSEzK0s,604
         | 
| @@ -186,10 +186,10 @@ airbyte_cdk/sources/file_based/stream/__init__.py,sha256=QPDqdgjsabOQD93dSFqHGaF | |
| 186 186 | 
             
            airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py,sha256=cmO1SQt5PIQRNNoh2KBv6aeY8NEY9x2dlmiRwGwU1vg,6557
         | 
| 187 187 | 
             
            airbyte_cdk/sources/file_based/stream/default_file_based_stream.py,sha256=qS0DJzXlVew6armFDJ0eNcSxRCmkA7JWQYFl6gcv3dU,13113
         | 
| 188 188 | 
             
            airbyte_cdk/sources/file_based/stream/concurrent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 189 | 
            -
            airbyte_cdk/sources/file_based/stream/concurrent/adapters.py,sha256= | 
| 189 | 
            +
            airbyte_cdk/sources/file_based/stream/concurrent/adapters.py,sha256=G93wu1oo1OOUWrl3z16wISYN8JyqJitkpfeqv5016vc,12806
         | 
| 190 190 | 
             
            airbyte_cdk/sources/file_based/stream/concurrent/cursor/__init__.py,sha256=4gi-_oETrHYZClD12okh13INBsI2OfugKhToXtohRTs,310
         | 
| 191 191 | 
             
            airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py,sha256=UYLE2A2RdV-5FaQ70naZZWY34l5AEJkIRlTH05-e_-k,1961
         | 
| 192 | 
            -
            airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py,sha256= | 
| 192 | 
            +
            airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py,sha256=Bs8e05pbY1OhTUsklhIqrfeCataME_fkg0ToakifHgY,14331
         | 
| 193 193 | 
             
            airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_noop_cursor.py,sha256=wblXBgNw-QLVPNOAL8DihlQBXbvPC1zCdDWMsPdZPzQ,1852
         | 
| 194 194 | 
             
            airbyte_cdk/sources/file_based/stream/cursor/__init__.py,sha256=MhFB5hOo8sjwvCh8gangaymdg3EJWYt_72brFOZt068,191
         | 
| 195 195 | 
             
            airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py,sha256=i-FPeK8lwCzX34GCcmvL5Yvdh8-uu7FeCVYDoFbD7IY,1920
         | 
| @@ -199,16 +199,16 @@ airbyte_cdk/sources/message/repository.py,sha256=tQOmtWxrAp1CMiOKi5SdIEWzcmgnCUY | |
| 199 199 | 
             
            airbyte_cdk/sources/singer/__init__.py,sha256=D3zQSiWT0B9t0kKE4JPZjrcDnP2YnFNJ3dfYqSaxo9w,246
         | 
| 200 200 | 
             
            airbyte_cdk/sources/singer/singer_helpers.py,sha256=q1LmgjFxSnN-dobMy7nikUwcK-9FvW5QQfgTqiclbAE,15649
         | 
| 201 201 | 
             
            airbyte_cdk/sources/singer/source.py,sha256=3YY8UTOXmctvMVUnYmIegmL3_IxF55iGP_bc_s2MZdY,8530
         | 
| 202 | 
            -
            airbyte_cdk/sources/streams/__init__.py,sha256= | 
| 202 | 
            +
            airbyte_cdk/sources/streams/__init__.py,sha256=VBGcpSl-SdcY5ajsHe99GLNC4NXAYIm1k1MZxWYllAE,244
         | 
| 203 203 | 
             
            airbyte_cdk/sources/streams/availability_strategy.py,sha256=7BM0qLvXS0QrlKvnVkBEw4Cw8i7PCENCBLcIAcuD3nY,1007
         | 
| 204 204 | 
             
            airbyte_cdk/sources/streams/call_rate.py,sha256=5T4J8WxMNov76iXRUtD5KlM1CsROxuAQPwGQAZyvpHg,20555
         | 
| 205 | 
            -
            airbyte_cdk/sources/streams/core.py,sha256= | 
| 205 | 
            +
            airbyte_cdk/sources/streams/core.py,sha256=UdJfpc1jwT6igY-e5w-ow5ciT5feHq8F79Dxvpc1Sss,16741
         | 
| 206 206 | 
             
            airbyte_cdk/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
         | 
| 207 207 | 
             
            airbyte_cdk/sources/streams/concurrent/abstract_stream.py,sha256=HlnmAh-LcQbs9g1r0iUAUe3IN0RmUSZits5Nyers51g,3792
         | 
| 208 208 | 
             
            airbyte_cdk/sources/streams/concurrent/abstract_stream_facade.py,sha256=QTry1QCBUwJDw1QSCEvz23s7zIEx_7QMxkPq9j-oPIQ,1358
         | 
| 209 | 
            -
            airbyte_cdk/sources/streams/concurrent/adapters.py,sha256= | 
| 209 | 
            +
            airbyte_cdk/sources/streams/concurrent/adapters.py,sha256=ok68ZE2tYH3XR0Ti3mwSsCO_WQSGyqTCXTFI5Gc5WZk,16267
         | 
| 210 210 | 
             
            airbyte_cdk/sources/streams/concurrent/availability_strategy.py,sha256=8xDRpfktnARBbRi_RwznvKuoGrpPF2b6tQyloMwogkM,2013
         | 
| 211 | 
            -
            airbyte_cdk/sources/streams/concurrent/cursor.py,sha256= | 
| 211 | 
            +
            airbyte_cdk/sources/streams/concurrent/cursor.py,sha256=KaLdNz5auNIOtLPF94g4un-BVUH9JHSvw3UrCwBUqAg,9732
         | 
| 212 212 | 
             
            airbyte_cdk/sources/streams/concurrent/default_stream.py,sha256=qPhMaLxGdR29kyMeA-YrHg-XePgPNDjactQPKbp56RA,3009
         | 
| 213 213 | 
             
            airbyte_cdk/sources/streams/concurrent/exceptions.py,sha256=-WETGIY5_QFmVeDFiqm4WhRJ_nNCkfcDwOQqx6cSqrI,365
         | 
| 214 214 | 
             
            airbyte_cdk/sources/streams/concurrent/helpers.py,sha256=FPdGovWg0_hPxoTCAJnqs2SEqEq32pRGKlvPMP7hGWo,1290
         | 
| @@ -282,16 +282,16 @@ unit_tests/singer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU | |
| 282 282 | 
             
            unit_tests/singer/test_singer_helpers.py,sha256=pZV6VxJuK-3-FICNGmoGbokrA_zkaFZEd4rYZCVpSRU,1762
         | 
| 283 283 | 
             
            unit_tests/singer/test_singer_source.py,sha256=edN_kv7dnYAdBveWdUYOs74ak0dK6p8uaX225h_ZILA,4442
         | 
| 284 284 | 
             
            unit_tests/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 285 | 
            -
            unit_tests/sources/test_abstract_source.py,sha256= | 
| 285 | 
            +
            unit_tests/sources/test_abstract_source.py,sha256=TZ9Mn_kT9A2A_CRJLS9OQjBnlWrPRqf8EyNd8eJ_ZrM,57197
         | 
| 286 286 | 
             
            unit_tests/sources/test_concurrent_source.py,sha256=3i7pSRetKSoP6LBpXyuXpWi2_VOwta_aTm_kgnDaLqk,3704
         | 
| 287 287 | 
             
            unit_tests/sources/test_config.py,sha256=lxjeaf48pOMF4Pf3-Z1ux_tHTyjRFCdG_hpnxw3e7uQ,2839
         | 
| 288 | 
            -
            unit_tests/sources/test_connector_state_manager.py,sha256= | 
| 288 | 
            +
            unit_tests/sources/test_connector_state_manager.py,sha256=PGvBh90FAtG7vp_4y8nZxcjx1mEcFn382PCdBH-r9-I,19588
         | 
| 289 289 | 
             
            unit_tests/sources/test_http_logger.py,sha256=VT6DqgspI3DcRnoBQkkQX0z4dF_AOiYZ5P_zxmMW8oU,9004
         | 
| 290 290 | 
             
            unit_tests/sources/test_integration_source.py,sha256=qcWld9evB1rAjALWX8SDshGz7seYkN3HCamQ6KQ2Idw,4269
         | 
| 291 | 
            -
            unit_tests/sources/test_source.py,sha256= | 
| 291 | 
            +
            unit_tests/sources/test_source.py,sha256=zwyU7pLwQaEzeozxPJzNeRvZXb2xddeWO4bLqdMt9BM,28343
         | 
| 292 292 | 
             
            unit_tests/sources/test_source_read.py,sha256=n9XpVQLfsQH8eh6D99MDiNVBBKcf6UtouThDJcGH6SU,17186
         | 
| 293 293 | 
             
            unit_tests/sources/concurrent_source/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
         | 
| 294 | 
            -
            unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py,sha256= | 
| 294 | 
            +
            unit_tests/sources/concurrent_source/test_concurrent_source_adapter.py,sha256=ZEJzOm9pVFHrXBS3cT43XdK_vopICZmoqzFsTE2E8Tk,3675
         | 
| 295 295 | 
             
            unit_tests/sources/declarative/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
         | 
| 296 296 | 
             
            unit_tests/sources/declarative/external_component.py,sha256=lU2gL736bLEWtmrGm1B2k83RXt_3XkROimLIahZd5dg,293
         | 
| 297 297 | 
             
            unit_tests/sources/declarative/test_create_partial.py,sha256=s_KIywQqt8RlauOCWNJVk3HC3KBTAtSwFTN6JVQgu80,2636
         | 
| @@ -373,13 +373,13 @@ unit_tests/sources/file_based/helpers.py,sha256=JSKXrPL7iSBSH7nkKde-jcylVuDohJid | |
| 373 373 | 
             
            unit_tests/sources/file_based/in_memory_files_source.py,sha256=1UCfRMgaovPdhkORT5k5Izj6e0ldPp802iiaffG2ghk,8550
         | 
| 374 374 | 
             
            unit_tests/sources/file_based/test_file_based_scenarios.py,sha256=llrPRotbYvUrORrqOFH8nMqQZ_QSs4sYwwgvqzkQsvc,15355
         | 
| 375 375 | 
             
            unit_tests/sources/file_based/test_file_based_stream_reader.py,sha256=P6yTp7tbPfREzi5SXg4SSSql5nxiRV571YdOmwb_SzY,9219
         | 
| 376 | 
            -
            unit_tests/sources/file_based/test_scenarios.py,sha256= | 
| 376 | 
            +
            unit_tests/sources/file_based/test_scenarios.py,sha256=85qb_CXTGNTS7pk-N73EGd55J0Cnky8i1G900cs4sek,9405
         | 
| 377 377 | 
             
            unit_tests/sources/file_based/test_schema_helpers.py,sha256=IYIDdLRK41RkSG_ZW2cagAt9krV4QLbkzu6r7vPx9Js,12047
         | 
| 378 378 | 
             
            unit_tests/sources/file_based/availability_strategy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 379 379 | 
             
            unit_tests/sources/file_based/availability_strategy/test_default_file_based_availability_strategy.py,sha256=14ffoRWC4RHPrmBFZpplnAd1Uezn8neuQrIyZqvjTK0,4964
         | 
| 380 380 | 
             
            unit_tests/sources/file_based/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 381 381 | 
             
            unit_tests/sources/file_based/config/test_abstract_file_based_spec.py,sha256=_9Gr1r0dR-dpqQlciG8VuS6ziuCYBjpPvGEiYvS7s7w,1169
         | 
| 382 | 
            -
            unit_tests/sources/file_based/config/test_csv_format.py,sha256= | 
| 382 | 
            +
            unit_tests/sources/file_based/config/test_csv_format.py,sha256=zFgnyJnCQeqsqHH31ZerVeC_km32wU4Sbe-OKoR7wNY,1282
         | 
| 383 383 | 
             
            unit_tests/sources/file_based/config/test_file_based_stream_config.py,sha256=aDVLvD5ngKJiLMyAO5tSJFCc026tVlqTUGGASnTeKBI,3320
         | 
| 384 384 | 
             
            unit_tests/sources/file_based/discovery_policy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 385 385 | 
             
            unit_tests/sources/file_based/discovery_policy/test_default_discovery_policy.py,sha256=RTbPxseKWNLzASeOSxxX72APJoIFm8VpQM9Ok7NE5C0,1132
         | 
| @@ -387,15 +387,15 @@ unit_tests/sources/file_based/file_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5 | |
| 387 387 | 
             
            unit_tests/sources/file_based/file_types/test_avro_parser.py,sha256=LCoGa0fvOberrnDu7-Ox_gVNHj_6ERLoWionz0sD0eU,11177
         | 
| 388 388 | 
             
            unit_tests/sources/file_based/file_types/test_csv_parser.py,sha256=qgTT67wVgbhcvwuxbzr2UykRGIhsOYCTYdEcvgI4q_s,22615
         | 
| 389 389 | 
             
            unit_tests/sources/file_based/file_types/test_jsonl_parser.py,sha256=foTf9U9LyAS8OR0BonwNgFWPqTrmzFV2lpPUfRMrioE,6134
         | 
| 390 | 
            -
            unit_tests/sources/file_based/file_types/test_parquet_parser.py,sha256= | 
| 390 | 
            +
            unit_tests/sources/file_based/file_types/test_parquet_parser.py,sha256=HXNpISvdV0ePYj59c_EzczOj-ZqPWh7sOSEEaKKIEk8,18163
         | 
| 391 391 | 
             
            unit_tests/sources/file_based/file_types/test_unstructured_parser.py,sha256=wJ9J9SbE6gq5ZEnGrDmtKNIimn9xwma06vKsIYa7SDc,23689
         | 
| 392 392 | 
             
            unit_tests/sources/file_based/scenarios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 393 393 | 
             
            unit_tests/sources/file_based/scenarios/avro_scenarios.py,sha256=oeQUmCV7d2aTShreYc-PvVb4cWqLSsVwHfg-lcKjzPs,30554
         | 
| 394 394 | 
             
            unit_tests/sources/file_based/scenarios/check_scenarios.py,sha256=0xkt21ASTnTAMP0RYJEsF3yMGsNN7wWOoG_tmzL9PYw,6750
         | 
| 395 | 
            -
            unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py,sha256= | 
| 395 | 
            +
            unit_tests/sources/file_based/scenarios/concurrent_incremental_scenarios.py,sha256=EnVhPLSmUmB2lRc2ugb-HF7UkLFulj2EAHs4enAK5dI,102362
         | 
| 396 396 | 
             
            unit_tests/sources/file_based/scenarios/csv_scenarios.py,sha256=2tyWtFOtbxPh24qAhKMZcVsoSsmnFT3k6MBGzRmx3lU,121727
         | 
| 397 397 | 
             
            unit_tests/sources/file_based/scenarios/file_based_source_builder.py,sha256=3gAFkguYH87v_WpV0lUttTKu7LG8a-viokDW232ecUw,4123
         | 
| 398 | 
            -
            unit_tests/sources/file_based/scenarios/incremental_scenarios.py,sha256= | 
| 398 | 
            +
            unit_tests/sources/file_based/scenarios/incremental_scenarios.py,sha256=7ZYe0tsoJ85rNT-s4Z9toXRp2BKmA1pxpPTCyTnNd_8,67340
         | 
| 399 399 | 
             
            unit_tests/sources/file_based/scenarios/jsonl_scenarios.py,sha256=quo_o8ofuv5LQ2eni6_HudbNq7IgAFQ5uzf_QTElLuY,31719
         | 
| 400 400 | 
             
            unit_tests/sources/file_based/scenarios/parquet_scenarios.py,sha256=0DZbrb2wbaGSQ3OjD8gCH673dPqtVcLCR_LVkA_qVpA,26658
         | 
| 401 401 | 
             
            unit_tests/sources/file_based/scenarios/scenario_builder.py,sha256=ynywaMWNvPnJ8Mg2h3vYZPLfaOzHcSFYj7e8bmY_0gY,9894
         | 
| @@ -406,8 +406,8 @@ unit_tests/sources/file_based/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu | |
| 406 406 | 
             
            unit_tests/sources/file_based/stream/test_default_file_based_cursor.py,sha256=XhtCGvgSBFyeQwgqGciPsIB1HIlWqTcXROwnxrjutHc,13109
         | 
| 407 407 | 
             
            unit_tests/sources/file_based/stream/test_default_file_based_stream.py,sha256=IuAnysO7s3MXm6JViPSrlfIlpIYcqWpsKokRpABX39c,10075
         | 
| 408 408 | 
             
            unit_tests/sources/file_based/stream/concurrent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 409 | 
            -
            unit_tests/sources/file_based/stream/concurrent/test_adapters.py,sha256= | 
| 410 | 
            -
            unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py,sha256= | 
| 409 | 
            +
            unit_tests/sources/file_based/stream/concurrent/test_adapters.py,sha256=ptjPNSLh_2Z1GFN82gkC1lH4Ov5h5UyEr3AO7CO2fqM,14760
         | 
| 410 | 
            +
            unit_tests/sources/file_based/stream/concurrent/test_file_based_concurrent_cursor.py,sha256=MuIE6y7b-7vF5vuwMozOxBDBLxSU_7dMmSwvK4vvm7U,19874
         | 
| 411 411 | 
             
            unit_tests/sources/fixtures/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
         | 
| 412 412 | 
             
            unit_tests/sources/fixtures/source_test_fixture.py,sha256=dvpISgio2sOp-U3bXudH_49vY4c68sO_PMs1JZTMaj0,5502
         | 
| 413 413 | 
             
            unit_tests/sources/message/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| @@ -415,10 +415,10 @@ unit_tests/sources/message/test_repository.py,sha256=oiScwg4cAdnYDl7PPN1nZniDGpA | |
| 415 415 | 
             
            unit_tests/sources/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 416 416 | 
             
            unit_tests/sources/streams/test_availability_strategy.py,sha256=vJrSEk9NwRghu0YsSNoMYHKWzA9UFemwyClpke8Mk2s,2315
         | 
| 417 417 | 
             
            unit_tests/sources/streams/test_call_rate.py,sha256=5QsokqxIFoR438QTd7p_eb0K-LW6awZXDtQiMTAb_Qo,13069
         | 
| 418 | 
            -
            unit_tests/sources/streams/test_stream_read.py,sha256= | 
| 418 | 
            +
            unit_tests/sources/streams/test_stream_read.py,sha256=MpRVbr_uCnpEiVHGuKQzuOzCYVUPwhBwA-ISr4GxHm0,16571
         | 
| 419 419 | 
             
            unit_tests/sources/streams/test_streams_core.py,sha256=YOC7XqWFJ13Z4YuO9Nh4AR4AwpJ-s111vqPplFfpxk4,5059
         | 
| 420 420 | 
             
            unit_tests/sources/streams/concurrent/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
         | 
| 421 | 
            -
            unit_tests/sources/streams/concurrent/test_adapters.py,sha256= | 
| 421 | 
            +
            unit_tests/sources/streams/concurrent/test_adapters.py,sha256=rIGY_V7D7-2TOcNopGxQySIPZsj62n2saijN2kl3oZM,14934
         | 
| 422 422 | 
             
            unit_tests/sources/streams/concurrent/test_concurrent_read_processor.py,sha256=qsoSin6ILGhhztEWF-WRdO6nvXJ-MfBH5CNpApEyKSc,27026
         | 
| 423 423 | 
             
            unit_tests/sources/streams/concurrent/test_cursor.py,sha256=9TmJUOHCsX8Acmm7yDvfcpB5WXwPBXP4d5dizRI-msw,5951
         | 
| 424 424 | 
             
            unit_tests/sources/streams/concurrent/test_datetime_state_converter.py,sha256=BWEKIT3a6B1NYAiXLZ-STgRu2kJ1T3QzEwQpfgsZkHs,14177
         | 
| @@ -427,9 +427,9 @@ unit_tests/sources/streams/concurrent/test_partition_enqueuer.py,sha256=Vj8-aOZU | |
| 427 427 | 
             
            unit_tests/sources/streams/concurrent/test_partition_reader.py,sha256=bNFEQXqkSb1yBW5Nruar3HuVqx6r5hNXzU2VFtIRZgw,2544
         | 
| 428 428 | 
             
            unit_tests/sources/streams/concurrent/test_thread_pool_manager.py,sha256=l0rwdDX79MRip0IKTXKGIqEZy2NptMTUTPYYQQU5yjQ,4203
         | 
| 429 429 | 
             
            unit_tests/sources/streams/concurrent/scenarios/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
         | 
| 430 | 
            -
            unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py,sha256= | 
| 430 | 
            +
            unit_tests/sources/streams/concurrent/scenarios/incremental_scenarios.py,sha256=pRbArlvAOglahwb_VHNqYOqfbn4DmJ3rtOCw-NyxJ2M,9858
         | 
| 431 431 | 
             
            unit_tests/sources/streams/concurrent/scenarios/stream_facade_builder.py,sha256=HKtWlCbx81CdS8hqCs-d43JndiLL6Tp4K0Yf8VdycDg,6239
         | 
| 432 | 
            -
            unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py,sha256= | 
| 432 | 
            +
            unit_tests/sources/streams/concurrent/scenarios/stream_facade_scenarios.py,sha256=XIXBunoVtRfCvc-cOGbRtO0t6_km0uoKMFrtvymr28Q,13927
         | 
| 433 433 | 
             
            unit_tests/sources/streams/concurrent/scenarios/test_concurrent_scenarios.py,sha256=Z_4-ClsxBupmN7Pbl8lF9bkSA9wnjLtrgA9WR_8VRi8,3757
         | 
| 434 434 | 
             
            unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_scenarios.py,sha256=Qa1z48QLKy8xOViyiqpkIEhREF4rZHqJh8FwJ8fzqiQ,13435
         | 
| 435 435 | 
             
            unit_tests/sources/streams/concurrent/scenarios/thread_based_concurrent_stream_source_builder.py,sha256=dY9iAX8YlJcQ2nyPePPCjj6VXzUN_wmJ3bGz6wZQzFE,5734
         | 
| @@ -456,8 +456,8 @@ unit_tests/utils/test_schema_inferrer.py,sha256=Z2jHBZ540wnYkylIdV_2xr75Vtwlxuyg | |
| 456 456 | 
             
            unit_tests/utils/test_secret_utils.py,sha256=CdKK8A2-5XVxbXVtX22FK9dwwMeP5KNqDH6luWRXSNw,5256
         | 
| 457 457 | 
             
            unit_tests/utils/test_stream_status_utils.py,sha256=Xr8MZ2HWgTVIyMbywDvuYkRaUF4RZLQOT8-JjvcfR24,2970
         | 
| 458 458 | 
             
            unit_tests/utils/test_traced_exception.py,sha256=bDFP5zMBizFenz6V2WvEZTRCKGB5ijh3DBezjbfoYIs,4198
         | 
| 459 | 
            -
            airbyte_cdk-0.67. | 
| 460 | 
            -
            airbyte_cdk-0.67. | 
| 461 | 
            -
            airbyte_cdk-0.67. | 
| 462 | 
            -
            airbyte_cdk-0.67. | 
| 463 | 
            -
            airbyte_cdk-0.67. | 
| 459 | 
            +
            airbyte_cdk-0.67.2.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
         | 
| 460 | 
            +
            airbyte_cdk-0.67.2.dist-info/METADATA,sha256=ZtTIvhUGdmrevYVp8JtOUbobpySWO91ui3_FzJd5Yfc,11074
         | 
| 461 | 
            +
            airbyte_cdk-0.67.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
         | 
| 462 | 
            +
            airbyte_cdk-0.67.2.dist-info/top_level.txt,sha256=edvsDKTnE6sD2wfCUaeTfKf5gQIL6CPVMwVL2sWZzqo,51
         | 
| 463 | 
            +
            airbyte_cdk-0.67.2.dist-info/RECORD,,
         | 
| @@ -81,13 +81,14 @@ def test_concurrent_source_adapter(): | |
| 81 81 | 
             
            def _mock_stream(name: str, data=[], available: bool = True):
         | 
| 82 82 | 
             
                s = Mock()
         | 
| 83 83 | 
             
                s.name = name
         | 
| 84 | 
            +
                s.namespace = None
         | 
| 84 85 | 
             
                s.as_airbyte_stream.return_value = AirbyteStream(
         | 
| 85 86 | 
             
                    name=name,
         | 
| 86 87 | 
             
                    json_schema={},
         | 
| 87 88 | 
             
                    supported_sync_modes=[SyncMode.full_refresh],
         | 
| 88 89 | 
             
                )
         | 
| 89 90 | 
             
                s.check_availability.return_value = (True, None) if available else (False, "not available")
         | 
| 90 | 
            -
                s. | 
| 91 | 
            +
                s.read.return_value = iter(data)
         | 
| 91 92 | 
             
                s.primary_key = None
         | 
| 92 93 | 
             
                return s
         | 
| 93 94 |  | 
| @@ -5,7 +5,7 @@ | |
| 5 5 | 
             
            import unittest
         | 
| 6 6 |  | 
| 7 7 | 
             
            import pytest
         | 
| 8 | 
            -
            from airbyte_cdk.sources.file_based.config.csv_format import CsvHeaderAutogenerated, CsvHeaderFromCsv, CsvHeaderUserProvided
         | 
| 8 | 
            +
            from airbyte_cdk.sources.file_based.config.csv_format import CsvFormat, CsvHeaderAutogenerated, CsvHeaderFromCsv, CsvHeaderUserProvided
         | 
| 9 9 | 
             
            from pydantic import ValidationError
         | 
| 10 10 |  | 
| 11 11 |  | 
| @@ -26,3 +26,8 @@ class CsvHeaderDefinitionTest(unittest.TestCase): | |
| 26 26 |  | 
| 27 27 | 
             
                def test_given_from_csv_then_csv_has_header_row(self) -> None:
         | 
| 28 28 | 
             
                    assert CsvHeaderFromCsv().has_header_row()
         | 
| 29 | 
            +
             | 
| 30 | 
            +
             | 
| 31 | 
            +
            class CsvDelimiterTest(unittest.TestCase):
         | 
| 32 | 
            +
                def test_tab_delimter(self):
         | 
| 33 | 
            +
                    assert CsvFormat(delimiter=r"\t").delimiter == '\\t'
         |