Mercurial > code > home > repos > light9
annotate src/light9/background_loop.py @ 2405:69ca2b2fc133
overcomplicated attempt at persisting the pane layout in the rdf graph
this was hard because we have to somehow wait for the graph to load before config'ing the panes
author | drewp@bigasterisk.com |
---|---|
date | Fri, 17 May 2024 16:58:26 -0700 |
parents | 6f023afd6c16 |
children |
rev | line source |
---|---|
2397 | 1 # this is a fork of https://bigasterisk.com/code/background_loop/files/tip/ |
2215 | 2 import asyncio |
3 import logging | |
4 import time | |
5 import traceback | |
6 from typing import Any, Awaitable, Callable, Union | |
7 | |
8 from prometheus_client import Gauge, Summary | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
9 from light9.recentfps import RecentFps |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
10 from braillegraph import horizontal_graph |
2215 | 11 |
2397 | 12 log = logging.getLogger() |
2215 | 13 |
14 _created = [] | |
15 | |
16 # todo: some tricky typing | |
17 F_RET = Any | |
18 F_KWARGS = Any | |
19 | |
2397 | 20 UserAsyncFunc = Callable[ |
21 ..., # always called with at least kwarg first_run=bool | |
22 Awaitable[F_RET]] | |
23 UserSyncFunc = Callable[ | |
24 ..., # see above | |
25 F_RET] | |
2215 | 26 UserFunc = Union[UserAsyncFunc, UserSyncFunc] |
27 | |
28 | |
29 class Loop: | |
30 | |
31 def __init__( | |
32 self, | |
33 func: UserFunc, | |
34 sleep_period: float, | |
35 metric_prefix: str, | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
36 extra_sleep_on_error: float = 2, |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
37 log_fps=False, |
2215 | 38 ): |
39 self.func = func | |
40 self.sleep_period = sleep_period | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
41 self.extra_sleep_on_error = extra_sleep_on_error |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
42 self.metric_prefix = metric_prefix |
2215 | 43 |
44 self.up_metric = Gauge(f'{metric_prefix}_up', 'not erroring') | |
45 self.call_metric = Summary(f'{metric_prefix}_calls', 'calls') | |
46 self.lastSuccessRun = 0 | |
47 self.everSucceeded = False | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
48 self.succeeding = False |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
49 self.fps = RecentFps() if log_fps else None |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
50 self.lastFpsLog = 0 |
2215 | 51 |
52 async def call_func(self, first_run: bool, call_kwargs={}) -> F_RET: | |
53 with self.call_metric.time(): | |
54 if asyncio.iscoroutinefunction(self.func): | |
55 ret = await self.func(first_run=first_run, **call_kwargs) | |
56 else: | |
57 ret = self.func(first_run=first_run, **call_kwargs) | |
58 return ret | |
59 | |
60 async def _run(self): | |
61 self.first_run = True | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
62 self.newlyFailing = True |
2215 | 63 while True: |
64 await self._runOne() | |
65 await asyncio.sleep(self.sleep_period) | |
66 | |
67 async def runNow(self, **kwargs: F_KWARGS) -> F_RET: | |
68 """unlike background runs, you get to pass more args and get your func's | |
69 return value. | |
70 """ | |
71 return await self._runOne(call_kwargs=kwargs) | |
72 | |
73 async def _runOne(self, call_kwargs={}): | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
74 now = time.time() |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
75 self._updateFps(now) |
2215 | 76 try: |
77 result = await self.call_func(self.first_run, call_kwargs) | |
78 self.lastSuccessRun = time.time() | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
79 if self.fps: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
80 self.fps.mark() |
2215 | 81 self.up_metric.set(1) |
82 self.everSucceeded = True | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
83 self.newlyFailing = True |
2215 | 84 self.first_run = False |
85 except Exception as ex: | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
86 if self.newlyFailing: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
87 traceback.print_exc() |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
88 self.newlyFailing = False |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
89 else: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
90 log.error(ex) |
2215 | 91 self.up_metric.set(0) |
92 result = None | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
93 self.succeeding = False |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
94 |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
95 await asyncio.sleep(self.extra_sleep_on_error) |
2215 | 96 # todo: something that reveals error ratio |
97 return result | |
98 | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
99 def _updateFps(self, now: float): |
2218 | 100 # not sure i even want this- it's redundant with some metrics code |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
101 if self.fps is None: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
102 return |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
103 if now < self.lastFpsLog + 5: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
104 return |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
105 d = self.fps() |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
106 y_hi = 1 / self.sleep_period |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
107 if not d: |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
108 return |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
109 pts = [int(min(4, y / y_hi * 4)) for y in d['recents']] |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
110 log.info(f'{self.metric_prefix} fps={d["average"]:3.1f} (req={y_hi:3.1f}) {horizontal_graph(pts)}') |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
111 self.lastFpsLog = now |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
112 |
2215 | 113 |
114 def loop_forever( | |
115 func: UserFunc, | |
116 sleep_period: float, | |
117 metric_prefix='background_loop', | |
118 up_metric=None, | |
119 call_metric=None, | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
120 extra_sleep_on_error=2, |
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
121 log_fps=False, |
2215 | 122 ): |
123 """ | |
124 sleep_period is the sleep time after however long func takes to run | |
125 """ | |
126 if up_metric is not None or call_metric is not None: | |
127 raise NotImplementedError('remove old-style metrics') | |
128 | |
2216
acf1b68d031a
fancier background_loop reporting for faders
drewp@bigasterisk.com
parents:
2215
diff
changeset
|
129 loop = Loop(func, sleep_period, metric_prefix, extra_sleep_on_error, log_fps) |
2215 | 130 _created.append(asyncio.create_task(loop._run())) |
131 return loop |