Skip to content

Fix page size

Fix page size #3463

GitHub Actions / `source-twilio` Connector Test Results failed Aug 12, 2025 in 0s

3 fail, 21 pass in 35s

24 tests   - 5   21 ✅  - 5   35s ⏱️ -8s
 1 suites  - 1    0 💤  - 3 
 1 files    - 1    3 ❌ +3 

Results for commit cc7bd36. ± Comparison against earlier commit 14b890c.

Annotations

Check warning on line 0 in airbyte-integrations.connectors.source-twilio.unit_tests.test_source

See this annotation in the file changed.

@github-actions github-actions / `source-twilio` Connector Test Results

test_check_connection_handles_exceptions[exception0-Unable to connect to Twilio API with the provided credentials - ConnectionError('Connection aborted')] (airbyte-integrations.connectors.source-twilio.unit_tests.test_source) failed

airbyte-integrations/connectors/source-twilio/build/test-results/pytest-unit-tests-junit.xml [took 0s]
Raw output
AttributeError: 'NoneType' object has no attribute 'error'
self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
stream_name_to_stream = {'accounts': DeclarativeStream(retriever=SimpleRetriever(requester=HttpRequester(name='accounts', config={'account_sid...s.Alerts object at 0x7f48f3f26090>, 'applications': <source_twilio.streams.Applications object at 0x7f48f3f24fd0>, ...}
stream_name = 'accounts', logger = None

    def _check_stream_availability(
        self,
        stream_name_to_stream: Dict[str, Union[Stream, AbstractStream]],
        stream_name: str,
        logger: logging.Logger,
    ) -> Tuple[bool, Any]:
        """Checks if streams are available."""
        try:
            stream = stream_name_to_stream[stream_name]
>           stream_is_available, reason = evaluate_availability(stream, logger)
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:24: in evaluate_availability
    return HttpAvailabilityStrategy().check_availability(stream, logger)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/availability_strategy.py:48: in check_availability
    self.get_first_record_for_slice(stream, stream_slice)
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/availability_strategy.py:82: in get_first_record_for_slice
    return next(records_for_slice)
           ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_stream.py:161: in read_records
    yield from self.retriever.read_records(self.get_json_schema(), stream_slice)  # type: ignore # records are of the correct type
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:525: in read_records
    for stream_data in self._read_pages(record_generator, self.state, _slice):
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:395: in _read_pages
    response = self._fetch_next_page(stream_state, stream_slice, next_page_token)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:326: in _fetch_next_page
    return self.requester.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/requesters/http_requester.py:458: in send_request
    request, response = self._http_client.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:524: in send_request
    response: requests.Response = self._send_with_retry(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:270: in _send_with_retry
    response = backoff_handler(rate_limit_backoff_handler(user_backoff_handler))(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:302: in _send
    response = self._session.send(request, **request_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/call_rate.py:694: in send
    response = super().send(request, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1124: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1128: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock id='139951307988752'>, args = (<PreparedRequest [GET]>,)
kwargs = {'stream': False}, effect = ConnectionError('Connection aborted')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               ConnectionError: Connection aborted

/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1183: ConnectionError

During handling of the above exception, another exception occurred:

mocker = <pytest_mock.plugin.MockerFixture object at 0x7f48f3f63350>
config = {'account_sid': 'airbyte.io', 'auth_token': 'secret', 'lookback_window': 0, 'start_date': '2022-01-01T00:00:00Z'}
exception = ConnectionError('Connection aborted')
expected_error_msg = "Unable to connect to Twilio API with the provided credentials - ConnectionError('Connection aborted')"

    @pytest.mark.parametrize(
        "exception, expected_error_msg",
        (
            (
                ConnectionError("Connection aborted"),
                "Unable to connect to Twilio API with the provided credentials - ConnectionError('Connection aborted')",
            ),
            (
                TimeoutError("Socket timed out"),
                "Unable to connect to Twilio API with the provided credentials - TimeoutError('Socket timed out')",
            ),
            (
                DefaultBackoffException(
                    None, None, "Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/"
                ),
                "Unable to connect to Twilio API with the provided credentials - "
                "DefaultBackoffException('Unexpected exception in error handler: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')",
            ),
        ),
    )
    def test_check_connection_handles_exceptions(mocker, config, exception, expected_error_msg):
        mocker.patch.object(requests.Session, "send", Mock(side_effect=exception))
>       status_ok, error = TEST_INSTANCE.check_connection(logger=None, config=config)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

unit_tests/test_source.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_source.py:39: in check_connection
    return self.connection_checker.check_connection(self, logger, config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:84: in check_connection
    stream_availability, message = self._check_stream_availability(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:116: in _check_stream_availability
    return self._log_error(logger, f"checking availability of stream {stream_name}", error)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
logger = None, action = 'checking availability of stream accounts'
error = ConnectionError('Connection aborted')

    def _log_error(self, logger: logging.Logger, action: str, error: Exception) -> Tuple[bool, str]:
        """Logs an error and returns a formatted error message."""
        error_message = f"Encountered an error while {action}. Error: {error}"
>       logger.error(error_message + f"Error traceback: \n {traceback.format_exc()}", exc_info=True)
        ^^^^^^^^^^^^
E       AttributeError: 'NoneType' object has no attribute 'error'

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:63: AttributeError

Check warning on line 0 in airbyte-integrations.connectors.source-twilio.unit_tests.test_source

See this annotation in the file changed.

@github-actions github-actions / `source-twilio` Connector Test Results

test_check_connection_handles_exceptions[exception1-Unable to connect to Twilio API with the provided credentials - TimeoutError('Socket timed out')] (airbyte-integrations.connectors.source-twilio.unit_tests.test_source) failed

airbyte-integrations/connectors/source-twilio/build/test-results/pytest-unit-tests-junit.xml [took 0s]
Raw output
AttributeError: 'NoneType' object has no attribute 'error'
self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
stream_name_to_stream = {'accounts': DeclarativeStream(retriever=SimpleRetriever(requester=HttpRequester(name='accounts', config={'account_sid...s.Alerts object at 0x7f48f3d34a50>, 'applications': <source_twilio.streams.Applications object at 0x7f48f3d36c10>, ...}
stream_name = 'accounts', logger = None

    def _check_stream_availability(
        self,
        stream_name_to_stream: Dict[str, Union[Stream, AbstractStream]],
        stream_name: str,
        logger: logging.Logger,
    ) -> Tuple[bool, Any]:
        """Checks if streams are available."""
        try:
            stream = stream_name_to_stream[stream_name]
>           stream_is_available, reason = evaluate_availability(stream, logger)
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:24: in evaluate_availability
    return HttpAvailabilityStrategy().check_availability(stream, logger)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/availability_strategy.py:48: in check_availability
    self.get_first_record_for_slice(stream, stream_slice)
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/availability_strategy.py:82: in get_first_record_for_slice
    return next(records_for_slice)
           ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_stream.py:161: in read_records
    yield from self.retriever.read_records(self.get_json_schema(), stream_slice)  # type: ignore # records are of the correct type
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:525: in read_records
    for stream_data in self._read_pages(record_generator, self.state, _slice):
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:395: in _read_pages
    response = self._fetch_next_page(stream_state, stream_slice, next_page_token)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:326: in _fetch_next_page
    return self.requester.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/requesters/http_requester.py:458: in send_request
    request, response = self._http_client.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:524: in send_request
    response: requests.Response = self._send_with_retry(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:270: in _send_with_retry
    response = backoff_handler(rate_limit_backoff_handler(user_backoff_handler))(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:302: in _send
    response = self._session.send(request, **request_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/call_rate.py:694: in send
    response = super().send(request, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1124: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1128: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock id='139951308441360'>, args = (<PreparedRequest [GET]>,)
kwargs = {'stream': False}, effect = TimeoutError('Socket timed out')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               TimeoutError: Socket timed out

/opt/hostedtoolcache/Python/3.11.13/x64/lib/python3.11/unittest/mock.py:1183: TimeoutError

During handling of the above exception, another exception occurred:

mocker = <pytest_mock.plugin.MockerFixture object at 0x7f48f3d03e90>
config = {'account_sid': 'airbyte.io', 'auth_token': 'secret', 'lookback_window': 0, 'start_date': '2022-01-01T00:00:00Z'}
exception = TimeoutError('Socket timed out')
expected_error_msg = "Unable to connect to Twilio API with the provided credentials - TimeoutError('Socket timed out')"

    @pytest.mark.parametrize(
        "exception, expected_error_msg",
        (
            (
                ConnectionError("Connection aborted"),
                "Unable to connect to Twilio API with the provided credentials - ConnectionError('Connection aborted')",
            ),
            (
                TimeoutError("Socket timed out"),
                "Unable to connect to Twilio API with the provided credentials - TimeoutError('Socket timed out')",
            ),
            (
                DefaultBackoffException(
                    None, None, "Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/"
                ),
                "Unable to connect to Twilio API with the provided credentials - "
                "DefaultBackoffException('Unexpected exception in error handler: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')",
            ),
        ),
    )
    def test_check_connection_handles_exceptions(mocker, config, exception, expected_error_msg):
        mocker.patch.object(requests.Session, "send", Mock(side_effect=exception))
>       status_ok, error = TEST_INSTANCE.check_connection(logger=None, config=config)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

unit_tests/test_source.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_source.py:39: in check_connection
    return self.connection_checker.check_connection(self, logger, config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:84: in check_connection
    stream_availability, message = self._check_stream_availability(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:116: in _check_stream_availability
    return self._log_error(logger, f"checking availability of stream {stream_name}", error)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
logger = None, action = 'checking availability of stream accounts'
error = TimeoutError('Socket timed out')

    def _log_error(self, logger: logging.Logger, action: str, error: Exception) -> Tuple[bool, str]:
        """Logs an error and returns a formatted error message."""
        error_message = f"Encountered an error while {action}. Error: {error}"
>       logger.error(error_message + f"Error traceback: \n {traceback.format_exc()}", exc_info=True)
        ^^^^^^^^^^^^
E       AttributeError: 'NoneType' object has no attribute 'error'

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:63: AttributeError

Check warning on line 0 in airbyte-integrations.connectors.source-twilio.unit_tests.test_source

See this annotation in the file changed.

@github-actions github-actions / `source-twilio` Connector Test Results

test_check_connection_handles_exceptions[exception2-Unable to connect to Twilio API with the provided credentials - DefaultBack… Unauthorized for url: https://api.twilio.com/')] (airbyte-integrations.connectors.source-twilio.unit_tests.test_source) failed

airbyte-integrations/connectors/source-twilio/build/test-results/pytest-unit-tests-junit.xml [took 31s]
Raw output
AttributeError: 'NoneType' object has no attribute 'error'
self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
stream_name_to_stream = {'accounts': DeclarativeStream(retriever=SimpleRetriever(requester=HttpRequester(name='accounts', config={'account_sid...s.Alerts object at 0x7f48f3e3edd0>, 'applications': <source_twilio.streams.Applications object at 0x7f48f3e3c150>, ...}
stream_name = 'accounts', logger = None

    def _check_stream_availability(
        self,
        stream_name_to_stream: Dict[str, Union[Stream, AbstractStream]],
        stream_name: str,
        logger: logging.Logger,
    ) -> Tuple[bool, Any]:
        """Checks if streams are available."""
        try:
            stream = stream_name_to_stream[stream_name]
>           stream_is_available, reason = evaluate_availability(stream, logger)
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:24: in evaluate_availability
    return HttpAvailabilityStrategy().check_availability(stream, logger)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/availability_strategy.py:48: in check_availability
    self.get_first_record_for_slice(stream, stream_slice)
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/availability_strategy.py:82: in get_first_record_for_slice
    return next(records_for_slice)
           ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_stream.py:161: in read_records
    yield from self.retriever.read_records(self.get_json_schema(), stream_slice)  # type: ignore # records are of the correct type
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:525: in read_records
    for stream_data in self._read_pages(record_generator, self.state, _slice):
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:395: in _read_pages
    response = self._fetch_next_page(stream_state, stream_slice, next_page_token)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/retrievers/simple_retriever.py:326: in _fetch_next_page
    return self.requester.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/requesters/http_requester.py:458: in send_request
    request, response = self._http_client.send_request(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:524: in send_request
    response: requests.Response = self._send_with_retry(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:270: in _send_with_retry
    response = backoff_handler(rate_limit_backoff_handler(user_backoff_handler))(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/backoff/_sync.py:105: in retry
    ret = target(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:340: in _send
    self._handle_error_resolution(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <airbyte_cdk.sources.streams.http.http_client.HttpClient object at 0x7f48f3d86ad0>
response = None
exc = DefaultBackoffException('Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')
request = <PreparedRequest [GET]>
error_resolution = ErrorResolution(response_action=<ResponseAction.RETRY: 'RETRY'>, failure_type=<FailureType.system_error: 'system_error...koffException: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')
exit_on_rate_limit = True

    def _handle_error_resolution(
        self,
        response: Optional[requests.Response],
        exc: Optional[requests.RequestException],
        request: requests.PreparedRequest,
        error_resolution: ErrorResolution,
        exit_on_rate_limit: Optional[bool] = False,
    ) -> None:
        if error_resolution.response_action not in self._ACTIONS_TO_RETRY_ON:
            self._evict_key(request)
    
        # Emit stream status RUNNING with the reason RATE_LIMITED to log that the rate limit has been reached
        if error_resolution.response_action == ResponseAction.RATE_LIMITED:
            # TODO: Update to handle with message repository when concurrent message repository is ready
            reasons = [AirbyteStreamStatusReason(type=AirbyteStreamStatusReasonType.RATE_LIMITED)]
            message = orjson.dumps(
                AirbyteMessageSerializer.dump(
                    stream_status_as_airbyte_message(
                        StreamDescriptor(name=self._name), AirbyteStreamStatus.RUNNING, reasons
                    )
                )
            ).decode()
    
            # Simply printing the stream status is a temporary solution and can cause future issues. Currently, the _send method is
            # wrapped with backoff decorators, and we can only emit messages by iterating record_iterator in the abstract source at the
            # end of the retry decorator behavior. This approach does not allow us to emit messages in the queue before exiting the
            # backoff retry loop. Adding `\n` to the message and ignore 'end' ensure that few messages are printed at the same time.
            print(f"{message}\n", end="", flush=True)
    
        if error_resolution.response_action == ResponseAction.FAIL:
            if response is not None:
                filtered_response_message = filter_secrets(
                    f"Request (body): '{str(request.body)}'. Response (body): '{self._get_response_body(response)}'. Response (headers): '{response.headers}'."
                )
                error_message = f"'{request.method}' request to '{request.url}' failed with status code '{response.status_code}' and error message: '{self._error_message_parser.parse_response_error_message(response)}'. {filtered_response_message}"
            else:
                error_message = (
                    f"'{request.method}' request to '{request.url}' failed with exception: '{exc}'"
                )
    
            # ensure the exception message is emitted before raised
            self._logger.error(error_message)
    
            raise MessageRepresentationAirbyteTracedErrors(
                internal_message=error_message,
                message=error_resolution.error_message or error_message,
                failure_type=error_resolution.failure_type,
            )
    
        elif error_resolution.response_action == ResponseAction.IGNORE:
            if response is not None:
                log_message = f"Ignoring response for '{request.method}' request to '{request.url}' with response code '{response.status_code}'"
            else:
                log_message = f"Ignoring response for '{request.method}' request to '{request.url}' with error '{exc}'"
    
            self._logger.info(error_resolution.error_message or log_message)
    
        # TODO: Consider dynamic retry count depending on subsequent error codes
        elif (
            error_resolution.response_action == ResponseAction.RETRY
            or error_resolution.response_action == ResponseAction.RATE_LIMITED
        ):
            user_defined_backoff_time = None
            for backoff_strategy in self._backoff_strategies:
                backoff_time = backoff_strategy.backoff_time(
                    response_or_exception=response if response is not None else exc,
                    attempt_count=self._request_attempt_count[request],
                )
                if backoff_time:
                    user_defined_backoff_time = backoff_time
                    break
            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}."
            )
    
            retry_endlessly = (
                error_resolution.response_action == ResponseAction.RATE_LIMITED
                and not exit_on_rate_limit
            )
    
            if user_defined_backoff_time:
                raise UserDefinedBackoffException(
                    backoff=user_defined_backoff_time,
                    request=request,
                    response=(response if response is not None else exc),
                    error_message=error_message,
                )
    
            elif retry_endlessly:
                raise RateLimitBackoffException(
                    request=request,
                    response=(response if response is not None else exc),
                    error_message=error_message,
                )
    
>           raise DefaultBackoffException(
                request=request,
                response=(response if response is not None else exc),
                error_message=error_message,
            )
E           airbyte_cdk.sources.streams.http.exceptions.DefaultBackoffException: DefaultBackoffException: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/streams/http/http_client.py:480: DefaultBackoffException

During handling of the above exception, another exception occurred:

mocker = <pytest_mock.plugin.MockerFixture object at 0x7f48f3e29cd0>
config = {'account_sid': 'airbyte.io', 'auth_token': 'secret', 'lookback_window': 0, 'start_date': '2022-01-01T00:00:00Z'}
exception = DefaultBackoffException('Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')
expected_error_msg = "Unable to connect to Twilio API with the provided credentials - DefaultBackoffException('Unexpected exception in error handler: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')"

    @pytest.mark.parametrize(
        "exception, expected_error_msg",
        (
            (
                ConnectionError("Connection aborted"),
                "Unable to connect to Twilio API with the provided credentials - ConnectionError('Connection aborted')",
            ),
            (
                TimeoutError("Socket timed out"),
                "Unable to connect to Twilio API with the provided credentials - TimeoutError('Socket timed out')",
            ),
            (
                DefaultBackoffException(
                    None, None, "Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/"
                ),
                "Unable to connect to Twilio API with the provided credentials - "
                "DefaultBackoffException('Unexpected exception in error handler: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')",
            ),
        ),
    )
    def test_check_connection_handles_exceptions(mocker, config, exception, expected_error_msg):
        mocker.patch.object(requests.Session, "send", Mock(side_effect=exception))
>       status_ok, error = TEST_INSTANCE.check_connection(logger=None, config=config)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

unit_tests/test_source.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/declarative_source.py:39: in check_connection
    return self.connection_checker.check_connection(self, logger, config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:84: in check_connection
    stream_availability, message = self._check_stream_availability(
/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:116: in _check_stream_availability
    return self._log_error(logger, f"checking availability of stream {stream_name}", error)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = CheckStream(stream_names=['accounts'], dynamic_streams_check_configs=[])
logger = None, action = 'checking availability of stream accounts'
error = DefaultBackoffException('DefaultBackoffException: Unexpected exception in error handler: 401 Client Error: Unauthorized for url: https://api.twilio.com/')

    def _log_error(self, logger: logging.Logger, action: str, error: Exception) -> Tuple[bool, str]:
        """Logs an error and returns a formatted error message."""
        error_message = f"Encountered an error while {action}. Error: {error}"
>       logger.error(error_message + f"Error traceback: \n {traceback.format_exc()}", exc_info=True)
        ^^^^^^^^^^^^
E       AttributeError: 'NoneType' object has no attribute 'error'

/home/runner/.cache/pypoetry/virtualenvs/source-twilio-EkCgkBZG-py3.11/lib/python3.11/site-packages/airbyte_cdk/sources/declarative/checks/check_stream.py:63: AttributeError