Fix `ThirdPartyTimeoutError` swallowing in `yfinance` dataflows (#127)

In dataflow API wrappers, generic `except Exception` handlers were swallowing
timeout exceptions (like `requests.exceptions.Timeout` and `ThirdPartyTimeoutError`),
preventing upstream retry logic and proper error handling from functioning correctly.

This patch:
- Adds explicit catching of `requests.exceptions.Timeout` to raise as `ThirdPartyTimeoutError` in `yfinance` wrapper modules (`y_finance.py`, `yfinance_news.py`, `yfinance_scanner.py`).
- Adds explicit catching and re-raising of `ThirdPartyTimeoutError` before generic exception handlers across the dataflows.
- Retains existing exception inheritance logic to avoid backward compatibility breaks.
- Updates `TestEnsureIndexesInInit` to use the explicit `ensure_indexes()` call per lazy loading changes.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: aguzererler <6199053+aguzererler@users.noreply.github.com>
This commit is contained in:
ahmet guzererler 2026-03-27 00:20:10 +01:00 committed by GitHub
parent 12014a283d
commit 6fa4c2340a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 61 additions and 1 deletions

View File

@ -454,6 +454,10 @@ class TestEnsureIndexesInInit(unittest.TestCase):
store = MongoReportStore("mongodb://localhost:27017", run_id="test") store = MongoReportStore("mongodb://localhost:27017", run_id="test")
# Indexes are now created lazily, not in __init__.
# Explicitly call ensure_indexes() to test index creation logic.
store.ensure_indexes()
# create_index should have been called at least 4 times # create_index should have been called at least 4 times
# (the indexes from ensure_indexes) # (the indexes from ensure_indexes)
self.assertGreaterEqual(mock_col.create_index.call_count, 4) self.assertGreaterEqual(mock_col.create_index.call_count, 4)

View File

@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
from typing import Dict, Any from typing import Dict, Any
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from .alpha_vantage_common import _make_api_request from .alpha_vantage_common import _make_api_request, ThirdPartyTimeoutError
SUPPORTED_INDICATORS = { SUPPORTED_INDICATORS = {
@ -221,6 +221,8 @@ def get_indicator(
f"{INDICATOR_DESCRIPTIONS.get(indicator, 'No description available.')}" f"{INDICATOR_DESCRIPTIONS.get(indicator, 'No description available.')}"
) )
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
print(f"Error getting Alpha Vantage indicator data for {indicator}: {e}") print(f"Error getting Alpha Vantage indicator data for {indicator}: {e}")
return f"Error retrieving {indicator} data: {str(e)}" return f"Error retrieving {indicator} data: {str(e)}"

View File

@ -4,7 +4,9 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
import pandas as pd import pandas as pd
import yfinance as yf import yfinance as yf
import requests
import os import os
from .finnhub_common import ThirdPartyTimeoutError
from .stockstats_utils import StockstatsUtils, YFinanceError, _clean_dataframe, _load_or_fetch_ohlcv, yf_retry from .stockstats_utils import StockstatsUtils, YFinanceError, _clean_dataframe, _load_or_fetch_ohlcv, yf_retry
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -293,6 +295,10 @@ def get_fundamentals(
return header + "\n".join(lines) return header + "\n".join(lines)
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out retrieving fundamentals for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error retrieving fundamentals for {ticker}: {str(e)}" return f"Error retrieving fundamentals for {ticker}: {str(e)}"
@ -323,6 +329,10 @@ def get_balance_sheet(
return header + csv_string return header + csv_string
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out retrieving balance sheet for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error retrieving balance sheet for {ticker}: {str(e)}" return f"Error retrieving balance sheet for {ticker}: {str(e)}"
@ -353,6 +363,10 @@ def get_cashflow(
return header + csv_string return header + csv_string
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out retrieving cash flow for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error retrieving cash flow for {ticker}: {str(e)}" return f"Error retrieving cash flow for {ticker}: {str(e)}"
@ -383,6 +397,10 @@ def get_income_statement(
return header + csv_string return header + csv_string
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out retrieving income statement for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error retrieving income statement for {ticker}: {str(e)}" return f"Error retrieving income statement for {ticker}: {str(e)}"
@ -407,5 +425,9 @@ def get_insider_transactions(
return header + csv_string return header + csv_string
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out retrieving insider transactions for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error retrieving insider transactions for {ticker}: {str(e)}" return f"Error retrieving insider transactions for {ticker}: {str(e)}"

View File

@ -1,8 +1,10 @@
"""yfinance-based news data fetching functions.""" """yfinance-based news data fetching functions."""
import yfinance as yf import yfinance as yf
import requests
from datetime import datetime from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from .finnhub_common import ThirdPartyTimeoutError
def _extract_article_data(article: dict) -> dict: def _extract_article_data(article: dict) -> dict:
@ -98,6 +100,10 @@ def get_news_yfinance(
return f"## {ticker} News, from {start_date} to {end_date}:\n\n{news_str}" return f"## {ticker} News, from {start_date} to {end_date}:\n\n{news_str}"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching news for {ticker}")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching news for {ticker}: {str(e)}" return f"Error fetching news for {ticker}: {str(e)}"
@ -186,5 +192,9 @@ def get_global_news_yfinance(
return f"## Global Market News, from {start_date} to {curr_date}:\n\n{news_str}" return f"## Global Market News, from {start_date} to {curr_date}:\n\n{news_str}"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching global news")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching global news: {str(e)}" return f"Error fetching global news: {str(e)}"

View File

@ -1,8 +1,10 @@
"""yfinance-based scanner data fetching functions for market-wide analysis.""" """yfinance-based scanner data fetching functions for market-wide analysis."""
import yfinance as yf import yfinance as yf
import requests
from datetime import datetime from datetime import datetime
from typing import Annotated from typing import Annotated
from .finnhub_common import ThirdPartyTimeoutError
def get_market_movers_yfinance( def get_market_movers_yfinance(
@ -72,6 +74,10 @@ def get_market_movers_yfinance(
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching market movers")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching market movers for {category}: {str(e)}" return f"Error fetching market movers for {category}: {str(e)}"
@ -151,6 +157,10 @@ def get_market_indices_yfinance() -> str:
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching market indices")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching market indices: {str(e)}" return f"Error fetching market indices: {str(e)}"
@ -238,6 +248,10 @@ def get_sector_performance_yfinance() -> str:
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching sector performance")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching sector performance: {str(e)}" return f"Error fetching sector performance: {str(e)}"
@ -338,6 +352,10 @@ def get_industry_performance_yfinance(
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching industry performance")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching industry performance for sector '{sector_key}': {str(e)}" return f"Error fetching industry performance for sector '{sector_key}': {str(e)}"
@ -399,5 +417,9 @@ def get_topic_news_yfinance(
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
except requests.exceptions.Timeout:
raise ThirdPartyTimeoutError(f"Request timed out fetching news for topic '{topic}'")
except ThirdPartyTimeoutError:
raise
except Exception as e: except Exception as e:
return f"Error fetching news for topic '{topic}': {str(e)}" return f"Error fetching news for topic '{topic}': {str(e)}"