class TimedMessageCache(Transparent):
    def __init__(self,
                 cacheTimeout=1 * 60 * 60,
                 returnCachedValueInCaseOfException=False,
                 backoffTimeout=None,
                 **kwargs):
        Transparent.__init__(self, **kwargs)
        self._cache = TimedDictionary(timeout=cacheTimeout)
        self._returnCachedValueInCaseOfException = returnCachedValueInCaseOfException
        self._backoffTimeout = backoffTimeout
        self._backoffStarted = None

    def clear(self):
        self._cache.clear()

    def setTimeout(self, timeout):
        self._cache.setTimeout(timeout)

    def getSize(self):
        return self._cache.size()

    def any_unknown(self, message, *args, **kwargs):
        found = False
        key = (message, repr(args), repr(kwargs))
        try:
            value = self._cache.peek(key)
        except KeyError:
            pass
        else:
            found = True
            if not self._cache.hasExpired(key):
                return value
        if self._backoffStarted:
            if self._backoffStarted + self._backoffTimeout < now():
                self._backoffStarted = None
            elif found:
                return value
            else:
                raise BackoffException()
        try:
            value = yield self.any.unknown(message, *args, **kwargs)
            self._cache[key] = value
        except (SystemExit, KeyboardInterrupt, AssertionError):
            raise
        except Exception as e:
            if self._backoffTimeout and isinstance(e, TimeoutException):
                self._backoffStarted = now()
                if not (self._returnCachedValueInCaseOfException and found):
                    raise BackoffException()
            if not (self._returnCachedValueInCaseOfException and found):
                raise
        return value
        yield
class TimedMessageCache(Transparent):
    def __init__(self, cacheTimeout=1*60*60, returnCachedValueInCaseOfException=False, backoffTimeout=None, **kwargs):
        Transparent.__init__(self, **kwargs)
        self._cache = TimedDictionary(timeout=cacheTimeout)
        self._returnCachedValueInCaseOfException = returnCachedValueInCaseOfException
        self._backoffTimeout = backoffTimeout
        self._backoffStarted = None

    def clear(self):
        self._cache.clear()

    def setTimeout(self, timeout):
        self._cache.setTimeout(timeout)

    def getSize(self):
        return self._cache.size()

    def any_unknown(self, message, *args, **kwargs):
        found = False
        key = (message, repr(args), repr(kwargs))
        try:
            value = self._cache.peek(key)
        except KeyError:
            pass
        else:
            found = True
            if not self._cache.hasExpired(key):
                raise StopIteration(value)
        if self._backoffStarted:
            if self._backoffStarted + self._backoffTimeout < now():
                self._backoffStarted = None
            elif found:
                raise StopIteration(value)
            else:
                raise BackoffException()
        try:
            value = yield self.any.unknown(message, *args, **kwargs)
            self._cache[key] = value
        except (SystemExit, KeyboardInterrupt, AssertionError):
            raise
        except Exception, e:
            if self._backoffTimeout and isinstance(e, TimeoutException):
                self._backoffStarted = now()
                if not (self._returnCachedValueInCaseOfException and found):
                    raise BackoffException()
            if not (self._returnCachedValueInCaseOfException and found):
                raise
        raise StopIteration(value)
        yield