Skip to content

Commit

Permalink
Merge pull request #107 from python-astrodynamics/feature/improve-cache
Browse files Browse the repository at this point in the history
Make cache path configurable and include URL in cache key
  • Loading branch information
RazerM authored Feb 24, 2025
2 parents b91aa5a + ccdd72b commit 71786ee
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/spacetrack/aio.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(
rush_key_prefix="",
httpx_client=None,
additional_rate_limit=None,
cache_path=None,
):
if httpx_client is None:
httpx_client = httpx.AsyncClient()
Expand All @@ -61,6 +62,7 @@ def __init__(
rush_key_prefix=rush_key_prefix,
httpx_client=httpx_client,
additional_rate_limit=additional_rate_limit,
cache_path=cache_path,
)

def _setup_finalizer(self):
Expand Down
21 changes: 14 additions & 7 deletions src/spacetrack/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import hashlib
import json
import re
import sys
Expand All @@ -18,7 +19,7 @@
import outcome
from filelock import FileLock
from logbook import Logger
from platformdirs import PlatformDirs
from platformdirs import user_cache_path
from represent import ReprHelper, ReprHelperMixin
from rush.limiters.periodic import PeriodicLimiter
from rush.quota import Quota
Expand Down Expand Up @@ -179,6 +180,7 @@ class SpaceTrackClient:
for a proxy).
additional_rate_limit: Optionally, a :class:`rush.quota.Quota` if you
want to restrict the rate limit further than the defaults.
cache_path: Optionally, which directory to use for the cache.
For more information, refer to the `Space-Track documentation`_.
Expand Down Expand Up @@ -318,6 +320,7 @@ def __init__(
rush_key_prefix="",
httpx_client=None,
additional_rate_limit=None,
cache_path=None,
):
if httpx_client is None:
httpx_client = httpx.Client()
Expand Down Expand Up @@ -363,7 +366,10 @@ def __init__(
else:
raise TypeError("additional_rate_limit must be a rush.quota.Quota")

self._dirs = PlatformDirs("spacetrack")
if cache_path is None:
self._cache_path = user_cache_path("spacetrack")
else:
self._cache_path = Path(cache_path)

self._setup_finalizer()

Expand Down Expand Up @@ -850,16 +856,17 @@ def _get_predicates_generator(self, class_, controller, *, force=False):
key = f"{controller}.{class_}"

if key not in self._predicates or (force and self._predicates[key] is None):
cache_path = self._dirs.user_cache_path

cache_file = cache_path / f"predicates-{key}.json"

hasher = hashlib.sha256()
hasher.update(self.base_url.encode())
hasher.update(key.encode())
hashkey = hasher.hexdigest()[:16]
cache_file = self._cache_path / f"predicates-{hashkey}.json"
predicates_data = self._read_cache_file(
cache_file, PREDICATE_CACHE_EXPIRY_TIME
)

if predicates_data is None:
cache_path.mkdir(parents=True, exist_ok=True)
self._cache_path.mkdir(parents=True, exist_ok=True)

lock_file = cache_file.with_name(cache_file.name + ".lock")
lock = self._file_lock_cls(lock_file)
Expand Down
11 changes: 3 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,10 @@ def respx_mock(respx_router):

@pytest.fixture(autouse=True)
def temporary_cache_dir(monkeypatch, tmp_path):
class MockPlatformDirs:
def __init__(self, appname):
pass
def user_cache_path(appname):
return tmp_path

@property
def user_cache_path(self):
return tmp_path

with patch("spacetrack.base.PlatformDirs", MockPlatformDirs):
with patch("spacetrack.base.user_cache_path", user_cache_path):
yield


Expand Down
18 changes: 12 additions & 6 deletions tests/test_aio.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,10 @@ async def test_modeldef_cache(respx_mock, mock_auth, cache_file_mangler):
assert await client.gp(norad_cat_id=25541) == "dummy"
assert modeldef_route.call_count == 1

cache_path = client._dirs.user_cache_path
cache_files = list(cache_path.glob("*.json"))
assert [str(p.relative_to(cache_path)) for p in cache_files] == [
"predicates-basicspacedata.gp.json"
]
cache_files = list(client._cache_path.glob("*.json"))
assert len(cache_files) == 1
assert cache_files[0].name.startswith("predicates-")
assert cache_files[0].name.endswith(".json")

for file in cache_files:
cache_file_mangler(file)
Expand Down Expand Up @@ -234,6 +233,13 @@ async def test_modeldef_not_used_trio(respx_mock, mock_auth):
assert await client.gp(norad_cat_id=25541) == "dummy"
assert modeldef_route.call_count == 1

cache_path = client._dirs.user_cache_path
cache_path = client._cache_path
cache_files = list(cache_path.glob("*.json"))
assert cache_files == []


async def test_custom_cache_path(async_runner, respx_mock, tmp_path):
async with AsyncSpaceTrackClient(
"identity", "password", cache_path=tmp_path
) as client:
assert client._cache_path == tmp_path
14 changes: 9 additions & 5 deletions tests/test_spacetrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,10 @@ def test_modeldef_cache(respx_mock, mock_auth, cache_file_mangler):
assert client.gp(norad_cat_id=25541) == "dummy"
assert modeldef_route.call_count == 1

cache_path = client._dirs.user_cache_path
cache_files = list(cache_path.glob("*.json"))
assert [str(p.relative_to(cache_path)) for p in cache_files] == [
"predicates-basicspacedata.gp.json"
]
cache_files = list(client._cache_path.glob("*.json"))
assert len(cache_files) == 1
assert cache_files[0].name.startswith("predicates-")
assert cache_files[0].name.endswith(".json")

for file in cache_files:
cache_file_mangler(file)
Expand All @@ -614,3 +613,8 @@ def test_modeldef_cache(respx_mock, mock_auth, cache_file_mangler):
def test_implicit_cleanup_warning():
with pytest.warns(ResourceWarning, match="without being closed explicitly"):
SpaceTrackClient("identity", "password")


def test_custom_cache_path(respx_mock, tmp_path):
with SpaceTrackClient("identity", "password", cache_path=tmp_path) as client:
assert client._cache_path == tmp_path

0 comments on commit 71786ee

Please sign in to comment.