diff --git a/google/auth/_cache.py b/google/auth/_cache.py new file mode 100644 index 000000000..f03480230 --- /dev/null +++ b/google/auth/_cache.py @@ -0,0 +1,42 @@ +from collections import OrderedDict + + +class LRUCache(dict): + def __init__(self, maxsize): + super().__init__() + self._order = OrderedDict() + self.maxsize = maxsize + + def clear(self): + super().clear() + self._order.clear() + + def __getitem__(self, key): + value = super().__getitem__(key) + self._update(key) + return value + + def __setitem__(self, key, value): + maxsize = self.maxsize + if maxsize <= 0: + return + if key not in self: + while len(self) >= maxsize: + self.popitem() + super().__setitem__(key, value) + self._update(key) + + def __delitem__(self, key): + super().__delitem__(key) + del self._order[key] + + def popitem(self): + """Remove and return the least recently used key-value pair.""" + key, _ = self._order.popitem(last=False) + return key, super().pop(key) + + def _update(self, key): + try: + self._order.move_to_end(key) + except KeyError: + self._order[key] = None diff --git a/google/auth/jwt.py b/google/auth/jwt.py index 1ebd565d4..3225d9e2e 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -50,8 +50,7 @@ import json import urllib -import cachetools - +from google.auth import _cache from google.auth import _helpers from google.auth import _service_account_info from google.auth import crypt @@ -629,7 +628,7 @@ def __init__( token_lifetime (int): The amount of time in seconds for which the token is valid. Defaults to 1 hour. max_cache_size (int): The maximum number of JWT tokens to keep in - cache. Tokens are cached using :class:`cachetools.LRUCache`. + cache. Tokens are cached using :class:`google.auth._cache.LRUCache`. quota_project_id (Optional[str]): The project ID used for quota and billing. @@ -645,7 +644,7 @@ def __init__( additional_claims = {} self._additional_claims = additional_claims - self._cache = cachetools.LRUCache(maxsize=max_cache_size) + self._cache = _cache.LRUCache(maxsize=max_cache_size) @classmethod def _from_signer_and_info(cls, signer, info, **kwargs): diff --git a/noxfile.py b/noxfile.py index 728e8c7cc..fdd5050e6 100644 --- a/noxfile.py +++ b/noxfile.py @@ -98,7 +98,6 @@ def mypy(session): session.install("-e", ".") session.install( "mypy", - "types-cachetools", "types-certifi", "types-freezegun", "types-pyOpenSSL", diff --git a/setup.py b/setup.py index 20f79ce66..d6cdef0a5 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ DEPENDENCIES = ( - "cachetools>=2.0.0,<7.0", "pyasn1-modules>=0.2.1", # rsa==4.5 is the last version to support 2.7 # https://github.com/sybrenstuvel/python-rsa/issues/152#issuecomment-643470233 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index f3fd641a9..52ad3af91 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -5,7 +5,6 @@ # # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", # Then this file should have foo==1.14.0 -cachetools==2.0.0 pyasn1-modules==0.2.1 setuptools==40.3.0 rsa==3.1.4 diff --git a/tests/test__cache.py b/tests/test__cache.py new file mode 100644 index 000000000..6ac9326fb --- /dev/null +++ b/tests/test__cache.py @@ -0,0 +1,22 @@ +from google.auth._cache import LRUCache + + +def test_lru_cache(): + lru_cache = LRUCache(2) + lru_cache["a"] = 1 + lru_cache["b"] = 2 + assert lru_cache["a"] == 1 + lru_cache["c"] = 3 + assert "b" not in lru_cache + assert lru_cache["a"] == 1 + assert lru_cache["c"] == 3 + lru_cache["d"] = 4 + assert "a" not in lru_cache + assert lru_cache["c"] == 3 + assert lru_cache["d"] == 4 + + +def test_zero_size_lru_cache(): + lru_cache = LRUCache(0) + lru_cache["a"] = 1 + assert "a" not in lru_cache