diff --git a/dogpile/cache/util.py b/dogpile/cache/util.py index 7fddaa5..148653f 100644 --- a/dogpile/cache/util.py +++ b/dogpile/cache/util.py @@ -4,7 +4,7 @@ from ..util import langhelpers -def function_key_generator(namespace, fn, to_str=str): +def function_key_generator(namespace, fn, to_str=str, use_qual_name=False): """Return a function that generates a string key, based on a given function as well as arguments to the returned function itself. @@ -16,17 +16,30 @@ def function_key_generator(namespace, fn, to_str=str): the :paramref:`.CacheRegion.function_key_generator` argument for :class:`.CacheRegion`. + :param namespace: A string namespace to be used in the key. + :param fn: The function which will be called. + :param to_str: A function that will convert arguments to strings. + :param use_qual_name: If True, use the qualified name of the function + instead of just the name. This is useful to avoid collision in modules + that defines more than one class containing methods with the same name. + + .. versionadded:: 1.3.4 + .. seealso:: :func:`.kwarg_function_key_generator` - similar function that also takes keyword arguments into account """ + if use_qual_name: + fn_name = fn.__qualname__ + else: + fn_name = fn.__name__ if namespace is None: - namespace = "%s:%s" % (fn.__module__, fn.__name__) + namespace = "%s:%s" % (fn.__module__, fn_name) else: - namespace = "%s:%s|%s" % (fn.__module__, fn.__name__, namespace) + namespace = "%s:%s|%s" % (fn.__module__, fn_name, namespace) args = compat.inspect_getargspec(fn) has_self = args[0] and args[0][0] in ("self", "cls") diff --git a/tests/cache/test_util.py b/tests/cache/test_util.py new file mode 100644 index 0000000..d75065d --- /dev/null +++ b/tests/cache/test_util.py @@ -0,0 +1,69 @@ +from dogpile.cache import util + + +class A: + @classmethod + def class_method(cls): + pass + + @staticmethod + def static_method(): + pass + + def instance_method(self): + pass + + def nested_method(self): + def nested(): + pass + + return nested + + +def test_function_key_generator_qualname(): + key_generator = util.function_key_generator( + None, A.class_method, use_qual_name=True + ) + assert key_generator() == "tests.cache.test_util:A.class_method|" + + key_generator = util.function_key_generator( + None, A.static_method, use_qual_name=True + ) + assert key_generator() == "tests.cache.test_util:A.static_method|" + + key_generator = util.function_key_generator( + None, A.instance_method, use_qual_name=True + ) + assert key_generator() == "tests.cache.test_util:A.instance_method|" + + key_generator = util.function_key_generator( + "namespace", A.class_method, use_qual_name=True + ) + assert key_generator() == "tests.cache.test_util:A.class_method|namespace|" + + nested = A().nested_method() + key_generator = util.function_key_generator( + None, nested, use_qual_name=True + ) + assert ( + key_generator() + == "tests.cache.test_util:A.nested_method..nested|" + ) + + +def test_function_key_generator(): + key_generator = util.function_key_generator(None, A.class_method) + assert key_generator() == "tests.cache.test_util:class_method|" + + key_generator = util.function_key_generator(None, A.static_method) + assert key_generator() == "tests.cache.test_util:static_method|" + + key_generator = util.function_key_generator(None, A.instance_method) + assert key_generator() == "tests.cache.test_util:instance_method|" + + key_generator = util.function_key_generator("namespace", A.class_method) + assert key_generator() == "tests.cache.test_util:class_method|namespace|" + + nested = A().nested_method() + key_generator = util.function_key_generator(None, nested) + assert key_generator() == "tests.cache.test_util:nested|"