pyobs-core 2.0.0.dev45__py3-none-any.whl → 2.0.0.dev47__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1018,7 +1018,7 @@ class XmppComm(Comm):
1018
1018
  attempt += 1
1019
1019
  if attempt == 3:
1020
1020
  log.warning(
1021
- "Still failing to get capabilities for %s from %s after %d attempts (%s), "
1021
+ "Still failing to get capabilities for %s from %s after %d attempts (%r), "
1022
1022
  "will keep retrying",
1023
1023
  interface.__name__,
1024
1024
  module,
pyobs/modules/module.py CHANGED
@@ -126,6 +126,12 @@ class Module(Object, IModule, IConfig):
126
126
  """
127
127
  Object.__init__(self, **kwargs)
128
128
 
129
+ # detect the event loop itself stalling (e.g. a blocking call somewhere in this
130
+ # module or one of its background tasks) directly in this module's own log, instead
131
+ # of only showing up indirectly as this module failing to answer other modules'
132
+ # requests in time
133
+ self.add_background_task(self._watch_event_loop_lag)
134
+
129
135
  # get list of client interfaces
130
136
  self._interfaces: list[type[Interface]] = []
131
137
  self._methods: dict[str, tuple[Callable[..., Any], inspect.Signature, dict[Any, Any]]] = {}
@@ -170,6 +176,38 @@ class Module(Object, IModule, IConfig):
170
176
  self._remote_exception_log: dict[tuple[type[exc.PyobsError], str], list[exc.LoggedException]] = {}
171
177
  self._exception_handlers: list[exc.ExceptionHandler] = []
172
178
 
179
+ # how often to check for event loop lag, and how much lag counts as worth a warning
180
+ _LOOP_LAG_CHECK_INTERVAL = 1.0
181
+ _LOOP_LAG_WARN_THRESHOLD = 0.5
182
+
183
+ async def _watch_event_loop_lag(self) -> None:
184
+ """Detects the event loop stalling by timing its own wakeups against how long it
185
+ actually asked to sleep for. Works even though it runs on the loop it's measuring:
186
+ if something else blocks the loop synchronously, this task's own wakeup is delayed
187
+ right along with everything else, and the resulting gap shows up here retroactively.
188
+
189
+ Logs once when a stall starts and once when it clears, rather than on every check
190
+ while it persists -- a single long block only ever produces one check anyway (this
191
+ task can't run until the block clears either), but a sustained-yet-fluctuating
192
+ overload (still yielding briefly, but staying over threshold for many checks in a
193
+ row) would otherwise log once per check for as long as it lasts.
194
+ """
195
+ loop = asyncio.get_running_loop()
196
+ last = loop.time()
197
+ stalled_since: float | None = None
198
+ while True:
199
+ await asyncio.sleep(self._LOOP_LAG_CHECK_INTERVAL)
200
+ now = loop.time()
201
+ lag = now - last - self._LOOP_LAG_CHECK_INTERVAL
202
+ if lag > self._LOOP_LAG_WARN_THRESHOLD:
203
+ if stalled_since is None:
204
+ stalled_since = last
205
+ log.warning("Event loop stalled for %.2fs -- some call is blocking it.", lag)
206
+ elif stalled_since is not None:
207
+ log.warning("Event loop recovered after being stalled for %.2fs total.", now - stalled_since)
208
+ stalled_since = None
209
+ last = now
210
+
173
211
  # exception types that always need local attention, regardless of _disable_exception_logging:
174
212
  # ModuleError means the module itself is broken; UnclassifiedError means something escaped
175
213
  # un-typed, either locally or across an RPC boundary. Neither is part of the deliberate
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev45
3
+ Version: 2.0.0.dev47
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -22,7 +22,7 @@ pyobs/comm/xmpp/__init__.py,sha256=U8A_ZAdKS3qH-SjdzZ28tEBzCrnnpAL3qrfoEl92Q_E,5
22
22
  pyobs/comm/xmpp/rpc.py,sha256=gUenqPK3FedaJCFOxc-uJ_bqokmkyXkoQ2wk2mkZrk8,14917
23
23
  pyobs/comm/xmpp/serializer.py,sha256=HFCPuzWIp3lnnJH_8WHiagzVUQaXyJEhKw6wZ1uPnw0,18999
24
24
  pyobs/comm/xmpp/xmppclient.py,sha256=d-5NqQ2ZXBFiyLbqdaJHrXVNrS4Z_19Zrnnjtsd_Fcs,7940
25
- pyobs/comm/xmpp/xmppcomm.py,sha256=qUNw370UvIBLd06PV0zxATIbkyl1IitO69FaQt5V7_w,48707
25
+ pyobs/comm/xmpp/xmppcomm.py,sha256=41aB4VHlAs_HwLFsY2Dx9sXQzHKrtKqOUXOusONNMQE,48707
26
26
  pyobs/comm/xmpp/xep_0009/__init__.py,sha256=YbgcfeEcRH-M3HQ0IyHEPhPLHIj8xcWYKHSRYIIuX8k,103
27
27
  pyobs/comm/xmpp/xep_0009/binding.py,sha256=bbBwpc68e9hkt9hwU8AE_OHpt-Rpakl6JvNAg2xFHZM,5995
28
28
  pyobs/comm/xmpp/xep_0009/rpc.py,sha256=_Xzzghl4yDsdVwjL3QjmgzaiqAYhQN0y9R4vlYnTinw,2067
@@ -199,7 +199,7 @@ pyobs/mixins/pipeline.py,sha256=2QIe7ihEX2iD07U-6TJ9NoFhEKJflqM_OA5uVyQPPTY,1437
199
199
  pyobs/mixins/waitformotion.py,sha256=PFm51CqPjn23_SZ3Mgi5RPfvVBsbPaP5YF0J8G8PWSo,3002
200
200
  pyobs/mixins/weatheraware.py,sha256=CEdDCCGwFx5OSbjlS5yS6Yvloujeg8gporpJyr7o8xk,5745
201
201
  pyobs/modules/__init__.py,sha256=3jIJUmO1Vc7PD3rMCzhtYNanOME7f-2z-mot5f8ugwY,2447
202
- pyobs/modules/module.py,sha256=UVPfLAroom_mcnJ_ESWXA7emRLQf2ZIWdvn0bWJmSyc,41492
202
+ pyobs/modules/module.py,sha256=sH1f31z29qtU2yvN9GcxgmyOja9ImTp7EGFkHItDcnA,43601
203
203
  pyobs/modules/camera/__init__.py,sha256=ZlkLRWssKCPi6jIFEw275jXcpv9ORjVCjsT8Ag3fK2E,421
204
204
  pyobs/modules/camera/adaptive.py,sha256=o06lkJSNnLPT19Abql0npi0RtIARKgn_CROwr7556qU,14013
205
205
  pyobs/modules/camera/basecamera.py,sha256=NaE-KP5wkr8_YmsVQ5T8mxbPS84sG0jb7kjxIr3wkWs,21267
@@ -439,8 +439,8 @@ pyobs/vfs/vfs.py,sha256=buHTmO6VUjSypkEMVxY8rDKrpMz65AY2OE2hGEsQsLI,10780
439
439
  pyobs/vfs/filelists/__init__.py,sha256=NaTGFjLGh043j_F7kWEx_cXaII7t3CbYguRk2g5hEqw,111
440
440
  pyobs/vfs/filelists/filelist.py,sha256=kCco_5G2gLH-nxWik-wcs6lGuc_-3LeeyR9L4_srf30,201
441
441
  pyobs/vfs/filelists/testing.py,sha256=M8f2BULdjRCc7JFOKhAH7Kwre_uVQC0xLcv9xGTfNFw,425
442
- pyobs_core-2.0.0.dev45.dist-info/METADATA,sha256=1XyRb2dUXGaVR74fnemz1Syc0eRl4tJoIuyxaAKnK_A,1990
443
- pyobs_core-2.0.0.dev45.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
444
- pyobs_core-2.0.0.dev45.dist-info/entry_points.txt,sha256=0ceIn3dTDwWdGdJ5VdTvYim7vKkdTe6aqqpoaG065UY,109
445
- pyobs_core-2.0.0.dev45.dist-info/licenses/LICENSE,sha256=gC8djBliCENDmuRvzCoejpWbsn8eqKx61Hc8bmDZ2LI,1099
446
- pyobs_core-2.0.0.dev45.dist-info/RECORD,,
442
+ pyobs_core-2.0.0.dev47.dist-info/METADATA,sha256=NXVszl4w8Vf_u-jFr0ahuhk-3iEUG-hVZqF3HQUQT2s,1990
443
+ pyobs_core-2.0.0.dev47.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
444
+ pyobs_core-2.0.0.dev47.dist-info/entry_points.txt,sha256=0ceIn3dTDwWdGdJ5VdTvYim7vKkdTe6aqqpoaG065UY,109
445
+ pyobs_core-2.0.0.dev47.dist-info/licenses/LICENSE,sha256=gC8djBliCENDmuRvzCoejpWbsn8eqKx61Hc8bmDZ2LI,1099
446
+ pyobs_core-2.0.0.dev47.dist-info/RECORD,,