pyobs-aravis 2.0.0.dev8__tar.gz → 2.0.0.dev10__tar.gz

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.
Files changed (20) hide show
  1. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/PKG-INFO +2 -2
  2. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/pyobs_aravis/araviscamera.py +41 -4
  3. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/pyproject.toml +2 -2
  4. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/uv.lock +506 -11
  5. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/.github/workflows/pypi.yml +0 -0
  6. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/.github/workflows/ruff.yml +0 -0
  7. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/.pre-commit-config.yaml +0 -0
  8. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/.readthedocs.yml +0 -0
  9. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/CHANGELOG.rst +0 -0
  10. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/LICENSE +0 -0
  11. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/README.md +0 -0
  12. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/Makefile +0 -0
  13. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/make.bat +0 -0
  14. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/requirements.txt +0 -0
  15. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/source/_static/pyobs.gif +0 -0
  16. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/source/conf.py +0 -0
  17. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/docs/source/index.rst +0 -0
  18. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/pyobs_aravis/__init__.py +0 -0
  19. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/pyobs_aravis/aravis.py +0 -0
  20. {pyobs_aravis-2.0.0.dev8 → pyobs_aravis-2.0.0.dev10}/pyobs_aravis/gui.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-aravis
3
- Version: 2.0.0.dev8
3
+ Version: 2.0.0.dev10
4
4
  Summary: pyobs module for Aravis cameras
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
7
7
  License-File: LICENSE
8
- Requires-Python: <3.14,>=3.11
8
+ Requires-Python: >=3.11
9
9
  Requires-Dist: numpy<3,>=2.2.5
10
10
  Requires-Dist: pyobs-core<3,>=2.0.0.dev1
11
11
  Provides-Extra: gui
@@ -16,6 +16,11 @@ log = logging.getLogger(__name__)
16
16
  # rather than let a single dead camera freeze the whole module.
17
17
  _SDK_CALL_TIMEOUT = 5.0
18
18
 
19
+ # pop_frame() is polled from _capture() and is expected to legitimately take a while (up to the
20
+ # camera's own frame interval/exposure time), unlike the other SDK calls above -- a much more
21
+ # generous timeout than _SDK_CALL_TIMEOUT, so normal operation never trips it.
22
+ _FRAME_WAIT_TIMEOUT = 30.0
23
+
19
24
 
20
25
  class AravisCamera(BaseVideo, IExposureTime):
21
26
  """A pyobs module for Aravis cameras."""
@@ -153,10 +158,10 @@ class AravisCamera(BaseVideo, IExposureTime):
153
158
  await asyncio.sleep(0.1)
154
159
  continue
155
160
 
156
- frame: npt.NDArray[Any] = self._camera.pop_frame() # type: ignore[union-attr]
157
- while frame is None:
158
- await asyncio.sleep(0.01)
159
- frame = self._camera.pop_frame() # type: ignore[union-attr]
161
+ frame = await self._wait_for_frame()
162
+ if frame is None:
163
+ # camera went away, or the wait timed out -- back off and retry
164
+ continue
160
165
 
161
166
  if time.time() - last < self._interval:
162
167
  await asyncio.sleep(0.01)
@@ -168,6 +173,38 @@ class AravisCamera(BaseVideo, IExposureTime):
168
173
  except Exception:
169
174
  await asyncio.sleep(1)
170
175
 
176
+ async def _wait_for_frame(self, timeout: float = _FRAME_WAIT_TIMEOUT) -> npt.NDArray[Any] | None:
177
+ """Waits for the next frame without blocking the event loop.
178
+
179
+ Polls pop_frame() from a background thread (see _run_blocking) rather than polling it
180
+ directly from the async loop with a sleep in between each attempt -- pop_frame() is
181
+ assumed non-blocking in the common case, but if the underlying aravis/GLib call ever
182
+ doesn't honor that (camera hiccup, network stall for GigE Vision), polling it directly
183
+ would freeze the whole module for as long as that lasts, repeatedly, for the module's
184
+ entire runtime. Runs the whole "poll until ready" loop as a single blocking call instead,
185
+ so only one thread gets spawned per delivered frame rather than one per 10ms poll.
186
+
187
+ Returns:
188
+ The next frame, or None if the camera disappeared mid-wait or the wait timed out.
189
+ """
190
+ result: list[npt.NDArray[Any]] = []
191
+
192
+ def _poll() -> None:
193
+ camera = self._camera
194
+ while camera is not None:
195
+ frame = camera.pop_frame() # type: ignore[union-attr]
196
+ # pop_frame() can return a non-None array that's empty along axis 0 instead of
197
+ # None -- treat that the same as "not ready yet" rather than a real frame
198
+ if frame is not None and frame.size != 0: # type: ignore[union-attr]
199
+ result.append(frame) # type: ignore[arg-type]
200
+ return
201
+ time.sleep(0.01)
202
+
203
+ if not await self._run_blocking(_poll, timeout=timeout):
204
+ log.error("Timed out waiting for a frame after %.1fs.", timeout)
205
+ return None
206
+ return result[0] if result else None
207
+
171
208
  async def set_exposure_time(self, exposure_time: float, **kwargs: Any) -> None:
172
209
  """Set the exposure time in seconds.
173
210
 
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "pyobs-aravis"
3
- version = "2.0.0.dev8"
3
+ version = "2.0.0.dev10"
4
4
  description = "pyobs module for Aravis cameras"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
- requires-python = ">=3.11,<3.14"
6
+ requires-python = ">=3.11"
7
7
  license = "MIT"
8
8
  dependencies = [
9
9
  "numpy>=2.2.5,<3",