# 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
            value = inst._cache[self.__name__]
        except (KeyError, AttributeError):
            value = self.fget(inst)
            try:
                cache = inst._cache
            except AttributeError:
                cache = inst._cache = {}
            cache[self.__name__] = value
        return value

_cached_method_id_allocator = itertools.count()

def _get_instancemethod_cache_entry(method_id, *args, **kwargs):
    if len(args) + len(kwargs) == 0:
        return method_id
    try:
        kwargs_keys = list(kwargs.keys())
        kwargs_keys.sort()
        key = (method_id,) + args + tuple([kwargs[key] for key in kwargs_keys])
        _ = {key: None}
        return key
    except TypeError:
        return None

def cached_method(func):
    """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.
    """
    method_id = next(_cached_method_id_allocator)

# 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

            value = inst._cache[self.__name__]
        except (KeyError, AttributeError):
            value = self.fget(inst)
            try:
                cache = inst._cache
            except AttributeError:
                cache = inst._cache = {}
            cache[self.__name__] = value
        return value

_cached_method_id_allocator = itertools.count()

def _get_instancemethod_cache_entry(method_id, *args, **kwargs):
    if len(args) + len(kwargs) == 0:
        return method_id
    try:
        kwargs_keys = list(kwargs.keys())
        kwargs_keys.sort()
        key = (method_id,) + args + tuple([kwargs[key] for key in kwargs_keys])
        _ = {key: None}
        return key
    except TypeError:
        return None

def cached_method(func):
    """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.
    """
    method_id = next(_cached_method_id_allocator)

    @wraps(func)