pyobs-aravis 2.0.0.dev10__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.
- pyobs_aravis-2.0.0.dev12/.github/dependabot.yml +15 -0
- pyobs_aravis-2.0.0.dev12/.github/workflows/pyrefly.yml +23 -0
- pyobs_aravis-2.0.0.dev12/.github/workflows/pytest.yml +28 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/PKG-INFO +4 -4
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/araviscamera.py +21 -19
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/pyproject.toml +10 -5
- pyobs_aravis-2.0.0.dev12/tests/test_araviscamera.py +42 -0
- pyobs_aravis-2.0.0.dev12/tests/test_import.py +26 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/uv.lock +1121 -905
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/.github/workflows/pypi.yml +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/.github/workflows/ruff.yml +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/.pre-commit-config.yaml +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/.readthedocs.yml +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/CHANGELOG.rst +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/LICENSE +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/README.md +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/Makefile +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/make.bat +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/requirements.txt +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/source/_static/pyobs.gif +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/source/conf.py +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/docs/source/index.rst +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/__init__.py +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/aravis.py +0 -0
- {pyobs_aravis-2.0.0.dev10 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/gui.py +0 -0
|
@@ -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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pyobs-aravis
|
|
3
|
-
Version: 2.0.0.
|
|
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.
|
|
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.
|
|
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,27 +61,18 @@ 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
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
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
|
|
|
72
|
+
# publish initial exposure-time state -- otherwise a caller doing wait_for_state()
|
|
73
|
+
# before the first set_exposure_time() call would time out with nothing ever published
|
|
74
|
+
await self.comm.set_state(IExposureTime, ExposureTimeState(exposure_time=self._exposure_time))
|
|
75
|
+
|
|
82
76
|
async def close(self) -> None:
|
|
83
77
|
"""Close the module."""
|
|
84
78
|
await BaseVideo.close(self)
|
|
@@ -118,22 +112,30 @@ class AravisCamera(BaseVideo, IExposureTime):
|
|
|
118
112
|
|
|
119
113
|
Returns:
|
|
120
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.
|
|
121
118
|
"""
|
|
122
119
|
loop = asyncio.get_running_loop()
|
|
123
120
|
future: asyncio.Future[None] = loop.create_future()
|
|
121
|
+
error: list[BaseException] = []
|
|
124
122
|
|
|
125
123
|
def _wrapper() -> None:
|
|
126
124
|
try:
|
|
127
125
|
func()
|
|
126
|
+
except BaseException as exc:
|
|
127
|
+
error.append(exc)
|
|
128
128
|
finally:
|
|
129
129
|
loop.call_soon_threadsafe(future.set_result, None)
|
|
130
130
|
|
|
131
131
|
threading.Thread(target=_wrapper, daemon=True).start()
|
|
132
132
|
try:
|
|
133
133
|
await asyncio.wait_for(future, timeout=timeout)
|
|
134
|
-
return True
|
|
135
134
|
except TimeoutError:
|
|
136
135
|
return False
|
|
136
|
+
if error:
|
|
137
|
+
raise error[0]
|
|
138
|
+
return True
|
|
137
139
|
|
|
138
140
|
async def _activate_camera(self) -> None:
|
|
139
141
|
"""Open camera on activation."""
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyobs-aravis"
|
|
3
|
-
version = "2.0.0.
|
|
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.
|
|
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.
|
|
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,<
|
|
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)
|