From 16360c00fe0fd61693b7bdef6229021a3f1d8b99 Mon Sep 17 00:00:00 2001 From: alexpipipi Date: Tue, 16 Jun 2026 14:28:35 -0400 Subject: [PATCH] fix: handle free-tier 'warning' column in get_historical_data (#66) A free API key requesting more than one year of historical data receives an extra 'warning' column in the response. get_historical_data relabels columns by fixed position, so the extra column caused a confusing pandas error: "ValueError: Length mismatch: Expected axis has 9 elements, new values have 8". Added _strip_free_tier_warning(): it surfaces the API warning message via the console and drops the 'warning' column before relabelling, so the call returns the available (one-year) data with a clear log message instead of crashing. Applied to both the EOD and intraday branches. Added regression tests. Co-Authored-By: Claude Opus 4.8 (1M context) --- eodhd/apiclient.py | 14 +++++ tests/test_historical_free_tier_warning.py | 65 ++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/test_historical_free_tier_warning.py diff --git a/eodhd/apiclient.py b/eodhd/apiclient.py index bece975..0060157 100644 --- a/eodhd/apiclient.py +++ b/eodhd/apiclient.py @@ -186,6 +186,18 @@ def _rest_get(self, endpoint: str = "", uri: str = "", querystring: str = "") -> else: return pd.DataFrame(json_data, index=[0]) + def _strip_free_tier_warning(self, df_data: pd.DataFrame) -> pd.DataFrame: + """Free API keys append a 'warning' column (e.g. data limited to one year) + to historical responses. Surface the message and drop the column so the + downstream fixed-width column relabelling does not raise a confusing + "Length mismatch" pandas error (see issue #66).""" + if "warning" in df_data.columns: + messages = df_data["warning"].dropna() + if not messages.empty: + self.console.log("EODHD API warning:", messages.iloc[-1]) + df_data = df_data.drop(columns=["warning"]) + return df_data + def get_exchanges(self) -> pd.DataFrame: """Get supported exchanges""" @@ -313,6 +325,7 @@ def get_historical_data( sys.exit() df_data = self._rest_get("eod", symbol, f"&period={interval}&from={str(date_from)}&to={str(date_to)}") + df_data = self._strip_free_tier_warning(df_data) if len(df_data) == 0: columns_eod = [ @@ -424,6 +437,7 @@ def get_historical_data( sys.exit() df_data = self._rest_get("intraday", symbol, f"&interval={interval}&from={str(date_from)}&to={str(date_to)}") + df_data = self._strip_free_tier_warning(df_data) if len(df_data) == 0: columns_eod = [ diff --git a/tests/test_historical_free_tier_warning.py b/tests/test_historical_free_tier_warning.py new file mode 100644 index 0000000..03d5a23 --- /dev/null +++ b/tests/test_historical_free_tier_warning.py @@ -0,0 +1,65 @@ +"""Regression test for issue #66. + +A free API key requesting more than one year of historical data gets an extra +'warning' column appended to the response. The fixed-width column relabelling in +get_historical_data then raised a confusing pandas "Length mismatch" error. +The warning must be surfaced and the column dropped instead. +""" + +from unittest.mock import patch + +import pandas as pd +import pytest + +from eodhd.apiclient import APIClient + + +@pytest.fixture +def client(): + with patch("eodhd.apiclient.requests.Session"): + yield APIClient(api_key="demo1234567890123456") + + +def _free_tier_eod_frame(): + """Mimics the EOD response a free key returns for a >1y request: a trailing + 'warning' column that is NaN on every row except the last.""" + return pd.DataFrame( + { + "date": ["2025-04-22", "2025-04-23"], + "open": [14347.8896, 14404.0498], + "high": [14414.0996, 14645.2598], + "low": [14293.0303, 14403.5498], + "close": [14404.0498, 14549.4199], + "adjusted_close": [14404.0498, 14549.4199], + "volume": [0, 0], + "warning": [None, "Data is limited by one year as you have free subscription."], + } + ) + + +def test_free_tier_warning_does_not_raise(client): + with patch.object(client, "_rest_get", return_value=_free_tier_eod_frame()): + df = client.get_historical_data("BUKAC.INDX", interval="d", results=10000) + + # Previously raised: ValueError: Length mismatch: Expected axis has 9 elements... + assert "warning" not in df.columns + assert list(df.columns) == [ + "symbol", + "interval", + "open", + "high", + "low", + "close", + "adjusted_close", + "volume", + ] + assert len(df) == 2 + + +def test_free_tier_warning_is_logged(client): + with patch.object(client, "_rest_get", return_value=_free_tier_eod_frame()): + with patch.object(client.console, "log") as mock_log: + client.get_historical_data("BUKAC.INDX", interval="d", results=10000) + + logged = " ".join(str(a) for call in mock_log.call_args_list for a in call.args) + assert "warning" in logged.lower()