borgend/dreamtime.py

changeset 113
6993964140bd
parent 111
c3bc27cf5ece
equal deleted inserted replaced
112:173d9d7048b6 113:6993964140bd
55 55
56 def sleeping(self): 56 def sleeping(self):
57 _, sleeping=self.dreamtime_sleeping() 57 _, sleeping=self.dreamtime_sleeping()
58 return sleeping 58 return sleeping
59 59
60 # Python's default arguments are purely idiotic (aka. pythonic): generated
61 # only once. This makes sense in a purely functional language, which Python
62 # lightyears away from, but severely limits their usefulness in an imperative
63 # language. Decorators also seem clumsy for this, as one would have to tell
64 # the number of positional arguments for things to work nice, being able to
65 # pass the snapshot both positionally and as keyword. No luck.
66 # So have to do things the old-fashioned hard way.
67 def ensure_snapshot(snapshot):
68 if not snapshot:
69 return Snapshot()
70 else:
71 return snapshot
72
60 # The main Time class, for time advancing in various paces 73 # The main Time class, for time advancing in various paces
61 class Time: 74 class Time:
62 def __init__(self, when): 75 def __init__(self, when):
63 self._value=when 76 self._value=when
64 77
65 def _monotonic(self, snapshot):
66 raise NotImplementedError
67
68 def _realtime(self, snapshot):
69 raise NotImplementedError
70
71 @staticmethod 78 @staticmethod
72 def _now(snapshot): 79 def _now(snapshot):
73 raise NotImplementedError 80 raise NotImplementedError
74 81
75 @classmethod 82 def remaining(self, snapshot=None):
76 def now(cls): 83 snapshot=ensure_snapshot(snapshot)
77 return cls(cls._now(Snapshot())) 84 return self._value-self._now(snapshot)
78 85
79 @classmethod 86 # Mostly equal to remaining() but can be ∞ (math.inf) for DreamTime.
80 def from_realtime(cls, realtime, snapshot=Snapshot()): 87 def horizon(self, snapshot=None):
88 snapshot=ensure_snapshot(snapshot)
89 return self.remaining(snapshot)
90
91 def realtime(self, snapshot=None):
92 snapshot=ensure_snapshot(snapshot)
93 return snapshot.realtime()+self.remaining(snapshot)
94
95 def monotonic(self, snapshot=None):
96 snapshot=ensure_snapshot(snapshot)
97 return snapshot.monotonic()+self.remaining(snapshot)
98
99 @classmethod
100 def now(cls, snapshot=None):
101 snapshot=ensure_snapshot(snapshot)
102 return cls(cls._now(snapshot))
103
104 @classmethod
105 def from_realtime(cls, realtime, snapshot=None):
106 snapshot=ensure_snapshot(snapshot)
81 return cls(realtime-snapshot.realtime()+cls._now(snapshot)) 107 return cls(realtime-snapshot.realtime()+cls._now(snapshot))
82 108
83 @classmethod 109 @classmethod
84 def from_monotonic(cls, monotonic, snapshot=Snapshot()): 110 def from_monotonic(cls, monotonic, snapshot=None):
111 snapshot=ensure_snapshot(snapshot)
85 return cls(monotonic-snapshot.monotonic()+cls._now(snapshot)) 112 return cls(monotonic-snapshot.monotonic()+cls._now(snapshot))
86 113
87 @classmethod 114 @classmethod
88 def after(cls, seconds, snapshot=Snapshot()): 115 def after(cls, seconds, snapshot=None):
116 snapshot=ensure_snapshot(snapshot)
89 return cls(cls._now(snapshot)+seconds) 117 return cls(cls._now(snapshot)+seconds)
90 118
91 @classmethod 119 @classmethod
92 def after_other(cls, other, seconds, snapshot=Snapshot()): 120 def after_other(cls, other, seconds, snapshot=None):
121 snapshot=ensure_snapshot(snapshot)
93 if isinstance(other, cls): 122 if isinstance(other, cls):
94 return cls(other._value+seconds) 123 return cls(other._value+seconds)
95 else: 124 else:
96 return cls.from_monotonic(other._monotonic(snapshot)+seconds, 125 return cls.from_monotonic(other.monotonic(snapshot)+seconds,
97 snapshot) 126 snapshot)
98 127
99 def datetime(self, snapshot=Snapshot()): 128 def datetime(self, snapshot=None):
100 return datetime.datetime.fromtimestamp(self._realtime(snapshot)) 129 snapshot=ensure_snapshot(snapshot)
101 130 return datetime.datetime.fromtimestamp(self.realtime(snapshot))
102 def seconds_to(self, snapshot=Snapshot()): 131
103 return self._value-self._now(snapshot) 132 def isoformat(self, snapshot=None):
104 133 snapshot=ensure_snapshot(snapshot)
105 def isoformat(self): 134 return self.datetime(snapshot).isoformat()
106 return self.datetime().isoformat()
107
108 def realtime(self, snapshot=Snapshot()):
109 return self._realtime(snapshot)
110
111 def monotonic(self, snapshot=Snapshot()):
112 return self._monotonic(snapshot)
113
114 # Counted from the monotonic epoch, how far is this event? Usually should
115 # equal self.monotonic(), but Dreamtime can be stopped by system sleep,
116 # and will return ∞ (math.inf).
117 def horizon(self, snapshot=Snapshot()):
118 return self._monotonic(snapshot)
119 135
120 def __compare(self, other, fn): 136 def __compare(self, other, fn):
121 if isinstance(other, self.__class__): 137 if isinstance(other, self.__class__):
122 return fn(self._value, other._value) 138 return fn(self._value, other._value)
123 else: 139 else:
124 snapshot=Snapshot() 140 snapshot=Snapshot()
125 return fn(self._monotonic(snapshot), other._monotonic(snapshot)) 141 return fn(self.monotonic(snapshot), other.monotonic(snapshot))
126 142
127 def __lt__(self, other): 143 def __lt__(self, other):
128 return self.__compare(other, lambda x, y: x < y) 144 return self.__compare(other, lambda x, y: x < y)
129 145
130 def __gt__(self, other): 146 def __gt__(self, other):
137 return self.__compare(other, lambda x, y: x >= y) 153 return self.__compare(other, lambda x, y: x >= y)
138 154
139 def __eq__(self, other): 155 def __eq__(self, other):
140 return self.__compare(other, lambda x, y: x == y) 156 return self.__compare(other, lambda x, y: x == y)
141 157
158
142 class RealTime(Time): 159 class RealTime(Time):
143 def _realtime(self, snapshot):
144 return self._value
145
146 def _monotonic(self, snapshot):
147 return self._value+(snapshot.monotonic()-snapshot.realtime())
148
149 @staticmethod 160 @staticmethod
150 def _now(snapshot): 161 def _now(snapshot):
151 return snapshot.realtime() 162 return snapshot.realtime()
152 163
164 def realtime(self, snapshot=None):
165 return self._value
166
167
153 class MonotonicTime(Time): 168 class MonotonicTime(Time):
154 def _realtime(self, snapshot):
155 return self._value+(snapshot.realtime()-snapshot.monotonic())
156
157 def _monotonic(self, snapshot):
158 return self._value
159
160 @staticmethod 169 @staticmethod
161 def _now(snapshot): 170 def _now(snapshot):
162 return snapshot.monotonic() 171 return snapshot.monotonic()
163 172
164 # class Infinity(Time): 173 def monotonic(self, snapshot=None):
165 # def __init__(self): 174 return self._value
166 # super().__init__(math.inf) 175
167 #
168 # def _realtime(self, snapshot):
169 # return math.inf
170 #
171 # def _monotonic(self, snapshot):
172 # return math.inf
173 #
174 # @staticmethod
175 # def _now(snapshot):
176 # return 0
177 176
178 class DreamTime(Time): 177 class DreamTime(Time):
179 def _realtime(self, snapshot): 178 @staticmethod
180 return self._value+(snapshot.realtime()-snapshot.dreamtime()) 179 def _now(snapshot):
181 180 return snapshot.dreamtime()
182 # Important: monotonic is "static" within a wakeup period 181
183 # and does not need to call time.monotonic(), as it gets compared 182 def horizon(self, snapshot=None):
184 # to a specific time.monotonic() realisation 183 snapshot=ensure_snapshot(snapshot)
185 def _monotonic(self, snapshot):
186 return self._value+(snapshot.monotonic()-snapshot.dreamtime())
187
188 def horizon(self, snapshot=Snapshot()):
189 if snapshot.sleeping(): 184 if snapshot.sleeping():
190 return math.inf 185 return math.inf
191 else: 186 else:
192 return self._monotonic(snapshot) 187 return self.remaining(snapshot)
193 188
194 @staticmethod
195 def _now(snapshot):
196 return snapshot.dreamtime()
197 189
198 190
199 if platform.system()=='Darwin': 191 if platform.system()=='Darwin':
200 192
201 import Foundation 193 import Foundation
236 with self.__lock: 228 with self.__lock:
237 self.__sleeptime=time.time() 229 self.__sleeptime=time.time()
238 now_m=time.monotonic() 230 now_m=time.monotonic()
239 self.__dreamtime_last_sleep=(self.__dreamtime_last_sleep+now_m 231 self.__dreamtime_last_sleep=(self.__dreamtime_last_sleep+now_m
240 -self.__monotonic_last_wakeup) 232 -self.__monotonic_last_wakeup)
233 #logger.debug("Sleeping; monotonic time now: %f; dreamtime_last_sleep: %f" % (now_m, self.__dreamtime_last_sleep))
241 callbacks=self.__callbacks.copy() 234 callbacks=self.__callbacks.copy()
242 do_callbacks(callbacks, False) 235 do_callbacks(callbacks, False)
243 236
244 @protect_noreturn 237 @protect_noreturn
245 def handleWakeNotification_(self, aNotification): 238 def handleWakeNotification_(self, aNotification):
247 with self.__lock: 240 with self.__lock:
248 if self.__sleeptime: 241 if self.__sleeptime:
249 self.__monotonic_last_wakeup=time.monotonic() 242 self.__monotonic_last_wakeup=time.monotonic()
250 now=time.time() 243 now=time.time()
251 slept=max(0, now-self.__sleeptime) 244 slept=max(0, now-self.__sleeptime)
252 logger.info("Slept %f seconds" % slept)
253 self.__slept=self.__slept+slept 245 self.__slept=self.__slept+slept
254 self.__sleeptime=None 246 self.__sleeptime=None
247 logger.info("Slept %f seconds", slept)
248 #logger.debug("Slept %f seconds; total: %f; monotonic time now: %f" % (slept, self.__slept, self.__monotonic_last_wakeup))
255 callbacks=self.__callbacks.copy() 249 callbacks=self.__callbacks.copy()
256 do_callbacks(callbacks, True) 250 do_callbacks(callbacks, True)
257 251
258 # Return dreamtime 252 # Return dreamtime
259 def dreamtime_sleeping(self, snapshot=Snapshot()): 253 def dreamtime_sleeping(self, snapshot=Snapshot()):
268 now_dreamtime=self.__dreamtime_last_sleep 262 now_dreamtime=self.__dreamtime_last_sleep
269 else: 263 else:
270 sleeping=False 264 sleeping=False
271 now=snapshot.monotonic() 265 now=snapshot.monotonic()
272 now_dreamtime=(self.__dreamtime_last_sleep 266 now_dreamtime=(self.__dreamtime_last_sleep
273 +now-self.__monotonic_last_wakeup); 267 +now-self.__monotonic_last_wakeup)
268 #logger.debug("Dreamtime request: last_sleep: %f now: %f; last wakeup: %f => now %f dreamtime "
269 # % (self.__dreamtime_last_sleep, now, self.__monotonic_last_wakeup, now_dreamtime))
274 return now_dreamtime, sleeping 270 return now_dreamtime, sleeping
275 271
276 # Weirdo (Py)ObjC naming to stop it form choking up 272 # Weirdo (Py)ObjC naming to stop it form choking up
277 def addForObj_aCallback_(self, obj, callback): 273 def addForObj_aCallback_(self, obj, callback):
278 with self.__lock: 274 with self.__lock:

mercurial