From 38914aaef0c02bbe9baf3d299eaed11b5cf1bb0d Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 17 Jan 2023 17:39:45 -0800 Subject: [PATCH] Make caching of statuses optional and configurable. --- CHANGELOG.md | 3 +++ gosundpy/gosund.py | 12 +++++++++--- gosundpy/utils.py | 30 ++++++++---------------------- tests/unit/conftest.py | 9 +++++++-- tests/unit/gosund_test.py | 10 ++++++++++ 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d56638..a7cdb9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # CHANGELOG ## Unreleased +### Bug Fixes ++ Make caching of `Gosund.get_device_statuses` optional via opt in and + configurable ## 0.6.1 ### Bug Fixes diff --git a/gosundpy/gosund.py b/gosundpy/gosund.py index 968aed1..a491629 100644 --- a/gosundpy/gosund.py +++ b/gosundpy/gosund.py @@ -7,7 +7,7 @@ class Gosund(object): def __init__(self, username, password, access_id, access_key, country=1, - endpoint='https://openapi.tuyaus.com'): + endpoint='https://openapi.tuyaus.com', status_cache_seconds=None): self.api = TuyaOpenAPI(endpoint, access_id, access_key, auth_type=AuthType.CUSTOM) resp = self.api.connect(username, password, country) @@ -15,6 +15,12 @@ def __init__(self, username, password, access_id, access_key, country=1, self.manager = TuyaDeviceManager(self.api, TuyaOpenMQ(self.api)) self._known_devices = {} + self._status_caching = False + if status_cache_seconds is not None: + self.get_device_statuses = cache_response( + seconds=status_cache_seconds)(self.get_device_statuses) + self._status_caching = True + def get_device(self, device_id): resp = self.manager.get_device_functions(device_id) assert_response_success('get device', resp) @@ -28,7 +34,6 @@ def get_device_status(self, device_id): f'unable to find status for device with id "{device_id}"') return status - @cache_response(seconds=60) def get_device_statuses(self): # limit 20 device_ids per api call resp = self.manager.get_device_list_status(self._known_devices) @@ -44,7 +49,8 @@ def _remove_known_device(self, device_id): self._clear_statuses_cache() def _clear_statuses_cache(self): - self.get_device_statuses.clear_cache() + if self._status_caching: + self.get_device_statuses.clear_cache() def send_commands(self, device_id, commands): resp = self.manager.send_commands(device_id, commands) diff --git a/gosundpy/utils.py b/gosundpy/utils.py index 8efbd13..5933d35 100644 --- a/gosundpy/utils.py +++ b/gosundpy/utils.py @@ -16,29 +16,15 @@ def reset(self): def cache_response(hours=0, minutes=0, seconds=0): seconds += 60 * 60 * hours + 60 * minutes - - class _cache_response(object): - - def __init__(self, fn): - # wraps class instance methods only - self.fn = fn - self.cache_key = f'_{fn.__name__}_cache' - - def call(self, instance, *args, **kwargs): + def _rate_limit(fn): + @functools.wraps(fn) + def _call(*args, **kwargs): now = time.time() - cache = getattr(instance, self.cache_key) if now - cache.last_call < seconds: return cache.value - cache.set(now, self.fn(instance, *args, **kwargs)) + cache.set(now, fn(*args, **kwargs)) return cache.value - - def __get__(self, instance, owner): - @functools.wraps(self.fn) - def _call(*args, **kwargs): - return self.call(instance, *args, **kwargs) - if not hasattr(instance, self.cache_key): - setattr(instance, self.cache_key, _timed_cache()) - _call.clear_cache = getattr(instance, self.cache_key).reset - return _call - - return _cache_response + cache = _timed_cache() + _call.clear_cache = cache.reset + return _call + return _rate_limit diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 7547b12..fcf261e 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -14,8 +14,12 @@ def gosund(): def gosund2(): return _gosund() +@pytest.fixture +def gosund_non_caching(): + return _gosund(caching_secs=None) + @responses.activate -def _gosund(): +def _gosund(caching_secs=60): users_login_uri = f'{BASEURL}/users/login' responses.add( responses.POST, @@ -23,7 +27,8 @@ def _gosund(): json={'success': True}, status=200, ) - return Gosund('username', 'password', 'access_id', 'access_key') + return Gosund('username', 'password', 'access_id', 'access_key', + status_cache_seconds=caching_secs) def patch_get_device(device_id, category): responses.add( diff --git a/tests/unit/gosund_test.py b/tests/unit/gosund_test.py index ca5b4ef..33443d7 100644 --- a/tests/unit/gosund_test.py +++ b/tests/unit/gosund_test.py @@ -174,3 +174,13 @@ def test_gosund_get_device_statuses_cache_per_instance(gosund, gosund2): resp2 = gosund2.get_device_statuses() assert resp2 == {} assert id(resp1) != id(resp2) + +@responses.activate +def test_gosund_get_device_statuses_non_caching(gosund_non_caching): + _patch_testing_requests() + + gosund_non_caching.get_device(_test_device_id_1) + resp1 = gosund_non_caching.get_device_statuses() + resp2 = gosund_non_caching.get_device_statuses() + assert resp1 == {_test_device_id_1: _test_status_1} + assert id(resp1) != id(resp2)