# Path: infi/pyutils/decorators.py
# def wraps(wrapped):
#     """ a convenience function on top of functools.wraps:
# 
#     - adds the original function to the wrapped function as __wrapped__ attribute."""
#     def new_decorator(f):
#         returned = functools.wraps(wrapped)(f)
#         returned.__wrapped__ = wrapped
#         return returned
#     return new_decorator
# 
# Path: infi/pyutils/python_compat.py
# _PYTHON_VERSION = platform.python_version()
# _IS_PYTHON_3 = _PYTHON_VERSION >= '3'
# _IS_BELOW_PYTHON_2_7 = _PYTHON_VERSION < '2.7'
# def get_underlying_function(f):
# def _get_underlying_classmethod_function(f):
#     def create_bound_method(func, self):
#     class TemporaryClass(object):


import itertools
import time
from .decorators import wraps
from .python_compat import iteritems
from logging import getLogger
from types import MethodType, FunctionType
    from inspect import getmembers
        """Decorator that caches a method's return value each time it is called.
        If called later with the same arguments, the cached value is returned, and
        not re-evaluated.
        decorated class must implement inst.init_cache() which creates inst._cache dictionary.
        """
        method_id = next(_cached_method_id_allocator)
        @wraps(func)
        def callee(inst, *args, **kwargs):
            key = _get_instancemethod_cache_entry(method_id, *args, **kwargs)
            func_name = func.__name__
            if key is None:
                logger.debug("Passed arguments to {0} are mutable, so the returned value will not be cached".format(func_name))
                return func(inst, *args, **kwargs)
            try:
                return inst._cache[func_name][key]
            except (KeyError, AttributeError):
                value = func(inst, *args, **kwargs)
                if not hasattr(inst, "_cache"):
                    inst._cache = CacheData()
                if inst._cache.get(func_name, None) is None:
                    #cache class creator returns a dict 
                    inst._cache[func_name] = self.cache_class()
                inst._cache[func_name][key] = value
            return value

        callee.__cached_method__ = True
        callee.__method_id__ = method_id
        return callee

def _get_function_cache_entry(args, kwargs):

# Path: infi/pyutils/decorators.py
# def wraps(wrapped):
#     """ a convenience function on top of functools.wraps:
# 
#     - adds the original function to the wrapped function as __wrapped__ attribute."""
#     def new_decorator(f):
#         returned = functools.wraps(wrapped)(f)
#         returned.__wrapped__ = wrapped
#         return returned
#     return new_decorator
# 
# Path: infi/pyutils/python_compat.py
# _PYTHON_VERSION = platform.python_version()
# _IS_PYTHON_3 = _PYTHON_VERSION >= '3'
# _IS_BELOW_PYTHON_2_7 = _PYTHON_VERSION < '2.7'
# def get_underlying_function(f):
# def _get_underlying_classmethod_function(f):
#     def create_bound_method(func, self):
#     class TemporaryClass(object):


# Path: infi/pyutils/lazy.py
import itertools
import time
from .decorators import wraps
from .python_compat import iteritems
from logging import getLogger
from types import MethodType, FunctionType
    from inspect import getmembers

        """Decorator that caches a method's return value each time it is called.
        If called later with the same arguments, the cached value is returned, and
        not re-evaluated.
        decorated class must implement inst.init_cache() which creates inst._cache dictionary.
        """
        method_id = next(_cached_method_id_allocator)
        @wraps(func)
        def callee(inst, *args, **kwargs):
            key = _get_instancemethod_cache_entry(method_id, *args, **kwargs)
            func_name = func.__name__
            if key is None:
                logger.debug("Passed arguments to {0} are mutable, so the returned value will not be cached".format(func_name))
                return func(inst, *args, **kwargs)
            try:
                return inst._cache[func_name][key]
            except (KeyError, AttributeError):
                value = func(inst, *args, **kwargs)
                if not hasattr(inst, "_cache"):
                    inst._cache = CacheData()
                if inst._cache.get(func_name, None) is None:
                    #cache class creator returns a dict 
                    inst._cache[func_name] = self.cache_class()
                inst._cache[func_name][key] = value
            return value

        callee.__cached_method__ = True
        callee.__method_id__ = method_id
        return callee

def _get_function_cache_entry(args, kwargs):

    return (tuple(args), frozenset(iteritems(kwargs)))