pyobs-aravis 2.0.0.dev11__tar.gz → 2.0.0.dev12__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 (25) hide show
  1. pyobs_aravis-2.0.0.dev12/.github/dependabot.yml +15 -0
  2. pyobs_aravis-2.0.0.dev12/.github/workflows/pyrefly.yml +23 -0
  3. pyobs_aravis-2.0.0.dev12/.github/workflows/pytest.yml +28 -0
  4. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/PKG-INFO +4 -4
  5. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/araviscamera.py +17 -19
  6. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyproject.toml +10 -5
  7. pyobs_aravis-2.0.0.dev12/tests/test_araviscamera.py +42 -0
  8. pyobs_aravis-2.0.0.dev12/tests/test_import.py +26 -0
  9. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/uv.lock +1121 -905
  10. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.github/workflows/pypi.yml +0 -0
  11. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.github/workflows/ruff.yml +0 -0
  12. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.pre-commit-config.yaml +0 -0
  13. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.readthedocs.yml +0 -0
  14. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/CHANGELOG.rst +0 -0
  15. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/LICENSE +0 -0
  16. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/README.md +0 -0
  17. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/Makefile +0 -0
  18. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/make.bat +0 -0
  19. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/requirements.txt +0 -0
  20. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/_static/pyobs.gif +0 -0
  21. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/conf.py +0 -0
  22. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/index.rst +0 -0
  23. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/__init__.py +0 -0
  24. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/aravis.py +0 -0
  25. {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/gui.py +0 -0
@@ -0,0 +1,15 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "tuesday"
8
+ target-branch: "develop"
9
+ groups:
10
+ patch-and-minor:
11
+ update-types:
12
+ - "minor"
13
+ - "patch"
14
+ labels:
15
+ - "dependencies"
@@ -0,0 +1,23 @@
1
+ name: pyrefly
2
+ on: push
3
+
4
+ jobs:
5
+ pyrefly:
6
+ runs-on: ubuntu-latest
7
+ timeout-minutes: 10
8
+
9
+ steps:
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: astral-sh/setup-uv@v5
15
+ with:
16
+ enable-cache: true
17
+ python-version: "3.13"
18
+
19
+ - name: Install packages
20
+ run: uv sync --all-extras --dev
21
+
22
+ - name: Run pyrefly
23
+ run: uv run pyrefly check
@@ -0,0 +1,28 @@
1
+ name: PyTest
2
+ on: push
3
+
4
+ jobs:
5
+ test:
6
+ runs-on: ubuntu-latest
7
+ timeout-minutes: 10
8
+
9
+ steps:
10
+ - name: Check out repository code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: astral-sh/setup-uv@v5
15
+ with:
16
+ enable-cache: true
17
+ python-version: "3.13"
18
+
19
+ - name: Install system dependencies
20
+ run: sudo apt-get install -y python3-gi python3-gi-cairo gir1.2-aravis-0.8
21
+
22
+ - name: Install packages
23
+ run: uv venv --system-site-packages --clear .venv && uv sync --all-extras --dev
24
+
25
+ - name: Run tests
26
+ run: uv run pytest tests
27
+ env:
28
+ CI: true
@@ -1,13 +1,13 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pyobs-aravis
3
- Version: 2.0.0.dev11
3
+ Version: 2.0.0.dev12
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
8
  Requires-Python: >=3.11
9
9
  Requires-Dist: numpy<3,>=2.2.5
10
- Requires-Dist: pyobs-core<3,>=2.0.0.dev1
10
+ Requires-Dist: pyobs-core<3,>=2.0.0.dev48
11
11
  Provides-Extra: gui
12
- Requires-Dist: pyobs-core[gui]<3,>=2.0.0.dev1; extra == 'gui'
12
+ Requires-Dist: pyobs-core[gui]<3,>=2.0.0.dev48; extra == 'gui'
13
13
  Requires-Dist: qasync>=0.28.0; extra == 'gui'
@@ -37,7 +37,10 @@ class AravisCamera(BaseVideo, IExposureTime):
37
37
  """Initializes a new AravisCamera.
38
38
 
39
39
  Args:
40
- device: Name of camera to connect to.
40
+ device: Name or IP address of camera to connect to. An IP address connects directly,
41
+ without relying on the network's broadcast-based device discovery -- useful on
42
+ networks that block or drop that broadcast traffic even though unicast GVCP
43
+ control traffic works fine (verify with e.g. `arv-tool-0.8 -a <ip> features`).
41
44
  settings: Dictionary of camera settings to apply on connect.
42
45
  buffers: Number of acquisition buffers.
43
46
  """
@@ -58,25 +61,12 @@ class AravisCamera(BaseVideo, IExposureTime):
58
61
 
59
62
  async def open(self) -> None:
60
63
  """Open module."""
61
- from . import aravis
62
-
63
64
  await BaseVideo.open(self)
64
65
 
65
- # device discovery is a blocking, network-based scan (GigE Vision/USB3 Vision devices
66
- # reply to a broadcast query) that can take multiple seconds -- run it like the other
67
- # aravis/GLib calls (see _run_blocking) instead of freezing the whole module's event
68
- # loop, and with it, the ability to respond to any other module, for that long
69
- ids: list[str] = []
70
-
71
- def _list_device_ids() -> None:
72
- ids.extend(aravis.get_device_ids()) # type: ignore[arg-type]
73
-
74
- if not await self._run_blocking(_list_device_ids):
75
- raise TimeoutError(f"Timed out listing available cameras after {_SDK_CALL_TIMEOUT}s.")
76
-
77
- if self._camera_device_name not in ids:
78
- raise ValueError("Could not find given device name in list of available cameras.")
79
-
66
+ # connecting (see _open_camera) validates the device itself -- no separate discovery
67
+ # pre-check here, since that relies on a network-wide broadcast query that some networks
68
+ # block or drop even though unicast GVCP control traffic (i.e. the actual connection)
69
+ # works fine
80
70
  await self.activate_camera()
81
71
 
82
72
  # publish initial exposure-time state -- otherwise a caller doing wait_for_state()
@@ -122,22 +112,30 @@ class AravisCamera(BaseVideo, IExposureTime):
122
112
 
123
113
  Returns:
124
114
  True if func completed within timeout, False if it's still running in the background.
115
+
116
+ Raises:
117
+ Whatever exception func() raised, if it completed within timeout.
125
118
  """
126
119
  loop = asyncio.get_running_loop()
127
120
  future: asyncio.Future[None] = loop.create_future()
121
+ error: list[BaseException] = []
128
122
 
129
123
  def _wrapper() -> None:
130
124
  try:
131
125
  func()
126
+ except BaseException as exc:
127
+ error.append(exc)
132
128
  finally:
133
129
  loop.call_soon_threadsafe(future.set_result, None)
134
130
 
135
131
  threading.Thread(target=_wrapper, daemon=True).start()
136
132
  try:
137
133
  await asyncio.wait_for(future, timeout=timeout)
138
- return True
139
134
  except TimeoutError:
140
135
  return False
136
+ if error:
137
+ raise error[0]
138
+ return True
141
139
 
142
140
  async def _activate_camera(self) -> None:
143
141
  """Open camera on activation."""
@@ -1,18 +1,18 @@
1
1
  [project]
2
2
  name = "pyobs-aravis"
3
- version = "2.0.0.dev11"
3
+ version = "2.0.0.dev12"
4
4
  description = "pyobs module for Aravis cameras"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"
7
7
  license = "MIT"
8
8
  dependencies = [
9
9
  "numpy>=2.2.5,<3",
10
- "pyobs-core>=2.0.0.dev1,<3",
10
+ "pyobs-core>=2.0.0.dev48,<3",
11
11
  ]
12
12
 
13
13
  [project.optional-dependencies]
14
14
  gui = [
15
- "pyobs-core[gui]>=2.0.0.dev1,<3",
15
+ "pyobs-core[gui]>=2.0.0.dev48,<3",
16
16
  "qasync>=0.28.0",
17
17
  ]
18
18
 
@@ -21,10 +21,12 @@ aravis-gui = 'pyobs_aravis.gui:main'
21
21
 
22
22
  [dependency-groups]
23
23
  dev = [
24
- "black>=25.1.0,<26",
24
+ "black>=25.1.0,<27",
25
25
  "ruff>=0.9.0",
26
26
  "pyrefly>=1.1.1",
27
27
  "pre-commit>=4.2.0,<5",
28
+ "pytest>=8.0.0",
29
+ "pytest-asyncio>=0.24.0",
28
30
  ]
29
31
 
30
32
  [build-system]
@@ -45,4 +47,7 @@ exclude = ["pyobs_aravis/aravis.py"]
45
47
 
46
48
  [tool.pyrefly]
47
49
  python-version = "3.11"
48
- project-excludes = ["pyobs_aravis/aravis.py"]
50
+ project-excludes = ["pyobs_aravis/aravis.py", "pyobs_aravis/gui.py"]
51
+
52
+ [tool.pytest.ini_options]
53
+ asyncio_mode = "strict"
@@ -0,0 +1,42 @@
1
+ """Unit tests for the non-hardware logic in AravisCamera: the _run_blocking timeout
2
+ wrapper. gi/Aravis are not needed for these.
3
+ """
4
+
5
+ import asyncio
6
+ import threading
7
+
8
+ import pytest
9
+
10
+ from pyobs_aravis import AravisCamera
11
+
12
+
13
+ @pytest.mark.asyncio
14
+ async def test_run_blocking_runs_func_and_returns_true() -> None:
15
+ ran: list[bool] = []
16
+
17
+ def fast() -> None:
18
+ ran.append(True)
19
+
20
+ assert await AravisCamera._run_blocking(fast) is True
21
+ assert ran == [True]
22
+
23
+
24
+ @pytest.mark.asyncio
25
+ async def test_run_blocking_times_out() -> None:
26
+ done = threading.Event()
27
+
28
+ def slow() -> None:
29
+ done.wait()
30
+
31
+ assert await AravisCamera._run_blocking(slow, timeout=0.01) is False
32
+ done.set()
33
+ await asyncio.sleep(0.05)
34
+
35
+
36
+ @pytest.mark.asyncio
37
+ async def test_run_blocking_reraises_func_exception() -> None:
38
+ def failing() -> None:
39
+ raise ValueError("camera not found")
40
+
41
+ with pytest.raises(ValueError, match="camera not found"):
42
+ await AravisCamera._run_blocking(failing)
@@ -0,0 +1,26 @@
1
+ """Smoke tests: importing the package must not require gi/Aravis; instantiating the
2
+ driver does (AravisCamera.__init__ pulls in the vendored aravis.py -> gi -> Aravis
3
+ typelib), so the instantiate test skips when gi isn't available.
4
+
5
+ In CI the pytest workflow installs python3-gi + gir1.2-aravis-0.8 (mirroring
6
+ ruff.yml), so the instantiate test actually runs there.
7
+ """
8
+
9
+ import pytest
10
+ from pyobs.interfaces import IExposureTime, IVideo
11
+ from pyobs.modules import Module
12
+
13
+ from pyobs_aravis import AravisCamera
14
+
15
+
16
+ def test_import_module_without_native() -> None:
17
+ assert AravisCamera is not None
18
+
19
+
20
+ def test_instantiate_camera() -> None:
21
+ pytest.importorskip("gi")
22
+
23
+ camera = AravisCamera(device="test-camera")
24
+ assert isinstance(camera, Module)
25
+ assert isinstance(camera, IExposureTime)
26
+ assert isinstance(camera, IVideo)