diff --git a/.travis.yml b/.travis.yml index 82e3474..cdc1f4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ +dist: xenial language: python matrix: include: - python: "3.5" - python: "3.6" - python: "3.7" - dist: xenial - python: pypy3 cache: pip install: diff --git a/src/cache_memoize/__init__.py b/src/cache_memoize/__init__.py index 8199667..1b93015 100644 --- a/src/cache_memoize/__init__.py +++ b/src/cache_memoize/__init__.py @@ -1,9 +1,11 @@ from functools import wraps import hashlib +from urllib.parse import quote + from django.core.cache import caches, DEFAULT_CACHE_ALIAS -from django.utils.encoding import force_text, force_bytes +from django.utils.encoding import force_bytes MARKER = object() @@ -91,8 +93,8 @@ def noop(*args): def decorator(func): def _default_make_cache_key(*args, **kwargs): cache_key = ":".join( - [force_text(x) for x in args_rewrite(*args)] - + [force_text("{}={}".format(k, v)) for k, v in kwargs.items()] + [quote(str(x)) for x in args_rewrite(*args)] + + [quote("{}={}".format(k, v)) for k, v in kwargs.items()] ) return hashlib.md5( force_bytes("cache_memoize" + (prefix or func.__name__) + cache_key) diff --git a/tests/test_cache_memoize.py b/tests/test_cache_memoize.py index 5ef227a..403bce2 100644 --- a/tests/test_cache_memoize.py +++ b/tests/test_cache_memoize.py @@ -2,6 +2,7 @@ import random from threading import Thread, Lock +import pytest from django.core.cache import cache from cache_memoize import cache_memoize @@ -39,6 +40,9 @@ def runmeonce(a, b, k="bla"): runmeonce("A" * 200, "B" * 200, {"C" * 100: "D" * 100}) assert len(calls_made) == 5 + +def test_prefixes(): + calls_made = [] # different prefixes @cache_memoize(10, prefix="first") def foo(value): @@ -51,9 +55,13 @@ def bar(value): return "ho" foo("hey") + assert len(calls_made) == 1 bar("hey") - assert len(calls_made) == 7 + assert len(calls_made) == 2 + +def test_no_store_result(): + calls_made = [] # Test when you don't care about the result @cache_memoize(10, store_result=False, prefix="different") def returnnothing(a, b, k="bla"): @@ -62,7 +70,28 @@ def returnnothing(a, b, k="bla"): returnnothing(1, 2) returnnothing(1, 2) - assert len(calls_made) == 8 + assert len(calls_made) == 1 + + +@pytest.mark.parametrize( + "bits", [("a", "b", "c"), ("ä", "á", "ö"), ("ë".encode(), b"\02", b"i")] +) +def test_colons(bits): + calls_made = [] + + @cache_memoize(10) + def fun(a, b, k="bla"): + calls_made.append((a, b, k)) + return (a, b, k) + + sep = ":" + if isinstance(bits[0], bytes): + sep = sep.encode() + a1, a2 = (sep.join(bits[:2]), bits[2]) + b1, b2 = (bits[0], sep.join(bits[1:])) + fun(a1, a2) + fun(b1, b2) + assert len(calls_made) == 2 def test_cache_memoize_hit_miss_callables():