Skip to content

Commit 8be66ed

Browse files
committed
Rough draft of new end-to-end workflow
1 parent d26ee26 commit 8be66ed

10 files changed

Lines changed: 1762 additions & 58 deletions

File tree

.github/workflows/wheel.yml

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,25 @@ jobs:
147147
with:
148148
python-version: '3.9'
149149

150+
- name: Install uv
151+
uses: astral-sh/setup-uv@v5
152+
with:
153+
version: "0.6.13"
154+
150155
- name: Build wheels
151156
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
152157
env:
153158
CIBW_BUILD: ${{ matrix.python }}
154159
CIBW_ARCHS: ${{ matrix.arch }}
155160
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
156161

157-
- name: Build stubs
158-
if: matrix.build == 'CPython 3.9 64 bits manylinux_2_28'
159-
run: |
160-
pip install ./wheelhouse/*.whl
161-
pipx run ./src/python/generate_stubs.py ./out
162-
cat ./out/*.pyi
162+
- name: Build stub wheel
163+
# The stubs are verified (in the previous step) and built (in this step)
164+
# for only one element within the matrix.
165+
# Note: this identifier needs to be kept in sync with `tool.cibuildwheel.overrides.select`
166+
# value in the root pyproject.toml
167+
if: matrix.python == 'cp311-manylinux_x86_64'
168+
run: uv build --wheel --out-dir ./wheelhouse/ src/python/stubs
163169

164170
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
165171
with:
@@ -215,11 +221,6 @@ jobs:
215221
with:
216222
python-version: '3.9'
217223

218-
- name: Install uv
219-
uses: astral-sh/setup-uv@v5
220-
with:
221-
version: "0.6.13"
222-
223224
- name: Build wheels
224225
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
225226
env:
@@ -275,11 +276,6 @@ jobs:
275276
with:
276277
python-version: '3.9'
277278

278-
- name: Install uv
279-
uses: astral-sh/setup-uv@v5
280-
with:
281-
version: "0.6.13"
282-
283279
- name: Build wheels
284280
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
285281
env:
@@ -339,11 +335,6 @@ jobs:
339335
with:
340336
python-version: '3.8'
341337

342-
- name: Install uv
343-
uses: astral-sh/setup-uv@v5
344-
with:
345-
version: "0.6.13"
346-
347338
- name: Build wheels
348339
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
349340
env:
@@ -399,11 +390,6 @@ jobs:
399390
with:
400391
python-version: '3.9'
401392

402-
- name: Install uv
403-
uses: astral-sh/setup-uv@v5
404-
with:
405-
version: "0.6.13"
406-
407393
- name: Build wheels
408394
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
409395
env:

INSTALL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,13 @@ the headers, and the CLI tools to a platform-specific, Python-specific location.
389389
See the [scikit-build-core docs](https://scikit-build-core.readthedocs.io/en/latest/configuration.html#configuring-cmake-arguments-and-defines)
390390
for more information on customizing and overriding build-tool options and CMake arguments.
391391

392+
This repo contains python type stubs which are generated from `pybind11` signatures.
393+
The workflow for releasing new stubs
394+
395+
- ```make pystubs```: this will generate updated stubs in `src/python/stubs/OpenImageIO/`
396+
- commit the new stubs and push to Github
397+
- CI actions will verify and build the `OpenImageIO-stubs` wheel
398+
- The wheel is then published to PyPI as a separate project
392399

393400
Test Images
394401
-----------

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ pystubs: config
247247
@ ( cd ${build_dir} ; \
248248
${CMAKE} --build . --config ${CMAKE_BUILD_TYPE} --target pystubs \
249249
)
250+
@ uv build --wheel ${working_dir}/src/python/stubs
250251

251252
# 'make install' builds everthing and installs it in 'dist'.
252253
# Suppress pointless output from docs installation.

pyproject.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,14 @@ CXXFLAGS = "-Wno-error=stringop-overflow -Wno-pragmas"
122122
SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel"
123123

124124
[tool.cibuildwheel.windows.environment]
125-
SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel"
125+
SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel"
126+
127+
[[tool.cibuildwheel.overrides]]
128+
# Trigger the build of the python stubs for certain platforms. In CI, this only
129+
# runs on a single platform (manylinux_x86_64), but we use * here to allow local
130+
# tests on aarch64 without running docker in emulation (i.e. new Mac silicon).
131+
# If this `select` value is updated it should be reflected in wheel.yml
132+
select = "cp311-manylinux_*64"
133+
test-requires = "mypy~=1.15.0 stubgenlib~=0.1.0"
134+
inherit.test-command = "append"
135+
test-command = ["python {project}/src/python/generate_stubs.py '/output' '{project}/src/python'"]

src/python/CMakeLists.txt

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,26 @@ set_target_properties(PyOpenImageIO PROPERTIES
2020
set_source_files_properties(${python_srcs} PROPERTIES
2121
UNITY_GROUP PyOpenImageIO)
2222

23-
set (_stub_gen "${CMAKE_SOURCE_DIR}/src/python/generate_stubs.py")
2423
message("PROC: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
2524

26-
set (_wheel_name "openimageio-${OpenImageIO_VERSION}-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl")
27-
set (_output_wheel_path "${CMAKE_BINARY_DIR}/wheelhouse/${_wheel_name}")
28-
# this path is valid inside the container
29-
set (OIIO_PYTHON_WHEEL "/wheelhouse/${_wheel_name}")
30-
31-
# copy the file and create missing directory
32-
configure_file ("${CMAKE_SOURCE_DIR}/src/python/generate_stubs.py" "${CMAKE_BINARY_DIR}/wheelhouse/generate_stubs.py")
33-
34-
# FIXME: call uname -m to detect platform
35-
# file $(which cmake)
25+
# FIXME: choose aarch vs x86_64 based on the current platform
3626

3727
add_custom_command (
38-
COMMAND cd ${CMAKE_SOURCE_DIR} && cibuildwheel "." --output-dir "${CMAKE_BINARY_DIR}/wheelhouse" --only "cp311-manylinux_aarch64"
39-
COMMENT "pystubs: creating python wheel"
40-
OUTPUT ${_output_wheel_path}
41-
DEPENDS "${CMAKE_BINARY_DIR}/wheelhouse/generate_stubs.py"
42-
)
28+
COMMAND cd ${CMAKE_SOURCE_DIR} &&
29+
cibuildwheel "." --output-dir "${CMAKE_BINARY_DIR}/wheelhouse" --only "cp311-manylinux_aarch64"
30+
OUTPUT "${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
31+
COMMENT "pystubs: creating python wheel"
32+
)
4333

4434
add_custom_command (
45-
COMMAND cd "${CMAKE_BINARY_DIR}/wheelhouse" && docker run -v ${CMAKE_BINARY_DIR}/wheelhouse:/wheelhouse --workdir=/wheelhouse --rm -it quay.io/pypa/manylinux2014_aarch64:2025.03.23-1 pipx run --python=/opt/python/cp311-cp311/bin/python ./generate_stubs.py "./"
46-
COMMENT "pystubs: creating python stubs"
47-
OUTPUT "${CMAKE_BINARY_DIR}/wheelhouse/__init__.pyi"
48-
DEPENDS ${_output_wheel_path}
49-
)
50-
51-
add_custom_target (
52-
pystubs
53-
DEPENDS "${CMAKE_BINARY_DIR}/wheelhouse/__init__.pyi"
54-
)
35+
OUTPUT "${CMAKE_SOURCE_DIR}/src/python/stubs/OpenImageIO-stubs/__init__.pyi"
36+
COMMAND ${CMAKE_COMMAND} -E copy
37+
"${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
38+
"${CMAKE_SOURCE_DIR}/src/python/stubs/OpenImageIO-stubs/__init__.pyi"
39+
DEPENDS "${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
40+
COMMENT "pystubs: Copying generated stubs to source"
41+
)
42+
43+
add_custom_target (pystubs
44+
DEPENDS "${CMAKE_SOURCE_DIR}/src/python/stubs/OpenImageIO-stubs/__init__.pyi"
45+
)

src/python/generate_stubs.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# dependencies = [
33
# "mypy~=1.15.0",
44
# "stubgenlib~=0.1.0",
5-
# "OpenImageIO @ file://@OIIO_PYTHON_WHEEL@",
65
# ]
76
# ///
87

@@ -119,15 +118,57 @@ def set_defined_names(self, defined_names: set[str]) -> None:
119118
mypy.stubgen.InspectionStubGenerator = InspectionStubGenerator # type: ignore[attr-defined,misc]
120119
mypy.stubgenc.InspectionStubGenerator = InspectionStubGenerator # type: ignore[misc]
121120

121+
122+
def get_colored_diff(old_text: str, new_text: str):
123+
"""
124+
Generates a colored diff between two strings.
125+
126+
Returns:
127+
A string containing the colored diff output.
128+
"""
129+
import difflib
130+
131+
red = '\033[31m' # ANSI escape code for red
132+
green = '\033[32m' # ANSI escape code for green
133+
reset = '\033[0m' # ANSI escape code to reset color
134+
135+
diff = difflib.unified_diff(old_text.splitlines(keepends=True), new_text.splitlines(keepends=True), lineterm="")
136+
lines = []
137+
for line in diff:
138+
if line.startswith('-'):
139+
lines.append(f"{red}{line}{reset}")
140+
elif line.startswith('+'):
141+
lines.append(f"{green}{line}{reset}")
142+
else:
143+
lines.append(line)
144+
return "".join(lines)
145+
146+
122147
if __name__ == "__main__":
148+
import pathlib
123149
import os
124150
import sys
125-
out_path = sys.argv[1]
151+
out_path = pathlib.Path(sys.argv[1])
152+
validate_path = pathlib.Path(sys.argv[2])
126153
print(f"Stub output directory: {out_path}")
127154
# perform import so we can see the traceback if it fails.
128155
import OpenImageIO
129-
sys.argv[1:] = ["-p", "OpenImageIO", "-o", out_path]
156+
sys.argv[1:] = ["-p", "OpenImageIO", "-o", str(out_path)]
130157
mypy.stubgen.main()
131-
dest = os.path.join(out_path, "OpenImageIO", "__init__.pyi")
158+
source = out_path.joinpath("OpenImageIO", "OpenImageIO.pyi")
159+
if not source.exists():
160+
print("Stub generation failed")
161+
sys.exit(1)
162+
163+
dest = out_path.joinpath("OpenImageIO", "__init__.pyi")
132164
print(f"Renaming to {dest}")
133-
os.rename(os.path.join(out_path, "OpenImageIO", "OpenImageIO.pyi"), dest)
165+
os.rename(source, dest)
166+
167+
if "GITHUB_ACTIONS" in os.environ:
168+
# in CI, we don't overwrite the output path, we merely validate that what has
169+
# been committed to the repo is what we expect.
170+
old_text = validate_path.text()
171+
new_text = dest.text()
172+
if old_text != new_text:
173+
print(get_colored_diff(old_text, new_text))
174+
sys.exit(2)

0 commit comments

Comments
 (0)