
from __future__ import absolute_import
from __future__ import division

import time
import weakref

from vdsm.common.time import monotonic_time


def retry(func, expectedException=Exception, tries=None,
          timeout=None, sleep=1, stopCallback=None):
    if tries in [0, None]:
        tries = -1

    if timeout in [0, None]:
        timeout = -1

    startTime = monotonic_time()

    while True:
        tries -= 1
        try:
            return func()
        except expectedException:
            if tries == 0:
                raise

            if (timeout > 0) and ((monotonic_time() - startTime) >
                                  timeout):
                raise

            if stopCallback is not None and stopCallback():
                raise

            time.sleep(sleep)


class InvalidatedWeakRef(Exception):
    """
    Stale weakref, the object was deallocated
    """


def weakmethod(meth):
    func = meth.__func__
    ref = weakref.ref(meth.__self__)

    def wrapper(*args, **kwargs):
        inst = ref()
        if inst is None:
            raise InvalidatedWeakRef()
        return func(inst, *args, **kwargs)

    return wrapper
