From e09401ce904e85121a2e8699def5c3ef744f5c87 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 23 May 2024 08:58:55 -0700 Subject: [PATCH 01/29] Update reference for `ResponseAction` --- .../parsers/model_to_component_factory.py | 2 +- .../error_handlers/composite_error_handler.py | 2 +- .../error_handlers/default_error_handler.py | 2 +- .../error_handlers/http_response_filter.py | 2 +- .../requesters/error_handlers/response_action.py | 16 ---------------- .../requesters/error_handlers/response_status.py | 2 +- .../declarative/requesters/http_requester.py | 2 +- .../parsers/test_model_to_component_factory.py | 2 +- .../test_composite_error_handler.py | 2 +- .../error_handlers/test_default_error_handler.py | 2 +- .../error_handlers/test_http_response_filter.py | 2 +- .../error_handlers/test_response_status.py | 2 +- 12 files changed, 11 insertions(+), 27 deletions(-) delete mode 100644 airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index bb0790f9ef45..01d51f54a7f9 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -107,7 +107,7 @@ WaitTimeFromHeaderBackoffStrategy, WaitUntilTimeFromHeaderBackoffStrategy, ) -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.paginators import DefaultPaginator, NoPagination, PaginatorTestReadDecorator from airbyte_cdk.sources.declarative.requesters.paginators.strategies import ( CursorPaginationStrategy, diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 5c6232039d83..5efd2b0fe8e7 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -8,7 +8,7 @@ import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import requests from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 1207ec344243..cf89786c5365 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -13,7 +13,7 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.types import Config diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 670e65338944..0bf73d2011a6 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -8,7 +8,7 @@ import requests from airbyte_cdk.sources.declarative.interpolation import InterpolatedString from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.streams.http.http import HttpStream from airbyte_cdk.sources.types import Config diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py deleted file mode 100644 index d5dece93499e..000000000000 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_action.py +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -from enum import Enum - - -class ResponseAction(Enum): - """ - Response statuses for non retriable responses - """ - - SUCCESS = "SUCCESS" # "Request was successful" - FAIL = "FAIL" # "Request failed unexpectedly" - IGNORE = "IGNORE" # "Request failed but can be ignored" - RETRY = "RETRY" # Request failed and should be retried diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py index 3b476372b7a1..940ff218887d 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py @@ -4,7 +4,7 @@ from typing import Any, Final, Optional, Union -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction class ResponseStatus: diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index 81cd406d482f..b3324c71e858 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -19,7 +19,7 @@ from airbyte_cdk.sources.declarative.exceptions import ReadException from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( InterpolatedRequestOptionsProvider, diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py b/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py index 6306a854ec6e..73c37b305fa5 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py @@ -55,7 +55,7 @@ WaitTimeFromHeaderBackoffStrategy, WaitUntilTimeFromHeaderBackoffStrategy, ) -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.paginators import DefaultPaginator from airbyte_cdk.sources.declarative.requesters.paginators.strategies import ( CursorPaginationStrategy, diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py index c5943e94d180..17a9eca3322f 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py @@ -9,7 +9,7 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter from airbyte_cdk.sources.declarative.requesters.error_handlers.composite_error_handler import CompositeErrorHandler from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler, ResponseStatus -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction SOME_BACKOFF_TIME = 60 diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index 60e74c6dffb5..febfd54533f1 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -13,7 +13,7 @@ HttpResponseFilter, ResponseStatus, ) -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction SOME_BACKOFF_TIME = 60 diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index 03096f040413..a2199a050dc5 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -7,7 +7,7 @@ import pytest import requests from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py index f9682261aa4f..228df3cd5af8 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py @@ -3,7 +3,7 @@ # import pytest -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_action import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus From 3d4037a8de4a24233d4b46c9aa7b05e0cf76ce3a Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Fri, 24 May 2024 11:31:56 -0700 Subject: [PATCH 02/29] Updates `ErrorHandler`, `BackoffStrategy`, and `HttpRequester` for new error handling flow --- .../declarative_component_schema.yaml | 1 - .../models/declarative_component_schema.py | 10 +-- .../parsers/model_to_component_factory.py | 2 +- .../constant_backoff_strategy.py | 4 +- .../exponential_backoff_strategy.py | 6 +- .../error_handlers/backoff_strategy.py | 19 +---- .../error_handlers/composite_error_handler.py | 28 ++----- .../error_handlers/default_error_handler.py | 77 ++++++++--------- .../error_handlers/error_handler.py | 35 +------- .../error_handlers/http_response_filter.py | 73 +++++++++------- .../declarative/requesters/http_requester.py | 84 +++++++++++-------- .../http_status_error_handler.py | 4 +- .../http/error_handlers/response_models.py | 9 ++ .../test_model_to_component_factory.py | 2 +- .../test_default_error_handler.py | 6 +- .../test_http_response_filter.py | 2 +- .../error_handlers/test_response_status.py | 2 +- 17 files changed, 166 insertions(+), 198 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/declarative_component_schema.yaml b/airbyte-cdk/python/airbyte_cdk/sources/declarative/declarative_component_schema.yaml index 9e908839904a..c8b5e42edd40 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/declarative_component_schema.yaml +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/declarative_component_schema.yaml @@ -1545,7 +1545,6 @@ definitions: type: object required: - type - - action properties: type: type: string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py index 7302ba28e135..011ecb0492e5 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py @@ -481,8 +481,8 @@ class Action(Enum): class HttpResponseFilter(BaseModel): type: Literal['HttpResponseFilter'] - action: Action = Field( - ..., + action: Optional[Action] = Field( + None, description='Action to execute if a response matches the filter.', examples=['SUCCESS', 'FAIL', 'RETRY', 'IGNORE'], title='Action', @@ -874,11 +874,7 @@ class CursorPagination(BaseModel): cursor_value: str = Field( ..., description='Value of the cursor defining the next page to fetch.', - examples=[ - '{{ headers.link.next.cursor }}', - "{{ last_record['key'] }}", - "{{ response['nextPage'] }}", - ], + examples=['{{ headers.link.next.cursor }}', "{{ last_record['key'] }}", "{{ response['nextPage'] }}"], title='Cursor Value', ) page_size: Optional[int] = Field(None, description='The number of records to include in each pages.', examples=[100], title='Page Size') diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index 01d51f54a7f9..4f524c05544c 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -107,7 +107,6 @@ WaitTimeFromHeaderBackoffStrategy, WaitUntilTimeFromHeaderBackoffStrategy, ) -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.paginators import DefaultPaginator, NoPagination, PaginatorTestReadDecorator from airbyte_cdk.sources.declarative.requesters.paginators.strategies import ( CursorPaginationStrategy, @@ -127,6 +126,7 @@ from airbyte_cdk.sources.declarative.transformations import AddFields, RecordTransformation, RemoveFields from airbyte_cdk.sources.declarative.transformations.add_fields import AddedFieldDefinition from airbyte_cdk.sources.message import InMemoryMessageRepository, LogAppenderMessageRepositoryDecorator, MessageRepository +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.types import Config from airbyte_cdk.sources.utils.transform import TypeTransformer from isodate import parse_duration diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py index 39cda5ea33df..fd29724a60de 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py @@ -7,7 +7,7 @@ import requests from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString -from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy +from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy from airbyte_cdk.sources.types import Config @@ -32,5 +32,5 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.backoff_time_in_seconds = InterpolatedString.create(self.backoff_time_in_seconds, parameters=parameters) - def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time(self, response_or_exception: Optional[Union[requests.Response, Exception]], attempt_count: int) -> Optional[float]: return self.backoff_time_in_seconds.eval(self.config) # type: ignore # backoff_time_in_seconds is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py index 1d429599b57c..8dd0b30bdd97 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py @@ -7,7 +7,7 @@ import requests from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString -from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy +from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy from airbyte_cdk.sources.types import Config @@ -32,5 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.factor = InterpolatedString.create(self.factor, parameters=parameters) - def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time( + self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int + ) -> Optional[float]: return self.factor.eval(self.config) * 2**attempt_count # type: ignore # factor is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py index c7c07b6acd0a..b53b4a18eb63 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategy.py @@ -2,24 +2,13 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # -from abc import abstractmethod -from dataclasses import dataclass -from typing import Optional +from abc import ABC -import requests +from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy as HttpBackoffStrategy -@dataclass -class BackoffStrategy: +class BackoffStrategy(HttpBackoffStrategy, ABC): """ Backoff strategy defining how long to wait before retrying a request that resulted in an error. + References Python CDK BackoffStrategy """ - - @abstractmethod - def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]: - """ - Return time to wait before retrying the request. - :param response: response received for the request to retry - :param attempt_count: number of attempts to submit the request - :return: time to wait in seconds - """ diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 5efd2b0fe8e7..f9adbed1ac12 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -3,13 +3,11 @@ # from dataclasses import InitVar, dataclass -from typing import Any, List, Mapping, Union +from typing import Any, List, Mapping, Optional, Union -import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import requests -from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus +from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, ErrorResolution @dataclass @@ -44,20 +42,8 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: if not self.error_handlers: raise ValueError("CompositeErrorHandler expects at least 1 underlying error handler") - @property - def max_retries(self) -> Union[int, None]: - return self.error_handlers[0].max_retries - - @property - def max_time(self) -> Union[int, None]: - return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) - - def interpret_response(self, response: requests.Response) -> ResponseStatus: - should_retry = ResponseStatus(ResponseAction.FAIL) + def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: for retrier in self.error_handlers: - should_retry = retrier.interpret_response(response) - if should_retry.action == ResponseAction.SUCCESS: - return response_status.SUCCESS - if should_retry == response_status.IGNORE or should_retry.action == ResponseAction.RETRY: - return should_retry - return should_retry + error_resolution = retrier.interpret_response(response_or_exception) + return error_resolution + return DEFAULT_ERROR_RESOLUTION diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index cf89786c5365..e6c3ce84e5b7 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -5,16 +5,20 @@ from dataclasses import InitVar, dataclass, field from typing import Any, List, Mapping, MutableMapping, Optional, Union -import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import requests +from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import ( ExponentialBackoffStrategy, ) from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy -from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus +from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ( + DEFAULT_ERROR_RESOLUTION, + SUCCESS_RESOLUTION, + ErrorResolution, + ResponseAction, +) from airbyte_cdk.sources.types import Config @@ -103,55 +107,40 @@ class DefaultErrorHandler(ErrorHandler): def __post_init__(self, parameters: Mapping[str, Any]) -> None: self.response_filters = self.response_filters or [] - if not self.response_filters: - self.response_filters.append( - HttpResponseFilter( - ResponseAction.RETRY, http_codes=HttpResponseFilter.DEFAULT_RETRIABLE_ERRORS, config=self.config, parameters={} - ) - ) - self.response_filters.append(HttpResponseFilter(ResponseAction.IGNORE, config={}, parameters={})) + self.response_filters.append(HttpResponseFilter(config=self.config, parameters={})) if not self.backoff_strategies: self.backoff_strategies = [DefaultErrorHandler.DEFAULT_BACKOFF_STRATEGY(parameters=parameters, config=self.config)] self._last_request_to_attempt_count: MutableMapping[requests.PreparedRequest, int] = {} - @property # type: ignore # overwrite the property to handle the case where max_retries is not provided in the constructor - def max_retries(self) -> Union[int, None]: - return self._max_retries - - @max_retries.setter - def max_retries(self, value: int) -> None: - # Covers the case where max_retries is not provided in the constructor, which causes the property object - # to be set which we need to avoid doing - if not isinstance(value, property): - self._max_retries = value - - def interpret_response(self, response: requests.Response) -> ResponseStatus: - request = response.request - - if request not in self._last_request_to_attempt_count: - self._last_request_to_attempt_count = {request: 1} - else: - self._last_request_to_attempt_count[request] += 1 - if self.response_filters: - for response_filter in self.response_filters: - matched_status = response_filter.matches( - response=response, backoff_time=self._backoff_time(response, self._last_request_to_attempt_count[request]) - ) - if matched_status is not None: - return matched_status - - if response.ok: - return response_status.SUCCESS - # Fail if the response matches no filters - return response_status.FAIL - - def _backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: + + if isinstance(response_or_exception, requests.Response): + if response_or_exception.ok: + return SUCCESS_RESOLUTION + + request = response_or_exception.request + + if request not in self._last_request_to_attempt_count: + self._last_request_to_attempt_count = {request: 1} + else: + self._last_request_to_attempt_count[request] += 1 + if self.response_filters: + for response_filter in self.response_filters: + matched_status = response_filter.matches(response=response_or_exception) + if matched_status is not None: + return matched_status + # Return default error resolution (retry) + return DEFAULT_ERROR_RESOLUTION + + def backoff_time( + self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int + ) -> Optional[float]: backoff = None if self.backoff_strategies: for backoff_strategies in self.backoff_strategies: - backoff = backoff_strategies.backoff(response, attempt_count) + backoff = backoff_strategies.backoff_time(response_or_exception, attempt_count) if backoff: return backoff return backoff diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py index 0a6e8a10166b..279545e7c4d9 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py @@ -2,42 +2,15 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # -from abc import abstractmethod +from abc import ABC from dataclasses import dataclass -from typing import Union -import requests -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus +from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler as HttpErrorHandler @dataclass -class ErrorHandler: +class ErrorHandler(HttpErrorHandler, ABC): """ Defines whether a request was successful and how to handle a failure. + References Python CDK ErrorHandler """ - - @property - @abstractmethod - def max_retries(self) -> Union[int, None]: - """ - Specifies maximum amount of retries for backoff policy. Return None for no limit. - """ - pass - - @property - @abstractmethod - def max_time(self) -> Union[int, None]: - """ - Specifies maximum total waiting time (in seconds) for backoff policy. Return None for no limit. - """ - pass - - @abstractmethod - def interpret_response(self, response: requests.Response) -> ResponseStatus: - """ - Evaluate response status describing whether a failing request should be retried or ignored. - - :param response: response to evaluate - :return: response status - """ - pass diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 0bf73d2011a6..f3d2e19c5dd5 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -6,11 +6,12 @@ from typing import Any, Mapping, Optional, Set, Union import requests +from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.interpolation import InterpolatedString from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus -from airbyte_cdk.sources.streams.http.http import HttpStream +from airbyte_cdk.sources.streams.http.error_handlers import JsonErrorMessageParser +from airbyte_cdk.sources.streams.http.error_handlers.http_status_error_handler import HttpStatusErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction from airbyte_cdk.sources.types import Config @@ -27,53 +28,67 @@ class HttpResponseFilter: error_message (Union[InterpolatedString, str): error message to display if the response matches the filter """ - TOO_MANY_REQUESTS_ERRORS = {429} - DEFAULT_RETRIABLE_ERRORS = set([x for x in range(500, 600)]).union(TOO_MANY_REQUESTS_ERRORS) + _DEFAULT_ERROR_MAPPING = HttpStatusErrorHandler.DEFAULT_ERROR_MAPPING - action: Union[ResponseAction, str] config: Config parameters: InitVar[Mapping[str, Any]] + action: Optional[Union[ResponseAction, str]] = None http_codes: Optional[Set[int]] = None error_message_contains: Optional[str] = None predicate: Union[InterpolatedBoolean, str] = "" error_message: Union[InterpolatedString, str] = "" def __post_init__(self, parameters: Mapping[str, Any]) -> None: - if isinstance(self.action, str): + + if self.http_codes is not None and self.action is None: + raise ValueError("HttpResponseFilter requires a prescribed action if http_codes are specified") + + if self.action is not None and isinstance(self.action, str): self.action = ResponseAction[self.action] self.http_codes = self.http_codes or set() if isinstance(self.predicate, str): self.predicate = InterpolatedBoolean(condition=self.predicate, parameters=parameters) self.error_message = InterpolatedString.create(string_or_interpolated=self.error_message, parameters=parameters) - def matches(self, response: requests.Response, backoff_time: Optional[float] = None) -> Optional[ResponseStatus]: - filter_action = self._matches_filter(response) + def matches(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> Optional[ErrorResolution]: + filter_action = self._matches_filter(response_or_exception) + mapped_key = ( + response_or_exception.status_code if isinstance(response_or_exception, requests.Response) else response_or_exception.__class__ + ) + if filter_action is not None: - error_message = self._create_error_message(response) - if filter_action == ResponseAction.RETRY: - return ResponseStatus( - response_action=ResponseAction.RETRY, - retry_in=backoff_time, - error_message=error_message, - ) - else: - return ResponseStatus(filter_action, error_message=error_message) + error_message = self._create_error_message(response_or_exception) + default_mapped_error_resolution = self._match_default_error_mapping(mapped_key) + failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error + return ErrorResolution( + response_action=filter_action, + failure_type=failure_type, + error_message=error_message, + ) + else: + mapped_error_resolution = self._match_default_error_mapping(mapped_key) + if mapped_error_resolution: + return mapped_error_resolution + return None - def _matches_filter(self, response: requests.Response) -> Optional[ResponseAction]: + def _match_default_error_mapping(self, mapped_key: Union[int, type[Exception]]) -> Optional[ErrorResolution]: + return self._DEFAULT_ERROR_MAPPING.get(mapped_key) + + def _matches_filter(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> Optional[ResponseAction]: """ - Apply the filter on the response and return the action to execute if it matches + Apply the HTTP filter on the response and return the action to execute if it matches :param response: The HTTP response to evaluate :return: The action to execute. None if the response does not match the filter """ - if ( - response.status_code in self.http_codes # type: ignore # http_codes set is always initialized to a value in __post_init__ - or (self._response_matches_predicate(response)) - or (self._response_contains_error_message(response)) - ): - return self.action # type: ignore # action is always cast to a ResponseAction not a str - else: - return None + if isinstance(response_or_exception, requests.Response): + if ( + response_or_exception.status_code in self.http_codes # type: ignore # http_codes set is always initialized to a value in __post_init__ + or (self._response_matches_predicate(response_or_exception)) + or (self._response_contains_error_message(response_or_exception)) + ): + return self.action # type: ignore # action is always cast to a ResponseAction not a str + return None @staticmethod def _safe_response_json(response: requests.Response) -> dict[str, Any]: @@ -97,5 +112,5 @@ def _response_contains_error_message(self, response: requests.Response) -> bool: if not self.error_message_contains: return False else: - error_message = HttpStream.parse_response_error_message(response) + error_message = JsonErrorMessageParser.parse_response_error_message(response) return bool(error_message and self.error_message_contains in error_message) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index b3324c71e858..9ba04e02dbe1 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -19,20 +19,20 @@ from airbyte_cdk.sources.declarative.exceptions import ReadException from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( InterpolatedRequestOptionsProvider, ) from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod, Requester from airbyte_cdk.sources.http_config import MAX_CONNECTION_POOL_SIZE from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository +from airbyte_cdk.sources.streams.http.error_handlers import ErrorResolution, ResponseAction from airbyte_cdk.sources.streams.http.exceptions import DefaultBackoffException, RequestBodyException, UserDefinedBackoffException from airbyte_cdk.sources.streams.http.http import BODY_REQUEST_METHODS -from airbyte_cdk.sources.streams.http.rate_limiting import default_backoff_handler, user_defined_backoff_handler +from airbyte_cdk.sources.streams.http.rate_limiting import http_client_default_backoff_handler, user_defined_backoff_handler from airbyte_cdk.sources.types import Config, StreamSlice, StreamState from airbyte_cdk.utils.constants import ENV_REQUEST_CACHE_PATH from airbyte_cdk.utils.mapping_helpers import combine_mappings +from airbyte_cdk.utils.traced_exception import AirbyteTracedException from requests.auth import AuthBase @@ -141,7 +141,7 @@ def get_path( def get_method(self) -> HttpMethod: return self._http_method - def interpret_response_status(self, response: requests.Response) -> ResponseStatus: + def interpret_response_status(self, response: requests.Response) -> ErrorResolution: if self.error_handler is None: raise ValueError("Cannot interpret response status without an error handler") @@ -257,10 +257,11 @@ def _backoff_time(self, response: requests.Response) -> Optional[float]: """ if self.error_handler is None: return None - should_retry = self.interpret_response_status(response) - if should_retry.action != ResponseAction.RETRY: - raise ValueError(f"backoff_time can only be applied on retriable response action. Got {should_retry.action}") - assert should_retry.action == ResponseAction.RETRY + error_resolution = self.interpret_response_status(response) + should_retry = error_resolution.response_action == ResponseAction.RETRY + if not should_retry: + raise ValueError(f"backoff_time can only be applied on retriable response action. Got {error_resolution.response_action}") + assert error_resolution.repsonse_action == ResponseAction.RETRY return should_retry.retry_in def _error_message(self, response: requests.Response) -> str: @@ -458,7 +459,7 @@ def send_request( ) response = self._send_with_retry(request, log_formatter=log_formatter) - return self._validate_response(response) + return response def _send_with_retry( self, @@ -501,7 +502,7 @@ def _send_with_retry( max_tries = max(0, max_tries) + 1 user_backoff_handler = user_defined_backoff_handler(max_tries=max_tries, max_time=max_time)(self._send) # type: ignore # we don't pass in kwargs to the backoff handler - backoff_handler = default_backoff_handler(max_tries=max_tries, max_time=max_time, factor=self._DEFAULT_RETRY_FACTOR) + backoff_handler = http_client_default_backoff_handler(max_tries=max_tries, max_time=max_time, factor=self._DEFAULT_RETRY_FACTOR) # backoff handlers wrap _send, so it will always return a response return backoff_handler(user_backoff_handler)(request, log_formatter=log_formatter) # type: ignore @@ -531,43 +532,56 @@ def _send( self.logger.debug( "Making outbound API request", extra={"headers": request.headers, "url": request.url, "request_body": request.body} ) + response: requests.Response = self._session.send(request) + + error_resolution: ErrorResolution = self.interpret_response_status(response) + self.logger.debug("Receiving response", extra={"headers": response.headers, "status": response.status_code, "body": response.text}) + if log_formatter: formatter = log_formatter self.message_repository.log_message( Level.DEBUG, lambda: formatter(response), ) - if self._should_retry(response): - custom_backoff_time = self._backoff_time(response) - if custom_backoff_time: - raise UserDefinedBackoffException(backoff=custom_backoff_time, request=request, response=response) - else: - raise DefaultBackoffException(request=request, response=response) - return response - def _validate_response( - self, - response: requests.Response, - ) -> Optional[requests.Response]: - # if fail -> raise exception - # if ignore -> ignore response and return None - # else -> delegate to caller - if self.error_handler is None: - return response + if error_resolution.response_action == ResponseAction.FAIL: + error_message = f"'{request.method}' request to '{request.url}' failed with status code '{response.status_code}' and error message '{self.parse_response_error_message(response)}'" - response_status = self.interpret_response_status(response) - if response_status.action == ResponseAction.FAIL: - error_message = ( - response_status.error_message - or f"Request to {response.request.url} failed with status code {response.status_code} and error message {HttpRequester.parse_response_error_message(response)}" + raise AirbyteTracedException( + internal_message=error_message, + message=error_resolution.error_message or error_message, + failure_type=error_resolution.failure_type, ) - raise ReadException(error_message) - elif response_status.action == ResponseAction.IGNORE: - self.logger.info( - f"Ignoring response for failed request with error message {HttpRequester.parse_response_error_message(response)}" + + elif error_resolution.response_action == ResponseAction.IGNORE: + log_message = f"Ignoring response for '{request.method}' request to '{request.url}' with response code '{response.status_code}'" + + self.logger.info(error_resolution.error_message or log_message) + + elif error_resolution.response_action == ResponseAction.RETRY: + custom_backoff_time = self.error_handler.backoff_time(response) + error_message = ( + error_resolution.error_message + or f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action}." ) + if custom_backoff_time: + raise UserDefinedBackoffException( + backoff=custom_backoff_time, + request=request, + response=(response), + error_message=error_message, + ) + else: + raise DefaultBackoffException(request=request, response=(response), error_message=error_message) + + elif response: + try: + response.raise_for_status() + except requests.HTTPError as e: + self.logger.error(response.text) + raise e return response diff --git a/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py index 2187651f9abd..17a2f4c35f86 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/http_status_error_handler.py @@ -13,7 +13,7 @@ class HttpStatusErrorHandler(ErrorHandler): - _DEFAULT_ERROR_MAPPING: Mapping[Union[int, str, Type[Exception]], ErrorResolution] = { + DEFAULT_ERROR_MAPPING: Mapping[Union[int, str, Type[Exception]], ErrorResolution] = { RequestException: ErrorResolution( response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, @@ -87,7 +87,7 @@ def __init__( :param error_mapping: Custom error mappings to extend or override the default mappings. """ self._logger = logger - self._error_mapping = error_mapping or self._DEFAULT_ERROR_MAPPING + self._error_mapping = error_mapping or self.DEFAULT_ERROR_MAPPING def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution: """ diff --git a/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/response_models.py b/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/response_models.py index 5d628d201c38..6ec720e00026 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/response_models.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/streams/http/error_handlers/response_models.py @@ -19,3 +19,12 @@ class ErrorResolution: response_action: Optional[ResponseAction] = None failure_type: Optional[FailureType] = None error_message: Optional[str] = None + + +DEFAULT_ERROR_RESOLUTION = ErrorResolution( + response_action=ResponseAction.RETRY, + failure_type=FailureType.system_error, + error_message="The request failed due to an unknown error.", +) + +SUCCESS_RESOLUTION = ErrorResolution(response_action=ResponseAction.SUCCESS, failure_type=None, error_message=None) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py b/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py index 73c37b305fa5..892c9fd989b0 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/parsers/test_model_to_component_factory.py @@ -55,7 +55,6 @@ WaitTimeFromHeaderBackoffStrategy, WaitUntilTimeFromHeaderBackoffStrategy, ) -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.paginators import DefaultPaginator from airbyte_cdk.sources.declarative.requesters.paginators.strategies import ( CursorPaginationStrategy, @@ -75,6 +74,7 @@ from airbyte_cdk.sources.declarative.transformations import AddFields, RemoveFields from airbyte_cdk.sources.declarative.transformations.add_fields import AddedFieldDefinition from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.streams.http.requests_native_auth.oauth import SingleUseRefreshTokenOauth2Authenticator from unit_tests.sources.declarative.parsers.testing_components import TestingCustomSubstreamPartitionRouter, TestingSomeComponent diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index febfd54533f1..c23ff2e0615d 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -8,11 +8,7 @@ import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import pytest from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.constant_backoff_strategy import ConstantBackoffStrategy -from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import ( - DefaultErrorHandler, - HttpResponseFilter, - ResponseStatus, -) +from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler, HttpResponseFilter from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction SOME_BACKOFF_TIME = 60 diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index a2199a050dc5..ee39af52e5ec 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -7,8 +7,8 @@ import pytest import requests from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction @pytest.mark.parametrize( diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py index 228df3cd5af8..f07fe659a4ee 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py @@ -3,8 +3,8 @@ # import pytest -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction @pytest.mark.parametrize( From b49b0dbdb8b4dc6784e62695b517806e615c1ac1 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Fri, 24 May 2024 12:07:57 -0700 Subject: [PATCH 03/29] Update backoff strategies for new format --- .../wait_time_from_header_backoff_strategy.py | 2 +- .../wait_until_time_from_header_backoff_strategy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py index bd25f5e35999..20c866552cda 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py @@ -32,7 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: self.regex = InterpolatedString.create(self.regex, parameters=parameters) if self.regex else None self.header = InterpolatedString.create(self.header, parameters=parameters) - def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: header = self.header.eval(config=self.config) # type: ignore # header is always cast to an interpolated stream if self.regex: evaled_regex = self.regex.eval(self.config) # type: ignore # header is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py index 356cbf8c788c..eb17fe6f08f3 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py @@ -39,7 +39,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: if not isinstance(self.min_wait, InterpolatedString): self.min_wait = InterpolatedString.create(str(self.min_wait), parameters=parameters) - def backoff(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: now = time.time() header = self.header.eval(self.config) # type: ignore # header is always cast to an interpolated string if self.regex: From 2f02df92cd17a81bb5ebfca2bcc2582bebd66dbe Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 09:15:40 -0700 Subject: [PATCH 04/29] Updates factory method to provide default http response filter --- .../parsers/model_to_component_factory.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index 4f524c05544c..7950f312703a 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -685,16 +685,13 @@ def create_default_error_handler(self, model: DefaultErrorHandlerModel, config: if model.response_filters: for response_filter_model in model.response_filters: response_filters.append(self._create_component_from_model(model=response_filter_model, config=config)) - else: - response_filters.append( - HttpResponseFilter( - ResponseAction.RETRY, - http_codes=HttpResponseFilter.DEFAULT_RETRIABLE_ERRORS, - config=config, - parameters=model.parameters or {}, - ) - ) - response_filters.append(HttpResponseFilter(ResponseAction.IGNORE, config=config, parameters=model.parameters or {})) + # else: + # response_filters.append( + # HttpResponseFilter( + # config=config, + # parameters=model.parameters or {}, + # ) + # ) return DefaultErrorHandler( backoff_strategies=backoff_strategies, From 1c0848eb597d7b3a63170f7eb8dd784670f637aa Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 14:02:48 -0700 Subject: [PATCH 05/29] Update composite handler to include valid max_retries --- .../requesters/error_handlers/composite_error_handler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index f9adbed1ac12..31268e85ae5f 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -42,6 +42,14 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: if not self.error_handlers: raise ValueError("CompositeErrorHandler expects at least 1 underlying error handler") + @property + def max_retries(self) -> Union[int, None]: + return self.error_handlers[0].max_retries + + @property + def max_time(self) -> Union[int, None]: + return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) + def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: for retrier in self.error_handlers: error_resolution = retrier.interpret_response(response_or_exception) From be1c8bf24ffe2aedb1b9c80341fd8daf7f0a2cce Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 16:35:03 -0700 Subject: [PATCH 06/29] Update error handler, http_requester, and model_to_componet_factory, and requester to align with python cdk error handler and backoff strategy. Removes `ResponseStatus`. --- airbyte-cdk/python/airbyte_cdk/__init__.py | 1 - .../parsers/model_to_component_factory.py | 11 +-- .../error_handlers/default_error_handler.py | 23 +++---- .../error_handlers/http_response_filter.py | 15 ++-- .../error_handlers/response_status.py | 68 ------------------- .../declarative/requesters/http_requester.py | 59 +--------------- .../declarative/requesters/requester.py | 4 +- 7 files changed, 28 insertions(+), 153 deletions(-) delete mode 100644 airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py diff --git a/airbyte-cdk/python/airbyte_cdk/__init__.py b/airbyte-cdk/python/airbyte_cdk/__init__.py index 1d8e8306a5c4..522ec1a8ee6a 100644 --- a/airbyte-cdk/python/airbyte_cdk/__init__.py +++ b/airbyte-cdk/python/airbyte_cdk/__init__.py @@ -42,7 +42,6 @@ from .sources.declarative.requesters import Requester, HttpRequester from .sources.declarative.requesters.error_handlers import BackoffStrategy -from .sources.declarative.requesters.error_handlers.response_status import ResponseStatus from .sources.declarative.requesters.paginators import DefaultPaginator, PaginationStrategy from .sources.declarative.requesters.paginators.strategies import OffsetIncrement, CursorPaginationStrategy, PageIncrement, StopConditionPaginationStrategyDecorator diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index 7950f312703a..8ec6c5d30131 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -685,13 +685,8 @@ def create_default_error_handler(self, model: DefaultErrorHandlerModel, config: if model.response_filters: for response_filter_model in model.response_filters: response_filters.append(self._create_component_from_model(model=response_filter_model, config=config)) - # else: - # response_filters.append( - # HttpResponseFilter( - # config=config, - # parameters=model.parameters or {}, - # ) - # ) + else: + response_filters.append(HttpResponseFilter(config=config, parameters=model.parameters or {})) return DefaultErrorHandler( backoff_strategies=backoff_strategies, @@ -787,7 +782,7 @@ def create_http_requester(self, model: HttpRequesterModel, config: Config, *, na @staticmethod def create_http_response_filter(model: HttpResponseFilterModel, config: Config, **kwargs: Any) -> HttpResponseFilter: - action = ResponseAction(model.action.value) + action = ResponseAction(model.action.value) or None http_codes = ( set(model.http_codes) if model.http_codes else set() ) # JSON schema notation has no set data type. The schema enforces an array of unique elements diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index e6c3ce84e5b7..ccb6f4c64a7d 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -6,19 +6,13 @@ from typing import Any, List, Mapping, MutableMapping, Optional, Union import requests -from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import ( ExponentialBackoffStrategy, ) from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ( - DEFAULT_ERROR_RESOLUTION, - SUCCESS_RESOLUTION, - ErrorResolution, - ResponseAction, -) +from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution from airbyte_cdk.sources.types import Config @@ -105,9 +99,9 @@ class DefaultErrorHandler(ErrorHandler): backoff_strategies: Optional[List[BackoffStrategy]] = None def __post_init__(self, parameters: Mapping[str, Any]) -> None: - self.response_filters = self.response_filters or [] - self.response_filters.append(HttpResponseFilter(config=self.config, parameters={})) + if not self.response_filters: + self.response_filters = [HttpResponseFilter(config=self.config, parameters={})] if not self.backoff_strategies: self.backoff_strategies = [DefaultErrorHandler.DEFAULT_BACKOFF_STRATEGY(parameters=parameters, config=self.config)] @@ -117,8 +111,6 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: if isinstance(response_or_exception, requests.Response): - if response_or_exception.ok: - return SUCCESS_RESOLUTION request = response_or_exception.request @@ -128,9 +120,12 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp self._last_request_to_attempt_count[request] += 1 if self.response_filters: for response_filter in self.response_filters: - matched_status = response_filter.matches(response=response_or_exception) - if matched_status is not None: - return matched_status + matched_error_resolution = response_filter.matches(response_or_exception=response_or_exception) + if matched_error_resolution is not None: + return matched_error_resolution + + if response_or_exception.ok: + return SUCCESS_RESOLUTION # Return default error resolution (retry) return DEFAULT_ERROR_RESOLUTION diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index f3d2e19c5dd5..237aa1e5fc2b 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -40,8 +40,8 @@ class HttpResponseFilter: def __post_init__(self, parameters: Mapping[str, Any]) -> None: - if self.http_codes is not None and self.action is None: - raise ValueError("HttpResponseFilter requires a prescribed action if http_codes are specified") + if self.action is not None and (self.http_codes is None and self.predicate is None and self.error_message_contains is None): + raise ValueError("HttpResponseFilter requires a filter condition if an action is specified") if self.action is not None and isinstance(self.action, str): self.action = ResponseAction[self.action] @@ -50,6 +50,8 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: self.predicate = InterpolatedBoolean(condition=self.predicate, parameters=parameters) self.error_message = InterpolatedString.create(string_or_interpolated=self.error_message, parameters=parameters) + self._error_message_parser = JsonErrorMessageParser() + def matches(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> Optional[ErrorResolution]: filter_action = self._matches_filter(response_or_exception) mapped_key = ( @@ -57,9 +59,14 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep ) if filter_action is not None: - error_message = self._create_error_message(response_or_exception) default_mapped_error_resolution = self._match_default_error_mapping(mapped_key) + error_message = ( + default_mapped_error_resolution.error_message + if default_mapped_error_resolution + else self._create_error_message(response_or_exception) + ) failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error + return ErrorResolution( response_action=filter_action, failure_type=failure_type, @@ -112,5 +119,5 @@ def _response_contains_error_message(self, response: requests.Response) -> bool: if not self.error_message_contains: return False else: - error_message = JsonErrorMessageParser.parse_response_error_message(response) + error_message = self._error_message_parser.parse_response_error_message(response=response) return bool(error_message and self.error_message_contains in error_message) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py deleted file mode 100644 index 940ff218887d..000000000000 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/response_status.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -from typing import Any, Final, Optional, Union - -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction - - -class ResponseStatus: - """ - ResponseAction amended with backoff time if an action is RETRY - """ - - def __init__(self, response_action: Union[ResponseAction, str], retry_in: Optional[float] = None, error_message: str = ""): - """ - :param response_action: response action to execute - :param retry_in: backoff time (if action is RETRY) - :param error_message: the error to be displayed back to the customer - """ - if isinstance(response_action, str): - response_action = ResponseAction[response_action] - if retry_in and response_action != ResponseAction.RETRY: - raise ValueError(f"Unexpected backoff time ({retry_in} for non-retryable response action {response_action}") - self._retry_in = retry_in - self._action = response_action - self._error_message = error_message - - @property - def action(self) -> Union[ResponseAction, str]: - """The ResponseAction to execute when a response matches the filter""" - return self._action - - @property - def retry_in(self) -> Optional[float]: - """How long to backoff before retrying a response. None if no wait required.""" - return self._retry_in - - @property - def error_message(self) -> str: - """The message to be displayed when an error response is received""" - return self._error_message - - @classmethod - def retry(cls, retry_in: Optional[float]) -> "ResponseStatus": - """ - Returns a ResponseStatus defining how long to backoff before retrying - - :param retry_in: how long to backoff before retrying. None if no wait required - :return: A response status defining how long to backoff before retrying - """ - return ResponseStatus(ResponseAction.RETRY, retry_in) - - def __eq__(self, other: Any) -> bool: - if not other: - return not self - return bool(self.action == other.action and self.retry_in == other.retry_in) - - def __hash__(self) -> int: - return hash([self.action, self.retry_in]) - - -"""Response is successful. No need to retry""" -SUCCESS: Final[ResponseStatus] = ResponseStatus(ResponseAction.SUCCESS) -"""Response is unsuccessful. The failure needs to be handled""" -FAIL: Final[ResponseStatus] = ResponseStatus(ResponseAction.FAIL) -"""Response is unsuccessful, but can be ignored. No need to retry""" -IGNORE: Final[ResponseStatus] = ResponseStatus(ResponseAction.IGNORE) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index 9ba04e02dbe1..8ef911e478b5 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -6,7 +6,6 @@ import os import urllib from dataclasses import InitVar, dataclass -from functools import lru_cache from pathlib import Path from typing import Any, Callable, Mapping, MutableMapping, Optional, Union from urllib.parse import urljoin @@ -16,7 +15,6 @@ from airbyte_cdk.models import Level from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator, NoAuth from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder -from airbyte_cdk.sources.declarative.exceptions import ReadException from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( @@ -222,57 +220,6 @@ def max_time(self) -> Union[int, None]: def logger(self) -> logging.Logger: return logging.getLogger(f"airbyte.HttpRequester.{self.name}") - def _should_retry(self, response: requests.Response) -> bool: - """ - Specifies conditions for backoff based on the response from the server. - - By default, back off on the following HTTP response statuses: - - 429 (Too Many Requests) indicating rate limiting - - 500s to handle transient server errors - - Unexpected but transient exceptions (connection timeout, DNS resolution failed, etc..) are retried by default. - """ - if self.error_handler is None: - return response.status_code == 429 or 500 <= response.status_code < 600 - - if self.use_cache: - interpret_response_status = self.interpret_response_status - else: - # Use a tiny cache to limit the memory footprint. It doesn't have to be large because we mostly - # only care about the status of the last response received - # Cache the result because the HttpStream first checks if we should retry before looking at the backoff time - interpret_response_status = lru_cache(maxsize=10)(self.interpret_response_status) - - return bool(interpret_response_status(response).action == ResponseAction.RETRY) - - def _backoff_time(self, response: requests.Response) -> Optional[float]: - """ - Specifies backoff time. - - This method is called only if should_backoff() returns True for the input request. - - :param response: - :return how long to backoff in seconds. The return value may be a floating point number for subsecond precision. Returning None defers backoff - to the default backoff behavior (e.g using an exponential algorithm). - """ - if self.error_handler is None: - return None - error_resolution = self.interpret_response_status(response) - should_retry = error_resolution.response_action == ResponseAction.RETRY - if not should_retry: - raise ValueError(f"backoff_time can only be applied on retriable response action. Got {error_resolution.response_action}") - assert error_resolution.repsonse_action == ResponseAction.RETRY - return should_retry.retry_in - - def _error_message(self, response: requests.Response) -> str: - """ - Constructs an error message which can incorporate the HTTP response received from the partner API. - - :param response: The incoming HTTP response from the partner API - :return The error message string to be emitted - """ - return self.interpret_response_status(response).error_message - def _get_request_options( self, stream_state: Optional[StreamState], @@ -561,7 +508,7 @@ def _send( self.logger.info(error_resolution.error_message or log_message) elif error_resolution.response_action == ResponseAction.RETRY: - custom_backoff_time = self.error_handler.backoff_time(response) + custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None error_message = ( error_resolution.error_message or f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action}." @@ -570,11 +517,11 @@ def _send( raise UserDefinedBackoffException( backoff=custom_backoff_time, request=request, - response=(response), + response=response, error_message=error_message, ) else: - raise DefaultBackoffException(request=request, response=(response), error_message=error_message) + raise DefaultBackoffException(request=request, response=response, error_message=error_message) elif response: try: diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/requester.py index db9a632336a1..a0de6d8b7654 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/requester.py @@ -8,8 +8,8 @@ import requests from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.declarative.requesters.request_options.request_options_provider import RequestOptionsProvider +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution from airbyte_cdk.sources.types import StreamSlice, StreamState @@ -69,7 +69,7 @@ def get_request_params( """ @abstractmethod - def interpret_response_status(self, response: requests.Response) -> ResponseStatus: + def interpret_response_status(self, response: requests.Response) -> ErrorResolution: """ Specifies conditions for backoff, error handling and reporting based on the response from the server. From d79e99411e12abba6d71fea3ba005380e16b8d3b Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 16:36:17 -0700 Subject: [PATCH 07/29] Updated tests for default_error_handler --- .../test_default_error_handler.py | 288 +++++++++--------- 1 file changed, 145 insertions(+), 143 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index c23ff2e0615d..ba023d345ee5 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -5,170 +5,172 @@ from http import HTTPStatus from unittest.mock import MagicMock -import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import pytest +import requests from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.constant_backoff_strategy import ConstantBackoffStrategy +from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import ( + ExponentialBackoffStrategy, +) from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler, HttpResponseFilter -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers import HttpStatusErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, FailureType, ResponseAction SOME_BACKOFF_TIME = 60 @pytest.mark.parametrize( - "test_name, http_code, retry_response_filter, ignore_response_filter, response_headers, should_retry, backoff_strategy", - [ - ("test_bad_gateway", HTTPStatus.BAD_GATEWAY, None, None, {}, ResponseStatus.retry(10), None), - ("test_too_many_requests", HTTPStatus.TOO_MANY_REQUESTS, None, None, {}, ResponseStatus.retry(10), None), - ( - "test_bad_gateway_constant_retry", - HTTPStatus.BAD_GATEWAY, - None, - None, - {}, - ResponseStatus.retry(SOME_BACKOFF_TIME), - [ConstantBackoffStrategy(parameters={}, backoff_time_in_seconds=SOME_BACKOFF_TIME, config={})], - ), - ("test_exponential_backoff", HTTPStatus.BAD_GATEWAY, None, None, {}, ResponseStatus.retry(10), None), - ( - "test_bad_gateway_exponential_backoff_explicit_parameter", - HTTPStatus.BAD_GATEWAY, - None, - None, - {}, - ResponseStatus.retry(10), - [DefaultErrorHandler.DEFAULT_BACKOFF_STRATEGY(parameters={}, config={})], - ), - ("test_chain_backoff_strategy", HTTPStatus.BAD_GATEWAY, None, None, {}, ResponseStatus.retry(10), None), - ( - "test_bad_gateway_chain_backoff", - HTTPStatus.BAD_GATEWAY, - None, - None, - {}, - ResponseStatus.retry(10), - [ - DefaultErrorHandler.DEFAULT_BACKOFF_STRATEGY(parameters={}, config={}), - ConstantBackoffStrategy(parameters={}, backoff_time_in_seconds=SOME_BACKOFF_TIME, config={}), - ], - ), - ("test_200", HTTPStatus.OK, None, None, {}, response_status.SUCCESS, None), - ("test_3XX", HTTPStatus.PERMANENT_REDIRECT, None, None, {}, response_status.SUCCESS, None), - ("test_403", HTTPStatus.FORBIDDEN, None, None, {}, response_status.FAIL, None), - ( - "test_403_ignore_error_message", - HTTPStatus.FORBIDDEN, - None, - HttpResponseFilter(action=ResponseAction.IGNORE, error_message_contains="found", config={}, parameters={}), - {}, - response_status.IGNORE, - None, - ), - ( - "test_403_dont_ignore_error_message", - HTTPStatus.FORBIDDEN, - None, - HttpResponseFilter(action=ResponseAction.IGNORE, error_message_contains="not_found", config={}, parameters={}), - {}, - response_status.FAIL, - None, - ), - ("test_429", HTTPStatus.TOO_MANY_REQUESTS, None, None, {}, ResponseStatus.retry(10), None), - ( - "test_ignore_403", - HTTPStatus.FORBIDDEN, - None, - HttpResponseFilter(action=ResponseAction.IGNORE, http_codes={HTTPStatus.FORBIDDEN}, config={}, parameters={}), - {}, - response_status.IGNORE, - None, - ), - ( - "test_403_with_predicate", - HTTPStatus.FORBIDDEN, - HttpResponseFilter(action=ResponseAction.RETRY, predicate="{{ 'code' in response }}", config={}, parameters={}), - None, - {}, - ResponseStatus.retry(10), - None, - ), - ( - "test_403_with_predicate", - HTTPStatus.FORBIDDEN, - HttpResponseFilter(action=ResponseAction.RETRY, predicate="{{ 'some_absent_field' in response }}", config={}, parameters={}), - None, - {}, - response_status.FAIL, - None, - ), - ( - "test_200_fail_with_predicate", - HTTPStatus.OK, - HttpResponseFilter(action=ResponseAction.FAIL, error_message_contains="found", config={}, parameters={}), - None, - {}, - response_status.FAIL, - None, - ), - ( - "test_retry_403", - HTTPStatus.FORBIDDEN, - HttpResponseFilter(action=ResponseAction.RETRY, http_codes={HTTPStatus.FORBIDDEN}, config={}, parameters={}), - None, - {}, - ResponseStatus.retry(10), - None, - ), - ( - "test_200_fail_with_predicate_from_header", - HTTPStatus.OK, - HttpResponseFilter(action=ResponseAction.FAIL, predicate="{{ headers['fail'] }}", config={}, parameters={}), - None, - {"fail": True}, - response_status.FAIL, - None, - ), - ], + "test_name, http_status_code, expected_error_resolution", + [ + ( + "_with_http_response_status_200", + 200, + ErrorResolution( + response_action=ResponseAction.SUCCESS, + failure_type=None, + error_message=None, + ), + ), + ( + "_with_http_response_status_400", + 400, + HttpStatusErrorHandler.DEFAULT_ERROR_MAPPING[400], + ), + ( + "_with_http_response_status_404", + 404, + HttpStatusErrorHandler.DEFAULT_ERROR_MAPPING[404], + ), + ( + "_with_http_response_status_408", + 408, + HttpStatusErrorHandler.DEFAULT_ERROR_MAPPING[408], + ), + ( + "_with_unmapped_http_status_418", + 418, + ErrorResolution( + response_action=ResponseAction.RETRY, + failure_type=FailureType.system_error, + error_message="The request failed due to an unknown error.", + ), + ) + ], ) -def test_default_error_handler( - test_name, http_code, retry_response_filter, ignore_response_filter, response_headers, should_retry, backoff_strategy -): - response_mock = create_response(http_code, headers=response_headers, json_body={"code": "1000", "error": "found"}) - response_mock.ok = http_code < 400 - response_filters = [f for f in [retry_response_filter, ignore_response_filter] if f] - error_handler = DefaultErrorHandler(response_filters=response_filters, backoff_strategies=backoff_strategy, config={}, parameters={}) - actual_should_retry = error_handler.interpret_response(response_mock) - assert actual_should_retry == should_retry - if should_retry.action == ResponseAction.RETRY: - assert actual_should_retry.retry_in == should_retry.retry_in +def test_default_error_handler_with_default_response_filter(test_name, http_status_code: int, expected_error_resolution: ErrorResolution): + response_mock = create_response(http_status_code) + error_handler = DefaultErrorHandler(config={}, parameters={}) + actual_error_resolution = error_handler.interpret_response(response_mock) + assert actual_error_resolution.response_action == expected_error_resolution.response_action + assert actual_error_resolution.failure_type == expected_error_resolution.failure_type + assert actual_error_resolution.error_message == expected_error_resolution.error_message -def test_default_error_handler_attempt_count_increases(): - status_code = 500 - response_mock = create_response(status_code) - error_handler = DefaultErrorHandler(config={}, parameters={}) - actual_should_retry = error_handler.interpret_response(response_mock) - assert actual_should_retry == ResponseStatus.retry(10) - assert actual_should_retry.retry_in == 10 +@pytest.mark.parametrize( + "test_name, http_status_code, test_response_filter, response_action, failure_type, error_message", + [ + ( + "_with_http_response_status_400_fail_with_default_failure_type", + 400, + HttpResponseFilter( + http_codes=[400], + action=ResponseAction.RETRY, + config={}, + parameters={}, + ), + ResponseAction.RETRY, + FailureType.system_error, + "Bad request. Please check your request parameters.", + ), + ( + "_with_http_response_status_402_fail_with_default_failure_type", + 402, + HttpResponseFilter( + http_codes=[402], + action=ResponseAction.FAIL, + config={}, + parameters={}, + ), + ResponseAction.FAIL, + FailureType.system_error, + "", + ), + ( + "_with_http_response_status_403_fail_with_default_failure_type", + 403, + HttpResponseFilter( + http_codes=[403], + action="FAIL", + config={}, + parameters={}, + ), + ResponseAction.FAIL, + FailureType.config_error, + "Forbidden. You don't have permission to access this resource.", + ), + ( + "_with_http_response_status_200_fail_with_contained_error_message", + 200, + HttpResponseFilter( + action=ResponseAction.FAIL, + error_message_contains="test", + config={}, + parameters={}, + ), + ResponseAction.FAIL, + FailureType.system_error, + "", + ), + ( + "_fail_with_predicate", + 200, + HttpResponseFilter( + action=ResponseAction.FAIL, + predicate="{{ 'error' in response }}", + config={}, + parameters={}, + ), + ResponseAction.FAIL, + FailureType.system_error, + "", + ), + ], +) +def test_default_error_handler_with_custom_response_filter(test_name, http_status_code, test_response_filter, response_action, failure_type, error_message): + response_mock = create_response(http_status_code) + if http_status_code == 200: + response_mock.json.return_value = {"error": "test"} - # This is the same request, so the count should increase - actual_should_retry = error_handler.interpret_response(response_mock) - assert actual_should_retry == ResponseStatus.retry(20) - assert actual_should_retry.retry_in == 20 + response_filter = test_response_filter + error_handler = DefaultErrorHandler(config={}, parameters={}, response_filters=[response_filter]) + actual_error_resolution = error_handler.interpret_response(response_mock) + assert actual_error_resolution.response_action == response_action + assert actual_error_resolution.failure_type == failure_type + assert actual_error_resolution.error_message == error_message - # This is a different request, so the count should not increase - another_identical_request = create_response(status_code) - actual_should_retry = error_handler.interpret_response(another_identical_request) - assert actual_should_retry == ResponseStatus.retry(10) - assert actual_should_retry.retry_in == 10 +def test_default_error_handler_with_constant_backoff_strategy(): + response_mock = create_response(429) + error_handler = DefaultErrorHandler(config={}, parameters={}, backoff_strategies=[ConstantBackoffStrategy(SOME_BACKOFF_TIME, config={}, parameters={})]) + assert error_handler.backoff_time(response_or_exception=response_mock, attempt_count=0) == SOME_BACKOFF_TIME +@pytest.mark.parametrize( + "attempt_count", + [ + 0, 1, 2, 3, 4, 5, 6, + ], +) +def test_default_error_handler_with_exponential_backoff_strategy(attempt_count): + response_mock = create_response(429) + error_handler = DefaultErrorHandler(config={}, parameters={}, backoff_strategies=[ExponentialBackoffStrategy(factor=1, config={}, parameters={})]) + assert error_handler.backoff_time(response_or_exception=response_mock, attempt_count=attempt_count) == (1 * 2**attempt_count) def create_response(status_code: int, headers=None, json_body=None): url = "https://airbyte.io" - response_mock = MagicMock() + response_mock = MagicMock(spec=requests.Response) response_mock.status_code = status_code response_mock.ok = status_code < 400 or status_code >= 600 response_mock.url = url response_mock.headers = headers or {} response_mock.json.return_value = json_body or {} + response_mock.request = MagicMock(spec=requests.PreparedRequest) return response_mock From 5c629d8434f343769ee9bb73e371bc66533ede91 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 17:38:59 -0700 Subject: [PATCH 08/29] Add method for determining optimal error resolution when provided multiple --- .../error_handlers/composite_error_handler.py | 24 ++++++++++++-- .../error_handlers/default_error_handler.py | 33 +++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 31268e85ae5f..1450089ac864 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -7,7 +7,7 @@ import requests from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, ErrorResolution +from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, ErrorResolution, ResponseAction @dataclass @@ -50,8 +50,28 @@ def max_retries(self) -> Union[int, None]: def max_time(self) -> Union[int, None]: return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) + def _new_error_resolution_is_preferred( + self, new_error_resolution: Optional[ErrorResolution], current_error_resolution: Optional[ErrorResolution] + ) -> bool: + if new_error_resolution is None: + return False + + if current_error_resolution is None: + return True + + priority = {ResponseAction.FAIL: 0, ResponseAction.RETRY: 1, ResponseAction.IGNORE: 2, ResponseAction.SUCCESS: 3} + + return priority[new_error_resolution.response_action] > priority[current_error_resolution.response_action] + def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: + optimal_error_resolution = None for retrier in self.error_handlers: error_resolution = retrier.interpret_response(response_or_exception) - return error_resolution + if error_resolution is not None and self._new_error_resolution_is_preferred( + new_error_resolution=error_resolution, current_error_resolution=optimal_error_resolution + ): + optimal_error_resolution = error_resolution + + if optimal_error_resolution is not None: + return optimal_error_resolution return DEFAULT_ERROR_RESOLUTION diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index ccb6f4c64a7d..05233bd84674 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -12,7 +12,12 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ( + DEFAULT_ERROR_RESOLUTION, + SUCCESS_RESOLUTION, + ErrorResolution, + ResponseAction, +) from airbyte_cdk.sources.types import Config @@ -119,16 +124,38 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp else: self._last_request_to_attempt_count[request] += 1 if self.response_filters: + + optimal_error_resolution = None + for response_filter in self.response_filters: matched_error_resolution = response_filter.matches(response_or_exception=response_or_exception) - if matched_error_resolution is not None: - return matched_error_resolution + + if matched_error_resolution is not None and self._new_error_resolution_is_preferred( + new_error_resolution=matched_error_resolution, current_error_resolution=optimal_error_resolution + ): + optimal_error_resolution = matched_error_resolution if response_or_exception.ok: return SUCCESS_RESOLUTION + + if optimal_error_resolution is not None: + return optimal_error_resolution # Return default error resolution (retry) return DEFAULT_ERROR_RESOLUTION + def _new_error_resolution_is_preferred( + self, new_error_resolution: Optional[ErrorResolution], current_error_resolution: Optional[ErrorResolution] + ) -> bool: + if new_error_resolution is None: + return False + + if current_error_resolution is None: + return True + + priority = {ResponseAction.FAIL: 0, ResponseAction.RETRY: 1, ResponseAction.IGNORE: 2, ResponseAction.SUCCESS: 3} + + return priority[new_error_resolution.response_action] > priority[current_error_resolution.response_action] + def backoff_time( self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int ) -> Optional[float]: From 2bd6c60c8a799bc6861f9342f16b6c245afc1863 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Tue, 28 May 2024 17:40:34 -0700 Subject: [PATCH 09/29] Update default error handler and composite error handler unit tests. Removes response status unit tests. --- .../test_composite_error_handler.py | 132 +++++++++--------- .../test_default_error_handler.py | 66 ++++++++- .../error_handlers/test_response_status.py | 44 ------ 3 files changed, 126 insertions(+), 116 deletions(-) delete mode 100644 airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py index 17a9eca3322f..d2603afcf972 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py @@ -4,12 +4,12 @@ from unittest.mock import MagicMock -import airbyte_cdk.sources.declarative.requesters.error_handlers.response_status as response_status import pytest +import requests from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter from airbyte_cdk.sources.declarative.requesters.error_handlers.composite_error_handler import CompositeErrorHandler -from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler, ResponseStatus -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction +from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, FailureType, ResponseAction SOME_BACKOFF_TIME = 60 @@ -19,76 +19,70 @@ [ ( "test_chain_retrier_ok_ok", - response_status.SUCCESS, - response_status.SUCCESS, - response_status.SUCCESS, + ErrorResolution( + response_action=ResponseAction.SUCCESS, + failure_type=None, + error_message=None, + ), + ErrorResolution( + response_action=ResponseAction.SUCCESS, + failure_type=None, + error_message=None, + ), + ErrorResolution( + response_action=ResponseAction.SUCCESS, + failure_type=None, + error_message=None, + ) ), ( "test_chain_retrier_ignore_fail", - response_status.IGNORE, - response_status.FAIL, - response_status.IGNORE, + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), + ErrorResolution( + response_action=ResponseAction.FAIL, + ), + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), ), ( "test_chain_retrier_fail_ignore", - response_status.FAIL, - response_status.IGNORE, - response_status.IGNORE, + ErrorResolution( + response_action=ResponseAction.FAIL, + ), + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), ), ( "test_chain_retrier_ignore_retry", - response_status.IGNORE, - ResponseStatus.retry(SOME_BACKOFF_TIME), - response_status.IGNORE, - ), - ( - "test_chain_retrier_retry_ignore", - ResponseStatus.retry(SOME_BACKOFF_TIME), - response_status.IGNORE, - ResponseStatus.retry(SOME_BACKOFF_TIME), - ), - ( - "test_chain_retrier_retry_fail", - ResponseStatus.retry(SOME_BACKOFF_TIME), - response_status.FAIL, - ResponseStatus.retry(SOME_BACKOFF_TIME), - ), - ( - "test_chain_retrier_fail_retry", - response_status.FAIL, - ResponseStatus.retry(SOME_BACKOFF_TIME), - ResponseStatus.retry(SOME_BACKOFF_TIME), - ), - ( - "test_chain_retrier_ignore_ok", - response_status.IGNORE, - response_status.SUCCESS, - response_status.IGNORE, - ), - ( - "test_chain_retrier_ok_ignore", - response_status.SUCCESS, - response_status.IGNORE, - response_status.SUCCESS, - ), - ( - "test_chain_retrier_ok_retry", - response_status.SUCCESS, - ResponseStatus.retry(SOME_BACKOFF_TIME), - response_status.SUCCESS, - ), - ( - "test_chain_retrier_retry_ok", - ResponseStatus.retry(SOME_BACKOFF_TIME), - response_status.SUCCESS, - ResponseStatus.retry(SOME_BACKOFF_TIME), + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), + ErrorResolution( + response_action=ResponseAction.RETRY, + ), + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), ), ( - "test_chain_retrier_return_first_retry", - ResponseStatus.retry(SOME_BACKOFF_TIME), - ResponseStatus.retry(2 * SOME_BACKOFF_TIME), - ResponseStatus.retry(SOME_BACKOFF_TIME), - ), + "test_chain_retrier_ignore_success", + ErrorResolution( + response_action=ResponseAction.IGNORE, + ), + ErrorResolution( + response_action=ResponseAction.SUCCESS, + ), + ErrorResolution( + response_action=ResponseAction.SUCCESS, + ), + ) ], ) def test_composite_error_handler(test_name, first_handler_behavior, second_handler_behavior, expected_behavior): @@ -96,11 +90,10 @@ def test_composite_error_handler(test_name, first_handler_behavior, second_handl first_error_handler.interpret_response.return_value = first_handler_behavior second_error_handler = MagicMock() second_error_handler.interpret_response.return_value = second_handler_behavior - second_error_handler.interpret_response.return_value = second_handler_behavior retriers = [first_error_handler, second_error_handler] retrier = CompositeErrorHandler(error_handlers=retriers, parameters={}) response_mock = MagicMock() - response_mock.ok = first_handler_behavior == response_status.SUCCESS or second_handler_behavior == response_status.SUCCESS + response_mock.ok = first_handler_behavior.response_action == ResponseAction.SUCCESS or second_handler_behavior == ResponseAction.SUCCESS assert retrier.interpret_response(response_mock) == expected_behavior @@ -131,8 +124,8 @@ def test_error_handler_compatibility_simple(): ], parameters={}, ) - assert default_error_handler.interpret_response(response_mock).action == expected_action - assert composite_error_handler.interpret_response(response_mock).action == expected_action + assert default_error_handler.interpret_response(response_mock).response_action == expected_action + assert composite_error_handler.interpret_response(response_mock).response_action == expected_action @pytest.mark.parametrize( @@ -169,17 +162,18 @@ def test_error_handler_compatibility_multiple_filters(test_name, status_code, ex ], parameters={}, ) - actual_action_multiple_filters = error_handler_with_multiple_filters.interpret_response(response_mock).action + actual_action_multiple_filters = error_handler_with_multiple_filters.interpret_response(response_mock).response_action assert actual_action_multiple_filters == expected_action - actual_action_single_filters = composite_error_handler_with_single_filters.interpret_response(response_mock).action + actual_action_single_filters = composite_error_handler_with_single_filters.interpret_response(response_mock).response_action assert actual_action_single_filters == expected_action def create_response(status_code: int, headers=None, json_body=None): url = "https://airbyte.io" - response_mock = MagicMock() + response_mock = MagicMock(spec=requests.Response) + response_mock.request = MagicMock(spec=requests.PreparedRequest) response_mock.status_code = status_code response_mock.ok = status_code < 400 or status_code >= 600 response_mock.url = url diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index ba023d345ee5..3344121fd277 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -109,7 +109,7 @@ def test_default_error_handler_with_default_response_filter(test_name, http_stat ), ( "_with_http_response_status_200_fail_with_contained_error_message", - 200, + 418, HttpResponseFilter( action=ResponseAction.FAIL, error_message_contains="test", @@ -122,7 +122,7 @@ def test_default_error_handler_with_default_response_filter(test_name, http_stat ), ( "_fail_with_predicate", - 200, + 418, HttpResponseFilter( action=ResponseAction.FAIL, predicate="{{ 'error' in response }}", @@ -137,7 +137,7 @@ def test_default_error_handler_with_default_response_filter(test_name, http_stat ) def test_default_error_handler_with_custom_response_filter(test_name, http_status_code, test_response_filter, response_action, failure_type, error_message): response_mock = create_response(http_status_code) - if http_status_code == 200: + if http_status_code == 418: response_mock.json.return_value = {"error": "test"} response_filter = test_response_filter @@ -147,6 +147,66 @@ def test_default_error_handler_with_custom_response_filter(test_name, http_statu assert actual_error_resolution.failure_type == failure_type assert actual_error_resolution.error_message == error_message +@pytest.mark.parametrize( + "http_status_code, expected_response_action", + [ + (400, ResponseAction.RETRY), + (402, ResponseAction.FAIL), + ], +) +def test_default_error_handler_with_multiple_response_filters(http_status_code, expected_response_action): + response_filter_one = HttpResponseFilter( + http_codes=[400], + action=ResponseAction.RETRY, + config={}, + parameters={}, + ) + response_filter_two = HttpResponseFilter( + http_codes=[402], + action=ResponseAction.FAIL, + config={}, + parameters={}, + ) + + response_mock = create_response(http_status_code) + error_handler = DefaultErrorHandler(config={}, parameters={}, response_filters=[response_filter_one, response_filter_two]) + actual_error_resolution = error_handler.interpret_response(response_mock) + assert actual_error_resolution.response_action == expected_response_action + + +@pytest.mark.parametrize( + "first_response_filter_action, second_response_filter_action, expected_response_action", + [ + (ResponseAction.RETRY, ResponseAction.FAIL, ResponseAction.RETRY), + (ResponseAction.FAIL, ResponseAction.RETRY, ResponseAction.RETRY), + (ResponseAction.IGNORE, ResponseAction.IGNORE, ResponseAction.IGNORE), + (ResponseAction.SUCCESS, ResponseAction.IGNORE, ResponseAction.SUCCESS), + (ResponseAction.IGNORE, ResponseAction.SUCCESS, ResponseAction.SUCCESS), + (ResponseAction.FAIL, ResponseAction.SUCCESS, ResponseAction.SUCCESS), + (ResponseAction.FAIL, ResponseAction.RETRY, ResponseAction.RETRY), + (ResponseAction.RETRY, ResponseAction.SUCCESS, ResponseAction.SUCCESS), + ] +) +def test_default_error_handler_with_conflicting_response_filters(first_response_filter_action, second_response_filter_action, expected_response_action): + response_filter_one = HttpResponseFilter( + http_codes=[400], + action=first_response_filter_action, + config={}, + parameters={}, + ) + response_filter_two = HttpResponseFilter( + http_codes=[400], + action=second_response_filter_action, + config={}, + parameters={}, + ) + + response_mock = create_response(400) + error_handler = DefaultErrorHandler(config={}, parameters={}, response_filters=[response_filter_one, response_filter_two]) + actual_error_resolution = error_handler.interpret_response(response_mock) + assert actual_error_resolution.response_action == expected_response_action + + def test_default_error_handler_with_constant_backoff_strategy(): response_mock = create_response(429) error_handler = DefaultErrorHandler(config={}, parameters={}, backoff_strategies=[ConstantBackoffStrategy(SOME_BACKOFF_TIME, config={}, parameters={})]) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py deleted file mode 100644 index f07fe659a4ee..000000000000 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_response_status.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (c) 2023 Airbyte, Inc., all rights reserved. -# - -import pytest -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction - - -@pytest.mark.parametrize( - "test_name, response_action, retry_in, error_message, expected_action, expected_backoff, expected_message", - [ - ("test_fail_with_backoff", ResponseAction.FAIL, 10, "", None, None, ""), - ( - "test_success_no_backoff_error_message", - ResponseAction.FAIL, - None, - "custom error message", - ResponseAction.FAIL, - None, - "custom error message", - ), - ("test_ignore_with_backoff", ResponseAction.IGNORE, 10, "", None, None, ""), - ("test_success_no_backoff", ResponseAction.IGNORE, None, "", ResponseAction.IGNORE, None, ""), - ("test_success_with_backoff", ResponseAction.SUCCESS, 10, "", None, None, ""), - ("test_success_no_backoff", ResponseAction.SUCCESS, None, "", ResponseAction.SUCCESS, None, ""), - ("test_retry_with_backoff", ResponseAction.RETRY, 10, "", ResponseAction.RETRY, 10, ""), - ("test_retry_no_backoff", ResponseAction.RETRY, None, "", ResponseAction.RETRY, None, ""), - ], -) -def test_response_status(test_name, response_action, retry_in, error_message, expected_action, expected_backoff, expected_message): - if expected_action or expected_backoff or expected_message: - response_status = ResponseStatus(response_action, retry_in, error_message) - assert ( - response_status.action == expected_action - and response_status.retry_in == expected_backoff - and response_status.error_message == expected_message - ) - else: - try: - ResponseStatus(response_action, retry_in) - assert False - except ValueError: - pass From 1523baed911a5fb31105e88d3945e953d2a56646 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 12:10:19 -0700 Subject: [PATCH 10/29] Updates error handlers to maintain current behavior on response action preference --- .../error_handlers/composite_error_handler.py | 30 +++++++------------ .../error_handlers/default_error_handler.py | 24 ++------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 1450089ac864..a966cb3fd63a 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -50,28 +50,18 @@ def max_retries(self) -> Union[int, None]: def max_time(self) -> Union[int, None]: return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) - def _new_error_resolution_is_preferred( - self, new_error_resolution: Optional[ErrorResolution], current_error_resolution: Optional[ErrorResolution] - ) -> bool: - if new_error_resolution is None: - return False - - if current_error_resolution is None: - return True + def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: + matched_error_resolution = None + for error_handler in self.error_handlers: + matched_error_resolution = error_handler.interpret_response(response_or_exception) - priority = {ResponseAction.FAIL: 0, ResponseAction.RETRY: 1, ResponseAction.IGNORE: 2, ResponseAction.SUCCESS: 3} + if matched_error_resolution.response_action == ResponseAction.SUCCESS: + return matched_error_resolution - return priority[new_error_resolution.response_action] > priority[current_error_resolution.response_action] + if matched_error_resolution.response_action == ResponseAction.RETRY or matched_error_resolution.response_action == ResponseAction.IGNORE: + return matched_error_resolution - def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: - optimal_error_resolution = None - for retrier in self.error_handlers: - error_resolution = retrier.interpret_response(response_or_exception) - if error_resolution is not None and self._new_error_resolution_is_preferred( - new_error_resolution=error_resolution, current_error_resolution=optimal_error_resolution - ): - optimal_error_resolution = error_resolution + if matched_error_resolution is not None: + return matched_error_resolution - if optimal_error_resolution is not None: - return optimal_error_resolution return DEFAULT_ERROR_RESOLUTION diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 05233bd84674..37ae4651c258 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -16,7 +16,6 @@ DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution, - ResponseAction, ) from airbyte_cdk.sources.types import Config @@ -125,37 +124,18 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp self._last_request_to_attempt_count[request] += 1 if self.response_filters: - optimal_error_resolution = None - for response_filter in self.response_filters: matched_error_resolution = response_filter.matches(response_or_exception=response_or_exception) - if matched_error_resolution is not None and self._new_error_resolution_is_preferred( - new_error_resolution=matched_error_resolution, current_error_resolution=optimal_error_resolution - ): - optimal_error_resolution = matched_error_resolution + if matched_error_resolution is not None: + return matched_error_resolution if response_or_exception.ok: return SUCCESS_RESOLUTION - if optimal_error_resolution is not None: - return optimal_error_resolution # Return default error resolution (retry) return DEFAULT_ERROR_RESOLUTION - def _new_error_resolution_is_preferred( - self, new_error_resolution: Optional[ErrorResolution], current_error_resolution: Optional[ErrorResolution] - ) -> bool: - if new_error_resolution is None: - return False - - if current_error_resolution is None: - return True - - priority = {ResponseAction.FAIL: 0, ResponseAction.RETRY: 1, ResponseAction.IGNORE: 2, ResponseAction.SUCCESS: 3} - - return priority[new_error_resolution.response_action] > priority[current_error_resolution.response_action] - def backoff_time( self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int ) -> Optional[float]: From d8f4857add13d453a53bf4f5695a35efc7a745b4 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 12:11:26 -0700 Subject: [PATCH 11/29] Updates error handler tests to expected behavior --- .../error_handlers/test_composite_error_handler.py | 2 +- .../requesters/error_handlers/test_default_error_handler.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py index d2603afcf972..146d412c2ca0 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py @@ -80,7 +80,7 @@ response_action=ResponseAction.SUCCESS, ), ErrorResolution( - response_action=ResponseAction.SUCCESS, + response_action=ResponseAction.IGNORE, ), ) ], diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index 3344121fd277..bc4c4b7f2e28 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -178,13 +178,9 @@ def test_default_error_handler_with_multiple_response_filters(http_status_code, "first_response_filter_action, second_response_filter_action, expected_response_action", [ (ResponseAction.RETRY, ResponseAction.FAIL, ResponseAction.RETRY), - (ResponseAction.FAIL, ResponseAction.RETRY, ResponseAction.RETRY), + (ResponseAction.FAIL, ResponseAction.RETRY, ResponseAction.FAIL), (ResponseAction.IGNORE, ResponseAction.IGNORE, ResponseAction.IGNORE), (ResponseAction.SUCCESS, ResponseAction.IGNORE, ResponseAction.SUCCESS), - (ResponseAction.IGNORE, ResponseAction.SUCCESS, ResponseAction.SUCCESS), - (ResponseAction.FAIL, ResponseAction.SUCCESS, ResponseAction.SUCCESS), - (ResponseAction.FAIL, ResponseAction.RETRY, ResponseAction.RETRY), - (ResponseAction.RETRY, ResponseAction.SUCCESS, ResponseAction.SUCCESS), ] ) def test_default_error_handler_with_conflicting_response_filters(first_response_filter_action, second_response_filter_action, expected_response_action): From be014170dbb0e85986b6433354fac43d4f691ca4 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 12:12:04 -0700 Subject: [PATCH 12/29] Updates reference to ResponseAction --- .../sources/declarative/retrievers/test_simple_retriever.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py b/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py index 0fed6be46cc8..f0f86f65a7e0 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py @@ -9,7 +9,6 @@ from airbyte_cdk.sources.declarative.auth.declarative_authenticator import NoAuth from airbyte_cdk.sources.declarative.incremental import DatetimeBasedCursor, DeclarativeCursor from airbyte_cdk.sources.declarative.partition_routers import SinglePartitionRouter -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus from airbyte_cdk.sources.declarative.requesters.request_option import RequestOptionType from airbyte_cdk.sources.declarative.requesters.requester import HttpMethod from airbyte_cdk.sources.declarative.retrievers.simple_retriever import SimpleRetriever, SimpleRetrieverTestReadDecorator @@ -61,7 +60,7 @@ def test_simple_retriever_full(mock_http_stream): http_method = HttpMethod.GET requester.get_method.return_value = http_method backoff_time = 60 - should_retry = ResponseStatus.retry(backoff_time) + should_retry = True requester.interpret_response_status.return_value = should_retry request_body_json = {"body": "json"} requester.request_body_json.return_value = request_body_json From a6569801d7dedb3bb9b6f04fd4db7048db68e822 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 12:46:45 -0700 Subject: [PATCH 13/29] Update http_response_filter tests to reflect updated default error mapping --- .../test_http_response_filter.py | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index ee39af52e5ec..53a6ff357a08 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -6,23 +6,22 @@ import pytest import requests +from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter -from airbyte_cdk.sources.declarative.requesters.error_handlers.response_status import ResponseStatus -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction, ErrorResolution @pytest.mark.parametrize( - "action, http_codes, predicate, error_contains, back_off, error_message, response, expected_response_status", + "action, http_codes, predicate, error_contains, error_message, response, expected_response_status", [ pytest.param( ResponseAction.FAIL, {501, 503}, "", "", - None, "custom error message", {"status_code": 503}, - ResponseStatus(response_action=ResponseAction.FAIL, error_message="custom error message"), + ErrorResolution(response_action=ResponseAction.FAIL, failure_type=FailureType.transient_error, error_message="custom error message"), id="test_http_code_matches", ), pytest.param( @@ -30,10 +29,9 @@ {403}, "", "", - None, "", {"status_code": 403}, - ResponseStatus(response_action=ResponseAction.IGNORE), + ErrorResolution(response_action=ResponseAction.IGNORE, failure_type=FailureType.config_error, error_message="Forbidden. You don't have permission to access this resource."), id="test_http_code_matches_ignore_action", ), pytest.param( @@ -41,10 +39,9 @@ {429}, "", "", - 30, "", {"status_code": 429}, - ResponseStatus(response_action=ResponseAction.RETRY, retry_in=30), + ErrorResolution(response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, error_message="Too many requests."), id="test_http_code_matches_retry_action", ), pytest.param( @@ -52,10 +49,9 @@ {}, '{{ response.the_body == "do_i_match" }}', "", - None, "error message was: {{ response.failure }}", {"status_code": 404, "json": {"the_body": "do_i_match", "failure": "i failed you"}}, - ResponseStatus(response_action=ResponseAction.FAIL, error_message="error message was: i failed you"), + ErrorResolution(response_action=ResponseAction.FAIL, failure_type=FailureType.system_error, error_message="error message was: i failed you"), id="test_predicate_matches_json", ), pytest.param( @@ -63,10 +59,9 @@ {}, '{{ headers.the_key == "header_match" }}', "", - None, "error from header: {{ headers.warning }}", {"status_code": 404, "headers": {"the_key": "header_match", "warning": "this failed"}}, - ResponseStatus(response_action=ResponseAction.FAIL, error_message="error from header: this failed"), + ErrorResolution(response_action=ResponseAction.FAIL, failure_type=FailureType.system_error, error_message="error from header: this failed"), id="test_predicate_matches_headers", ), pytest.param( @@ -74,10 +69,12 @@ {}, None, "DENIED", - None, "", {"status_code": 403, "json": {"error": "REQUEST_DENIED"}}, - ResponseStatus(response_action=ResponseAction.FAIL), + ErrorResolution( + response_action=ResponseAction.FAIL, + failure_type=FailureType.config_error, + error_message="Forbidden. You don't have permission to access this resource."), id="test_predicate_matches_headers", ), pytest.param( @@ -85,15 +82,17 @@ {400, 404}, '{{ headers.error == "invalid_input" or response.reason == "bad request"}}', "", - None, "", {"status_code": 403, "headers": {"error": "authentication_error"}, "json": {"reason": "permission denied"}}, - None, + ErrorResolution( + response_action=ResponseAction.FAIL, + failure_type=FailureType.config_error, + error_message="Forbidden. You don't have permission to access this resource."), id="test_response_does_not_match_filter", ), ], ) -def test_matches(requests_mock, action, http_codes, predicate, error_contains, back_off, error_message, response, expected_response_status): +def test_matches(requests_mock, action, http_codes, predicate, error_contains, error_message, response, expected_response_status): requests_mock.register_uri( "GET", "https://airbyte.io/", @@ -112,10 +111,10 @@ def test_matches(requests_mock, action, http_codes, predicate, error_contains, b error_message=error_message, ) - actual_response_status = response_filter.matches(response, backoff_time=back_off or 10) + actual_response_status = response_filter.matches(response) if expected_response_status: - assert actual_response_status.action == expected_response_status.action - assert actual_response_status.retry_in == expected_response_status.retry_in + assert actual_response_status.response_action == expected_response_status.response_action + assert actual_response_status.failure_type == expected_response_status.failure_type assert actual_response_status.error_message == expected_response_status.error_message else: assert actual_response_status is None From 153eea54474ef2164e21bcee3d719f23ed221148 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 14:19:45 -0700 Subject: [PATCH 14/29] Update HttpResponseFilter to prioritize user inputted error messages --- .../requesters/error_handlers/http_response_filter.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 237aa1e5fc2b..ff2081a44d68 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -61,9 +61,7 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep if filter_action is not None: default_mapped_error_resolution = self._match_default_error_mapping(mapped_key) error_message = ( - default_mapped_error_resolution.error_message - if default_mapped_error_resolution - else self._create_error_message(response_or_exception) + self._create_error_message(response_or_exception) or default_mapped_error_resolution.error_message ) failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error From 0a33f2f1751449c52c8a1cc92e2ab373c4ffd091 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 14:20:06 -0700 Subject: [PATCH 15/29] Update declarative error handlers tests for updated backoff_time method name --- .../backoff_strategies/test_constant_backoff.py | 2 +- .../backoff_strategies/test_exponential_backoff.py | 4 ++-- .../backoff_strategies/test_wait_time_from_header.py | 2 +- .../backoff_strategies/test_wait_until_time_from_header.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py index 685e5e89280d..ffa57da17aa3 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_constant_backoff.py @@ -30,5 +30,5 @@ def test_constant_backoff(test_name, attempt_count, backofftime, expected_backof backoff_strategy = ConstantBackoffStrategy( parameters={"backoff": PARAMETERS_BACKOFF_TIME}, backoff_time_in_seconds=backofftime, config={"backoff": CONFIG_BACKOFF_TIME} ) - backoff = backoff_strategy.backoff(response_mock, attempt_count) + backoff = backoff_strategy.backoff_time(response_mock, attempt_count) assert backoff == expected_backoff_time diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py index 8dac43cb869c..070fb41d64ca 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_exponential_backoff.py @@ -25,12 +25,12 @@ def test_exponential_backoff(test_name, attempt_count, factor, expected_backoff_time): response_mock = MagicMock() backoff_strategy = ExponentialBackoffStrategy(factor=factor, parameters=parameters, config=config) - backoff = backoff_strategy.backoff(response_mock, attempt_count) + backoff = backoff_strategy.backoff_time(response_mock, attempt_count) assert backoff == expected_backoff_time def test_exponential_backoff_default(): response_mock = MagicMock() backoff_strategy = ExponentialBackoffStrategy(parameters=parameters, config=config) - backoff = backoff_strategy.backoff(response_mock, 3) + backoff = backoff_strategy.backoff_time(response_mock, 3) assert backoff == 40 diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py index 90c71d7f0786..06e738734995 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_time_from_header.py @@ -31,5 +31,5 @@ def test_wait_time_from_header(test_name, header, header_value, regex, expected_ backoff_stratery = WaitTimeFromHeaderBackoffStrategy( header=header, regex=regex, parameters={"wait_time": "wait_time"}, config={"wait_time": "wait_time"} ) - backoff = backoff_stratery.backoff(response_mock, 1) + backoff = backoff_stratery.backoff_time(response_mock, 1) assert backoff == expected_backoff_time diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py index f7f971c536dd..efc090abd931 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py @@ -60,5 +60,5 @@ def test_wait_untiltime_from_header(time_mock, test_name, header, wait_until, mi parameters={"wait_until": "wait_until", "regex": REGEX, "min_wait": SOME_BACKOFF_TIME}, config={"wait_until": "wait_until", "regex": REGEX, "min_wait": SOME_BACKOFF_TIME}, ) - backoff = backoff_stratery.backoff(response_mock, 1) + backoff = backoff_stratery.backoff_time(response_mock, 1) assert backoff == expected_backoff_time From 2dc8a8954627f1d6a333693efaf2452e3f4cd6b6 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 14:39:51 -0700 Subject: [PATCH 16/29] chore: format code --- .../requesters/error_handlers/composite_error_handler.py | 5 ++++- .../requesters/error_handlers/default_error_handler.py | 6 +----- .../requesters/error_handlers/http_response_filter.py | 5 ++--- .../requesters/error_handlers/test_http_response_filter.py | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index a966cb3fd63a..895066420d3c 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -58,7 +58,10 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp if matched_error_resolution.response_action == ResponseAction.SUCCESS: return matched_error_resolution - if matched_error_resolution.response_action == ResponseAction.RETRY or matched_error_resolution.response_action == ResponseAction.IGNORE: + if ( + matched_error_resolution.response_action == ResponseAction.RETRY + or matched_error_resolution.response_action == ResponseAction.IGNORE + ): return matched_error_resolution if matched_error_resolution is not None: diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 37ae4651c258..494491278588 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -12,11 +12,7 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ( - DEFAULT_ERROR_RESOLUTION, - SUCCESS_RESOLUTION, - ErrorResolution, -) +from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution from airbyte_cdk.sources.types import Config diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index ff2081a44d68..f1dedcab5c5c 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -60,9 +60,8 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep if filter_action is not None: default_mapped_error_resolution = self._match_default_error_mapping(mapped_key) - error_message = ( - self._create_error_message(response_or_exception) or default_mapped_error_resolution.error_message - ) + default_error_message = default_mapped_error_resolution.error_message if default_mapped_error_resolution is not None else "" + error_message = self._create_error_message(response_or_exception) or default_error_message failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error return ErrorResolution( diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index 53a6ff357a08..bdb66898858a 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -8,7 +8,7 @@ import requests from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction, ErrorResolution +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction @pytest.mark.parametrize( From 3a93bbfb9aec82aca1727390bda1effe78ecc748 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 15:35:37 -0700 Subject: [PATCH 17/29] Update http_requester and associated tests --- .../error_handlers/default_error_handler.py | 2 +- .../declarative/requesters/http_requester.py | 5 +- .../requesters/test_http_requester.py | 76 +++++++++++-------- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 494491278588..22b6fcd58376 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -133,7 +133,7 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp return DEFAULT_ERROR_RESOLUTION def backoff_time( - self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int + self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int = 0 ) -> Optional[float]: backoff = None if self.backoff_strategies: diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index 8ef911e478b5..4da9759ad3c2 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -509,10 +509,7 @@ def _send( elif error_resolution.response_action == ResponseAction.RETRY: custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None - error_message = ( - error_resolution.error_message - or f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action}." - ) + error_message = f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action} with message: {error_resolution.error_message if error_resolution.error_message else None}" if custom_backoff_time: raise UserDefinedBackoffException( backoff=custom_backoff_time, diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py index f02073fab442..ff591b1fbb86 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py @@ -11,17 +11,20 @@ import pytest as pytest import requests import requests_cache +from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator, NoAuth from airbyte_cdk.sources.declarative.auth.token import BearerAuthenticator -from airbyte_cdk.sources.declarative.exceptions import ReadException from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString +from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies import ConstantBackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.http_requester import HttpMethod, HttpRequester from airbyte_cdk.sources.declarative.requesters.request_options import InterpolatedRequestOptionsProvider from airbyte_cdk.sources.message import MessageRepository +from airbyte_cdk.sources.streams.http.error_handlers import ErrorResolution, ResponseAction from airbyte_cdk.sources.streams.http.exceptions import DefaultBackoffException, RequestBodyException, UserDefinedBackoffException from airbyte_cdk.sources.types import Config +from airbyte_cdk.utils.traced_exception import AirbyteTracedException from requests import PreparedRequest from requests_cache import CachedResponse @@ -191,7 +194,7 @@ def create_requester( def test_basic_send_request(): options_provider = MagicMock() options_provider.get_request_headers.return_value = {"my_header": "my_value"} - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider requester.send_request() sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] @@ -243,7 +246,7 @@ def test_send_request_data_json( authenticator = MagicMock() authenticator.get_request_body_data.return_value = authenticator_data authenticator.get_request_body_json.return_value = authenticator_json - requester = create_requester(authenticator=authenticator) + requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -278,7 +281,7 @@ def test_send_request_string_data(provider_data, param_data, authenticator_data, options_provider.get_request_body_data.return_value = provider_data authenticator = MagicMock() authenticator.get_request_body_data.return_value = authenticator_data - requester = create_requester(authenticator=authenticator) + requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -316,7 +319,7 @@ def test_send_request_headers(provider_headers, param_headers, authenticator_hea options_provider.get_request_headers.return_value = provider_headers authenticator = MagicMock() authenticator.get_auth_header.return_value = authenticator_headers or {} - requester = create_requester(authenticator=authenticator) + requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -345,7 +348,7 @@ def test_send_request_params(provider_params, param_params, authenticator_params options_provider.get_request_params.return_value = provider_params authenticator = MagicMock() authenticator.get_request_params.return_value = authenticator_params - requester = create_requester(authenticator=authenticator) + requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -432,7 +435,7 @@ def test_request_param_interpolation(request_parameters, config, expected_query_ request_headers={}, parameters={}, ) - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider requester.send_request() sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] @@ -557,7 +560,7 @@ def test_request_body_interpolation(request_body_data, config, expected_request_ request_headers={}, parameters={}, ) - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider requester.send_request() sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] @@ -578,7 +581,7 @@ def test_request_body_interpolation(request_body_data, config, expected_request_ ], ) def test_send_request_path(requester_path, param_path, expected_path): - requester = create_requester(config={"config_key": "config_value"}, path=requester_path, parameters={"param_key": "param_value"}) + requester = create_requester(config={"config_key": "config_value"}, path=requester_path, parameters={"param_key": "param_value"}, error_handler=DefaultErrorHandler(parameters={}, config={})) requester.send_request(stream_slice={"start": "2012"}, next_page_token={"next_page_token": "pagetoken"}, path=param_path) sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] parsed_url = urlparse(sent_request.url) @@ -590,6 +593,7 @@ def test_send_request_url_base(): url_base="https://example.org/{{ config.config_key }}/{{ parameters.param_key }}", config={"config_key": "config_value"}, parameters={"param_key": "param_value"}, + error_handler=DefaultErrorHandler(parameters={}, config={}), ) requester.send_request() sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] @@ -598,7 +602,7 @@ def test_send_request_url_base(): def test_send_request_stream_slice_next_page_token(): options_provider = MagicMock() - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) requester._request_options_provider = options_provider stream_slice = {"id": "1234"} next_page_token = {"next_page_token": "next_page_token"} @@ -634,7 +638,7 @@ def test_stub_custom_backoff_http_stream(mocker): req = requests.Response() req.status_code = 429 - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) requester._backoff_time = lambda _: 0.5 requester._session.send.return_value = req @@ -653,7 +657,7 @@ def test_stub_custom_backoff_http_stream_retries(mocker, retries): req.status_code = HTTPStatus.TOO_MANY_REQUESTS requester._session.send.return_value = req - with pytest.raises(UserDefinedBackoffException, match="Request URL: https://example.com/deals, Response Code: 429") as excinfo: + with pytest.raises(UserDefinedBackoffException, match="Request to https://example.com/deals failed") as excinfo: requester.send_request() assert isinstance(excinfo.value.request, requests.PreparedRequest) assert isinstance(excinfo.value.response, requests.Response) @@ -690,24 +694,29 @@ def test_4xx_error_codes_http_stream(mocker, http_code): req.status_code = http_code requester._session.send.return_value = req - with pytest.raises(ReadException): + with pytest.raises(AirbyteTracedException): requester.send_request() def test_raise_on_http_errors_off_429(mocker): - requester = create_requester() + mocked_backoff_strategy = MagicMock(spec=ConstantBackoffStrategy) + mocked_backoff_strategy.backoff_time.return_value = 0 + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={}, max_retries=0, backoff_strategies=[mocked_backoff_strategy])) requester._DEFAULT_RETRY_FACTOR = 0.01 req = requests.Response() req.status_code = 429 requester._session.send.return_value = req - with pytest.raises(DefaultBackoffException, match="Request URL: https://example.com/deals, Response Code: 429"): + + with pytest.raises(DefaultBackoffException, match="Request to https://example.com/deals failed"): requester.send_request() @pytest.mark.parametrize("status_code", [500, 501, 503, 504]) def test_raise_on_http_errors_off_5xx(mocker, status_code): - requester = create_requester() + mocked_backoff_strategy = MagicMock(spec=ConstantBackoffStrategy) + mocked_backoff_strategy.backoff_time.return_value = 0 + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={}, max_retries=0, backoff_strategies=[mocked_backoff_strategy])) req = requests.Response() req.status_code = status_code requester._session.send.return_value = req @@ -718,16 +727,16 @@ def test_raise_on_http_errors_off_5xx(mocker, status_code): assert requester._session.send.call_count == requester.max_retries + 1 -@pytest.mark.parametrize("status_code", [400, 401, 402, 403, 416]) +@pytest.mark.parametrize("status_code", [400, 401, 403]) def test_raise_on_http_errors_off_non_retryable_4xx(mocker, status_code): - requester = create_requester() + requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={}, max_retries=0)) req = requests.Response() req.status_code = status_code requester._session.send.return_value = req requester._DEFAULT_RETRY_FACTOR = 0.01 - response = requester.send_request() - assert response.status_code == status_code + with pytest.raises(AirbyteTracedException): + requester.send_request() @pytest.mark.parametrize( @@ -820,6 +829,7 @@ def test_join_url(test_name, base_url, path, expected_full_url): request_options_provider=None, config={}, parameters={}, + error_handler=DefaultErrorHandler(parameters={}, config={}), ) requester._session.send = MagicMock() response = requests.Response() @@ -890,18 +900,20 @@ def test_duplicate_request_params_are_deduped(path, params, expected_url): @pytest.mark.parametrize( - "should_log, status_code, should_throw", + "should_log, status_code, should_retry, should_throw", [ - (True, 200, False), - (True, 400, False), - (True, 500, True), - (False, 200, False), - (False, 400, False), - (False, 500, True), + (True, 200, False, False), + (True, 400, False, True), + (True, 500, True, False), + (False, 200, False, False), + (False, 400, False, True), + (False, 500, True, False), ], ) -def test_log_requests(should_log, status_code, should_throw): +def test_log_requests(should_log, status_code, should_retry, should_throw): repository = MagicMock() + mocked_backoff_strategy = MagicMock(spec=ConstantBackoffStrategy) + mocked_backoff_strategy.backoff_time.return_value = 0 requester = HttpRequester( name="name", url_base="https://test_base_url.com", @@ -912,6 +924,7 @@ def test_log_requests(should_log, status_code, should_throw): parameters={}, message_repository=repository, disable_retries=True, + error_handler=DefaultErrorHandler(parameters={}, config={}, backoff_strategies=[mocked_backoff_strategy]), ) requester._session.send = MagicMock() response = requests.Response() @@ -919,9 +932,12 @@ def test_log_requests(should_log, status_code, should_throw): requester._session.send.return_value = response formatter = MagicMock() formatter.return_value = "formatted_response" - if should_throw: + if should_retry: with pytest.raises(DefaultBackoffException): requester.send_request(log_formatter=formatter if should_log else None) + elif should_throw: + with pytest.raises(AirbyteTracedException): + requester.send_request(log_formatter=formatter if should_log else None) else: requester.send_request(log_formatter=formatter if should_log else None) if should_log: @@ -955,7 +971,7 @@ def test_caching_session_with_enable_use_cache(http_requester_factory): def test_response_caching_with_enable_use_cache(http_requester_factory, requests_mock): - http_requester = http_requester_factory(use_cache=True) + http_requester = http_requester_factory(use_cache=True, error_handler=DefaultErrorHandler(parameters={}, config={})) requests_mock.register_uri("GET", http_requester.url_base, json=[{"id": 12, "title": "test_record"}]) http_requester.clear_cache() From 6c3315ced5257706183423a52fe495c197ac2d6b Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 15:40:13 -0700 Subject: [PATCH 18/29] chore: lint --- .../error_handlers/test_composite_error_handler.py | 2 +- .../error_handlers/test_default_error_handler.py | 4 +++- .../error_handlers/test_http_response_filter.py | 14 ++++++++------ .../declarative/requesters/test_http_requester.py | 3 --- .../retrievers/test_simple_retriever.py | 1 - 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py index 146d412c2ca0..b2b18a171212 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_composite_error_handler.py @@ -9,7 +9,7 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers import HttpResponseFilter from airbyte_cdk.sources.declarative.requesters.error_handlers.composite_error_handler import CompositeErrorHandler from airbyte_cdk.sources.declarative.requesters.error_handlers.default_error_handler import DefaultErrorHandler -from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, FailureType, ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.response_models import ErrorResolution, ResponseAction SOME_BACKOFF_TIME = 60 diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py index bc4c4b7f2e28..6f0ef47eab49 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_default_error_handler.py @@ -2,7 +2,6 @@ # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # -from http import HTTPStatus from unittest.mock import MagicMock import pytest @@ -147,6 +146,7 @@ def test_default_error_handler_with_custom_response_filter(test_name, http_statu assert actual_error_resolution.failure_type == failure_type assert actual_error_resolution.error_message == error_message + @pytest.mark.parametrize( "http_status_code, expected_response_action", [ @@ -208,6 +208,7 @@ def test_default_error_handler_with_constant_backoff_strategy(): error_handler = DefaultErrorHandler(config={}, parameters={}, backoff_strategies=[ConstantBackoffStrategy(SOME_BACKOFF_TIME, config={}, parameters={})]) assert error_handler.backoff_time(response_or_exception=response_mock, attempt_count=0) == SOME_BACKOFF_TIME + @pytest.mark.parametrize( "attempt_count", [ @@ -219,6 +220,7 @@ def test_default_error_handler_with_exponential_backoff_strategy(attempt_count): error_handler = DefaultErrorHandler(config={}, parameters={}, backoff_strategies=[ExponentialBackoffStrategy(factor=1, config={}, parameters={})]) assert error_handler.backoff_time(response_or_exception=response_mock, attempt_count=attempt_count) == (1 * 2**attempt_count) + def create_response(status_code: int, headers=None, json_body=None): url = "https://airbyte.io" diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index bdb66898858a..cf468a98b22a 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -72,9 +72,10 @@ "", {"status_code": 403, "json": {"error": "REQUEST_DENIED"}}, ErrorResolution( - response_action=ResponseAction.FAIL, - failure_type=FailureType.config_error, - error_message="Forbidden. You don't have permission to access this resource."), + response_action=ResponseAction.FAIL, + failure_type=FailureType.config_error, + error_message="Forbidden. You don't have permission to access this resource." + ), id="test_predicate_matches_headers", ), pytest.param( @@ -85,9 +86,10 @@ "", {"status_code": 403, "headers": {"error": "authentication_error"}, "json": {"reason": "permission denied"}}, ErrorResolution( - response_action=ResponseAction.FAIL, - failure_type=FailureType.config_error, - error_message="Forbidden. You don't have permission to access this resource."), + response_action=ResponseAction.FAIL, + failure_type=FailureType.config_error, + error_message="Forbidden. You don't have permission to access this resource." + ), id="test_response_does_not_match_filter", ), ], diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py index ff591b1fbb86..6f25b6726f8a 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py @@ -11,7 +11,6 @@ import pytest as pytest import requests import requests_cache -from airbyte_cdk.models import FailureType from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator, NoAuth from airbyte_cdk.sources.declarative.auth.token import BearerAuthenticator from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString @@ -21,7 +20,6 @@ from airbyte_cdk.sources.declarative.requesters.http_requester import HttpMethod, HttpRequester from airbyte_cdk.sources.declarative.requesters.request_options import InterpolatedRequestOptionsProvider from airbyte_cdk.sources.message import MessageRepository -from airbyte_cdk.sources.streams.http.error_handlers import ErrorResolution, ResponseAction from airbyte_cdk.sources.streams.http.exceptions import DefaultBackoffException, RequestBodyException, UserDefinedBackoffException from airbyte_cdk.sources.types import Config from airbyte_cdk.utils.traced_exception import AirbyteTracedException @@ -707,7 +705,6 @@ def test_raise_on_http_errors_off_429(mocker): req.status_code = 429 requester._session.send.return_value = req - with pytest.raises(DefaultBackoffException, match="Request to https://example.com/deals failed"): requester.send_request() diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py b/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py index f0f86f65a7e0..c3435944c84d 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/retrievers/test_simple_retriever.py @@ -59,7 +59,6 @@ def test_simple_retriever_full(mock_http_stream): requester.get_path.return_value = path http_method = HttpMethod.GET requester.get_method.return_value = http_method - backoff_time = 60 should_retry = True requester.interpret_response_status.return_value = should_retry request_body_json = {"body": "json"} From bef56d2ba6ce9db88d2649d1432b45f92fe70725 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 15:53:59 -0700 Subject: [PATCH 19/29] Update http requester to return response if no error handler, aligning with previous behavior --- .../declarative/requesters/http_requester.py | 3 +++ .../declarative/requesters/test_http_requester.py | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index 4da9759ad3c2..d6447bd8127a 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -482,6 +482,9 @@ def _send( response: requests.Response = self._session.send(request) + if self.error_handler is None: + return response + error_resolution: ErrorResolution = self.interpret_response_status(response) self.logger.debug("Receiving response", extra={"headers": response.headers, "status": response.status_code, "body": response.text}) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py index 6f25b6726f8a..65980de80239 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/test_http_requester.py @@ -192,7 +192,7 @@ def create_requester( def test_basic_send_request(): options_provider = MagicMock() options_provider.get_request_headers.return_value = {"my_header": "my_value"} - requester = create_requester(error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester() requester._request_options_provider = options_provider requester.send_request() sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] @@ -244,7 +244,7 @@ def test_send_request_data_json( authenticator = MagicMock() authenticator.get_request_body_data.return_value = authenticator_data authenticator.get_request_body_json.return_value = authenticator_json - requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester(authenticator=authenticator) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -279,7 +279,7 @@ def test_send_request_string_data(provider_data, param_data, authenticator_data, options_provider.get_request_body_data.return_value = provider_data authenticator = MagicMock() authenticator.get_request_body_data.return_value = authenticator_data - requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester(authenticator=authenticator) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -317,7 +317,7 @@ def test_send_request_headers(provider_headers, param_headers, authenticator_hea options_provider.get_request_headers.return_value = provider_headers authenticator = MagicMock() authenticator.get_auth_header.return_value = authenticator_headers or {} - requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester(authenticator=authenticator) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -346,7 +346,7 @@ def test_send_request_params(provider_params, param_params, authenticator_params options_provider.get_request_params.return_value = provider_params authenticator = MagicMock() authenticator.get_request_params.return_value = authenticator_params - requester = create_requester(authenticator=authenticator, error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester(authenticator=authenticator) requester._request_options_provider = options_provider if expected_exception is not None: with pytest.raises(expected_exception): @@ -579,7 +579,7 @@ def test_request_body_interpolation(request_body_data, config, expected_request_ ], ) def test_send_request_path(requester_path, param_path, expected_path): - requester = create_requester(config={"config_key": "config_value"}, path=requester_path, parameters={"param_key": "param_value"}, error_handler=DefaultErrorHandler(parameters={}, config={})) + requester = create_requester(config={"config_key": "config_value"}, path=requester_path, parameters={"param_key": "param_value"}) requester.send_request(stream_slice={"start": "2012"}, next_page_token={"next_page_token": "pagetoken"}, path=param_path) sent_request: PreparedRequest = requester._session.send.call_args_list[0][0][0] parsed_url = urlparse(sent_request.url) @@ -968,7 +968,7 @@ def test_caching_session_with_enable_use_cache(http_requester_factory): def test_response_caching_with_enable_use_cache(http_requester_factory, requests_mock): - http_requester = http_requester_factory(use_cache=True, error_handler=DefaultErrorHandler(parameters={}, config={})) + http_requester = http_requester_factory(use_cache=True) requests_mock.register_uri("GET", http_requester.url_base, json=[{"id": 12, "title": "test_record"}]) http_requester.clear_cache() From 572933e3df55886ba126b207c04eb839ca8e149c Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 16:41:35 -0700 Subject: [PATCH 20/29] mypy type checking --- .../parsers/model_to_component_factory.py | 7 +- .../constant_backoff_strategy.py | 2 +- .../exponential_backoff_strategy.py | 2 +- .../wait_time_from_header_backoff_strategy.py | 2 +- ...until_time_from_header_backoff_strategy.py | 6 +- .../error_handlers/composite_error_handler.py | 6 +- .../error_handlers/default_error_handler.py | 4 +- .../error_handlers/http_response_filter.py | 19 +- .../declarative/requesters/http_requester.py | 8 +- .../configured_catalog_test.json | 52 ++++ .../connectors/source-harvest/poetry.lock | 244 +++++++++++++++--- .../connectors/source-harvest/pyproject.toml | 2 +- 12 files changed, 289 insertions(+), 65 deletions(-) create mode 100644 airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index 11c26f27fac3..5323fb916e55 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -333,7 +333,7 @@ def create_legacy_to_per_partition_state_migration( declarative_stream: DeclarativeStreamModel, ) -> LegacyToPerPartitionStateMigration: retriever = declarative_stream.retriever - partition_router = retriever.partition_router + partition_router = retriever.partition_router # type: ignore # CustomRetriever would inherit from SimpleRetriever and therefore have partition_router if not isinstance(retriever, SimpleRetrieverModel): raise ValueError( @@ -792,7 +792,10 @@ def create_http_requester(self, model: HttpRequesterModel, config: Config, *, na @staticmethod def create_http_response_filter(model: HttpResponseFilterModel, config: Config, **kwargs: Any) -> HttpResponseFilter: - action = ResponseAction(model.action.value) or None + if model.action is not None: + action = ResponseAction(model.action.value) + else: + action = None http_codes = ( set(model.http_codes) if model.http_codes else set() ) # JSON schema notation has no set data type. The schema enforces an array of unique elements diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py index fd29724a60de..b5e4651f7f41 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py @@ -32,5 +32,5 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.backoff_time_in_seconds = InterpolatedString.create(self.backoff_time_in_seconds, parameters=parameters) - def backoff_time(self, response_or_exception: Optional[Union[requests.Response, Exception]], attempt_count: int) -> Optional[float]: + def backoff_time(self, response_or_exception: Optional[Union[requests.Response, Exception]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK return self.backoff_time_in_seconds.eval(self.config) # type: ignore # backoff_time_in_seconds is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py index 8dd0b30bdd97..1af55aa1eaec 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py @@ -32,7 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.factor = InterpolatedString.create(self.factor, parameters=parameters) - def backoff_time( + def backoff_time( # type: ignore # attempt_count maintained for compatibility with low code CDK self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int ) -> Optional[float]: return self.factor.eval(self.config) * 2**attempt_count # type: ignore # factor is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py index 20c866552cda..7436c484fc68 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py @@ -32,7 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: self.regex = InterpolatedString.create(self.regex, parameters=parameters) if self.regex else None self.header = InterpolatedString.create(self.header, parameters=parameters) - def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK header = self.header.eval(config=self.config) # type: ignore # header is always cast to an interpolated stream if self.regex: evaled_regex = self.regex.eval(self.config) # type: ignore # header is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py index eb17fe6f08f3..2aadff651c09 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py @@ -39,7 +39,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: if not isinstance(self.min_wait, InterpolatedString): self.min_wait = InterpolatedString.create(str(self.min_wait), parameters=parameters) - def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: + def backoff_time(self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK now = time.time() header = self.header.eval(self.config) # type: ignore # header is always cast to an interpolated string if self.regex: @@ -47,7 +47,9 @@ def backoff_time(self, response: requests.Response, attempt_count: int) -> Optio regex = re.compile(evaled_regex) else: regex = None - wait_until = get_numeric_value_from_header(response, header, regex) + wait_until = None + if isinstance(response_or_exception, requests.Response): + wait_until = get_numeric_value_from_header(response_or_exception, header, regex) min_wait = self.min_wait.eval(self.config) # type: ignore # header is always cast to an interpolated string if wait_until is None or not wait_until: return float(min_wait) if min_wait else None diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 895066420d3c..729910d46753 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -43,12 +43,12 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: raise ValueError("CompositeErrorHandler expects at least 1 underlying error handler") @property - def max_retries(self) -> Union[int, None]: - return self.error_handlers[0].max_retries + def max_retries(self) -> Optional[int]: + return self.error_handlers[0].max_retries # type: ignore # property not defined in ErrorHandler @property def max_time(self) -> Union[int, None]: - return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) + return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) # type: ignore # property not defined in ErrorHandler def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: matched_error_resolution = None diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 22b6fcd58376..6545e3aa1728 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -9,7 +9,7 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import ( ExponentialBackoffStrategy, ) -from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategy import BackoffStrategy +from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution @@ -138,7 +138,7 @@ def backoff_time( backoff = None if self.backoff_strategies: for backoff_strategies in self.backoff_strategies: - backoff = backoff_strategies.backoff_time(response_or_exception, attempt_count) + backoff = backoff_strategies.backoff_time(response_or_exception, attempt_count) # type: ignore # attempt_count maintained for compatibility with low code CDK if backoff: return backoff return backoff diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index f1dedcab5c5c..fa43877fa512 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -58,10 +58,17 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep response_or_exception.status_code if isinstance(response_or_exception, requests.Response) else response_or_exception.__class__ ) - if filter_action is not None: + if isinstance(mapped_key, (int, Exception)): default_mapped_error_resolution = self._match_default_error_mapping(mapped_key) + else: + default_mapped_error_resolution = None + + if filter_action is not None: default_error_message = default_mapped_error_resolution.error_message if default_mapped_error_resolution is not None else "" - error_message = self._create_error_message(response_or_exception) or default_error_message + if isinstance(response_or_exception, requests.Response): + error_message = self._create_error_message(response_or_exception) + else: + error_message = default_error_message failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error return ErrorResolution( @@ -69,10 +76,8 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep failure_type=failure_type, error_message=error_message, ) - else: - mapped_error_resolution = self._match_default_error_mapping(mapped_key) - if mapped_error_resolution: - return mapped_error_resolution + elif default_mapped_error_resolution: + return default_mapped_error_resolution return None @@ -101,7 +106,7 @@ def _safe_response_json(response: requests.Response) -> dict[str, Any]: except requests.exceptions.JSONDecodeError: return {} - def _create_error_message(self, response: requests.Response) -> str: + def _create_error_message(self, response: requests.Response) -> Optional[str]: """ Construct an error message based on the specified message template of the filter. :param response: The HTTP response which can be used during interpolation diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index d6447bd8127a..52c5a3960bfc 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -16,7 +16,7 @@ from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator, NoAuth from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString -from airbyte_cdk.sources.declarative.requesters.error_handlers.error_handler import ErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( InterpolatedRequestOptionsProvider, ) @@ -205,7 +205,7 @@ def max_retries(self) -> Union[int, None]: return 0 if self.error_handler is None: return self._DEFAULT_MAX_RETRY - return self.error_handler.max_retries + return self.error_handler.max_retries # type: ignore # parent class does not have max_retries @property def max_time(self) -> Union[int, None]: @@ -214,7 +214,7 @@ def max_time(self) -> Union[int, None]: """ if self.error_handler is None: return self._DEFAULT_MAX_TIME - return self.error_handler.max_time + return self.error_handler.max_time # type: ignore # parent class does not have max_time @property def logger(self) -> logging.Logger: @@ -511,7 +511,7 @@ def _send( self.logger.info(error_resolution.error_message or log_message) elif error_resolution.response_action == ResponseAction.RETRY: - custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None + custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None # type: ignore # parent class does not have backoff_time error_message = f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action} with message: {error_resolution.error_message if error_resolution.error_message else None}" if custom_backoff_time: raise UserDefinedBackoffException( diff --git a/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json b/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json new file mode 100644 index 000000000000..00e0a5a921f2 --- /dev/null +++ b/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json @@ -0,0 +1,52 @@ +{ + "streams": [ + { + "stream": { + "name": "clients", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": true, + "default_cursor_field": ["updated_at"], + "source_defined_primary_key": [["id"]] + }, + "sync_mode": "incremental", + "cursor_field": ["updated_at"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "contacts", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": true, + "default_cursor_field": ["updated_at"], + "source_defined_primary_key": [["id"]] + }, + "sync_mode": "incremental", + "cursor_field": ["updated_at"], + "destination_sync_mode": "append" + }, + { + "stream": { + "name": "company", + "json_schema": {}, + "supported_sync_modes": ["full_refresh"] + }, + "sync_mode": "full_refresh", + "destination_sync_mode": "overwrite" + }, + { + "stream": { + "name": "invoices", + "json_schema": {}, + "supported_sync_modes": ["full_refresh", "incremental"], + "source_defined_cursor": true, + "default_cursor_field": ["updated_at"], + "source_defined_primary_key": [["id"]] + }, + "sync_mode": "incremental", + "cursor_field": ["updated_at"], + "destination_sync_mode": "append" + } + ] +} diff --git a/airbyte-integrations/connectors/source-harvest/poetry.lock b/airbyte-integrations/connectors/source-harvest/poetry.lock index 6f476b47f734..85b633355d3a 100644 --- a/airbyte-integrations/connectors/source-harvest/poetry.lock +++ b/airbyte-integrations/connectors/source-harvest/poetry.lock @@ -1,32 +1,33 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "airbyte-cdk" -version = "0.80.0" +version = "1.0.1" description = "A framework for writing Airbyte Connectors." optional = false -python-versions = "<4.0,>=3.9" -files = [ - {file = "airbyte_cdk-0.80.0-py3-none-any.whl", hash = "sha256:060e92323a73674fa4e9e2e4a1eb312b9b9d072c9bbe5fa28f54ef21cb4974f3"}, - {file = "airbyte_cdk-0.80.0.tar.gz", hash = "sha256:1383512a83917fecca5b24cea4c72aa5c561cf96dd464485fbcefda48fe574c5"}, -] +python-versions = "^3.9" +files = [] +develop = true [package.dependencies] -airbyte-protocol-models = "0.5.1" +airbyte-protocol-models = ">=0.9.0, <1.0" backoff = "*" cachetools = "*" -Deprecated = ">=1.2,<1.3" -dpath = ">=2.0.1,<2.1.0" +cryptography = "^42.0.5" +Deprecated = "~1.2" +dpath = "~2.0.1" genson = "1.2.2" -isodate = ">=0.6.1,<0.7.0" -Jinja2 = ">=3.1.2,<3.2.0" -jsonref = ">=0.2,<0.3" -jsonschema = ">=3.2.0,<3.3.0" +isodate = "~0.6.1" +Jinja2 = "~3.1.2" +jsonref = "~0.2" +jsonschema = "~3.2.0" pendulum = "<3.0.0" -pydantic = ">=1.10.8,<2.0.0" -pyrate-limiter = ">=3.1.0,<3.2.0" +pydantic = "^1.10.8" +pyjwt = "^2.8.0" +pyrate-limiter = "~3.1.0" python-dateutil = "*" -PyYAML = ">=6.0.1,<7.0.0" +pytz = "2024.1" +PyYAML = "^6.0.1" requests = "*" requests_cache = "*" wcmatch = "8.4" @@ -34,17 +35,21 @@ wcmatch = "8.4" [package.extras] file-based = ["avro (>=1.11.2,<1.12.0)", "fastavro (>=1.8.0,<1.9.0)", "markdown", "pdf2image (==1.16.3)", "pdfminer.six (==20221105)", "pyarrow (>=15.0.0,<15.1.0)", "pytesseract (==0.3.10)", "unstructured.pytesseract (>=0.3.12)", "unstructured[docx,pptx] (==0.10.27)"] sphinx-docs = ["Sphinx (>=4.2,<4.3)", "sphinx-rtd-theme (>=1.0,<1.1)"] -vector-db-based = ["cohere (==4.21)", "langchain (==0.0.271)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] +vector-db-based = ["cohere (==4.21)", "langchain (==0.1.16)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] + +[package.source] +type = "directory" +url = "../../../airbyte-cdk/python" [[package]] name = "airbyte-protocol-models" -version = "0.5.1" +version = "0.11.0" description = "Declares the Airbyte Protocol." optional = false python-versions = ">=3.8" files = [ - {file = "airbyte_protocol_models-0.5.1-py3-none-any.whl", hash = "sha256:dfe84e130e51ce2ae81a06d5aa36f6c5ce3152b9e36e6f0195fad6c3dab0927e"}, - {file = "airbyte_protocol_models-0.5.1.tar.gz", hash = "sha256:7c8b16c7c1c7956b1996052e40585a3a93b1e44cb509c4e97c1ee4fe507ea086"}, + {file = "airbyte_protocol_models-0.11.0-py3-none-any.whl", hash = "sha256:2157757c1af8c13e471ab6a0304fd2f9a2a6af8cc9173937be1348a9553f7c32"}, + {file = "airbyte_protocol_models-0.11.0.tar.gz", hash = "sha256:1c7e46251b0d5a292b4aa382df24f415ac2a2a2b4719361b3c0f76368a043c23"}, ] [package.dependencies] @@ -148,6 +153,70 @@ files = [ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -258,6 +327,60 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cryptography" +version = "42.0.7" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "deprecated" version = "1.2.14" @@ -302,13 +425,13 @@ test = ["pytest (>=6)"] [[package]] name = "freezegun" -version = "1.5.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, - {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] @@ -525,13 +648,13 @@ pytzdata = ">=2020.1" [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -565,6 +688,17 @@ files = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" version = "1.10.15" @@ -617,6 +751,23 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pyrate-limiter" version = "3.1.1" @@ -728,6 +879,17 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + [[package]] name = "pytzdata" version = "2020.1" @@ -764,6 +926,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -800,13 +963,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, + {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, ] [package.dependencies] @@ -868,19 +1031,18 @@ fixture = ["fixtures"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -906,13 +1068,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -1042,4 +1204,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9,<3.12" -content-hash = "2aff7d489ba30abc8f6f649eb51145ab20ab4b4bd2e03657640a451ee40d2a99" +content-hash = "63668b24d8ce7b9285e468803d53474133e6b0b64b02724ccaef0f1f9213fe3c" diff --git a/airbyte-integrations/connectors/source-harvest/pyproject.toml b/airbyte-integrations/connectors/source-harvest/pyproject.toml index 024b3a22f208..78c418faf9cd 100644 --- a/airbyte-integrations/connectors/source-harvest/pyproject.toml +++ b/airbyte-integrations/connectors/source-harvest/pyproject.toml @@ -17,7 +17,7 @@ include = "source_harvest" [tool.poetry.dependencies] python = "^3.9,<3.12" -airbyte-cdk = "0.80.0" +airbyte-cdk = { path = "../../../airbyte-cdk/python", develop = true } [tool.poetry.scripts] source-harvest = "source_harvest.run:run" From 0cc790d91c35eed2c790893f60b75381f7f77c51 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 16:43:24 -0700 Subject: [PATCH 21/29] chore: format code --- .../declarative/parsers/model_to_component_factory.py | 4 ++-- .../backoff_strategies/constant_backoff_strategy.py | 2 +- .../backoff_strategies/exponential_backoff_strategy.py | 2 +- .../wait_time_from_header_backoff_strategy.py | 2 +- .../wait_until_time_from_header_backoff_strategy.py | 2 +- .../requesters/error_handlers/composite_error_handler.py | 4 ++-- .../requesters/error_handlers/default_error_handler.py | 5 ++--- .../requesters/error_handlers/http_response_filter.py | 2 +- .../sources/declarative/requesters/http_requester.py | 8 ++++---- 9 files changed, 15 insertions(+), 16 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py index 5323fb916e55..5440327e745e 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py @@ -333,7 +333,7 @@ def create_legacy_to_per_partition_state_migration( declarative_stream: DeclarativeStreamModel, ) -> LegacyToPerPartitionStateMigration: retriever = declarative_stream.retriever - partition_router = retriever.partition_router # type: ignore # CustomRetriever would inherit from SimpleRetriever and therefore have partition_router + partition_router = retriever.partition_router # type: ignore # CustomRetriever would inherit from SimpleRetriever and therefore have partition_router if not isinstance(retriever, SimpleRetrieverModel): raise ValueError( @@ -795,7 +795,7 @@ def create_http_response_filter(model: HttpResponseFilterModel, config: Config, if model.action is not None: action = ResponseAction(model.action.value) else: - action = None + action = None http_codes = ( set(model.http_codes) if model.http_codes else set() ) # JSON schema notation has no set data type. The schema enforces an array of unique elements diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py index b5e4651f7f41..2eb9c611e8a7 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/constant_backoff_strategy.py @@ -32,5 +32,5 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.backoff_time_in_seconds = InterpolatedString.create(self.backoff_time_in_seconds, parameters=parameters) - def backoff_time(self, response_or_exception: Optional[Union[requests.Response, Exception]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK + def backoff_time(self, response_or_exception: Optional[Union[requests.Response, Exception]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK return self.backoff_time_in_seconds.eval(self.config) # type: ignore # backoff_time_in_seconds is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py index 1af55aa1eaec..584726be8935 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/exponential_backoff_strategy.py @@ -32,7 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: else: self.factor = InterpolatedString.create(self.factor, parameters=parameters) - def backoff_time( # type: ignore # attempt_count maintained for compatibility with low code CDK + def backoff_time( # type: ignore # attempt_count maintained for compatibility with low code CDK self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int ) -> Optional[float]: return self.factor.eval(self.config) * 2**attempt_count # type: ignore # factor is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py index 7436c484fc68..65f83e1eaae4 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_time_from_header_backoff_strategy.py @@ -32,7 +32,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: self.regex = InterpolatedString.create(self.regex, parameters=parameters) if self.regex else None self.header = InterpolatedString.create(self.header, parameters=parameters) - def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK + def backoff_time(self, response: requests.Response, attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK header = self.header.eval(config=self.config) # type: ignore # header is always cast to an interpolated stream if self.regex: evaled_regex = self.regex.eval(self.config) # type: ignore # header is always cast to an interpolated string diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py index 2aadff651c09..82e9cf9afe39 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/wait_until_time_from_header_backoff_strategy.py @@ -39,7 +39,7 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: if not isinstance(self.min_wait, InterpolatedString): self.min_wait = InterpolatedString.create(str(self.min_wait), parameters=parameters) - def backoff_time(self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK + def backoff_time(self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], attempt_count: int) -> Optional[float]: # type: ignore # attempt_count maintained for compatibility with low code CDK now = time.time() header = self.header.eval(self.config) # type: ignore # header is always cast to an interpolated string if self.regex: diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py index 729910d46753..f4e5489c255f 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/composite_error_handler.py @@ -44,11 +44,11 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: @property def max_retries(self) -> Optional[int]: - return self.error_handlers[0].max_retries # type: ignore # property not defined in ErrorHandler + return self.error_handlers[0].max_retries # type: ignore # property not defined in ErrorHandler @property def max_time(self) -> Union[int, None]: - return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) # type: ignore # property not defined in ErrorHandler + return max([error_handler.max_time or 0 for error_handler in self.error_handlers]) # type: ignore # property not defined in ErrorHandler def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: matched_error_resolution = None diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py index 6545e3aa1728..e15194e44385 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.py @@ -9,9 +9,8 @@ from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.exponential_backoff_strategy import ( ExponentialBackoffStrategy, ) -from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy from airbyte_cdk.sources.declarative.requesters.error_handlers.http_response_filter import HttpResponseFilter -from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler +from airbyte_cdk.sources.streams.http.error_handlers import BackoffStrategy, ErrorHandler from airbyte_cdk.sources.streams.http.error_handlers.response_models import DEFAULT_ERROR_RESOLUTION, SUCCESS_RESOLUTION, ErrorResolution from airbyte_cdk.sources.types import Config @@ -138,7 +137,7 @@ def backoff_time( backoff = None if self.backoff_strategies: for backoff_strategies in self.backoff_strategies: - backoff = backoff_strategies.backoff_time(response_or_exception, attempt_count) # type: ignore # attempt_count maintained for compatibility with low code CDK + backoff = backoff_strategies.backoff_time(response_or_exception, attempt_count) # type: ignore # attempt_count maintained for compatibility with low code CDK if backoff: return backoff return backoff diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index fa43877fa512..bd9926890a8f 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -77,7 +77,7 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep error_message=error_message, ) elif default_mapped_error_resolution: - return default_mapped_error_resolution + return default_mapped_error_resolution return None diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py index 52c5a3960bfc..bc7682f70f73 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/http_requester.py @@ -16,7 +16,6 @@ from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator, NoAuth from airbyte_cdk.sources.declarative.decoders.json_decoder import JsonDecoder from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString -from airbyte_cdk.sources.streams.http.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( InterpolatedRequestOptionsProvider, ) @@ -24,6 +23,7 @@ from airbyte_cdk.sources.http_config import MAX_CONNECTION_POOL_SIZE from airbyte_cdk.sources.message import MessageRepository, NoopMessageRepository from airbyte_cdk.sources.streams.http.error_handlers import ErrorResolution, ResponseAction +from airbyte_cdk.sources.streams.http.error_handlers.error_handler import ErrorHandler from airbyte_cdk.sources.streams.http.exceptions import DefaultBackoffException, RequestBodyException, UserDefinedBackoffException from airbyte_cdk.sources.streams.http.http import BODY_REQUEST_METHODS from airbyte_cdk.sources.streams.http.rate_limiting import http_client_default_backoff_handler, user_defined_backoff_handler @@ -205,7 +205,7 @@ def max_retries(self) -> Union[int, None]: return 0 if self.error_handler is None: return self._DEFAULT_MAX_RETRY - return self.error_handler.max_retries # type: ignore # parent class does not have max_retries + return self.error_handler.max_retries # type: ignore # parent class does not have max_retries @property def max_time(self) -> Union[int, None]: @@ -214,7 +214,7 @@ def max_time(self) -> Union[int, None]: """ if self.error_handler is None: return self._DEFAULT_MAX_TIME - return self.error_handler.max_time # type: ignore # parent class does not have max_time + return self.error_handler.max_time # type: ignore # parent class does not have max_time @property def logger(self) -> logging.Logger: @@ -511,7 +511,7 @@ def _send( self.logger.info(error_resolution.error_message or log_message) elif error_resolution.response_action == ResponseAction.RETRY: - custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None # type: ignore # parent class does not have backoff_time + custom_backoff_time = self.error_handler.backoff_time(response) if self.error_handler is not None else None # type: ignore # parent class does not have backoff_time error_message = f"Request to {request.url} failed with failure type {error_resolution.failure_type}, response action {error_resolution.response_action} with message: {error_resolution.error_message if error_resolution.error_message else None}" if custom_backoff_time: raise UserDefinedBackoffException( From 42f9fd72a20e695a22d99d60bd24685a918685ec Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Wed, 29 May 2024 16:46:09 -0700 Subject: [PATCH 22/29] revert source-harvest files --- .../configured_catalog_test.json | 52 ---- .../connectors/source-harvest/poetry.lock | 244 +++--------------- .../connectors/source-harvest/pyproject.toml | 2 +- 3 files changed, 42 insertions(+), 256 deletions(-) delete mode 100644 airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json diff --git a/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json b/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json deleted file mode 100644 index 00e0a5a921f2..000000000000 --- a/airbyte-integrations/connectors/source-harvest/integration_tests/configured_catalog_test.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "streams": [ - { - "stream": { - "name": "clients", - "json_schema": {}, - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": true, - "default_cursor_field": ["updated_at"], - "source_defined_primary_key": [["id"]] - }, - "sync_mode": "incremental", - "cursor_field": ["updated_at"], - "destination_sync_mode": "append" - }, - { - "stream": { - "name": "contacts", - "json_schema": {}, - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": true, - "default_cursor_field": ["updated_at"], - "source_defined_primary_key": [["id"]] - }, - "sync_mode": "incremental", - "cursor_field": ["updated_at"], - "destination_sync_mode": "append" - }, - { - "stream": { - "name": "company", - "json_schema": {}, - "supported_sync_modes": ["full_refresh"] - }, - "sync_mode": "full_refresh", - "destination_sync_mode": "overwrite" - }, - { - "stream": { - "name": "invoices", - "json_schema": {}, - "supported_sync_modes": ["full_refresh", "incremental"], - "source_defined_cursor": true, - "default_cursor_field": ["updated_at"], - "source_defined_primary_key": [["id"]] - }, - "sync_mode": "incremental", - "cursor_field": ["updated_at"], - "destination_sync_mode": "append" - } - ] -} diff --git a/airbyte-integrations/connectors/source-harvest/poetry.lock b/airbyte-integrations/connectors/source-harvest/poetry.lock index 85b633355d3a..6f476b47f734 100644 --- a/airbyte-integrations/connectors/source-harvest/poetry.lock +++ b/airbyte-integrations/connectors/source-harvest/poetry.lock @@ -1,33 +1,32 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "airbyte-cdk" -version = "1.0.1" +version = "0.80.0" description = "A framework for writing Airbyte Connectors." optional = false -python-versions = "^3.9" -files = [] -develop = true +python-versions = "<4.0,>=3.9" +files = [ + {file = "airbyte_cdk-0.80.0-py3-none-any.whl", hash = "sha256:060e92323a73674fa4e9e2e4a1eb312b9b9d072c9bbe5fa28f54ef21cb4974f3"}, + {file = "airbyte_cdk-0.80.0.tar.gz", hash = "sha256:1383512a83917fecca5b24cea4c72aa5c561cf96dd464485fbcefda48fe574c5"}, +] [package.dependencies] -airbyte-protocol-models = ">=0.9.0, <1.0" +airbyte-protocol-models = "0.5.1" backoff = "*" cachetools = "*" -cryptography = "^42.0.5" -Deprecated = "~1.2" -dpath = "~2.0.1" +Deprecated = ">=1.2,<1.3" +dpath = ">=2.0.1,<2.1.0" genson = "1.2.2" -isodate = "~0.6.1" -Jinja2 = "~3.1.2" -jsonref = "~0.2" -jsonschema = "~3.2.0" +isodate = ">=0.6.1,<0.7.0" +Jinja2 = ">=3.1.2,<3.2.0" +jsonref = ">=0.2,<0.3" +jsonschema = ">=3.2.0,<3.3.0" pendulum = "<3.0.0" -pydantic = "^1.10.8" -pyjwt = "^2.8.0" -pyrate-limiter = "~3.1.0" +pydantic = ">=1.10.8,<2.0.0" +pyrate-limiter = ">=3.1.0,<3.2.0" python-dateutil = "*" -pytz = "2024.1" -PyYAML = "^6.0.1" +PyYAML = ">=6.0.1,<7.0.0" requests = "*" requests_cache = "*" wcmatch = "8.4" @@ -35,21 +34,17 @@ wcmatch = "8.4" [package.extras] file-based = ["avro (>=1.11.2,<1.12.0)", "fastavro (>=1.8.0,<1.9.0)", "markdown", "pdf2image (==1.16.3)", "pdfminer.six (==20221105)", "pyarrow (>=15.0.0,<15.1.0)", "pytesseract (==0.3.10)", "unstructured.pytesseract (>=0.3.12)", "unstructured[docx,pptx] (==0.10.27)"] sphinx-docs = ["Sphinx (>=4.2,<4.3)", "sphinx-rtd-theme (>=1.0,<1.1)"] -vector-db-based = ["cohere (==4.21)", "langchain (==0.1.16)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] - -[package.source] -type = "directory" -url = "../../../airbyte-cdk/python" +vector-db-based = ["cohere (==4.21)", "langchain (==0.0.271)", "openai[embeddings] (==0.27.9)", "tiktoken (==0.4.0)"] [[package]] name = "airbyte-protocol-models" -version = "0.11.0" +version = "0.5.1" description = "Declares the Airbyte Protocol." optional = false python-versions = ">=3.8" files = [ - {file = "airbyte_protocol_models-0.11.0-py3-none-any.whl", hash = "sha256:2157757c1af8c13e471ab6a0304fd2f9a2a6af8cc9173937be1348a9553f7c32"}, - {file = "airbyte_protocol_models-0.11.0.tar.gz", hash = "sha256:1c7e46251b0d5a292b4aa382df24f415ac2a2a2b4719361b3c0f76368a043c23"}, + {file = "airbyte_protocol_models-0.5.1-py3-none-any.whl", hash = "sha256:dfe84e130e51ce2ae81a06d5aa36f6c5ce3152b9e36e6f0195fad6c3dab0927e"}, + {file = "airbyte_protocol_models-0.5.1.tar.gz", hash = "sha256:7c8b16c7c1c7956b1996052e40585a3a93b1e44cb509c4e97c1ee4fe507ea086"}, ] [package.dependencies] @@ -153,70 +148,6 @@ files = [ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] -[[package]] -name = "cffi" -version = "1.16.0" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, -] - -[package.dependencies] -pycparser = "*" - [[package]] name = "charset-normalizer" version = "3.3.2" @@ -327,60 +258,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "cryptography" -version = "42.0.7" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, - {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, - {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, - {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, - {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, - {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, - {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - [[package]] name = "deprecated" version = "1.2.14" @@ -425,13 +302,13 @@ test = ["pytest (>=6)"] [[package]] name = "freezegun" -version = "1.5.1" +version = "1.5.0" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, + {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, ] [package.dependencies] @@ -648,13 +525,13 @@ pytzdata = ">=2020.1" [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.2.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] @@ -688,17 +565,6 @@ files = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - [[package]] name = "pydantic" version = "1.10.15" @@ -751,23 +617,6 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] -[[package]] -name = "pyjwt" -version = "2.8.0" -description = "JSON Web Token implementation in Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, - {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, -] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - [[package]] name = "pyrate-limiter" version = "3.1.1" @@ -879,17 +728,6 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "pytz" -version = "2024.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, -] - [[package]] name = "pytzdata" version = "2020.1" @@ -926,7 +764,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -963,13 +800,13 @@ files = [ [[package]] name = "requests" -version = "2.32.2" +version = "2.31.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] @@ -1031,18 +868,19 @@ fixture = ["fixtures"] [[package]] name = "setuptools" -version = "70.0.0" +version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1068,13 +906,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] @@ -1204,4 +1042,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9,<3.12" -content-hash = "63668b24d8ce7b9285e468803d53474133e6b0b64b02724ccaef0f1f9213fe3c" +content-hash = "2aff7d489ba30abc8f6f649eb51145ab20ab4b4bd2e03657640a451ee40d2a99" diff --git a/airbyte-integrations/connectors/source-harvest/pyproject.toml b/airbyte-integrations/connectors/source-harvest/pyproject.toml index 78c418faf9cd..024b3a22f208 100644 --- a/airbyte-integrations/connectors/source-harvest/pyproject.toml +++ b/airbyte-integrations/connectors/source-harvest/pyproject.toml @@ -17,7 +17,7 @@ include = "source_harvest" [tool.poetry.dependencies] python = "^3.9,<3.12" -airbyte-cdk = { path = "../../../airbyte-cdk/python", develop = true } +airbyte-cdk = "0.80.0" [tool.poetry.scripts] source-harvest = "source_harvest.run:run" From d231effff4edd015dd8cfe46b33e36cd2b3e3bfa Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 12:37:35 -0700 Subject: [PATCH 23/29] Updates poetry.lock --- airbyte-cdk/python/poetry.lock | 468 ++++++++++++++++----------------- 1 file changed, 232 insertions(+), 236 deletions(-) diff --git a/airbyte-cdk/python/poetry.lock b/airbyte-cdk/python/poetry.lock index b637314184c6..aa4e0b68ea34 100644 --- a/airbyte-cdk/python/poetry.lock +++ b/airbyte-cdk/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -637,63 +637,63 @@ test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] [[package]] name = "coverage" -version = "7.5.1" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, - {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, - {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, - {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, - {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.dependencies] @@ -1005,53 +1005,53 @@ pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "fonttools" -version = "4.51.0" +version = "4.52.4" description = "Tools to manipulate font files" optional = true python-versions = ">=3.8" files = [ - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, - {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, - {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, - {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, - {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, - {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, - {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, - {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, - {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, - {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, - {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, - {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, - {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, + {file = "fonttools-4.52.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb8cd6559f0ae3a8f5e146f80ab2a90ad0325a759be8d48ee82758a0b89fa0aa"}, + {file = "fonttools-4.52.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ecb88318ff249bd2a715e7aec36774ce7ae3441128007ef72a39a60601f4a8f"}, + {file = "fonttools-4.52.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a22cf1adaae7b2ba2ed7d8651a4193a4f348744925b4b740e6b38a94599c5b"}, + {file = "fonttools-4.52.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8873d6edd1dae5c088dd3d61c9fd4dd80c827c486fa224d368233e7f33dc98af"}, + {file = "fonttools-4.52.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:73ba38b98c012957940a04d9eb5439b42565ac892bba8cfc32e10d88e73921fe"}, + {file = "fonttools-4.52.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9725687db3c1cef13c0f40b380c3c15bea0113f4d0231b204d58edd5f2a53d90"}, + {file = "fonttools-4.52.4-cp310-cp310-win32.whl", hash = "sha256:9180775c9535389a665cae7c5282f8e07754beabf59b66aeba7f6bfeb32a3652"}, + {file = "fonttools-4.52.4-cp310-cp310-win_amd64.whl", hash = "sha256:46cc5d06ee05fd239c45d7935aaffd060ee773a88b97e901df50478247472643"}, + {file = "fonttools-4.52.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d272c7e173c3085308345ccc7fb2ad6ce7f415d777791dd6ce4e8140e354d09c"}, + {file = "fonttools-4.52.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21921e5855c399d10ddfc373538b425cabcf8b3258720b51450909e108896450"}, + {file = "fonttools-4.52.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f6001814ec5e0c961cabe89642f7e8d7e07892b565057aa526569b9ebb711c"}, + {file = "fonttools-4.52.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b0b9eb0f55dce9c7278ad4175f1cbaed23b799dce5ecc20e3213da241584140"}, + {file = "fonttools-4.52.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:70d87f2099006304d33438bdaa5101953b7e22e23a93b1c7b7ed0f32ff44b423"}, + {file = "fonttools-4.52.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e176249292eccd89f81d39f514f2b5e8c75dfc9cef8653bdc3021d06697e9eff"}, + {file = "fonttools-4.52.4-cp311-cp311-win32.whl", hash = "sha256:bb7d206fa5ba6e082ba5d5e1b7107731029fc3a55c71c48de65121710d817986"}, + {file = "fonttools-4.52.4-cp311-cp311-win_amd64.whl", hash = "sha256:346d08ff92e577b2dc5a0c228487667d23fe2da35a8b9a8bba22c2b6ba8be21c"}, + {file = "fonttools-4.52.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d2cc7906bc0afdd2689aaf88b910307333b1f936262d1d98f25dbf8a5eb2e829"}, + {file = "fonttools-4.52.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00d9abf4b400f98fb895566eb298f60432b4b29048e3dc02807427b09a06604e"}, + {file = "fonttools-4.52.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b419207e53db1599b3d385afd4bca6692c219d53732890d0814a2593104d0e2"}, + {file = "fonttools-4.52.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf694159528022daa71b1777cb6ec9e0ebbdd29859f3e9c845826cafaef4ca29"}, + {file = "fonttools-4.52.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a5d1b0475050056d2e3bc378014f2ea2230e8ae434eeac8dfb182aa8efaf642"}, + {file = "fonttools-4.52.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c3ad89204c2d7f419436f1d6fde681b070c5e20b888beb57ccf92f640628cc9"}, + {file = "fonttools-4.52.4-cp312-cp312-win32.whl", hash = "sha256:1dc626de4b204d025d029e646bae8fdbf5acd9217158283a567f4b523fda3bae"}, + {file = "fonttools-4.52.4-cp312-cp312-win_amd64.whl", hash = "sha256:309b617942041073ffa96090d320b99d75648ed16e0c67fb1aa7788e06c834de"}, + {file = "fonttools-4.52.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8b186cd6b8844f6cf04a7e0a174bc3649d3deddbfc10dc59846a4381f796d348"}, + {file = "fonttools-4.52.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ed23a03b7d9f0e29ca0679eafe5152aeccb0580312a3fc36f0662e178b4791b"}, + {file = "fonttools-4.52.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b53386214197bd5b3e3c753895bad691de84726ced3c222a59cde1dd12d57b"}, + {file = "fonttools-4.52.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7467161f1eed557dbcec152d5ee95540200b1935709fa73307da16bc0b7ca361"}, + {file = "fonttools-4.52.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b4cba644e2515d685d4ee3ca2fbb5d53930a0e9ec2cf332ed704dc341b145878"}, + {file = "fonttools-4.52.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:890e7a657574610330e42dd1e38d3b9e0a8cb0eff3da080f80995460a256d3dd"}, + {file = "fonttools-4.52.4-cp38-cp38-win32.whl", hash = "sha256:7dccf4666f716e5e0753f0fa28dad2f4431154c87747bc781c838b8a5dca990e"}, + {file = "fonttools-4.52.4-cp38-cp38-win_amd64.whl", hash = "sha256:a791f002d1b717268235cfae7e4957b7fd132e92e2c5400e521bf191f1b3a9a5"}, + {file = "fonttools-4.52.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:05e4291db6af66f466a203d9922e4c1d3e18ef16868f76f10b00e2c3b9814df2"}, + {file = "fonttools-4.52.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a64e72d2c144630e017ac9c1c416ddf8ac43bef9a083bf81fe08c0695f0baa95"}, + {file = "fonttools-4.52.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebb183ed8b789cece0bd6363121913fb6da4034af89a2fa5408e42a1592889a8"}, + {file = "fonttools-4.52.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4daf2751a98c69d9620717826ed6c5743b662ef0ae7bb33dc6c205425e48eba"}, + {file = "fonttools-4.52.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:15efb2ba4b8c2d012ee0bb7a850c2e4780c530cc83ec8e843b2a97f8b3a5fd4b"}, + {file = "fonttools-4.52.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:35af630404223273f1d7acd4761f399131c62820366f53eac029337069f5826a"}, + {file = "fonttools-4.52.4-cp39-cp39-win32.whl", hash = "sha256:d0184aa88865339d96f7f452e8c5b621186ef7638744d78bf9b775d67e206819"}, + {file = "fonttools-4.52.4-cp39-cp39-win_amd64.whl", hash = "sha256:e03dae26084bb3632b4a77b1cd0419159d2226911aff6dc4c7e3058df68648c6"}, + {file = "fonttools-4.52.4-py3-none-any.whl", hash = "sha256:95e8a5975d08d0b624a14eec0f987e204ad81b480e24c5436af99170054434b8"}, + {file = "fonttools-4.52.4.tar.gz", hash = "sha256:859399b7adc8ac067be8e5c80ef4bb2faddff97e9b40896a9de75606a43d0469"}, ] [package.extras] @@ -1682,13 +1682,13 @@ six = "*" [[package]] name = "langsmith" -version = "0.1.60" +version = "0.1.65" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = true python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.60-py3-none-any.whl", hash = "sha256:3c3520ea473de0a984237b3e9d638fdf23ef3acc5aec89a42e693225e72d6120"}, - {file = "langsmith-0.1.60.tar.gz", hash = "sha256:6a145b5454437f9e0f81525f23c4dcdbb8c07b1c91553b8f697456c418d6a599"}, + {file = "langsmith-0.1.65-py3-none-any.whl", hash = "sha256:ab4487029240e69cca30da1065f1e9138e5a7ca2bbe8c697f0bd7d5839f71cf7"}, + {file = "langsmith-0.1.65.tar.gz", hash = "sha256:d3c2eb2391478bd79989f02652cf66e29a7959d677614b6993a47cef43f7f43b"}, ] [package.dependencies] @@ -1777,13 +1777,9 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -2325,13 +2321,13 @@ requests = ["requests"] [[package]] name = "openpyxl" -version = "3.1.2" +version = "3.1.3" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" optional = true python-versions = ">=3.6" files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, + {file = "openpyxl-3.1.3-py2.py3-none-any.whl", hash = "sha256:25071b558db709de9e8782c3d3e058af3b23ffb2fc6f40c8f0c45a154eced2c3"}, + {file = "openpyxl-3.1.3.tar.gz", hash = "sha256:8dd482e5350125b2388070bb2477927be2e8ebc27df61178709bc8c8751da2f9"}, ] [package.dependencies] @@ -3267,104 +3263,104 @@ files = [ [[package]] name = "rapidfuzz" -version = "3.9.1" +version = "3.9.2" description = "rapid fuzzy string matching" optional = true python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f897a9bff517d5c6af6a90131796b4298b547b9a9a4df3cf285006be33aae5b"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83c570ce23b447625929c0e7c4f2eab6d90f5a576db2b26a5aa0594a53d560ea"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c6b6455ee8404a663e15477a8bfe98b1afb329ff224bcf6d15f623a3761b95"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa44aef769e5834fef4fde091fd646cc1c52a2813b3aa241ae54b3028960abaa"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25ea055ae40fb60f503f02b44b3ac35a39a9108be33f89e05b81bc4e3c849ec8"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb592bad9d58b47c6681f0c180767d2c98775a35f7267131d33723139c3d6c2e"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb023adfefa62410fff877f7cc70cd4758cbfbad963e87d146cf71b022dce197"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c883d2d2e31c759af1f3fdeb67ec151cf94e307f745b3d02ab3a2ef6595485f2"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8e11405d81e8baea4999a5757a982009566cff8f6a121d5ccf042aab81ae0230"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:02ed579f35ddd3552c7f74bc0c10800b432d9b09a4cebb19fd7a10b3b4759cc0"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9f8615a2a67a1f80b3aa7a3d7fbe6a2ed062a54c98988e3f9b664b49a3bc115e"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:64058f4a3698c6c8464df47a3b7da303db2477b2447142da3e67fc091f4c366a"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-win32.whl", hash = "sha256:2ef42c43c94139c890aeec40bc442c4bf8d48e15b456a88ce0f4cc5cfcad1896"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c99d001c45c31c2cd2f7361bc2036d3062b21db5f43beea8bc5109d43fe9f283"}, - {file = "rapidfuzz-3.9.1-cp310-cp310-win_arm64.whl", hash = "sha256:da3f495cf4f7a443b34a6d3c6805265595fcd13641b3253a8e2034289d828dd9"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8869dcf072227a40a6f9e87b3fc4eb020055a08ad12b63d751c354e3a973ccb"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f66f9d5f14141b4b017e76118ec4bda29266f6b281989026e3a9ba1a2aaf032"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07decc6b058f935d2219423a50aac426027928cc734809f793bc250de4a3756e"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c125095d1828fa10ac79077594dd2d8829167d9e184e20baa97620fc52ebdcc9"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a55bcc3abc9f8e38a1218cb5a09719126cfc4cba23ebd8caa27dfdc69cedd8"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50c2f7ad132dfeb6247c90b41431662af939a820f761cf930708d55912377ed8"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177bddf50577db59bcb00b6f7a5c2b70f2ec5a2aba40c8add7a6f7fd8609224e"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dec2792f864be731c8339cad99001caa6540aa909e6fd8bc688bb0419c501f44"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6437cba4b9460d5ee0bafd796e13ef9307091b81685bbe745b0f1619fb887ca"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:874317057a58a9c6ddf59fe1491e478217daa9fdb043a00358a15de4f62f9a2d"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5d8eb7fe39e81dc1530a3ec81a35e69770839c76607c461eb9d0902427fab3e1"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:08f85d6674d804a493c3e9ec10a807f9bd8f482781487eda064913b537f99d7f"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-win32.whl", hash = "sha256:eadf8c4b24b63aef8810ed585c24ac1fc022ee771211772a6e9f78c63aa949ff"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:0e931539edeb9158ef83537cd571051f8a9608737642c20b088a37bd5d76c5c9"}, - {file = "rapidfuzz-3.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:bcc0ffcaeb1e499e708f32ec30177ed690b3f25455c91ad8c2240986c69f9ebe"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8781e45c56f7f3a64940f4d594a4ffd69360147925a706569b2b0c57347b2225"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0edc950c6a04c19db10670cd04e33403b3eb0f175deb620f9668595d378b1005"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ceb0d7bdec910d93793d32633ba0cb644356cf6778f9d91b727da0075beaec1"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a231e8f3bae82f10e7188965b37c91d8bfb80136595c860c8a08eb0dd07764d"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bfa8c0a8ce09b4bcd36322f8f375750dca160fbdbeb2e763a695cef3ae9133e"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e872627d5359c840f3e431b0beb263518048917c3e076f624870552d84e7dc6"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f006c3af82c478df09a790fb4846b5acd00a187d75715674d71f5dc0ac982ce"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:32718fa69306df969bf4fca1719f8900b83df315a2a8153942d5b8906f4fd1d6"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a89b219c1a7933a0673b2dbb1ffe701057d82e5cb843552be4f55b61b557031e"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b26cc9459e096959fab3a4a8a17b96a6c7c961f9db5c37c1c3c7a06789316cf7"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2f9044a4470343087cde10beaa36266519d5da110a9a4597b43e6aa35fa928d3"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a3a48fc6dc274b803a366a4baec99e212792ae1b1e73d42235b2042cd3ade7c1"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-win32.whl", hash = "sha256:b71e7f99ed048a338e4a1ac34f56b3b3933a3ba2dfbb04450c786a8ddd97f4db"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:e58489934d0147f1edda693cb983bea40f2b45ae6756fd47c1005b538f817a2f"}, - {file = "rapidfuzz-3.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:829fbad93266fffa0f9d722a94cbb1b95b53e3c04be4e872193496a0cfbd66f0"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bca2b93c75f87cd85832cdd5bb06b4b5642e2a05c8e3550841ddf5d564ce4abb"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d3397630f22e6c60dda8be3e9dbcf6a341695d487df8a6c92f4a2f7ebcdaecf7"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7bba8d4a8fb8e7559a9e83dfc5385dc6fe89efd73e32d253667242faf1883c"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e70605350cb6ec5091e06de62d3dcb058f694b059b4e1a9d85bfbf892f70030"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:678fcaa5117ddb6263160a7c5f33cc9ea3df335465f5d53715707fad103e1d09"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08dcd347d408912b6da778a73a0d7a2adad7fe238a44263e5e3789f2a8d84669"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7149afaf0294882b6b15bb6fa9fc38ff1d761e50117460ee3561181c1c4e2230"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0c568b89a5016e76f0b3f85e9379036da99c5e7ec26b33935453d353a1938b74"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:a079164675d24eb715230bf9dd252683ae3c9c0c0a236f0b8098630268b899e9"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f6f4e8235d0acf1972f5eb4091c4a0473e5670a754f166c0c718ce21e945f879"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ea62c82eb2c65bd49651e95f4f46874483ae4da1c3b57997e58f1b4fb2de6c05"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3ab6ad7e70469aed24e24378b19a9e47fc757c847399b22c612a0fccacc795cb"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-win32.whl", hash = "sha256:bf5184b17e26a82b00c7ee05d9ec5d826113df55830bbc447bf6d6e7469c70fb"}, - {file = "rapidfuzz-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:493354f50b9855271ac846b213e394e08446e70cef5cc033e5302a2220f3ae7b"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c8b0e6640421e55d69e186ce7fb9e6c723cfd3b6f91beaeb28705c2a46c8a194"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc68fb8f2a8b5b3a4526b7a65e7d5c7f821882f56d9dcbcce4c6859a9e5bdcd7"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77767b119ac05662d216a8cc4092ac28dbc015d9caabebdbefe371b0dd82a38e"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dbcc4add07bd60ea73b94392fed28f83dba0fe796097da47627fd539bd6daca"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0899de4fc1a7a36f14be556a0dacf40ea5c0fe22c6b45b2ea2674e1ac47e269"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9f075366cee63a6b06bd7f9285eb9f1785382a6493afcb7054202e20508bf94"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:349e9c15092d20a1f6ff1795e068f39a9ee5e84c54b3addbc66d0ac469c4ef43"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a645f362dafc103dbe7f43a2ad34f76284773cd7d1b00514d1c591848a1c817f"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:03a0a4bc8d4bd3e6f882b4c2ac183825a9b6dabe7e5a97bb6a1075e4635c944d"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:fbce66cb2e331b0888c79b594eab76e2c609c2637050085daadff5325d471dc2"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e5d7b3154f6df9e05c2016de5e95f8cba4fe636a4e5520ebcd89bc6c54b8e4ed"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aaba665f92c011c6f284e933ab02b5dc129a6d3f48fce913ec4a214bd530135e"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-win32.whl", hash = "sha256:61b343c581f4926260248069d8fdbbbf293c19c12ef440ad5ced15bcff277a84"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:ce3335324198e1388a1c4e50d40f45107367010afe9fa09fd46278160f0ab591"}, - {file = "rapidfuzz-3.9.1-cp39-cp39-win_arm64.whl", hash = "sha256:998977df2ae01ff8b7bc3b29a860b4a863005e0533e323df3fd555a31ef33f0e"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dc4b5de5d6f9347d836d849b56bca630169353cbe5c10fa7fe93bb1677b49770"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f74d93148081049ccc86f276d54cd7c8c0692250245660b4fcd904ed1db1e01"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f9dfdcd75e16e5874efee233b28aec1322623b0f1f20641452d06ea2d8ba5ef"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97ab8f153984a5d827ebb5a5b80ee59563efcf2fa3e569dcd46ea7e7c9845e93"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8a6d5a8edc452920efdf1b499a2a47bb8a28440f7ab3fe28bb7d6636ccf71c3"}, - {file = "rapidfuzz-3.9.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:06879b598e798a4d33a283c2b4fa0d555d7706b6531e3321b161d62e986f7f57"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0f906ab6220778404498e0ce255c4cc89f98ea5e656e54cc59c5813c877eb86b"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d3da444890c9559fd15717d97f8373b1cd14007f68c9b037aa93ef7ca969b559"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4503841cd3cbe22b5ac44f15bc834ec97d811a3c3943f73f5643266c8674e1"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5713c56b30ed75ada3a8f008cf8e8e6323386ce48fac2bf2d07285fe6c91f5a4"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb3f1af099cd1d98001691fbdadd422f088f21eadcacf5698b393b7569e24dc4"}, - {file = "rapidfuzz-3.9.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:53477e1e6d85d603c9a319cfd00ab9f0a57b6d68bcdb268d6b15a79e64d693d0"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a47550eabf235e5d50e7d448c18f77f6e8082aa3571e9df511c8388525ea9372"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c6b11a38b61cc2462a113b123f5e932cda0e525f816d6fe4b68516f97d7f9d49"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:414644a2fc8a3e5fafda95b430214ed892faa4d0a07401d33892bc9ca5c84974"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1539e7439b68013c5d2ab7ed9d3d221480a15595207764145ae177077d28016d"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e18f0e9351f7e5d5387774ff4d5cabd824341e16b866eb1c8d3f557111b447ef"}, - {file = "rapidfuzz-3.9.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d2ff268a8bf57a76512804d5ca2097afaf98e64e8947d514cde7e2e8446aa5f7"}, - {file = "rapidfuzz-3.9.1.tar.gz", hash = "sha256:a42eb645241f39a59c45a7fc15e3faf61886bff3a4a22263fd0f7cfb90e91b7f"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45e0c3e279e70589381f47ad410de7211bac943e827eb09eb8339d2124abca90"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:280ef2f3066df9c486ffd3874d2489978fb8021044c47c006eb96be8d47917d7"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe128ac0e05ca3a71d8ff18e70884a64fde00b6fbd2b4d9f59f7a4d798257c55"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fbc0f6e1b6f4063b937d0edcf0a56cbc1d7179ade9b7d6c849c94e44a7b20f6"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:df19455c2fb85e86a721111b84ac8dd3685194f0edc9faefb226731ad3e134a7"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:801a5d97c465a3467b3cdf50cdcdadec129ddca582b24430f5d24c715c80be9b"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f218524596d261a6cb33cda965687e62dd30def478d39f0befa243642c3985"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5c61d53f293b4e3286919b0e081513367afabcb5aef0b6f899d006117778e558"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0ed70fc6627ae37319f822e5d8d21d561044e0b3331b6f0e6904476faa8d8ed7"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:96fa229d06ee005d2f46374fb2af65590a590a6fa2fd56e66474829f5fa9adfe"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6609e881b57cabb40d515cc226bbf570e32e768bd2cc688ba026a45ffbc60875"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:204fd4d293ef4d409c4142ddf830b7613924b998670f67e512ab1f880a60218a"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-win32.whl", hash = "sha256:5b331a09446bc8f8971cf488c9e6c0f7dbf2739828588e063cf08fd400638a24"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:01a9975984953fe549649e6a4c3f0d9c60707acf458184ec09678d6a57560112"}, + {file = "rapidfuzz-3.9.2-cp310-cp310-win_arm64.whl", hash = "sha256:ca4af5d7fc9c17bdc498aa1cab9ecf5140c8535c9cedeba1990bbe4b8be75098"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:300ab53981a5d6831fe7e0f30c407c79520ad0f0ab51b2cece8717689026f495"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f4828642acdb075154ce2ff3260f8afb6a17b5b0c8a437efbadac06e9995dd7b"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b262883c3ce93dee1a9a974992961c8098e96b8142e2e01cabdb15ea8105c4a"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf8582d85e35641734d6c1f43eb37c1f2a5eda338d3cfa8e651e078246b9ec58"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e33b61ef87e1876d216c479fa2256233b3bb0424465ab2db1d94ab7b8649ae1c"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fa1b3eb21756003a6a3977847dd4e0e9a26e2e02731d9daa5e92a9258e7f0db"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923ae0301a56356364f1159e3005fbeb2191e7a0e8705d5cc1b481d9eea27b97"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8e4041cfd87f0a022aa8a9a187d3b0824e35be2bd9b3bceada11578ddd9ad65"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1f832b430f976727bdbba009ee64acda25412602976fbfb2113d41e765d81849"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6ce5e57e0c6acf5a98ffbdfaf8bccb6e41fbddb9eda3e041f4cc69b7cade5fa0"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d65f34e71102d9cbe733d4ba1c645e7623eef850562501bab1ac79d217831436"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5dd9ba4df0db46b9f909289e4687cc7721c622985c4cd169969005dd30fc1e24"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-win32.whl", hash = "sha256:34c8bca3fef33d7e71f290de68be2184fac7a9e136fa0ed22b17ec597e181406"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e1a8872c0b8aef95c33db86d25e8bdea6f557b9cdf683123c25035b2bcfb8e"}, + {file = "rapidfuzz-3.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:ed02d73e46b7a4604d2bc1e0364b25f204862d40dd162f6b36ee22b9bf6d9df2"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ae6c4ba2778b097397968130f2b0cb795cdc415c115539a49ce798f606152ad5"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7270556ddebaa98fb777f493f17ed6a733b3527de16c43342bce1db109042845"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4625273447bdd94f2ab06b2951cd8b74356c3a48552208279a3ec2947ceee141"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5107b5ec8821453f7cac70b2d0bc4866699b25bff4819ada8b28bf2b11e87f65"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b04c851d309df8261ed42951444db657936234ceddf4032f4409b0214c95ecbe"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeefff80f3f5d6841c30ffe0cdc84d62874de5a64cff509ae26fbd7478297af8"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cdc106b5a99edd46443449c767287dbb5d4464a7536475a365e368e7ee4d651"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ce253a2b7a71a01a4abac71ac31fd05f6ac1f1cd2af2d98fa80fe5c402175e54"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5c30407cadbfe99753b7a996f0dd6da490b1e27d318c01db227e8f49770a01ec"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fb3fc387783f70387a91aababd8a5faeb230931b655ad99bcf838cd72404ba66"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c409852a89535ec8720301a847bab198c1c14d0f34ed07dfabbb90b1dbfc506d"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8603050e547249c1cc8a8dc6a49917076572ea69b04bc51eb1748c403cfc9f46"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-win32.whl", hash = "sha256:77bdb96e82d8831f0dd6db83e2ff0d4a731cff53e926d029c65a1dc3ae0f160a"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:09f354fa28e0fd170c6e4eea5e97eea0dba43761067df93109f49a5414ca8584"}, + {file = "rapidfuzz-3.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:168299c9a2b4f20f10c1bb96d8da0bb05bf1f3b9957be3a0bae5db65ce9f095f"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d87621d60078f87cb52082b1cbf9849afeaa1cb6d0a2b072fce25fe21c8675b4"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c447d0e534418ef3eaabcd890d85c7e9f289c1c6ef6e060a0b1f239799781747"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7161b205f25eff5f88ab809fb05a2a102634e06f452c0deb9535c9f41cd7b0a"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f13a6bbadba8fdd42676c1213ebc692bba9fac00f7db0ae92acc06bb734294c4"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54534743820a15bd0dc30a0a0010825be337973236550fd63587700a7950bbca"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea61851a4c2f93148aa2779458fb3f70a62342d77c9ec3d9d08445c8485b738"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e941f81a60351a842976fea208e6a6701a5899eb8a80b907e57d7c3099337900"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1bbfaf439e48efe3a48cada946cf7678b09c818ce9668e09dac40d05b772f6f8"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:574f464da18d660712e9776072572d462cf6a26144c833d18d9c93778286e023"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8a56c494246d29aacf5ac93ca3cf338d79588a1a5c05d8f496c3f4d7127e9031"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2943b0f17195c000948a7668bb11979ea0e50079a3d3db9d139e51b68c3a7c26"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:27214f93555d4f9b7b1baf107a6ba13e9daee21f1ec6e36418556d04a7ee4d9b"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-win32.whl", hash = "sha256:876c6628fec6241262c27f8fda3c73bab88e205e9b9394c8868361e2eda59048"}, + {file = "rapidfuzz-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf1952b486589ffcfbde2015ca9be15e0f4b0e63d1e2c25f3daec0263fda4e69"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ca9a135060ee4d887d6af86493c3e0eb1c99ca205bca943fe5994dc93e648d5"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:723518c9a18e8bda996d77aa9307b6f8b0e77905702b2772b020adf24191073a"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65eb9aeae73ac60e53a9d6c509daaa217ea256a5e184eb8920c9b15295c48677"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef2964f4eb9a37487c96e5e32167a3c4fa51bf8e899853d0ac67e0465a27702c"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c64a252c96f29667c206726903bb9705c5195f01850360c9b9268de92ac878dc"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b32b03398517b5e33c7f36d625a00fcb1c955b9fe3c939325688175fb21730"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec5f7b1bac77439b624f5acbd8bfe61e7b833678701068b43f7a489c151427c0"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5fd1b49fba8b4b9172eed5b131c1e9864d4d76bebea34359274f16a3591e5f44"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c05b033fc3ff043f48e744f67038af7fd34003047c7810f24bec7c01ce7da05b"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c3bea20db89b510d78d017b349b9d87159c32418693ddf091d9035dbe20b4dc0"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:77226a77590f83ee073f4f8cc86a1232da88e24d19d349361faa169fb17ba1cd"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:83ed8bc2c942dc61ab739bbca1ead791143b4639dc92156d3060bd0b6f4541ea"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-win32.whl", hash = "sha256:2db70f64974c10b76ae37d5cff6124dce791def815d4fdf5ac16fe60be88d905"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:bdead23114206dea4a22ed3aad6565b99a9e4b3fff9837c423afc556d2814b1a"}, + {file = "rapidfuzz-3.9.2-cp39-cp39-win_arm64.whl", hash = "sha256:0ec69ad076cfc7c88323d671613e40bb8754ba95a203556d9a7759e60f0544e8"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:018360654881e75131b227aa96cdaba543c438da881c70a12ca0c86e2c4083b2"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eaa8178ec9238f32f15b6e49f70b852accda0a848448c4e30bce77c6624ebaba"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32dd79b0f90ce609df96d0d48ef4327cf1f0415b9274588a466d3610a775d2f9"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04a1c38a72a50f3e6d346a33d53fa51ba390552b3592fca64a07e54d749b439b"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ca96eec40e815f0cf10b00008f295fd26ca43792a844cf62588a8ea614e160"}, + {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c01c515a928f295f49d588b6523f44b474f047f9f2de0079bc57bcd00b870778"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:07e14ef260b6f4ee03dff07a0ac95a16aff1ddbc7e6171e07e49d2d61526f3be"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:64f3480bddc12b89969930f12a50a1aeb53e09aad41cf8b27694d83ca1cc7864"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3c9e33ec21755bda1878095537cb84848e9cf6510d4837d22144ba04e33df29"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a70045e84225697ddf67d656aa25b70d6802e2ff339d51f9545fca5b9b13fb8c"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9ec1fd328518c33adb9171afe8735137cb7b492e4a81cddc23568f9980c235c"}, + {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1fd8458fdac232766d55593c1228c70968f382fdc376c25685273f99b5d1d921"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a373748fddb5403b562b6d682082de360bb08395f44e3cb7e74819461e39a16c"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:45f80856db3e22cb5f96ad1572aa1d004714514625ed4668144661d8a7c7e61f"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:663e52cf878e0ccbbad0744eb3e2bb83a784645b146f15611bac225bc218f19b"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbe4d3034a8cfe59a2b477375ad7d739b3e5935f10af08abdf64aae55780cad"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd38abfda97e42b30093f207108dcba944beab1edf6624ba757cf57354063177"}, + {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:16b41fe360387283a3184ce72d4d26d1928e7ce809268a88e8491a776dd770af"}, + {file = "rapidfuzz-3.9.2.tar.gz", hash = "sha256:c899d78709f8d4bd0059784fa27a9f6c53d04fc4aeaa21de7c0c8e34a7154e88"}, ] [package.extras] @@ -3460,13 +3456,13 @@ files = [ [[package]] name = "requests" -version = "2.32.2" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -3650,36 +3646,36 @@ tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc ( [[package]] name = "scipy" -version = "1.13.0" +version = "1.13.1" description = "Fundamental algorithms for scientific computing in Python" optional = true python-versions = ">=3.9" files = [ - {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, - {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, - {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, - {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, - {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, - {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, - {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, - {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, - {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, - {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, - {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, ] [package.dependencies] @@ -4162,13 +4158,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -4549,13 +4545,13 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.18.2" +version = "3.19.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = true python-versions = ">=3.8" files = [ - {file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"}, - {file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"}, + {file = "zipp-3.19.0-py3-none-any.whl", hash = "sha256:96dc6ad62f1441bcaccef23b274ec471518daf4fbbc580341204936a5a3dddec"}, + {file = "zipp-3.19.0.tar.gz", hash = "sha256:952df858fb3164426c976d9338d3961e8e8b3758e2e059e0f754b8c4262625ee"}, ] [package.extras] From 546c1d211020439afb140c356fe6f93db17f3c36 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 13:35:30 -0700 Subject: [PATCH 24/29] Update wait_until_header tests for MagicMock spec --- .../backoff_strategies/test_wait_until_time_from_header.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py index efc090abd931..5f2bc02f95cd 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/backoff_strategies/test_wait_until_time_from_header.py @@ -6,6 +6,7 @@ from unittest.mock import MagicMock, patch import pytest +import requests from airbyte_cdk.sources.declarative.requesters.error_handlers.backoff_strategies.wait_until_time_from_header_backoff_strategy import ( WaitUntilTimeFromHeaderBackoffStrategy, ) @@ -51,14 +52,14 @@ ) @patch("time.time", return_value=1600000000.0) def test_wait_untiltime_from_header(time_mock, test_name, header, wait_until, min_wait, regex, expected_backoff_time): - response_mock = MagicMock() + response_mock = MagicMock(spec=requests.Response) response_mock.headers = {"wait_until": wait_until} - backoff_stratery = WaitUntilTimeFromHeaderBackoffStrategy( + backoff_strategy = WaitUntilTimeFromHeaderBackoffStrategy( header=header, min_wait=min_wait, regex=regex, parameters={"wait_until": "wait_until", "regex": REGEX, "min_wait": SOME_BACKOFF_TIME}, config={"wait_until": "wait_until", "regex": REGEX, "min_wait": SOME_BACKOFF_TIME}, ) - backoff = backoff_stratery.backoff_time(response_mock, 1) + backoff = backoff_strategy.backoff_time(response_mock, 1) assert backoff == expected_backoff_time From 9740aa6c856c9ab1ea50ed0beef71d4b69f4a11f Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 13:57:00 -0700 Subject: [PATCH 25/29] Update test for http response filter for correct behavior --- .../declarative/models/declarative_component_schema.py | 2 +- .../requesters/error_handlers/http_response_filter.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py index 08ac1be0b46e..03c2f8b4876f 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/models/declarative_component_schema.py @@ -1086,7 +1086,7 @@ class Config: type: Literal['DeclarativeSource'] check: CheckStream streams: List[DeclarativeStream] - version: str = Field(..., description='The version of the CDK used to build and test the source.') + version: str = Field(..., description='The version of the Airbyte CDK used to build and test the source.') schemas: Optional[Schemas] = None definitions: Optional[Dict[str, Any]] = None spec: Optional[Spec] = None diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index bd9926890a8f..909f55aa5e6e 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -65,10 +65,10 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep if filter_action is not None: default_error_message = default_mapped_error_resolution.error_message if default_mapped_error_resolution is not None else "" + error_message = "" if isinstance(response_or_exception, requests.Response): error_message = self._create_error_message(response_or_exception) - else: - error_message = default_error_message + error_message = error_message if len(error_message) else default_error_message failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error return ErrorResolution( From 146738279d7efe5e61b7bbd13d2bd4711f8ef2fe Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 14:06:13 -0700 Subject: [PATCH 26/29] Update variable type --- .../requesters/error_handlers/http_response_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 909f55aa5e6e..289d41c63ca5 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -65,7 +65,7 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep if filter_action is not None: default_error_message = default_mapped_error_resolution.error_message if default_mapped_error_resolution is not None else "" - error_message = "" + error_message = None if isinstance(response_or_exception, requests.Response): error_message = self._create_error_message(response_or_exception) error_message = error_message if len(error_message) else default_error_message From 06258b2d2464db18f3a2c9138a5483fd59d1ab41 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 14:12:09 -0700 Subject: [PATCH 27/29] Update conditional for type checking --- .../requesters/error_handlers/http_response_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 289d41c63ca5..5deea9a0ff5a 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -68,7 +68,7 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep error_message = None if isinstance(response_or_exception, requests.Response): error_message = self._create_error_message(response_or_exception) - error_message = error_message if len(error_message) else default_error_message + error_message = error_message if error_message is not None else default_error_message failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error return ErrorResolution( From a3cdc2ee8e3e59e0be0285b8e1c0ad73f1407805 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 14:33:35 -0700 Subject: [PATCH 28/29] Update http response filter test --- .../requesters/error_handlers/test_http_response_filter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index cf468a98b22a..04fa13b21796 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -31,7 +31,7 @@ "", "", {"status_code": 403}, - ErrorResolution(response_action=ResponseAction.IGNORE, failure_type=FailureType.config_error, error_message="Forbidden. You don't have permission to access this resource."), + ErrorResolution(response_action=ResponseAction.IGNORE, failure_type=FailureType.config_error, error_message=""), id="test_http_code_matches_ignore_action", ), pytest.param( @@ -41,7 +41,7 @@ "", "", {"status_code": 429}, - ErrorResolution(response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, error_message="Too many requests."), + ErrorResolution(response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, error_message=""), id="test_http_code_matches_retry_action", ), pytest.param( @@ -74,7 +74,7 @@ ErrorResolution( response_action=ResponseAction.FAIL, failure_type=FailureType.config_error, - error_message="Forbidden. You don't have permission to access this resource." + error_message="" ), id="test_predicate_matches_headers", ), From f5fdf7d394d5fddf31023260a48447bf98049d59 Mon Sep 17 00:00:00 2001 From: Patrick Nilan Date: Thu, 30 May 2024 15:08:33 -0700 Subject: [PATCH 29/29] Update tests --- .../requesters/error_handlers/http_response_filter.py | 2 +- .../requesters/error_handlers/test_http_response_filter.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py index 5deea9a0ff5a..757c596d0619 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py @@ -68,7 +68,7 @@ def matches(self, response_or_exception: Optional[Union[requests.Response, Excep error_message = None if isinstance(response_or_exception, requests.Response): error_message = self._create_error_message(response_or_exception) - error_message = error_message if error_message is not None else default_error_message + error_message = error_message or default_error_message failure_type = default_mapped_error_resolution.failure_type if default_mapped_error_resolution else FailureType.system_error return ErrorResolution( diff --git a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py index 04fa13b21796..cf468a98b22a 100644 --- a/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py +++ b/airbyte-cdk/python/unit_tests/sources/declarative/requesters/error_handlers/test_http_response_filter.py @@ -31,7 +31,7 @@ "", "", {"status_code": 403}, - ErrorResolution(response_action=ResponseAction.IGNORE, failure_type=FailureType.config_error, error_message=""), + ErrorResolution(response_action=ResponseAction.IGNORE, failure_type=FailureType.config_error, error_message="Forbidden. You don't have permission to access this resource."), id="test_http_code_matches_ignore_action", ), pytest.param( @@ -41,7 +41,7 @@ "", "", {"status_code": 429}, - ErrorResolution(response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, error_message=""), + ErrorResolution(response_action=ResponseAction.RETRY, failure_type=FailureType.transient_error, error_message="Too many requests."), id="test_http_code_matches_retry_action", ), pytest.param( @@ -74,7 +74,7 @@ ErrorResolution( response_action=ResponseAction.FAIL, failure_type=FailureType.config_error, - error_message="" + error_message="Forbidden. You don't have permission to access this resource." ), id="test_predicate_matches_headers", ),