diff -r 173d9d7048b6 -r 6993964140bd borgend/dreamtime.py --- a/borgend/dreamtime.py Tue Feb 06 20:55:53 2018 +0000 +++ b/borgend/dreamtime.py Wed Feb 07 20:39:01 2018 +0000 @@ -57,72 +57,88 @@ _, sleeping=self.dreamtime_sleeping() return sleeping +# Python's default arguments are purely idiotic (aka. pythonic): generated +# only once. This makes sense in a purely functional language, which Python +# lightyears away from, but severely limits their usefulness in an imperative +# language. Decorators also seem clumsy for this, as one would have to tell +# the number of positional arguments for things to work nice, being able to +# pass the snapshot both positionally and as keyword. No luck. +# So have to do things the old-fashioned hard way. +def ensure_snapshot(snapshot): + if not snapshot: + return Snapshot() + else: + return snapshot + # The main Time class, for time advancing in various paces class Time: def __init__(self, when): self._value=when - def _monotonic(self, snapshot): - raise NotImplementedError - - def _realtime(self, snapshot): - raise NotImplementedError - @staticmethod def _now(snapshot): raise NotImplementedError - @classmethod - def now(cls): - return cls(cls._now(Snapshot())) + def remaining(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return self._value-self._now(snapshot) + + # Mostly equal to remaining() but can be ∞ (math.inf) for DreamTime. + def horizon(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return self.remaining(snapshot) + + def realtime(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return snapshot.realtime()+self.remaining(snapshot) + + def monotonic(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return snapshot.monotonic()+self.remaining(snapshot) @classmethod - def from_realtime(cls, realtime, snapshot=Snapshot()): + def now(cls, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return cls(cls._now(snapshot)) + + @classmethod + def from_realtime(cls, realtime, snapshot=None): + snapshot=ensure_snapshot(snapshot) return cls(realtime-snapshot.realtime()+cls._now(snapshot)) @classmethod - def from_monotonic(cls, monotonic, snapshot=Snapshot()): + def from_monotonic(cls, monotonic, snapshot=None): + snapshot=ensure_snapshot(snapshot) return cls(monotonic-snapshot.monotonic()+cls._now(snapshot)) @classmethod - def after(cls, seconds, snapshot=Snapshot()): + def after(cls, seconds, snapshot=None): + snapshot=ensure_snapshot(snapshot) return cls(cls._now(snapshot)+seconds) @classmethod - def after_other(cls, other, seconds, snapshot=Snapshot()): + def after_other(cls, other, seconds, snapshot=None): + snapshot=ensure_snapshot(snapshot) if isinstance(other, cls): return cls(other._value+seconds) else: - return cls.from_monotonic(other._monotonic(snapshot)+seconds, + return cls.from_monotonic(other.monotonic(snapshot)+seconds, snapshot) - def datetime(self, snapshot=Snapshot()): - return datetime.datetime.fromtimestamp(self._realtime(snapshot)) - - def seconds_to(self, snapshot=Snapshot()): - return self._value-self._now(snapshot) - - def isoformat(self): - return self.datetime().isoformat() + def datetime(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return datetime.datetime.fromtimestamp(self.realtime(snapshot)) - def realtime(self, snapshot=Snapshot()): - return self._realtime(snapshot) - - def monotonic(self, snapshot=Snapshot()): - return self._monotonic(snapshot) - - # Counted from the monotonic epoch, how far is this event? Usually should - # equal self.monotonic(), but Dreamtime can be stopped by system sleep, - # and will return ∞ (math.inf). - def horizon(self, snapshot=Snapshot()): - return self._monotonic(snapshot) + def isoformat(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) + return self.datetime(snapshot).isoformat() def __compare(self, other, fn): if isinstance(other, self.__class__): return fn(self._value, other._value) else: snapshot=Snapshot() - return fn(self._monotonic(snapshot), other._monotonic(snapshot)) + return fn(self.monotonic(snapshot), other.monotonic(snapshot)) def __lt__(self, other): return self.__compare(other, lambda x, y: x < y) @@ -139,61 +155,37 @@ def __eq__(self, other): return self.__compare(other, lambda x, y: x == y) + class RealTime(Time): - def _realtime(self, snapshot): - return self._value - - def _monotonic(self, snapshot): - return self._value+(snapshot.monotonic()-snapshot.realtime()) - @staticmethod def _now(snapshot): return snapshot.realtime() -class MonotonicTime(Time): - def _realtime(self, snapshot): - return self._value+(snapshot.realtime()-snapshot.monotonic()) - - def _monotonic(self, snapshot): + def realtime(self, snapshot=None): return self._value + +class MonotonicTime(Time): @staticmethod def _now(snapshot): return snapshot.monotonic() -# class Infinity(Time): -# def __init__(self): -# super().__init__(math.inf) -# -# def _realtime(self, snapshot): -# return math.inf -# -# def _monotonic(self, snapshot): -# return math.inf -# -# @staticmethod -# def _now(snapshot): -# return 0 + def monotonic(self, snapshot=None): + return self._value + class DreamTime(Time): - def _realtime(self, snapshot): - return self._value+(snapshot.realtime()-snapshot.dreamtime()) + @staticmethod + def _now(snapshot): + return snapshot.dreamtime() - # Important: monotonic is "static" within a wakeup period - # and does not need to call time.monotonic(), as it gets compared - # to a specific time.monotonic() realisation - def _monotonic(self, snapshot): - return self._value+(snapshot.monotonic()-snapshot.dreamtime()) - - def horizon(self, snapshot=Snapshot()): + def horizon(self, snapshot=None): + snapshot=ensure_snapshot(snapshot) if snapshot.sleeping(): return math.inf else: - return self._monotonic(snapshot) + return self.remaining(snapshot) - @staticmethod - def _now(snapshot): - return snapshot.dreamtime() if platform.system()=='Darwin': @@ -238,6 +230,7 @@ now_m=time.monotonic() self.__dreamtime_last_sleep=(self.__dreamtime_last_sleep+now_m -self.__monotonic_last_wakeup) + #logger.debug("Sleeping; monotonic time now: %f; dreamtime_last_sleep: %f" % (now_m, self.__dreamtime_last_sleep)) callbacks=self.__callbacks.copy() do_callbacks(callbacks, False) @@ -249,9 +242,10 @@ self.__monotonic_last_wakeup=time.monotonic() now=time.time() slept=max(0, now-self.__sleeptime) - logger.info("Slept %f seconds" % slept) self.__slept=self.__slept+slept self.__sleeptime=None + logger.info("Slept %f seconds", slept) + #logger.debug("Slept %f seconds; total: %f; monotonic time now: %f" % (slept, self.__slept, self.__monotonic_last_wakeup)) callbacks=self.__callbacks.copy() do_callbacks(callbacks, True) @@ -270,7 +264,9 @@ sleeping=False now=snapshot.monotonic() now_dreamtime=(self.__dreamtime_last_sleep - +now-self.__monotonic_last_wakeup); + +now-self.__monotonic_last_wakeup) + #logger.debug("Dreamtime request: last_sleep: %f now: %f; last wakeup: %f => now %f dreamtime " + # % (self.__dreamtime_last_sleep, now, self.__monotonic_last_wakeup, now_dreamtime)) return now_dreamtime, sleeping # Weirdo (Py)ObjC naming to stop it form choking up