Skip to content

Commit 720092f

Browse files
committed
testing: Add type annotations to the python test suite
Signed-off-by: Chad Dombrova <chadrik@gmail.com>
1 parent 1808e63 commit 720092f

19 files changed

Lines changed: 86 additions & 49 deletions

File tree

INSTALL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ The workflow for releasing new stubs is as follows:
394394

395395
- Install [`uv`](https://docs.astral.sh/uv/getting-started/installation/)
396396
- Run `make pystubs` locally to generate updated stubs in `src/python/stubs/__init__.pyi`
397+
- Run `make test-pystubs` locally to use mypy to test the stubs against the code in
398+
the python testsuite.
397399
- Commit the new stubs and push to Github
398400
- In CI, the stubs will be included in the wheels built by `cibuildwheel`, as defined in `.github/wheel.yml`
399401
- In CI, one of the `cibuildwheel` Github actions will rebuild the stubs to a

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,18 @@ build: config
243243
${CMAKE} --build . --config ${CMAKE_BUILD_TYPE} \
244244
)
245245

246+
# generate python stubs and add them to the repo
246247
pystubs: config
247248
@ ( cd ${build_dir} ; \
248249
${CMAKE} --build . --config ${CMAKE_BUILD_TYPE} --target pystubs \
249250
)
250251

252+
# run mypy on python tests to confirm the stubs are working
253+
test-pystubs: pystubs
254+
@ ( uv export --quiet --no-dev --no-build --no-emit-project --no-hashes -o ${build_dir}/requirements.txt ; \
255+
uvx --with-requirements ${build_dir}/requirements.txt mypy==1.15.0 \
256+
)
257+
251258
# 'make install' builds everthing and installs it in 'dist'.
252259
# Suppress pointless output from docs installation.
253260
install: build

pyproject.toml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,14 @@ test-requires = "mypy~=1.15.0 stubgenlib~=0.1.0"
139139
select = "cp311-manylinux_*64"
140140
inherit.test-command = "append"
141141
test-command = [
142-
"python {project}/src/python/stubs/generate_stubs.py --out-path '/output' --validate-path '{project}/src/python/stubs/__init__.pyi'",
142+
"python {project}/src/python/stubs/generate_stubs.py --out-path '/output' --validate-path '{project}/src/python/stubs/OpenImageIO/__init__.pyi'",
143143
]
144+
145+
[tool.mypy]
146+
files = [
147+
"testsuite/python-*/src/",
148+
]
149+
mypy_path = [
150+
"src/python/stubs",
151+
]
152+
check_untyped_defs = true

src/cmake/pythonutils.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ macro (setup_python_module)
156156
RUNTIME DESTINATION ${PYTHON_SITE_DIR} COMPONENT user
157157
LIBRARY DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
158158

159-
install (FILES __init__.py stubs/__init__.pyi stubs/py.typed
159+
install (FILES __init__.py stubs/OpenImageIO/__init__.pyi stubs/OpenImageIO/py.typed
160160
DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
161161

162162
endmacro ()

src/python/stubs/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Setup the pystub target, which is not built by default.
33

4-
set (_stub_file "${CMAKE_SOURCE_DIR}/src/python/stubs/__init__.pyi")
4+
set (_stub_file "${CMAKE_SOURCE_DIR}/src/python/stubs/OpenImageIO/__init__.pyi")
55

66
# Note: the python version must be kept in sync with `[[tool.cibuildwheel.overrides]]` in pyproject.toml.
77
# The stubs are generated within a container so the version of python does not need to match

testsuite/python-colorconfig/src/test_colorconfig.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# SPDX-License-Identifier: Apache-2.0
55
# https://github.com/AcademySoftwareFoundation/OpenImageIO
66

7+
from __future__ import annotations
8+
79
import os
810
import OpenImageIO as oiio
911

testsuite/python-deep/src/test_deep.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# SPDX-License-Identifier: Apache-2.0
55
# https://github.com/AcademySoftwareFoundation/OpenImageIO
66

7+
from __future__ import annotations
78

89
import OpenImageIO as oiio
910

@@ -15,19 +16,10 @@
1516
test_channames = ("R", "G", "B", "A", "Z", "Zback")
1617
print ("test_chantypes ", str(test_chantypes[0]), str(test_chantypes[1]), str(test_chantypes[2]), str(test_chantypes[3]), str(test_chantypes[4]), str(test_chantypes[5]))
1718

18-
def set_dd_sample (dd, pixel, sample, vals) :
19-
if dd.samples(pixel) <= sample :
20-
dd.set_samples(pixel, sample+1)
21-
for c in range(ib.nchannels) :
22-
dd.set_value (pixel, c, sample, vals[c])
23-
24-
def add_dd_sample (dd, pixel, sample, vals) :
25-
set_dd_sample (dd, pixel, dd.samples(pixel), vale)
26-
2719

2820
# Make a simple deep image
2921
# Only odd pixel indes have samples, and they have #samples = pixel index.
30-
def make_test_deep_image () :
22+
def make_test_deep_image () -> oiio.DeepData:
3123
dd = oiio.DeepData()
3224
dd.init (test_xres*test_yres, test_nchannels, test_chantypes, test_channames)
3325
for p in range(dd.pixels) :
@@ -54,7 +46,7 @@ def make_test_deep_image () :
5446

5547

5648

57-
def print_deep_image (dd, prefix="After init,") :
49+
def print_deep_image (dd: oiio.DeepData, prefix: str = "After init,") :
5850
print (prefix, "dd has", dd.pixels, "pixels,", dd.channels, "channels.")
5951
print (" Channel indices: Z=", dd.Z_channel, "Zback=", dd.Zback_channel, "A=", dd.A_channel, "AR=", dd.AR_channel, "AG=", dd.AG_channel, "AB=", dd.AB_channel)
6052
for p in range(dd.pixels) :
@@ -68,7 +60,7 @@ def print_deep_image (dd, prefix="After init,") :
6860
print ()
6961

7062

71-
def print_deep_imagebuf (buf, prefix) :
63+
def print_deep_imagebuf (buf: oiio.ImageBuf, prefix: str) :
7264
print_deep_image (buf.deepdata(), prefix)
7365

7466

@@ -241,13 +233,13 @@ def test_opaque_z () :
241233
print ()
242234

243235

244-
def set_ib_sample (ib, x, y, sample, vals) :
236+
def set_ib_sample (ib: oiio.ImageBuf, x: int, y: int, sample: int, vals: tuple[float, ...]) :
245237
if ib.deep_samples(x, y) <= sample :
246238
ib.set_deep_samples (x, y, 0, sample+1)
247239
for c in range(ib.nchannels) :
248240
ib.set_deep_value (x, y, 0, c, sample, vals[c])
249241

250-
def add_ib_sample (ib, x, y, vals) :
242+
def add_ib_sample (ib: oiio.ImageBuf, x: int, y: int, vals: tuple[float, ...]) :
251243
set_ib_sample (ib, x, y, ib.deep_samples(x,y), vals)
252244

253245

@@ -301,13 +293,15 @@ def test_iba_deep_holdout () :
301293
spec.channelformats = test_chantypes
302294
spec.deep = True
303295
output = oiio.ImageOutput.create ("deeptest.exr")
296+
assert output is not None
304297
output.open ("deeptest.exr", spec, "create")
305298
output.write_deep_image (dd)
306299
output.close ()
307300

308301
# read the exr file and double check it
309302
print ("\nReading image...")
310303
input = oiio.ImageInput.open ("deeptest.exr")
304+
assert input is not None
311305
ddr = input.read_native_deep_image ()
312306
if ddr :
313307
print_deep_image (ddr)

testsuite/python-imagebuf/src/test_imagebuf.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
# SPDX-License-Identifier: Apache-2.0
55
# https://github.com/AcademySoftwareFoundation/OpenImageIO
66

7+
from __future__ import annotations
78

89
import array
910
import numpy
1011
import OpenImageIO as oiio
1112

1213

1314
# Print the contents of an ImageSpec
14-
def print_imagespec (spec, subimage=0, mip=0, msg="") :
15+
def print_imagespec (spec: oiio.ImageSpec, subimage=0, mip=0, msg="") :
1516
if msg != "" :
1617
print (str(msg))
1718
if spec.depth <= 1 :
@@ -39,9 +40,9 @@ def print_imagespec (spec, subimage=0, mip=0, msg="") :
3940
print (" deep = ", spec.deep)
4041
for attrib in spec.extra_attribs :
4142
if type(attrib.value) == str :
42-
print (" ", attrib.name, "= \"" + attrib.value + "\"")
43+
print (" {} = \"{}\"".format(attrib.name, attrib.value))
4344
else :
44-
print (" ", attrib.name, "=", attrib.value)
45+
print (" {} = {}".format(attrib.name,attrib.value))
4546
# Equivalent, using indexing rather than iterating:
4647
#for i in range(len(spec.extra_attribs)) :
4748
# if type(spec.extra_attribs[i].value) == str :
@@ -50,7 +51,7 @@ def print_imagespec (spec, subimage=0, mip=0, msg="") :
5051
# print (" ", spec.extra_attribs[i].name, "=", spec.extra_attribs[i].value)
5152

5253

53-
def write (image, filename, format=oiio.UNKNOWN) :
54+
def write (image, filename: str, format=oiio.UNKNOWN) :
5455
if not image.has_error :
5556
image.write (filename, format)
5657
if image.has_error :
@@ -118,6 +119,7 @@ def test_multiimage () :
118119
print ("Writing multi-image file")
119120
spec = oiio.ImageSpec (128, 64, 3, "float")
120121
out = oiio.ImageOutput.create ("multipart.exr")
122+
assert out is not None
121123
# Open with intent to write two subimages, each with same spec
122124
if not out.open ("multipart.exr", (spec, spec)) :
123125
print ("Error on initial open:", out.geterror())

0 commit comments

Comments
 (0)