Skip to content

Commit

Permalink
remove depricated register for decorated functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Dima Kryukov committed Jan 2, 2024
1 parent 0b437a7 commit 5610599
Show file tree
Hide file tree
Showing 14 changed files with 20 additions and 253 deletions.
10 changes: 2 additions & 8 deletions cashews/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from typing import ContextManager

from .cache_condition import NOT_NONE, only_exceptions, with_exceptions
from .commands import Command
from .contrib import * # noqa
from .decorators import CacheDetect, context_cache_detect, fast_condition, thunder_protection
from .exceptions import CacheBackendInteractionError, CircuitBreakerOpen, LockedError, RateLimitError
from .formatter import default_formatter, get_template_and_func_for, get_template_for_key
from .formatter import default_formatter
from .helpers import add_prefix, all_keys_lower, memory_limit
from .key import get_cache_key_template, noself
from .validation import invalidate_further
Expand All @@ -20,7 +18,7 @@
hit = cache.hit
transaction = cache.transaction
setup = cache.setup
cache_detect: ContextManager[CacheDetect] = cache.detect
cache_detect = cache.detect

circuit_breaker = cache.circuit_breaker
dynamic = cache.dynamic
Expand All @@ -29,7 +27,6 @@
locked = cache.locked

invalidate = cache.invalidate
invalidate_func = cache.invalidate_func


mem = Cache(name="mem")
Expand All @@ -56,7 +53,6 @@
"slice_rate_limit",
"locked",
"invalidate",
"invalidate_func",
"NOT_NONE",
"only_exceptions",
"with_exceptions",
Expand All @@ -69,8 +65,6 @@
"LockedError",
"RateLimitError",
"default_formatter",
"get_template_and_func_for",
"get_template_for_key",
"add_prefix",
"all_keys_lower",
"memory_limit",
Expand Down
4 changes: 2 additions & 2 deletions cashews/decorators/cache/defaults.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import random
from contextvars import ContextVar
from typing import Any, Dict
from typing import Any, ContextManager, Dict

from cashews._typing import Key

Expand Down Expand Up @@ -77,4 +77,4 @@ def __exit__(self, exc_type, exc_val, exc_tb):
self._stop()


context_cache_detect = _ContextCacheDetect()
context_cache_detect: ContextManager[CacheDetect] = _ContextCacheDetect()
2 changes: 0 additions & 2 deletions cashews/decorators/cache/early.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from functools import wraps
from typing import TYPE_CHECKING, Callable

from cashews.formatter import register_template
from cashews.key import get_cache_key, get_cache_key_template
from cashews.ttl import ttl_to_seconds

Expand Down Expand Up @@ -51,7 +50,6 @@ def early(

def _decor(func: DecoratedFunc) -> DecoratedFunc:
_key_template = get_cache_key_template(func, key=key, prefix=prefix + ":v2")
register_template(func, _key_template)
for tag in tags:
backend.register_tag(tag, _key_template)

Expand Down
2 changes: 0 additions & 2 deletions cashews/decorators/cache/fail.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import TYPE_CHECKING, Callable

from cashews.backends.interface import _BackendInterface
from cashews.formatter import register_template
from cashews.key import get_cache_key, get_cache_key_template
from cashews.ttl import ttl_to_seconds

Expand Down Expand Up @@ -49,7 +48,6 @@ def failover(

def _decor(func: DecoratedFunc) -> DecoratedFunc:
_key_template = get_cache_key_template(func, key=key, prefix=prefix)
register_template(func, _key_template)

@wraps(func)
async def _wrap(*args, **kwargs):
Expand Down
2 changes: 0 additions & 2 deletions cashews/decorators/cache/hit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from functools import wraps
from typing import TYPE_CHECKING, Callable

from cashews.formatter import register_template
from cashews.key import get_cache_key, get_cache_key_template
from cashews.ttl import ttl_to_seconds

Expand Down Expand Up @@ -43,7 +42,6 @@ def hit(

def _decor(func: DecoratedFunc) -> DecoratedFunc:
_key_template = get_cache_key_template(func, key=key, prefix=prefix)
register_template(func, _key_template)
for tag in tags:
backend.register_tag(tag, _key_template + ":counter")
backend.register_tag(tag, _key_template)
Expand Down
2 changes: 0 additions & 2 deletions cashews/decorators/cache/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from functools import wraps
from typing import TYPE_CHECKING, Callable

from cashews.formatter import register_template
from cashews.key import get_cache_key, get_cache_key_template
from cashews.ttl import ttl_to_seconds

Expand Down Expand Up @@ -40,7 +39,6 @@ def cache(

def _decor(func: DecoratedFunc) -> DecoratedFunc:
_key_template = get_cache_key_template(func, key=key, prefix=prefix)
register_template(func, _key_template)
for tag in tags:
backend.register_tag(tag, _key_template)

Expand Down
2 changes: 0 additions & 2 deletions cashews/decorators/cache/soft.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from functools import wraps
from typing import TYPE_CHECKING, Callable

from cashews.formatter import register_template
from cashews.key import get_cache_key, get_cache_key_template
from cashews.ttl import ttl_to_seconds

Expand Down Expand Up @@ -47,7 +46,6 @@ def soft(

def _decor(func: DecoratedFunc) -> DecoratedFunc:
_key_template = get_cache_key_template(func, key=key, prefix=prefix)
register_template(func, _key_template)
for tag in tags:
backend.register_tag(tag, _key_template)

Expand Down
59 changes: 2 additions & 57 deletions cashews/formatter.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import base64
import json
import re
import warnings
from hashlib import md5, sha1, sha256
from string import Formatter
from typing import Any, Callable, Dict, Iterable, Optional, Pattern, Tuple
from typing import Any, Callable, Dict, Iterable, Pattern, Tuple

from ._typing import Key, KeyOrTemplate, KeyTemplate
from ._typing import KeyOrTemplate, KeyTemplate

TemplateValue = str

Expand Down Expand Up @@ -169,63 +168,9 @@ def _re_default(field_name):


_re_formatter = _ReplaceFormatter(default=_re_default)
_re_legacy_formatter = _ReplaceFormatter(default=_re_legacy)
FuncRegKey = Tuple[str, str]

_REGISTER: Dict[FuncRegKey, Dict[KeyTemplate, Pattern]] = {} # DEPRECATED


def template_to_re_pattern(template: KeyTemplate) -> Pattern:
pattern = _re_formatter.format(template)
return re.compile("^" + pattern + "$", flags=re.MULTILINE)


def _get_func_reg_key(func: Callable[..., Any]) -> FuncRegKey:
return func.__module__ or "", func.__name__


def register_template(func, template: KeyTemplate):
"""Deprecated. Will be removed in 6.0 version"""
func_key = _get_func_reg_key(func)
_REGISTER.setdefault(func_key, {})
if template not in _REGISTER[func_key]:
prefix = "(.*[:])?"
pattern = prefix + template_to_pattern(template, _formatter=_re_legacy_formatter) + "$"
compile_pattern = re.compile(pattern, flags=re.MULTILINE)
_REGISTER[func_key][template] = compile_pattern


def get_templates_for_func(func) -> Iterable[KeyTemplate]:
warnings.warn(
"Part of invalidation by function functionality that will be removed in 6.0. Use 'tags' feature instead",
DeprecationWarning,
)
func_key = _get_func_reg_key(func)
if func_key not in _REGISTER:
return ()
return (template for template in _REGISTER[func_key].keys())


def get_template_and_func_for(key: Key) -> Tuple[Optional[KeyTemplate], Optional[FuncRegKey]]:
warnings.warn(
"Part of invalidation by function functionality that will be removed in 6.0. Use 'tags' feature instead",
DeprecationWarning,
)
for func, templates in _REGISTER.items():
for template, compile_pattern in templates.items():
if compile_pattern.fullmatch(key):
return template_to_pattern(template), func
return None, None


def get_template_for_key(key: Key) -> Tuple[Optional[KeyTemplate], Optional[dict]]:
warnings.warn(
"Part of invalidation by function functionality that will be removed in 6.0. Use 'tags' feature instead",
DeprecationWarning,
)
for _, templates in _REGISTER.items():
for template, compile_pattern in templates.items():
match = compile_pattern.fullmatch(key)
if match:
return template, match.groupdict()
return None, None
25 changes: 5 additions & 20 deletions cashews/validation.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
import asyncio
import warnings
from contextlib import contextmanager
from contextvars import ContextVar
from functools import wraps
from typing import Any, Callable, Dict, Optional, Union
from typing import Any, Dict, Optional

from ._typing import AsyncCallable_T
from .backends.interface import _BackendInterface
from .commands import RETRIEVE_CMDS, Command
from .formatter import get_templates_for_func, template_to_pattern
from .key import get_call_values, get_func_params


async def invalidate_func(backend: _BackendInterface, func, kwargs: Optional[Dict] = None) -> None:
warnings.warn(
"invalidating by function object is deprecated. Use 'tags' feature instead", DeprecationWarning, stacklevel=2
)
values = {**{param: "*" for param in get_func_params(func)}, **kwargs} # type: ignore[dict-item]
for template in get_templates_for_func(func):
del_template = template_to_pattern(template, **values)
await backend.delete_match(del_template)
from .key import get_call_values


def invalidate(
backend: _BackendInterface,
target: Union[str, Callable],
target: str,
args_map: Optional[Dict[str, str]] = None,
defaults: Optional[Dict[str, Any]] = None,
):
Expand All @@ -40,11 +28,8 @@ async def _wrap(*args, **kwargs):
for source, dest in args_map.items():
if dest in _args:
_args[source] = _args.pop(dest)
if callable(target):
asyncio.create_task(invalidate_func(backend, target, _args))
else:
key = target.format(**{k: str(v) if v is not None else "" for k, v in _args.items()})
asyncio.create_task(backend.delete_match(key))
key = target.format(**{k: str(v) if v is not None else "" for k, v in _args.items()})
asyncio.create_task(backend.delete_match(key))
return result

return _wrap
Expand Down
2 changes: 0 additions & 2 deletions cashews/wrapper/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,6 @@ def invalidate(
defaults=defaults,
)

invalidate_func = validation.invalidate_func

def circuit_breaker(
self,
errors_rate: int,
Expand Down
8 changes: 7 additions & 1 deletion examples/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

from cashews import Command, add_prefix, all_keys_lower, cache # noqa: F401

cache.setup("redis://0.0.0.0/2", hash_key="test", digestmod="md5", middlewares=(add_prefix("test:"), all_keys_lower()))
cache.setup(
"redis://0.0.0.0/2",
client_name=None,
hash_key="test",
digestmod="md5",
middlewares=(add_prefix("test:"), all_keys_lower()),
)


async def basic():
Expand Down
56 changes: 1 addition & 55 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

import pytest

from cashews import Cache, decorators, noself
from cashews.formatter import _REGISTER, get_templates_for_func
from cashews import Cache, decorators

pytestmark = pytest.mark.asyncio

Expand Down Expand Up @@ -82,59 +81,6 @@ async def func():
assert mock.call_count == 1


async def test_cache_simple_key(cache: Cache):
_REGISTER.clear()

@cache(ttl=1, key="key:{some}")
async def func1(resp=b"ok", some="err"):
return resp

@cache(ttl=1)
async def func2(resp, some="err"):
return resp

await func1()
await func2("ok")

assert next(get_templates_for_func(func1)) == "key:{some}"
assert next(get_templates_for_func(func2)) == "tests.test_cache:func2:resp:{resp}:some:{some}"


async def test_cache_self_noself_key(cache: Cache):
_REGISTER.clear()

_noself = noself(cache)

class Klass:
@cache(ttl=1)
async def method(self, resp):
return resp

@staticmethod
@cache(ttl=1)
async def stat(resp):
return resp

@_noself(ttl=1)
async def method2(self, resp):
return resp

obj = Klass()
await obj.method("ok")
await obj.stat("ok")
await obj.method2("ok")

assert (
next(get_templates_for_func(obj.method))
== "tests.test_cache:test_cache_self_noself_key.<locals>.Klass.method:self:{self}:resp:{resp}"
)
assert (
next(get_templates_for_func(obj.method2))
== "tests.test_cache:test_cache_self_noself_key.<locals>.Klass.method2:resp:{resp}"
)
assert next(get_templates_for_func(obj.stat)) == "tests.test_cache:stat:resp:{resp}"


async def test_cache_simple_cond(cache: Cache):
mock = Mock()

Expand Down
Loading

0 comments on commit 5610599

Please sign in to comment.