Skip to content

Commit 614fdec

Browse files
committed
Fix ThetaData request error propagation
1 parent 03e4255 commit 614fdec

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

lumibot/tools/thetadata_helper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,9 @@ def get_request(url: str, headers: dict, querystring: dict, username: str, passw
16051605
except ThetaDataConnectionError as exc:
16061606
logger.error("Theta Data connection failed after supervised restarts: %s", exc)
16071607
raise
1608+
except ValueError:
1609+
# Preserve deliberate ValueError signals (e.g., ThetaData error_type responses)
1610+
raise
16081611
except Exception as e:
16091612
logger.warning(f"Exception during request (attempt {counter + 1}): {e}")
16101613
check_connection(username=username, password=password, wait_for_connection=True)
@@ -1614,7 +1617,7 @@ def get_request(url: str, headers: dict, querystring: dict, username: str, passw
16141617

16151618
counter += 1
16161619
if counter > 1:
1617-
raise ThetaDataConnectionError("Unable to connect to Theta Data after repeated retries.")
1620+
raise ValueError("Cannot connect to Theta Data!")
16181621

16191622
# Store this page's response data
16201623
page_count += 1

tests/test_thetadata_helper.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,13 @@ def test_get_request_error_in_json(mock_get, mock_check_connection):
881881
password="test_password",
882882
wait_for_connection=True,
883883
)
884-
assert mock_check_connection.call_count == 5
884+
assert mock_check_connection.call_count == 2
885+
first_call_kwargs = mock_check_connection.call_args_list[0].kwargs
886+
assert first_call_kwargs == {
887+
"username": "test_user",
888+
"password": "test_password",
889+
"wait_for_connection": False,
890+
}
885891

886892

887893
@patch('lumibot.tools.thetadata_helper.check_connection')
@@ -907,6 +913,44 @@ def test_get_request_exception_handling(mock_get, mock_check_connection):
907913
assert mock_check_connection.call_count == 3
908914

909915

916+
917+
@patch('lumibot.tools.thetadata_helper.start_theta_data_client')
918+
@patch('lumibot.tools.thetadata_helper.check_connection')
919+
def test_get_request_consecutive_474_triggers_restarts(mock_check_connection, mock_start_client, monkeypatch):
920+
mock_check_connection.return_value = (object(), True)
921+
922+
responses = [MagicMock(status_code=474, text='Connection lost to Theta Data MDDS.') for _ in range(9)]
923+
924+
def fake_get(*args, **kwargs):
925+
if not responses:
926+
raise AssertionError('Test exhausted mock responses unexpectedly')
927+
return responses.pop(0)
928+
929+
monkeypatch.setattr(thetadata_helper.requests, 'get', fake_get)
930+
monkeypatch.setattr(thetadata_helper.time, 'sleep', lambda *args, **kwargs: None)
931+
monkeypatch.setattr(thetadata_helper, 'BOOT_GRACE_PERIOD', 0, raising=False)
932+
monkeypatch.setattr(thetadata_helper, 'CONNECTION_RETRY_SLEEP', 0, raising=False)
933+
934+
with pytest.raises(ValueError, match='Cannot connect to Theta Data!'):
935+
thetadata_helper.get_request(
936+
url='http://test.com',
937+
headers={'Authorization': 'Bearer test_token'},
938+
querystring={'param1': 'value1'},
939+
username='test_user',
940+
password='test_password',
941+
)
942+
943+
assert mock_start_client.call_count == 3
944+
# Initial liveness probe plus retry coordination checks
945+
assert mock_check_connection.call_count > 3
946+
first_call_kwargs = mock_check_connection.call_args_list[0].kwargs
947+
assert first_call_kwargs == {
948+
'username': 'test_user',
949+
'password': 'test_password',
950+
'wait_for_connection': False,
951+
}
952+
953+
910954
@patch('lumibot.tools.thetadata_helper.get_request')
911955
def test_get_historical_data_stock(mock_get_request):
912956
# Arrange

0 commit comments

Comments
 (0)