def cache_region(region, *deco_args, **kwargs):
    """Decorate a function such that its return result is cached,
    using a "region" to indicate the cache arguments.

    :param region: Name of the region to cache to
    :type region: string
    :param \*deco_args: Optional ``str()``-compatible arguments which will
      uniquely identify the key used by this decorated function, in addition
      to the positional arguments passed to the function itself at call time.
      This is recommended as it is needed to distinguish between any two
      functions or methods that have the same name (regardless of parent
      class or not).
    :type deco_args: list

    .. note::

        The function being decorated must only be called with
        positional arguments, and the arguments must support
        being stringified with ``str()``.  The concatenation
        of the ``str()`` version of each argument, combined
        with that of the ``*args`` sent to the decorator,
        forms the unique cache key.

    Example::

        from retools.cache import cache_region

        @cache_region('short_term', 'load_things')
        def load(search_term, limit, offset):
            '''Load from a database given a search term, limit, offset.'''
            return database.query(search_term)[offset:offset + limit]

    The decorator can also be used with object methods.  The ``self``
    argument is not part of the cache key.  This is based on the
    actual string name ``self`` being in the first argument
    position::

        class MyThing(object):
            @cache_region('short_term', 'load_things')
            def load(self, search_term, limit, offset):
                '''Load from a database given a search term, limit, offset.'''
                return database.query(search_term)[offset:offset + limit]

    Classmethods work as well - use ``cls`` as the name of the class argument,
    and place the decorator around the function underneath ``@classmethod``::

        class MyThing(object):
            @classmethod
            @cache_region('short_term', 'load_things')
            def load(cls, search_term, limit, offset):
                '''Load from a database given a search term, limit, offset.'''
                return database.query(search_term)[offset:offset + limit]

    .. note::

        When a method on a class is decorated, the ``self`` or ``cls``
        argument in the first position is
        not included in the "key" used for caching.

    """
    def decorate(func):
        namespace = func_namespace(func, deco_args)
        skip_self = has_self_arg(func)
        regenerate = kwargs.get('regenerate', True)

        @wraps(func)
        def cached(*args):
            if region not in CacheRegion.regions:
                raise CacheConfigurationError(
                    'Cache region not configured: %s' % region)
            if not CacheRegion.enabled:
                return func(*args)

            if skip_self:
                try:
                    cache_key = " ".join(map(str, args[1:]))
                except UnicodeEncodeError:
                    cache_key = " ".join(map(unicode, args[1:]))
            else:
                try:
                    cache_key = " ".join(map(str, args))
                except UnicodeEncodeError:
                    cache_key = " ".join(map(unicode, args))

            def go():
                return func(*args)
            return CacheRegion.load(region, namespace, cache_key,
                                    regenerate=regenerate, callable=go)
        cached._region = region
        cached._namespace = namespace
        return cached
    return decorate
dataset/ETHPy150Open bbangert/retools/retools/cache.py/cache_region/original