Skip to content

Commit f7e8769

Browse files
committed
python: packaging and CI
1 parent 8d283c6 commit f7e8769

File tree

9 files changed

+62
-19
lines changed

9 files changed

+62
-19
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ jobs:
4444
version: 1.4.309.0
4545
cache: true
4646

47+
- name: Install uv
48+
uses: astral-sh/setup-uv@v7
49+
with:
50+
version: "0.9.15"
51+
4752
- name: Configure (Linux)
4853
if: matrix.os == 'ubuntu-22.04'
4954
run: >

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cmake_minimum_required(VERSION 3.28)
22

3-
project(vision.cpp VERSION 0.2.0 LANGUAGES CXX)
3+
project(vision.cpp VERSION 0.3.0 LANGUAGES CXX)
44

55
option(BUILD_SHARED_LIBS "Build shared libraries instead of static libraries" ON)
66
option(VISP_VULKAN "Enable Vulkan support" OFF)
@@ -145,6 +145,8 @@ if(VISP_CI OR VISP_DEV)
145145
set_target_properties(vision-cli PROPERTIES INSTALL_RPATH "\$ORIGIN/../${VISP_LIB_INSTALL_DIR}")
146146
endif()
147147

148+
install(DIRECTORY bindings/python DESTINATION ${CMAKE_INSTALL_PREFIX} PATTERN "__pycache__" EXCLUDE)
149+
148150
include(CMakePackageConfigHelpers)
149151

150152
configure_package_config_file(

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ ctest -C Release
174174
Some tests require a Python environment. It can be set up with [uv](https://docs.astral.sh/uv/):
175175
```sh
176176
# Setup venv and install dependencies (once only)
177-
uv sync
177+
uv sync --dev
178178

179179
# Run python tests
180180
uv run pytest

bindings/python/visioncpp/_lib.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,20 @@ def _load():
8787
if system == "windows":
8888
prefix = ""
8989
suffix = ".dll"
90+
libdir = "bin"
9091
elif system == "darwin":
9192
prefix = "lib"
9293
suffix = ".dylib"
94+
libdir = "lib"
9395
else: # assume Linux / Unix
9496
prefix = "lib"
9597
suffix = ".so"
98+
libdir = "lib"
9699
libname = f"{prefix}visioncpp{suffix}"
97100
paths = [
98101
cur_dir / libname,
99-
cur_dir.parent.parent.parent / "build" / "bin" / libname,
102+
cur_dir.parent.parent / libdir / libname,
103+
cur_dir.parent.parent.parent / "build" / libdir / libname,
100104
]
101105
error = None
102106
for path in paths:

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ dynamic = ["version"]
33
name = "vision.cpp"
44
requires-python = ">=3.12"
55
dependencies = [
6+
"pillow",
7+
]
8+
9+
[dependency-groups]
10+
dev = [
611
"torch",
712
"torchvision",
813
"timm",
@@ -12,6 +17,7 @@ dependencies = [
1217
"einops>=0.8.1",
1318
"spandrel>=0.4.1",
1419
"gguf>=0.17.1",
20+
"boto3~=1.39.0"
1521
]
1622

1723
[tool.uv]

tests/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ target_compile_options(vision-workbench PRIVATE ${VISP_COMP_OPTIONS})
4545
target_link_options(vision-workbench PRIVATE ${VISP_LINK_OPTIONS})
4646
target_link_libraries(vision-workbench PRIVATE visioncpp ggml ${VISP_FMT_LINK})
4747

48+
#
49+
# Python tests
50+
51+
if(VISP_CI)
52+
set(PYTHON_TESTS_ARGS "--ci")
53+
endif()
54+
add_test(NAME python COMMAND uv run pytest -vs tests ${PYTHON_TESTS_ARGS})
55+
4856
#
4957
# Benchmarks
5058

tests/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
3+
4+
def pytest_addoption(parser):
5+
parser.addoption(
6+
"--ci",
7+
action="store_true",
8+
default=False,
9+
help="Configure tests for continuous integration environment",
10+
)
11+
12+
13+
def pytest_collection_modifyitems(config, items):
14+
if config.getoption("--ci"):
15+
# Filter to keep only tests from test_python_bindings.py
16+
filtered_items = [item for item in items if "test_python_bindings.py" in item.nodeid]
17+
items[:] = filtered_items

tests/test_primitives.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def test_conv_transpose_2d(scenario: str):
7676
if scenario == "nhwc":
7777
result = to_nchw(result)
7878

79-
workbench.print_results(result, expected)
8079
assert torch.allclose(result, expected, rtol=1e-2)
8180

8281

tests/test_python_bindings.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
import numpy as np
3+
import pytest
34
from pathlib import Path
45
from PIL import Image
56

@@ -13,6 +14,12 @@
1314
result_dir.mkdir(parents=True, exist_ok=True)
1415
ref_dir = root_dir / "tests" / "reference"
1516

17+
@pytest.fixture
18+
def device(pytestconfig):
19+
if pytestconfig.getoption("ci"):
20+
return Device.init(Backend.cpu)
21+
return Device.init()
22+
1623

1724
def compare_images(name: str, result: Image.Image, tolerance: float = 0.015):
1825
name = f"{name}-gpu.png"
@@ -32,9 +39,8 @@ def compare_images(name: str, result: Image.Image, tolerance: float = 0.015):
3239
raise AssertionError(f"Images differ: RMSE={rmse} exceeds tolerance={tolerance}")
3340

3441

35-
def test_sam():
36-
dev = Device.init(Backend.gpu)
37-
model = Model.load(model_dir / "MobileSAM-F16.gguf", dev)
42+
def test_sam(device: Device):
43+
model = Model.load(model_dir / "MobileSAM-F16.gguf", device)
3844
assert model.arch is Arch.sam
3945

4046
img = Image.open(str(image_dir / "cat-and-hat.jpg"))
@@ -43,27 +49,24 @@ def test_sam():
4349
compare_images("mobile_sam-box", result_box)
4450
compare_images("mobile_sam-point", result_point)
4551

46-
def test_birefnet():
47-
dev = Device.init(Backend.gpu)
48-
model = Model.load(model_dir / "BiRefNet-lite-F16.gguf", dev)
52+
def test_birefnet(device: Device):
53+
model = Model.load(model_dir / "BiRefNet-lite-F16.gguf", device)
4954
assert model.arch is Arch.birefnet
5055

5156
img = Image.open(str(image_dir / "wardrobe.jpg"))
5257
result = model.compute(img)
5358
compare_images("birefnet", result)
5459

55-
def test_depth_anything():
56-
dev = Device.init(Backend.gpu)
57-
model = Model.load(model_dir / "Depth-Anything-V2-Small-F16.gguf", dev)
60+
def test_depth_anything(device: Device):
61+
model = Model.load(model_dir / "Depth-Anything-V2-Small-F16.gguf", device)
5862
assert model.arch is Arch.depth_anything
5963

6064
img = Image.open(str(image_dir / "wardrobe.jpg"))
6165
result = model.compute(img)
6266
compare_images("depth-anything", result)
6367

64-
def test_migan():
65-
dev = Device.init(Backend.gpu)
66-
model = Model.load(model_dir / "MIGAN-512-places2-F16.gguf", dev)
68+
def test_migan(device: Device):
69+
model = Model.load(model_dir / "MIGAN-512-places2-F16.gguf", device)
6770
assert model.arch is Arch.migan
6871

6972
img = Image.open(str(image_dir / "bench-image.jpg")).convert("RGBA")
@@ -72,9 +75,8 @@ def test_migan():
7275
result = Image.alpha_composite(img, result)
7376
compare_images("migan", result)
7477

75-
def test_esrgan():
76-
dev = Device.init(Backend.gpu)
77-
model = Model.load(str(model_dir / "RealESRGAN-x4plus_anime-6B-F16.gguf"), dev)
78+
def test_esrgan(device: Device):
79+
model = Model.load(str(model_dir / "RealESRGAN-x4plus_anime-6B-F16.gguf"), device)
7880
assert model.arch is Arch.esrgan
7981

8082
img = Image.open(str(image_dir / "vase-and-bowl.jpg"))

0 commit comments

Comments
 (0)