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.
- 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.dev11 → pyobs_aravis-2.0.0.dev12}/PKG-INFO +4 -4
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/araviscamera.py +17 -19
- {pyobs_aravis-2.0.0.dev11 → 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.dev11 → pyobs_aravis-2.0.0.dev12}/uv.lock +1121 -905
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.github/workflows/pypi.yml +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.github/workflows/ruff.yml +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.pre-commit-config.yaml +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/.readthedocs.yml +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/CHANGELOG.rst +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/LICENSE +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/README.md +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/Makefile +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/make.bat +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/requirements.txt +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/_static/pyobs.gif +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/conf.py +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/docs/source/index.rst +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/__init__.py +0 -0
- {pyobs_aravis-2.0.0.dev11 → pyobs_aravis-2.0.0.dev12}/pyobs_aravis/aravis.py +0 -0
- {pyobs_aravis-2.0.0.dev11 → 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,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
|
-
#
|
|
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
|
|
|
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.
|
|
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)
|