Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cache clearing and testing #10

Merged
merged 7 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/imf_reader/sdr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@
from imf_reader.sdr.read_interest_rate import fetch_interest_rates
from imf_reader.sdr.read_exchange_rate import fetch_exchange_rates
from imf_reader.sdr.read_announcements import fetch_allocations_holdings
from imf_reader.sdr.clear_cache import clear_cache
39 changes: 39 additions & 0 deletions src/imf_reader/sdr/clear_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from imf_reader.sdr.read_announcements import (
get_holdings_and_allocations_data,
get_latest_date,
)
from imf_reader.sdr.read_exchange_rate import fetch_exchange_rates
from imf_reader.sdr.read_interest_rate import fetch_interest_rates
from imf_reader.config import logger


def clear_cache():
"""Clear the cache for all lru_cache-decorated functions."""

cleared_caches = 0

# read_announcements
if (
get_holdings_and_allocations_data.cache_info().currsize > 0
or get_latest_date.cache_info().currsize > 0
):
get_holdings_and_allocations_data.cache_clear()
get_latest_date.cache_clear()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache_clear should function normally even if there is no cached data, so the condition check and counter might be redundant

cleared_caches += 1
logger.info("Cache cleared - Holdings and allocations")

# read_exchange_rate
if fetch_exchange_rates.cache_info().currsize > 0:
fetch_exchange_rates.cache_clear()
cleared_caches += 1
logger.info("Cache cleared - Exchange rates")

# read_interest_rate
if fetch_interest_rates.cache_info().currsize > 0:
fetch_interest_rates.cache_clear()
cleared_caches += 1
logger.info("Cache cleared - Interest rates")

if cleared_caches == 0:

logger.info("Unable to clear cache - No cached data")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be a useful message for the user who doesn't necessarily need to know if there is/isn't cached data

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be misleading to a user as they might think there is an issue with the function call

9 changes: 1 addition & 8 deletions src/imf_reader/sdr/read_announcements.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from datetime import datetime

from imf_reader.utils import make_request
from imf_reader.config import logger, NoDataError
from imf_reader.config import logger

BASE_URL = "https://www.imf.org/external/np/fin/tad/"
MAIN_PAGE_URL = "https://www.imf.org/external/np/fin/tad/extsdr1.aspx"
Expand Down Expand Up @@ -104,10 +104,3 @@ def fetch_allocations_holdings(date: tuple[int, int] | None = None) -> pd.DataFr
date = get_latest_date()

return get_holdings_and_allocations_data(*date)


def clear_cache():
"""Clear the cache for all lru_cache-decorated functions."""
get_holdings_and_allocations_data.cache_clear()
get_latest_date.cache_clear()
logger.info("Cache cleared")
6 changes: 0 additions & 6 deletions src/imf_reader/sdr/read_exchange_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,3 @@ def fetch_exchange_rates(unit_basis: Literal["SDR", "USD"] = "SDR") -> pd.DataFr

df = get_exchange_rates_data()
return parse_data(df, unit_basis)


def clear_cache():
"""Clear the cache for all lru_cache-decorated functions."""
fetch_exchange_rates.cache_clear()
logger.info("Cache cleared")
6 changes: 0 additions & 6 deletions src/imf_reader/sdr/read_interest_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,3 @@ def fetch_interest_rates() -> pd.DataFrame:
df = clean_data(df)

return df


def clear_cache():
"""Clear the cache for all lru_cache-decorated functions."""
fetch_interest_rates.cache_clear()
logger.info("Cache cleared")
8 changes: 4 additions & 4 deletions tests/test_sdr/test_read_announcements.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from unittest.mock import patch, Mock, MagicMock
from unittest.mock import patch, Mock
import pytest
import pandas as pd
from imf_reader import sdr
from imf_reader.sdr.read_announcements import (
read_tsv,
clean_df,
format_date,
get_holdings_and_allocations_data,
get_latest_date,
fetch_allocations_holdings,
clear_cache,
BASE_URL,
MAIN_PAGE_URL,
)
Expand All @@ -34,9 +34,9 @@ class TestReadAnnouncements:
"""Tests functions in the read_announcements module."""

@pytest.fixture(autouse=True)
def clear_cache(self):
def auto_clear_cache(self):
"""Clear cache before each test."""
clear_cache()
sdr.clear_cache()

@patch("pandas.read_csv")
def test_read_tsv_success(self, mock_read_csv):
Expand Down
43 changes: 5 additions & 38 deletions tests/test_sdr/test_read_exchange_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
import pytest
import requests
import pandas as pd
from imf_reader import sdr
from imf_reader.sdr.read_exchange_rate import (
preprocess_data,
fetch_exchange_rates,
get_exchange_rates_data,
extract_exchange_series,
extract_dates_series,
parse_data,
BASE_URL,
clear_cache,
BASE_URL
)


Expand All @@ -32,9 +32,9 @@ def input_df():
class TestExchangeRateModule:

@pytest.fixture(autouse=True)
def clear_cache(self):
def auto_clear_cache(self):
"""Clear cache before each test."""
clear_cache()
sdr.clear_cache()

@patch("requests.post")
def test_get_exchange_rates_data_success(self, mock_post):
Expand Down Expand Up @@ -207,37 +207,4 @@ def test_fetch_exchange_rates(self, mock_parse_data, mock_get_data, input_df):
mock_get_data.assert_called_once()
mock_parse_data.assert_called_once_with(mock_get_data.return_value, "USD")
pd.testing.assert_frame_equal(result, expected_df)
mock_logger.assert_called_once_with("Fetching exchange rate data")

@patch("imf_reader.sdr.read_exchange_rate.parse_data")
def test_cache_clear(input_df, mock_parse_data):
"""
Test cache clearing behavior.
"""
with patch(
"imf_reader.sdr.read_exchange_rate.get_exchange_rates_data"
) as mock_get_data:
# Simulate get_exchange_rates_data returning the input fixture
mock_get_data.return_value = input_df
mock_parse_data.return_value = pd.DataFrame(
{"date": pd.to_datetime(["2023-11-30"]), "exchange_rate": [0.123]}
)

# First call - populates the cache
result_1 = fetch_exchange_rates("USD")
assert mock_get_data.call_count == 1

# Second call - returns from cache
result_2 = fetch_exchange_rates("USD")
assert mock_get_data.call_count == 1 # Still cached

# Clear the cache
clear_cache()

# Third call - should re-execute
result_3 = fetch_exchange_rates("USD")
assert mock_get_data.call_count == 2 # Function re-executed

# Verify results are consistent
pd.testing.assert_frame_equal(result_1, result_2)
pd.testing.assert_frame_equal(result_1, result_3)
mock_logger.assert_called_once_with("Fetching exchange rate data")
9 changes: 4 additions & 5 deletions tests/test_sdr/test_read_interest_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
import pandas as pd
import requests
from unittest.mock import patch, MagicMock, ANY
from io import BytesIO
from imf_reader import sdr
from imf_reader.sdr.read_interest_rate import (
BASE_URL,
get_interest_rates_data,
preprocess_data,
_filter_data,
_format_data,
clean_data,
fetch_interest_rates,
clear_cache,
fetch_interest_rates
)


Expand All @@ -37,9 +36,9 @@ def input_df():
class TestReadInterestRate:

@pytest.fixture(autouse=True)
def clear_cache(self):
def auto_clear_cache(self):
"""Clear cache before each test."""
clear_cache()
sdr.clear_cache()

@patch("requests.post")
def test_get_interest_rates_data(self, mock_post):
Expand Down
Loading