diff --git a/CHANGELOG.md b/CHANGELOG.md index f093e30..8ef9f73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Dependencies + +- Bump `cachetools` from 6.1.0 to 7.2.0 (requires `>=7.1.7`). Runtime + use remains `TTLCache` only; typing updated for cachetools 7 stubs + (#119). + ## [4.3.2] - 2026-09-26 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index 972b288..71d4099 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dependencies = [ "pyyaml>=6.0.2", "orjson>=3.11.6", "lz4>=4.4.4", - "cachetools>=6.1.0", + "cachetools>=7.1.7", "deprecated>=1.2.18", ] diff --git a/src/project_x_py/client/cache.py b/src/project_x_py/client/cache.py index b51946f..cf517ca 100644 --- a/src/project_x_py/client/cache.py +++ b/src/project_x_py/client/cache.py @@ -19,7 +19,7 @@ import gc import io import logging -from typing import TYPE_CHECKING, Any, cast +from typing import Any import lz4.frame import polars as pl @@ -27,9 +27,6 @@ from project_x_py.models import Instrument -if TYPE_CHECKING: - pass - logger = logging.getLogger(__name__) @@ -84,8 +81,8 @@ def cache_ttl(self, value: float) -> None: """ self._cache_ttl = value # Recreate caches with new TTL - self._opt_instrument_cache = TTLCache(maxsize=1000, ttl=value) - self._opt_market_data_cache = TTLCache(maxsize=10000, ttl=value) + self._opt_instrument_cache = TTLCache[str, Instrument](maxsize=1000, ttl=value) + self._opt_market_data_cache = TTLCache[str, bytes](maxsize=10000, ttl=value) def _serialize_dataframe(self, df: pl.DataFrame) -> bytes: """ @@ -174,7 +171,7 @@ def get_cached_instrument(self, symbol: str) -> Instrument | None: Cached instrument or None if not found or expired. """ cache_key = symbol.upper() - instrument = cast(Instrument | None, self._opt_instrument_cache.get(cache_key)) + instrument = self._opt_instrument_cache.get(cache_key) if instrument: self.cache_hit_count += 1 return instrument diff --git a/src/project_x_py/order_manager/tracking.py b/src/project_x_py/order_manager/tracking.py index f79b249..12dda39 100644 --- a/src/project_x_py/order_manager/tracking.py +++ b/src/project_x_py/order_manager/tracking.py @@ -1364,18 +1364,20 @@ async def configure_memory_limits( old_tracked = dict(self.tracked_orders) old_status = dict(self.order_status_cache) - self.tracked_orders = TTLCache( + self.tracked_orders = TTLCache[str, dict[str, Any]]( maxsize=max_tracked_orders, ttl=self._order_ttl_seconds ) - self.order_status_cache = TTLCache( + self.order_status_cache = TTLCache[str, int]( maxsize=max_tracked_orders, ttl=self._order_ttl_seconds ) # Restore data up to new limit - for key, value in list(old_tracked.items())[-max_tracked_orders:]: - self.tracked_orders[key] = value - for key, value in list(old_status.items())[-max_tracked_orders:]: - self.order_status_cache[key] = value + for order_id, order_data in list(old_tracked.items())[ + -max_tracked_orders: + ]: + self.tracked_orders[order_id] = order_data + for order_id, status in list(old_status.items())[-max_tracked_orders:]: + self.order_status_cache[order_id] = status if order_ttl_seconds is not None: self._order_ttl_seconds = order_ttl_seconds diff --git a/tests/order_manager/test_order_tracking.py b/tests/order_manager/test_order_tracking.py index c0fe5ef..fbd503d 100644 --- a/tests/order_manager/test_order_tracking.py +++ b/tests/order_manager/test_order_tracking.py @@ -717,8 +717,17 @@ async def test_configure_memory_limits(self, mock_order_manager): assert om._order_ttl_seconds == 1800 assert om._cleanup_interval == 150 - # Should have kept most recent 3 orders + # Should have kept most recent 3 orders with typed cache values intact + assert isinstance(om.tracked_orders, TTLCache) + assert isinstance(om.order_status_cache, TTLCache) + assert om.tracked_orders.maxsize == 3 + assert om.order_status_cache.maxsize == 3 assert len(om.tracked_orders) <= 3 + assert len(om.order_status_cache) <= 3 + assert all( + isinstance(order_data, dict) for order_data in om.tracked_orders.values() + ) + assert all(isinstance(status, int) for status in om.order_status_cache.values()) def test_clear_order_tracking(self, mock_order_manager): """Test clearing all tracking data.""" diff --git a/uv.lock b/uv.lock index 753ea5c..bda7d79 100644 --- a/uv.lock +++ b/uv.lock @@ -435,11 +435,11 @@ filecache = [ [[package]] name = "cachetools" -version = "6.1.0" +version = "7.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/89/817ad5d0411f136c484d535952aef74af9b25e0d99e90cdffbe121e6d628/cachetools-6.1.0.tar.gz", hash = "sha256:b4c4f404392848db3ce7aac34950d17be4d864da4b8b66911008e430bc544587", size = 30714, upload-time = "2025-06-16T18:51:03.07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/2c/3f18755527b03ca9ff6be724bd5370cb777c76a87f17301377cf04a4729b/cachetools-7.2.0.tar.gz", hash = "sha256:bcac1a1b8da6909994a2957238a57b8140dab7c5c5c69a43669654fe87a33c1d", size = 41129, upload-time = "2026-09-16T20:48:27.209Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/f0/2ef431fe4141f5e334759d73e81120492b23b2824336883a91ac04ba710b/cachetools-6.1.0-py3-none-any.whl", hash = "sha256:1c7bb3cf9193deaf3508b7c5f2a79986c13ea38965c5adcff1f84519cf39163e", size = 11189, upload-time = "2025-06-16T18:51:01.514Z" }, + { url = "https://files.pythonhosted.org/packages/cc/bb/1c6e7a89b11da19f137e4ef5a5c5fe47a5bb686449ba343944ccf8fb34b2/cachetools-7.2.0-py3-none-any.whl", hash = "sha256:3045213f186b89fdd95d94441354c4bd87c570b7a38d8c3fd1d9dd37f6dc90d8", size = 16918, upload-time = "2026-09-16T20:48:25.575Z" }, ] [[package]] @@ -2854,7 +2854,7 @@ requires-dist = [ { name = "aioresponses", marker = "extra == 'test'", specifier = ">=0.7.6" }, { name = "black", marker = "extra == 'all'", specifier = ">=26.3.1" }, { name = "black", marker = "extra == 'dev'", specifier = ">=26.3.1" }, - { name = "cachetools", specifier = ">=6.1.0" }, + { name = "cachetools", specifier = ">=7.1.7" }, { name = "deprecated", specifier = ">=1.2.18" }, { name = "httpx", extras = ["http2"], specifier = ">=0.27.0" }, { name = "isort", marker = "extra == 'all'", specifier = ">=5.12.0" },