airbyte-cdk 6.5.3rc2__py3-none-any.whl → 6.5.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. airbyte_cdk/__init__.py +17 -2
  2. airbyte_cdk/config_observation.py +10 -3
  3. airbyte_cdk/connector.py +19 -9
  4. airbyte_cdk/connector_builder/connector_builder_handler.py +28 -8
  5. airbyte_cdk/connector_builder/main.py +26 -6
  6. airbyte_cdk/connector_builder/message_grouper.py +95 -25
  7. airbyte_cdk/destinations/destination.py +47 -14
  8. airbyte_cdk/destinations/vector_db_based/config.py +36 -14
  9. airbyte_cdk/destinations/vector_db_based/document_processor.py +49 -11
  10. airbyte_cdk/destinations/vector_db_based/embedder.py +52 -11
  11. airbyte_cdk/destinations/vector_db_based/test_utils.py +14 -4
  12. airbyte_cdk/destinations/vector_db_based/utils.py +8 -2
  13. airbyte_cdk/destinations/vector_db_based/writer.py +15 -4
  14. airbyte_cdk/entrypoint.py +82 -26
  15. airbyte_cdk/exception_handler.py +13 -3
  16. airbyte_cdk/logger.py +10 -2
  17. airbyte_cdk/models/airbyte_protocol.py +11 -5
  18. airbyte_cdk/models/airbyte_protocol_serializers.py +9 -3
  19. airbyte_cdk/models/well_known_types.py +1 -1
  20. airbyte_cdk/sources/abstract_source.py +63 -17
  21. airbyte_cdk/sources/concurrent_source/concurrent_read_processor.py +47 -14
  22. airbyte_cdk/sources/concurrent_source/concurrent_source.py +25 -7
  23. airbyte_cdk/sources/concurrent_source/concurrent_source_adapter.py +27 -6
  24. airbyte_cdk/sources/concurrent_source/thread_pool_manager.py +9 -3
  25. airbyte_cdk/sources/connector_state_manager.py +32 -10
  26. airbyte_cdk/sources/declarative/async_job/job.py +3 -1
  27. airbyte_cdk/sources/declarative/async_job/job_orchestrator.py +68 -14
  28. airbyte_cdk/sources/declarative/async_job/job_tracker.py +24 -6
  29. airbyte_cdk/sources/declarative/async_job/repository.py +3 -1
  30. airbyte_cdk/sources/declarative/auth/declarative_authenticator.py +3 -1
  31. airbyte_cdk/sources/declarative/auth/jwt.py +27 -7
  32. airbyte_cdk/sources/declarative/auth/oauth.py +35 -11
  33. airbyte_cdk/sources/declarative/auth/selective_authenticator.py +3 -1
  34. airbyte_cdk/sources/declarative/auth/token.py +25 -8
  35. airbyte_cdk/sources/declarative/checks/check_stream.py +12 -4
  36. airbyte_cdk/sources/declarative/checks/connection_checker.py +3 -1
  37. airbyte_cdk/sources/declarative/concurrency_level/concurrency_level.py +11 -3
  38. airbyte_cdk/sources/declarative/concurrent_declarative_source.py +106 -50
  39. airbyte_cdk/sources/declarative/datetime/min_max_datetime.py +20 -6
  40. airbyte_cdk/sources/declarative/declarative_source.py +3 -1
  41. airbyte_cdk/sources/declarative/declarative_stream.py +27 -6
  42. airbyte_cdk/sources/declarative/decoders/decoder.py +3 -1
  43. airbyte_cdk/sources/declarative/decoders/json_decoder.py +3 -1
  44. airbyte_cdk/sources/declarative/decoders/pagination_decoder_decorator.py +3 -1
  45. airbyte_cdk/sources/declarative/decoders/xml_decoder.py +6 -2
  46. airbyte_cdk/sources/declarative/extractors/dpath_extractor.py +6 -2
  47. airbyte_cdk/sources/declarative/extractors/record_filter.py +24 -7
  48. airbyte_cdk/sources/declarative/extractors/record_selector.py +10 -3
  49. airbyte_cdk/sources/declarative/extractors/response_to_file_extractor.py +15 -5
  50. airbyte_cdk/sources/declarative/incremental/datetime_based_cursor.py +96 -31
  51. airbyte_cdk/sources/declarative/incremental/global_substream_cursor.py +22 -8
  52. airbyte_cdk/sources/declarative/incremental/per_partition_cursor.py +46 -15
  53. airbyte_cdk/sources/declarative/incremental/per_partition_with_global.py +19 -5
  54. airbyte_cdk/sources/declarative/incremental/resumable_full_refresh_cursor.py +3 -1
  55. airbyte_cdk/sources/declarative/interpolation/interpolated_boolean.py +20 -2
  56. airbyte_cdk/sources/declarative/interpolation/interpolated_mapping.py +5 -1
  57. airbyte_cdk/sources/declarative/interpolation/interpolated_nested_mapping.py +10 -3
  58. airbyte_cdk/sources/declarative/interpolation/interpolated_string.py +6 -2
  59. airbyte_cdk/sources/declarative/interpolation/interpolation.py +7 -1
  60. airbyte_cdk/sources/declarative/interpolation/jinja.py +6 -2
  61. airbyte_cdk/sources/declarative/interpolation/macros.py +19 -4
  62. airbyte_cdk/sources/declarative/manifest_declarative_source.py +106 -24
  63. airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py +7 -2
  64. airbyte_cdk/sources/declarative/models/declarative_component_schema.py +656 -678
  65. airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py +13 -4
  66. airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py +9 -2
  67. airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +782 -232
  68. airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py +29 -7
  69. airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py +25 -7
  70. airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py +54 -15
  71. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +6 -2
  72. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/header_helper.py +3 -1
  73. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +17 -5
  74. airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +15 -5
  75. airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +3 -1
  76. airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +18 -8
  77. airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py +16 -7
  78. airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +51 -14
  79. airbyte_cdk/sources/declarative/requesters/http_job_repository.py +29 -8
  80. airbyte_cdk/sources/declarative/requesters/http_requester.py +58 -16
  81. airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py +49 -14
  82. airbyte_cdk/sources/declarative/requesters/paginators/no_pagination.py +3 -1
  83. airbyte_cdk/sources/declarative/requesters/paginators/paginator.py +3 -1
  84. airbyte_cdk/sources/declarative/requesters/paginators/strategies/cursor_pagination_strategy.py +17 -5
  85. airbyte_cdk/sources/declarative/requesters/paginators/strategies/offset_increment.py +24 -7
  86. airbyte_cdk/sources/declarative/requesters/paginators/strategies/page_increment.py +9 -3
  87. airbyte_cdk/sources/declarative/requesters/paginators/strategies/pagination_strategy.py +3 -1
  88. airbyte_cdk/sources/declarative/requesters/paginators/strategies/stop_condition.py +6 -2
  89. airbyte_cdk/sources/declarative/requesters/request_options/datetime_based_request_options_provider.py +19 -6
  90. airbyte_cdk/sources/declarative/requesters/request_options/default_request_options_provider.py +3 -1
  91. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_nested_request_input_provider.py +21 -7
  92. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_input_provider.py +18 -6
  93. airbyte_cdk/sources/declarative/requesters/request_options/interpolated_request_options_provider.py +27 -8
  94. airbyte_cdk/sources/declarative/requesters/requester.py +3 -1
  95. airbyte_cdk/sources/declarative/retrievers/async_retriever.py +12 -5
  96. airbyte_cdk/sources/declarative/retrievers/simple_retriever.py +105 -24
  97. airbyte_cdk/sources/declarative/schema/default_schema_loader.py +3 -1
  98. airbyte_cdk/sources/declarative/spec/spec.py +8 -2
  99. airbyte_cdk/sources/declarative/stream_slicers/stream_slicer.py +3 -1
  100. airbyte_cdk/sources/declarative/transformations/add_fields.py +12 -3
  101. airbyte_cdk/sources/declarative/transformations/remove_fields.py +6 -2
  102. airbyte_cdk/sources/declarative/types.py +8 -1
  103. airbyte_cdk/sources/declarative/yaml_declarative_source.py +3 -1
  104. airbyte_cdk/sources/embedded/base_integration.py +14 -4
  105. airbyte_cdk/sources/embedded/catalog.py +16 -4
  106. airbyte_cdk/sources/embedded/runner.py +19 -3
  107. airbyte_cdk/sources/embedded/tools.py +3 -1
  108. airbyte_cdk/sources/file_based/availability_strategy/abstract_file_based_availability_strategy.py +12 -4
  109. airbyte_cdk/sources/file_based/availability_strategy/default_file_based_availability_strategy.py +27 -7
  110. airbyte_cdk/sources/file_based/config/abstract_file_based_spec.py +12 -6
  111. airbyte_cdk/sources/file_based/config/csv_format.py +21 -9
  112. airbyte_cdk/sources/file_based/config/file_based_stream_config.py +6 -2
  113. airbyte_cdk/sources/file_based/config/unstructured_format.py +10 -3
  114. airbyte_cdk/sources/file_based/discovery_policy/abstract_discovery_policy.py +2 -4
  115. airbyte_cdk/sources/file_based/discovery_policy/default_discovery_policy.py +7 -2
  116. airbyte_cdk/sources/file_based/exceptions.py +13 -15
  117. airbyte_cdk/sources/file_based/file_based_source.py +82 -24
  118. airbyte_cdk/sources/file_based/file_based_stream_reader.py +16 -5
  119. airbyte_cdk/sources/file_based/file_types/avro_parser.py +58 -17
  120. airbyte_cdk/sources/file_based/file_types/csv_parser.py +89 -26
  121. airbyte_cdk/sources/file_based/file_types/excel_parser.py +25 -7
  122. airbyte_cdk/sources/file_based/file_types/file_transfer.py +8 -2
  123. airbyte_cdk/sources/file_based/file_types/file_type_parser.py +4 -1
  124. airbyte_cdk/sources/file_based/file_types/jsonl_parser.py +20 -6
  125. airbyte_cdk/sources/file_based/file_types/parquet_parser.py +57 -16
  126. airbyte_cdk/sources/file_based/file_types/unstructured_parser.py +64 -15
  127. airbyte_cdk/sources/file_based/schema_helpers.py +33 -10
  128. airbyte_cdk/sources/file_based/schema_validation_policies/abstract_schema_validation_policy.py +3 -1
  129. airbyte_cdk/sources/file_based/schema_validation_policies/default_schema_validation_policies.py +16 -5
  130. airbyte_cdk/sources/file_based/stream/abstract_file_based_stream.py +33 -10
  131. airbyte_cdk/sources/file_based/stream/concurrent/adapters.py +47 -11
  132. airbyte_cdk/sources/file_based/stream/concurrent/cursor/abstract_concurrent_file_based_cursor.py +13 -22
  133. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_concurrent_cursor.py +53 -17
  134. airbyte_cdk/sources/file_based/stream/concurrent/cursor/file_based_final_state_cursor.py +17 -5
  135. airbyte_cdk/sources/file_based/stream/cursor/abstract_file_based_cursor.py +3 -1
  136. airbyte_cdk/sources/file_based/stream/cursor/default_file_based_cursor.py +26 -9
  137. airbyte_cdk/sources/file_based/stream/default_file_based_stream.py +67 -21
  138. airbyte_cdk/sources/http_logger.py +5 -1
  139. airbyte_cdk/sources/message/repository.py +18 -4
  140. airbyte_cdk/sources/source.py +17 -7
  141. airbyte_cdk/sources/streams/availability_strategy.py +9 -3
  142. airbyte_cdk/sources/streams/call_rate.py +63 -19
  143. airbyte_cdk/sources/streams/checkpoint/checkpoint_reader.py +31 -7
  144. airbyte_cdk/sources/streams/checkpoint/substream_resumable_full_refresh_cursor.py +6 -2
  145. airbyte_cdk/sources/streams/concurrent/adapters.py +77 -22
  146. airbyte_cdk/sources/streams/concurrent/cursor.py +56 -20
  147. airbyte_cdk/sources/streams/concurrent/default_stream.py +9 -2
  148. airbyte_cdk/sources/streams/concurrent/helpers.py +6 -2
  149. airbyte_cdk/sources/streams/concurrent/partition_enqueuer.py +9 -2
  150. airbyte_cdk/sources/streams/concurrent/partition_reader.py +4 -1
  151. airbyte_cdk/sources/streams/concurrent/partitions/record.py +10 -2
  152. airbyte_cdk/sources/streams/concurrent/partitions/types.py +6 -2
  153. airbyte_cdk/sources/streams/concurrent/state_converters/abstract_stream_state_converter.py +25 -10
  154. airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py +32 -16
  155. airbyte_cdk/sources/streams/core.py +77 -22
  156. airbyte_cdk/sources/streams/http/availability_strategy.py +3 -1
  157. airbyte_cdk/sources/streams/http/error_handlers/default_error_mapping.py +4 -1
  158. airbyte_cdk/sources/streams/http/error_handlers/error_handler.py +3 -1
  159. airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py +16 -5
  160. airbyte_cdk/sources/streams/http/error_handlers/response_models.py +9 -3
  161. airbyte_cdk/sources/streams/http/exceptions.py +2 -2
  162. airbyte_cdk/sources/streams/http/http.py +133 -33
  163. airbyte_cdk/sources/streams/http/http_client.py +91 -29
  164. airbyte_cdk/sources/streams/http/rate_limiting.py +23 -7
  165. airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +19 -6
  166. airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +38 -11
  167. airbyte_cdk/sources/streams/http/requests_native_auth/token.py +13 -3
  168. airbyte_cdk/sources/types.py +5 -1
  169. airbyte_cdk/sources/utils/record_helper.py +12 -3
  170. airbyte_cdk/sources/utils/schema_helpers.py +9 -3
  171. airbyte_cdk/sources/utils/slice_logger.py +4 -1
  172. airbyte_cdk/sources/utils/transform.py +24 -9
  173. airbyte_cdk/sql/exceptions.py +19 -6
  174. airbyte_cdk/sql/secrets.py +3 -1
  175. airbyte_cdk/sql/shared/catalog_providers.py +13 -4
  176. airbyte_cdk/sql/shared/sql_processor.py +44 -14
  177. airbyte_cdk/test/catalog_builder.py +19 -8
  178. airbyte_cdk/test/entrypoint_wrapper.py +27 -8
  179. airbyte_cdk/test/mock_http/mocker.py +41 -11
  180. airbyte_cdk/test/mock_http/request.py +9 -3
  181. airbyte_cdk/test/mock_http/response.py +3 -1
  182. airbyte_cdk/test/mock_http/response_builder.py +29 -7
  183. airbyte_cdk/test/state_builder.py +10 -2
  184. airbyte_cdk/test/utils/data.py +6 -2
  185. airbyte_cdk/test/utils/http_mocking.py +3 -1
  186. airbyte_cdk/utils/airbyte_secrets_utils.py +3 -1
  187. airbyte_cdk/utils/analytics_message.py +10 -2
  188. airbyte_cdk/utils/datetime_format_inferrer.py +4 -1
  189. airbyte_cdk/utils/mapping_helpers.py +3 -1
  190. airbyte_cdk/utils/message_utils.py +11 -4
  191. airbyte_cdk/utils/print_buffer.py +6 -1
  192. airbyte_cdk/utils/schema_inferrer.py +30 -9
  193. airbyte_cdk/utils/spec_schema_transformations.py +3 -1
  194. airbyte_cdk/utils/traced_exception.py +35 -9
  195. {airbyte_cdk-6.5.3rc2.dist-info → airbyte_cdk-6.5.5.dist-info}/METADATA +7 -6
  196. {airbyte_cdk-6.5.3rc2.dist-info → airbyte_cdk-6.5.5.dist-info}/RECORD +198 -198
  197. {airbyte_cdk-6.5.3rc2.dist-info → airbyte_cdk-6.5.5.dist-info}/LICENSE.txt +0 -0
  198. {airbyte_cdk-6.5.3rc2.dist-info → airbyte_cdk-6.5.5.dist-info}/WHEEL +0 -0
@@ -14,11 +14,22 @@ from airbyte_cdk.models import Type as MessageType
14
14
  from airbyte_cdk.sources.message.repository import InMemoryMessageRepository
15
15
  from airbyte_cdk.sources.streams.call_rate import APIBudget
16
16
  from airbyte_cdk.sources.streams.checkpoint.cursor import Cursor
17
- from airbyte_cdk.sources.streams.checkpoint.resumable_full_refresh_cursor import ResumableFullRefreshCursor
18
- from airbyte_cdk.sources.streams.checkpoint.substream_resumable_full_refresh_cursor import SubstreamResumableFullRefreshCursor
17
+ from airbyte_cdk.sources.streams.checkpoint.resumable_full_refresh_cursor import (
18
+ ResumableFullRefreshCursor,
19
+ )
20
+ from airbyte_cdk.sources.streams.checkpoint.substream_resumable_full_refresh_cursor import (
21
+ SubstreamResumableFullRefreshCursor,
22
+ )
19
23
  from airbyte_cdk.sources.streams.core import CheckpointMixin, Stream, StreamData
20
- from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy, ErrorHandler, HttpStatusErrorHandler
21
- from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction
24
+ from airbyte_cdk.sources.streams.http.error_handlers import (
25
+ BackoffStrategy,
26
+ ErrorHandler,
27
+ HttpStatusErrorHandler,
28
+ )
29
+ from airbyte_cdk.sources.streams.http.error_handlers.response_models import (
30
+ ErrorResolution,
31
+ ResponseAction,
32
+ )
22
33
  from airbyte_cdk.sources.streams.http.http_client import HttpClient
23
34
  from airbyte_cdk.sources.types import Record, StreamSlice
24
35
  from airbyte_cdk.sources.utils.types import JsonType
@@ -35,9 +46,13 @@ class HttpStream(Stream, CheckpointMixin, ABC):
35
46
  """
36
47
 
37
48
  source_defined_cursor = True # Most HTTP streams use a source defined cursor (i.e: the user can't configure it like on a SQL table)
38
- page_size: Optional[int] = None # Use this variable to define page size for API http requests with pagination support
49
+ page_size: Optional[int] = (
50
+ None # Use this variable to define page size for API http requests with pagination support
51
+ )
39
52
 
40
- def __init__(self, authenticator: Optional[AuthBase] = None, api_budget: Optional[APIBudget] = None):
53
+ def __init__(
54
+ self, authenticator: Optional[AuthBase] = None, api_budget: Optional[APIBudget] = None
55
+ ):
41
56
  self._exit_on_rate_limit: bool = False
42
57
  self._http_client = HttpClient(
43
58
  name=self.name,
@@ -55,7 +70,11 @@ class HttpStream(Stream, CheckpointMixin, ABC):
55
70
  # 2. Streams with at least one cursor_field are incremental and thus a superior sync to RFR.
56
71
  # 3. Streams overriding read_records() do not guarantee that they will call the parent implementation which can perform
57
72
  # per-page checkpointing so RFR is only supported if a stream use the default `HttpStream.read_records()` method
58
- if not self.cursor and len(self.cursor_field) == 0 and type(self).read_records is HttpStream.read_records:
73
+ if (
74
+ not self.cursor
75
+ and len(self.cursor_field) == 0
76
+ and type(self).read_records is HttpStream.read_records
77
+ ):
59
78
  self.cursor = ResumableFullRefreshCursor()
60
79
 
61
80
  @property
@@ -100,7 +119,10 @@ class HttpStream(Stream, CheckpointMixin, ABC):
100
119
  return "GET"
101
120
 
102
121
  @property
103
- @deprecated(version="3.0.0", reason="You should set error_handler explicitly in HttpStream.get_error_handler() instead.")
122
+ @deprecated(
123
+ version="3.0.0",
124
+ reason="You should set error_handler explicitly in HttpStream.get_error_handler() instead.",
125
+ )
104
126
  def raise_on_http_errors(self) -> bool:
105
127
  """
106
128
  Override if needed. If set to False, allows opting-out of raising HTTP code exception.
@@ -108,7 +130,10 @@ class HttpStream(Stream, CheckpointMixin, ABC):
108
130
  return True
109
131
 
110
132
  @property
111
- @deprecated(version="3.0.0", reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.")
133
+ @deprecated(
134
+ version="3.0.0",
135
+ reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.",
136
+ )
112
137
  def max_retries(self) -> Union[int, None]:
113
138
  """
114
139
  Override if needed. Specifies maximum amount of retries for backoff policy. Return None for no limit.
@@ -116,7 +141,10 @@ class HttpStream(Stream, CheckpointMixin, ABC):
116
141
  return 5
117
142
 
118
143
  @property
119
- @deprecated(version="3.0.0", reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.")
144
+ @deprecated(
145
+ version="3.0.0",
146
+ reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.",
147
+ )
120
148
  def max_time(self) -> Union[int, None]:
121
149
  """
122
150
  Override if needed. Specifies maximum total waiting time (in seconds) for backoff policy. Return None for no limit.
@@ -124,7 +152,10 @@ class HttpStream(Stream, CheckpointMixin, ABC):
124
152
  return 60 * 10
125
153
 
126
154
  @property
127
- @deprecated(version="3.0.0", reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.")
155
+ @deprecated(
156
+ version="3.0.0",
157
+ reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.",
158
+ )
128
159
  def retry_factor(self) -> float:
129
160
  """
130
161
  Override if needed. Specifies factor for backoff policy.
@@ -262,7 +293,10 @@ class HttpStream(Stream, CheckpointMixin, ABC):
262
293
  """
263
294
  if hasattr(self, "should_retry"):
264
295
  error_handler = HttpStreamAdapterHttpStatusErrorHandler(
265
- stream=self, logger=logging.getLogger(), max_retries=self.max_retries, max_time=timedelta(seconds=self.max_time or 0)
296
+ stream=self,
297
+ logger=logging.getLogger(),
298
+ max_retries=self.max_retries,
299
+ max_time=timedelta(seconds=self.max_time or 0),
266
300
  )
267
301
  return error_handler
268
302
  else:
@@ -333,13 +367,17 @@ class HttpStream(Stream, CheckpointMixin, ABC):
333
367
  # A cursor_field indicates this is an incremental stream which offers better checkpointing than RFR enabled via the cursor
334
368
  if self.cursor_field or not isinstance(self.get_cursor(), ResumableFullRefreshCursor):
335
369
  yield from self._read_pages(
336
- lambda req, res, state, _slice: self.parse_response(res, stream_slice=_slice, stream_state=state),
370
+ lambda req, res, state, _slice: self.parse_response(
371
+ res, stream_slice=_slice, stream_state=state
372
+ ),
337
373
  stream_slice,
338
374
  stream_state,
339
375
  )
340
376
  else:
341
377
  yield from self._read_single_page(
342
- lambda req, res, state, _slice: self.parse_response(res, stream_slice=_slice, stream_state=state),
378
+ lambda req, res, state, _slice: self.parse_response(
379
+ res, stream_slice=_slice, stream_state=state
380
+ ),
343
381
  stream_slice,
344
382
  stream_state,
345
383
  )
@@ -373,7 +411,13 @@ class HttpStream(Stream, CheckpointMixin, ABC):
373
411
  def _read_pages(
374
412
  self,
375
413
  records_generator_fn: Callable[
376
- [requests.PreparedRequest, requests.Response, Mapping[str, Any], Optional[Mapping[str, Any]]], Iterable[StreamData]
414
+ [
415
+ requests.PreparedRequest,
416
+ requests.Response,
417
+ Mapping[str, Any],
418
+ Optional[Mapping[str, Any]],
419
+ ],
420
+ Iterable[StreamData],
377
421
  ],
378
422
  stream_slice: Optional[Mapping[str, Any]] = None,
379
423
  stream_state: Optional[Mapping[str, Any]] = None,
@@ -403,19 +447,29 @@ class HttpStream(Stream, CheckpointMixin, ABC):
403
447
  def _read_single_page(
404
448
  self,
405
449
  records_generator_fn: Callable[
406
- [requests.PreparedRequest, requests.Response, Mapping[str, Any], Optional[Mapping[str, Any]]], Iterable[StreamData]
450
+ [
451
+ requests.PreparedRequest,
452
+ requests.Response,
453
+ Mapping[str, Any],
454
+ Optional[Mapping[str, Any]],
455
+ ],
456
+ Iterable[StreamData],
407
457
  ],
408
458
  stream_slice: Optional[Mapping[str, Any]] = None,
409
459
  stream_state: Optional[Mapping[str, Any]] = None,
410
460
  ) -> Iterable[StreamData]:
411
- partition, cursor_slice, remaining_slice = self._extract_slice_fields(stream_slice=stream_slice)
461
+ partition, cursor_slice, remaining_slice = self._extract_slice_fields(
462
+ stream_slice=stream_slice
463
+ )
412
464
  stream_state = stream_state or {}
413
465
  next_page_token = cursor_slice or None
414
466
 
415
467
  request, response = self._fetch_next_page(remaining_slice, stream_state, next_page_token)
416
468
  yield from records_generator_fn(request, response, stream_state, remaining_slice)
417
469
 
418
- next_page_token = self.next_page_token(response) or {"__ab_full_refresh_sync_complete": True}
470
+ next_page_token = self.next_page_token(response) or {
471
+ "__ab_full_refresh_sync_complete": True
472
+ }
419
473
 
420
474
  cursor = self.get_cursor()
421
475
  if cursor:
@@ -425,7 +479,9 @@ class HttpStream(Stream, CheckpointMixin, ABC):
425
479
  yield from []
426
480
 
427
481
  @staticmethod
428
- def _extract_slice_fields(stream_slice: Optional[Mapping[str, Any]]) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]:
482
+ def _extract_slice_fields(
483
+ stream_slice: Optional[Mapping[str, Any]],
484
+ ) -> tuple[Mapping[str, Any], Mapping[str, Any], Mapping[str, Any]]:
429
485
  if not stream_slice:
430
486
  return {}, {}, {}
431
487
 
@@ -439,7 +495,11 @@ class HttpStream(Stream, CheckpointMixin, ABC):
439
495
  # fields for the partition and cursor_slice value
440
496
  partition = stream_slice.get("partition", {})
441
497
  cursor_slice = stream_slice.get("cursor_slice", {})
442
- remaining = {key: val for key, val in stream_slice.items() if key != "partition" and key != "cursor_slice"}
498
+ remaining = {
499
+ key: val
500
+ for key, val in stream_slice.items()
501
+ if key != "partition" and key != "cursor_slice"
502
+ }
443
503
  return partition, cursor_slice, remaining
444
504
 
445
505
  def _fetch_next_page(
@@ -448,18 +508,41 @@ class HttpStream(Stream, CheckpointMixin, ABC):
448
508
  stream_state: Optional[Mapping[str, Any]] = None,
449
509
  next_page_token: Optional[Mapping[str, Any]] = None,
450
510
  ) -> Tuple[requests.PreparedRequest, requests.Response]:
451
-
452
511
  request, response = self._http_client.send_request(
453
512
  http_method=self.http_method,
454
513
  url=self._join_url(
455
514
  self.url_base,
456
- self.path(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
515
+ self.path(
516
+ stream_state=stream_state,
517
+ stream_slice=stream_slice,
518
+ next_page_token=next_page_token,
519
+ ),
520
+ ),
521
+ request_kwargs=self.request_kwargs(
522
+ stream_state=stream_state,
523
+ stream_slice=stream_slice,
524
+ next_page_token=next_page_token,
525
+ ),
526
+ headers=self.request_headers(
527
+ stream_state=stream_state,
528
+ stream_slice=stream_slice,
529
+ next_page_token=next_page_token,
530
+ ),
531
+ params=self.request_params(
532
+ stream_state=stream_state,
533
+ stream_slice=stream_slice,
534
+ next_page_token=next_page_token,
535
+ ),
536
+ json=self.request_body_json(
537
+ stream_state=stream_state,
538
+ stream_slice=stream_slice,
539
+ next_page_token=next_page_token,
540
+ ),
541
+ data=self.request_body_data(
542
+ stream_state=stream_state,
543
+ stream_slice=stream_slice,
544
+ next_page_token=next_page_token,
457
545
  ),
458
- request_kwargs=self.request_kwargs(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
459
- headers=self.request_headers(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
460
- params=self.request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
461
- json=self.request_body_json(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
462
- data=self.request_body_data(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token),
463
546
  dedupe_query_params=True,
464
547
  log_formatter=self.get_log_formatter(),
465
548
  exit_on_rate_limit=self.exit_on_rate_limit,
@@ -482,18 +565,27 @@ class HttpSubStream(HttpStream, ABC):
482
565
  """
483
566
  super().__init__(**kwargs)
484
567
  self.parent = parent
485
- self.has_multiple_slices = True # Substreams are based on parent records which implies there are multiple slices
568
+ self.has_multiple_slices = (
569
+ True # Substreams are based on parent records which implies there are multiple slices
570
+ )
486
571
 
487
572
  # There are three conditions that dictate if RFR should automatically be applied to a stream
488
573
  # 1. Streams that explicitly initialize their own cursor should defer to it and not automatically apply RFR
489
574
  # 2. Streams with at least one cursor_field are incremental and thus a superior sync to RFR.
490
575
  # 3. Streams overriding read_records() do not guarantee that they will call the parent implementation which can perform
491
576
  # per-page checkpointing so RFR is only supported if a stream use the default `HttpStream.read_records()` method
492
- if not self.cursor and len(self.cursor_field) == 0 and type(self).read_records is HttpStream.read_records:
577
+ if (
578
+ not self.cursor
579
+ and len(self.cursor_field) == 0
580
+ and type(self).read_records is HttpStream.read_records
581
+ ):
493
582
  self.cursor = SubstreamResumableFullRefreshCursor()
494
583
 
495
584
  def stream_slices(
496
- self, sync_mode: SyncMode, cursor_field: Optional[List[str]] = None, stream_state: Optional[Mapping[str, Any]] = None
585
+ self,
586
+ sync_mode: SyncMode,
587
+ cursor_field: Optional[List[str]] = None,
588
+ stream_state: Optional[Mapping[str, Any]] = None,
497
589
  ) -> Iterable[Optional[Mapping[str, Any]]]:
498
590
  # read_stateless() assumes the parent is not concurrent. This is currently okay since the concurrent CDK does
499
591
  # not support either substreams or RFR, but something that needs to be considered once we do
@@ -509,7 +601,10 @@ class HttpSubStream(HttpStream, ABC):
509
601
  yield {"parent": parent_record}
510
602
 
511
603
 
512
- @deprecated(version="3.0.0", reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.")
604
+ @deprecated(
605
+ version="3.0.0",
606
+ reason="You should set backoff_strategies explicitly in HttpStream.get_backoff_strategy() instead.",
607
+ )
513
608
  class HttpStreamAdapterBackoffStrategy(BackoffStrategy):
514
609
  def __init__(self, stream: HttpStream):
515
610
  self.stream = stream
@@ -522,13 +617,18 @@ class HttpStreamAdapterBackoffStrategy(BackoffStrategy):
522
617
  return self.stream.backoff_time(response_or_exception) # type: ignore # noqa # HttpStream.backoff_time has been deprecated
523
618
 
524
619
 
525
- @deprecated(version="3.0.0", reason="You should set error_handler explicitly in HttpStream.get_error_handler() instead.")
620
+ @deprecated(
621
+ version="3.0.0",
622
+ reason="You should set error_handler explicitly in HttpStream.get_error_handler() instead.",
623
+ )
526
624
  class HttpStreamAdapterHttpStatusErrorHandler(HttpStatusErrorHandler):
527
625
  def __init__(self, stream: HttpStream, **kwargs): # type: ignore # noqa
528
626
  self.stream = stream
529
627
  super().__init__(**kwargs)
530
628
 
531
- def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
629
+ def interpret_response(
630
+ self, response_or_exception: Optional[Union[requests.Response, Exception]] = None
631
+ ) -> ErrorResolution:
532
632
  if isinstance(response_or_exception, Exception):
533
633
  return super().interpret_response(response_or_exception)
534
634
  elif isinstance(response_or_exception, requests.Response):
@@ -44,7 +44,9 @@ from airbyte_cdk.sources.streams.http.rate_limiting import (
44
44
  user_defined_backoff_handler,
45
45
  )
46
46
  from airbyte_cdk.utils.constants import ENV_REQUEST_CACHE_PATH
47
- from airbyte_cdk.utils.stream_status_utils import as_airbyte_message as stream_status_as_airbyte_message
47
+ from airbyte_cdk.utils.stream_status_utils import (
48
+ as_airbyte_message as stream_status_as_airbyte_message,
49
+ )
48
50
  from airbyte_cdk.utils.traced_exception import AirbyteTracedException
49
51
  from requests.auth import AuthBase
50
52
 
@@ -69,7 +71,6 @@ class MessageRepresentationAirbyteTracedErrors(AirbyteTracedException):
69
71
 
70
72
 
71
73
  class HttpClient:
72
-
73
74
  _DEFAULT_MAX_RETRY: int = 5
74
75
  _DEFAULT_MAX_TIME: int = 60 * 10
75
76
 
@@ -95,7 +96,10 @@ class HttpClient:
95
96
  self._use_cache = use_cache
96
97
  self._session = self._request_session()
97
98
  self._session.mount(
98
- "https://", requests.adapters.HTTPAdapter(pool_connections=MAX_CONNECTION_POOL_SIZE, pool_maxsize=MAX_CONNECTION_POOL_SIZE)
99
+ "https://",
100
+ requests.adapters.HTTPAdapter(
101
+ pool_connections=MAX_CONNECTION_POOL_SIZE, pool_maxsize=MAX_CONNECTION_POOL_SIZE
102
+ ),
99
103
  )
100
104
  if isinstance(authenticator, AuthBase):
101
105
  self._session.auth = authenticator
@@ -134,7 +138,9 @@ class HttpClient:
134
138
  sqlite_path = str(Path(cache_dir) / self.cache_filename)
135
139
  else:
136
140
  sqlite_path = "file::memory:?cache=shared"
137
- return CachedLimiterSession(sqlite_path, backend="sqlite", api_budget=self._api_budget, match_headers=True) # type: ignore # there are no typeshed stubs for requests_cache
141
+ return CachedLimiterSession(
142
+ sqlite_path, backend="sqlite", api_budget=self._api_budget, match_headers=True
143
+ ) # type: ignore # there are no typeshed stubs for requests_cache
138
144
  else:
139
145
  return LimiterSession(api_budget=self._api_budget)
140
146
 
@@ -145,7 +151,9 @@ class HttpClient:
145
151
  if isinstance(self._session, requests_cache.CachedSession):
146
152
  self._session.cache.clear() # type: ignore # cache.clear is not typed
147
153
 
148
- def _dedupe_query_params(self, url: str, params: Optional[Mapping[str, str]]) -> Mapping[str, str]:
154
+ def _dedupe_query_params(
155
+ self, url: str, params: Optional[Mapping[str, str]]
156
+ ) -> Mapping[str, str]:
149
157
  """
150
158
  Remove query parameters from params mapping if they are already encoded in the URL.
151
159
  :param url: URL with
@@ -157,7 +165,9 @@ class HttpClient:
157
165
  query_string = urllib.parse.urlparse(url).query
158
166
  query_dict = {k: v[0] for k, v in urllib.parse.parse_qs(query_string).items()}
159
167
 
160
- duplicate_keys_with_same_value = {k for k in query_dict.keys() if str(params.get(k)) == str(query_dict[k])}
168
+ duplicate_keys_with_same_value = {
169
+ k for k in query_dict.keys() if str(params.get(k)) == str(query_dict[k])
170
+ }
161
171
  return {k: v for k, v in params.items() if k not in duplicate_keys_with_same_value}
162
172
 
163
173
  def _create_prepared_request(
@@ -184,7 +194,9 @@ class HttpClient:
184
194
  args["json"] = json
185
195
  elif data:
186
196
  args["data"] = data
187
- prepared_request: requests.PreparedRequest = self._session.prepare_request(requests.Request(**args))
197
+ prepared_request: requests.PreparedRequest = self._session.prepare_request(
198
+ requests.Request(**args)
199
+ )
188
200
 
189
201
  return prepared_request
190
202
 
@@ -205,7 +217,11 @@ class HttpClient:
205
217
  """
206
218
  Determines the max time based on the provided error handler.
207
219
  """
208
- return self._error_handler.max_time if self._error_handler.max_time is not None else self._DEFAULT_MAX_TIME
220
+ return (
221
+ self._error_handler.max_time
222
+ if self._error_handler.max_time is not None
223
+ else self._DEFAULT_MAX_TIME
224
+ )
209
225
 
210
226
  def _send_with_retry(
211
227
  self,
@@ -229,11 +245,20 @@ class HttpClient:
229
245
  max_tries = max(0, max_retries) + 1
230
246
  max_time = self._max_time
231
247
 
232
- user_backoff_handler = user_defined_backoff_handler(max_tries=max_tries, max_time=max_time)(self._send)
248
+ user_backoff_handler = user_defined_backoff_handler(max_tries=max_tries, max_time=max_time)(
249
+ self._send
250
+ )
233
251
  rate_limit_backoff_handler = rate_limit_default_backoff_handler()
234
- backoff_handler = http_client_default_backoff_handler(max_tries=max_tries, max_time=max_time)
252
+ backoff_handler = http_client_default_backoff_handler(
253
+ max_tries=max_tries, max_time=max_time
254
+ )
235
255
  # backoff handlers wrap _send, so it will always return a response
236
- response = backoff_handler(rate_limit_backoff_handler(user_backoff_handler))(request, request_kwargs, log_formatter=log_formatter, exit_on_rate_limit=exit_on_rate_limit) # type: ignore # mypy can't infer that backoff_handler wraps _send
256
+ response = backoff_handler(rate_limit_backoff_handler(user_backoff_handler))(
257
+ request,
258
+ request_kwargs,
259
+ log_formatter=log_formatter,
260
+ exit_on_rate_limit=exit_on_rate_limit,
261
+ ) # type: ignore # mypy can't infer that backoff_handler wraps _send
237
262
 
238
263
  return response
239
264
 
@@ -244,7 +269,6 @@ class HttpClient:
244
269
  log_formatter: Optional[Callable[[requests.Response], Any]] = None,
245
270
  exit_on_rate_limit: Optional[bool] = False,
246
271
  ) -> requests.Response:
247
-
248
272
  if request not in self._request_attempt_count:
249
273
  self._request_attempt_count[request] = 1
250
274
  else:
@@ -253,7 +277,8 @@ class HttpClient:
253
277
  self._session.auth(request)
254
278
 
255
279
  self._logger.debug(
256
- "Making outbound API request", extra={"headers": request.headers, "url": request.url, "request_body": request.body}
280
+ "Making outbound API request",
281
+ extra={"headers": request.headers, "url": request.url, "request_body": request.body},
257
282
  )
258
283
 
259
284
  response: Optional[requests.Response] = None
@@ -264,7 +289,9 @@ class HttpClient:
264
289
  except requests.RequestException as e:
265
290
  exc = e
266
291
 
267
- error_resolution: ErrorResolution = self._error_handler.interpret_response(response if response is not None else exc)
292
+ error_resolution: ErrorResolution = self._error_handler.interpret_response(
293
+ response if response is not None else exc
294
+ )
268
295
 
269
296
  # Evaluation of response.text can be heavy, for example, if streaming a large response
270
297
  # Do it only in debug mode
@@ -276,11 +303,20 @@ class HttpClient:
276
303
  )
277
304
  else:
278
305
  self._logger.debug(
279
- "Receiving response", extra={"headers": response.headers, "status": response.status_code, "body": response.text}
306
+ "Receiving response",
307
+ extra={
308
+ "headers": response.headers,
309
+ "status": response.status_code,
310
+ "body": response.text,
311
+ },
280
312
  )
281
313
 
282
314
  # Request/response logging for declarative cdk
283
- if log_formatter is not None and response is not None and self._message_repository is not None:
315
+ if (
316
+ log_formatter is not None
317
+ and response is not None
318
+ and self._message_repository is not None
319
+ ):
284
320
  formatter = log_formatter
285
321
  self._message_repository.log_message(
286
322
  Level.DEBUG,
@@ -288,7 +324,11 @@ class HttpClient:
288
324
  )
289
325
 
290
326
  self._handle_error_resolution(
291
- response=response, exc=exc, request=request, error_resolution=error_resolution, exit_on_rate_limit=exit_on_rate_limit
327
+ response=response,
328
+ exc=exc,
329
+ request=request,
330
+ error_resolution=error_resolution,
331
+ exit_on_rate_limit=exit_on_rate_limit,
292
332
  )
293
333
 
294
334
  return response # type: ignore # will either return a valid response of type requests.Response or raise an exception
@@ -307,7 +347,9 @@ class HttpClient:
307
347
  reasons = [AirbyteStreamStatusReason(type=AirbyteStreamStatusReasonType.RATE_LIMITED)]
308
348
  message = orjson.dumps(
309
349
  AirbyteMessageSerializer.dump(
310
- stream_status_as_airbyte_message(StreamDescriptor(name=self._name), AirbyteStreamStatus.RUNNING, reasons)
350
+ stream_status_as_airbyte_message(
351
+ StreamDescriptor(name=self._name), AirbyteStreamStatus.RUNNING, reasons
352
+ )
311
353
  )
312
354
  ).decode()
313
355
 
@@ -321,7 +363,9 @@ class HttpClient:
321
363
  if response is not None:
322
364
  error_message = f"'{request.method}' request to '{request.url}' failed with status code '{response.status_code}' and error message '{self._error_message_parser.parse_response_error_message(response)}'"
323
365
  else:
324
- error_message = f"'{request.method}' request to '{request.url}' failed with exception: '{exc}'"
366
+ error_message = (
367
+ f"'{request.method}' request to '{request.url}' failed with exception: '{exc}'"
368
+ )
325
369
 
326
370
  raise MessageRepresentationAirbyteTracedErrors(
327
371
  internal_message=error_message,
@@ -331,20 +375,22 @@ class HttpClient:
331
375
 
332
376
  elif error_resolution.response_action == ResponseAction.IGNORE:
333
377
  if response is not None:
334
- log_message = (
335
- f"Ignoring response for '{request.method}' request to '{request.url}' with response code '{response.status_code}'"
336
- )
378
+ log_message = f"Ignoring response for '{request.method}' request to '{request.url}' with response code '{response.status_code}'"
337
379
  else:
338
380
  log_message = f"Ignoring response for '{request.method}' request to '{request.url}' with error '{exc}'"
339
381
 
340
382
  self._logger.info(error_resolution.error_message or log_message)
341
383
 
342
384
  # TODO: Consider dynamic retry count depending on subsequent error codes
343
- elif error_resolution.response_action == ResponseAction.RETRY or error_resolution.response_action == ResponseAction.RATE_LIMITED:
385
+ elif (
386
+ error_resolution.response_action == ResponseAction.RETRY
387
+ or error_resolution.response_action == ResponseAction.RATE_LIMITED
388
+ ):
344
389
  user_defined_backoff_time = None
345
390
  for backoff_strategy in self._backoff_strategies:
346
391
  backoff_time = backoff_strategy.backoff_time(
347
- response_or_exception=response if response is not None else exc, attempt_count=self._request_attempt_count[request]
392
+ response_or_exception=response if response is not None else exc,
393
+ attempt_count=self._request_attempt_count[request],
348
394
  )
349
395
  if backoff_time:
350
396
  user_defined_backoff_time = backoff_time
@@ -354,7 +400,10 @@ class HttpClient:
354
400
  or f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action}."
355
401
  )
356
402
 
357
- retry_endlessly = error_resolution.response_action == ResponseAction.RATE_LIMITED and not exit_on_rate_limit
403
+ retry_endlessly = (
404
+ error_resolution.response_action == ResponseAction.RATE_LIMITED
405
+ and not exit_on_rate_limit
406
+ )
358
407
 
359
408
  if user_defined_backoff_time:
360
409
  raise UserDefinedBackoffException(
@@ -365,10 +414,14 @@ class HttpClient:
365
414
  )
366
415
 
367
416
  elif retry_endlessly:
368
- raise RateLimitBackoffException(request=request, response=response or exc, error_message=error_message)
417
+ raise RateLimitBackoffException(
418
+ request=request, response=response or exc, error_message=error_message
419
+ )
369
420
 
370
421
  raise DefaultBackoffException(
371
- request=request, response=(response if response is not None else exc), error_message=error_message
422
+ request=request,
423
+ response=(response if response is not None else exc),
424
+ error_message=error_message,
372
425
  )
373
426
 
374
427
  elif response:
@@ -400,11 +453,20 @@ class HttpClient:
400
453
  """
401
454
 
402
455
  request: requests.PreparedRequest = self._create_prepared_request(
403
- http_method=http_method, url=url, dedupe_query_params=dedupe_query_params, headers=headers, params=params, json=json, data=data
456
+ http_method=http_method,
457
+ url=url,
458
+ dedupe_query_params=dedupe_query_params,
459
+ headers=headers,
460
+ params=params,
461
+ json=json,
462
+ data=data,
404
463
  )
405
464
 
406
465
  response: requests.Response = self._send_with_retry(
407
- request=request, request_kwargs=request_kwargs, log_formatter=log_formatter, exit_on_rate_limit=exit_on_rate_limit
466
+ request=request,
467
+ request_kwargs=request_kwargs,
468
+ log_formatter=log_formatter,
469
+ exit_on_rate_limit=exit_on_rate_limit,
408
470
  )
409
471
 
410
472
  return request, response
@@ -10,7 +10,11 @@ from typing import Any, Callable, Mapping, Optional
10
10
  import backoff
11
11
  from requests import PreparedRequest, RequestException, Response, codes, exceptions
12
12
 
13
- from .exceptions import DefaultBackoffException, RateLimitBackoffException, UserDefinedBackoffException
13
+ from .exceptions import (
14
+ DefaultBackoffException,
15
+ RateLimitBackoffException,
16
+ UserDefinedBackoffException,
17
+ )
14
18
 
15
19
  TRANSIENT_EXCEPTIONS = (
16
20
  DefaultBackoffException,
@@ -32,7 +36,9 @@ def default_backoff_handler(
32
36
  def log_retry_attempt(details: Mapping[str, Any]) -> None:
33
37
  _, exc, _ = sys.exc_info()
34
38
  if isinstance(exc, RequestException) and exc.response:
35
- logger.info(f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}")
39
+ logger.info(
40
+ f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}"
41
+ )
36
42
  logger.info(
37
43
  f"Caught retryable error '{str(exc)}' after {details['tries']} tries. Waiting {details['wait']} seconds then retrying..."
38
44
  )
@@ -71,7 +77,9 @@ def http_client_default_backoff_handler(
71
77
  def log_retry_attempt(details: Mapping[str, Any]) -> None:
72
78
  _, exc, _ = sys.exc_info()
73
79
  if isinstance(exc, RequestException) and exc.response:
74
- logger.info(f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}")
80
+ logger.info(
81
+ f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}"
82
+ )
75
83
  logger.info(
76
84
  f"Caught retryable error '{str(exc)}' after {details['tries']} tries. Waiting {details['wait']} seconds then retrying..."
77
85
  )
@@ -99,7 +107,9 @@ def user_defined_backoff_handler(
99
107
  _, exc, _ = sys.exc_info()
100
108
  if isinstance(exc, UserDefinedBackoffException):
101
109
  if exc.response:
102
- logger.info(f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}")
110
+ logger.info(
111
+ f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}"
112
+ )
103
113
  retry_after = exc.backoff
104
114
  logger.info(f"Retrying. Sleeping for {retry_after} seconds")
105
115
  time.sleep(retry_after + 1) # extra second to cover any fractions of second
@@ -107,7 +117,9 @@ def user_defined_backoff_handler(
107
117
  def log_give_up(details: Mapping[str, Any]) -> None:
108
118
  _, exc, _ = sys.exc_info()
109
119
  if isinstance(exc, RequestException):
110
- logger.error(f"Max retry limit reached in {details['elapsed']}s. Request: {exc.request}, Response: {exc.response}")
120
+ logger.error(
121
+ f"Max retry limit reached in {details['elapsed']}s. Request: {exc.request}, Response: {exc.response}"
122
+ )
111
123
  else:
112
124
  logger.error("Max retry limit reached for unknown request and response")
113
125
 
@@ -124,11 +136,15 @@ def user_defined_backoff_handler(
124
136
  )
125
137
 
126
138
 
127
- def rate_limit_default_backoff_handler(**kwargs: Any) -> Callable[[SendRequestCallableType], SendRequestCallableType]:
139
+ def rate_limit_default_backoff_handler(
140
+ **kwargs: Any,
141
+ ) -> Callable[[SendRequestCallableType], SendRequestCallableType]:
128
142
  def log_retry_attempt(details: Mapping[str, Any]) -> None:
129
143
  _, exc, _ = sys.exc_info()
130
144
  if isinstance(exc, RequestException) and exc.response:
131
- logger.info(f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}")
145
+ logger.info(
146
+ f"Status code: {exc.response.status_code!r}, Response Content: {exc.response.content!r}"
147
+ )
132
148
  logger.info(
133
149
  f"Caught retryable error '{str(exc)}' after {details['tries']} tries. Waiting {details['wait']} seconds then retrying..."
134
150
  )