borgend/dreamtime.py

changeset 113
6993964140bd
parent 111
c3bc27cf5ece
--- 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

mercurial