from django.conf import settings
from django.core.cache import cache

class ScopeCacheMiddleware(object):
    """ Middleware which adds scoping to all of the
    cache methods.
    If settings.CACHE_PREFIX is defined, replace
    all django.core.cache.cache methods with new
    methods that automatically prefix the keys
    appropriately.

    Warning: May not work with psyco!
    """
    def process_request(self, request):
        if getattr(settings, 'CACHE_PREFIX', False):
            prefix_cache_object(settings.CACHE_PREFIX, cache)
        return

def prefix_cache_object(cache_prefix, cache_obj):
    """ Prefix the Django cache object (passed in) with the given cache prefix.
    Example usage:
    >>> from django.conf import settings
    >>> from django.core.cache import cache
    >>> from scoped_caching import prefix_cache_object
    >>> settings.CACHE_PREFIX
    'FOO_'
    # Do this once a process (e.g. on import or Middleware)
    >>> prefix_cache_object(settings.CACHE_PREFIX, cache)
    >>> cache.set("pi", 3.14159)
    >>> cache.get("pi")
    3.14159
    >>> cache.get("pi", use_global_namespace=True)
    >>> cache.get("FOO_pi", use_global_namespace=True)
    3.14159
    >>> cache.set("FOO_e", 2.71828, use_global_namespace=True)
    >>> cache.get("e")
    2.71828
    """
    if not cache_prefix:
        return

    cache_class = cache_obj.__class__

    if getattr(cache_class, '_PREFIXED', False):
        # If we're already prefixed, return.
        return

    def prefix_key(method):
        def _new_func(self, key, *args, **kwargs):
            use_global = kwargs.get('use_global_namespace', False)
            if 'use_global_namespace' in kwargs:
                del kwargs['use_global_namespace']
            if use_global:
                return method(self, key, *args, **kwargs)
            return method(self, cache_prefix + key,
                          *args, **kwargs)

        _new_func.__doc__ = method.__doc__
        _new_func.__name__ = method.__name__
        return _new_func

    def prefix_many(method):
        def _new_func(self, keys, *args, **kwargs):
            use_global = kwargs.get('use_global_namespace', False)
            if 'use_global_namespace' in kwargs:
                del kwargs['use_global_namespace']
            if use_global:
                return method(self, keys, *args, **kwargs)
            keys = [cache_prefix + key for key in keys]
            return method(self, keys, *args, **kwargs)

        _new_func.__doc__ = method.__doc__
        _new_func.__name__ = method.__name__
        return _new_func

    cache_class._PREFIXED = True
    cache_class.get = prefix_key(cache_class.get)
    cache_class.set = prefix_key(cache_class.set)
    cache_class.delete = prefix_key(cache_class.delete)
    cache_class.has_key = prefix_key(cache_class.has_key)
    cache_class.get_many = prefix_many(cache_class.get_many)