Skip to content

Commit e787dc6

Browse files
authored
feat: Add python stub files (AcademySoftwareFoundation#4692)
Add python stub files for code completion and static analysis. Fixes AcademySoftwareFoundation#4682 No new test cases, since there is not any change to the behavior. I do have an idea for a kind of test that might validate the stubs against the python unit tests, but I don't know if it's worth it. --------- Signed-off-by: Chad Dombrova <chadrik@gmail.com>
1 parent 7a3b76d commit e787dc6

File tree

23 files changed

+2049
-49
lines changed

23 files changed

+2049
-49
lines changed

.github/workflows/wheel.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,12 @@ jobs:
150150
python-version: '3.9'
151151

152152
- name: Build wheels
153+
# Note: the version of cibuildwheel should be kept in sync with src/python/stubs/CMakeLists.txt
153154
uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1
154155
env:
156+
# pass GITHUB_ACTIONS through to the build container so that custom
157+
# processes can tell they are running in CI.
158+
CIBW_ENVIRONMENT_PASS_LINUX: GITHUB_ACTIONS
155159
CIBW_BUILD: ${{ matrix.python }}
156160
CIBW_ARCHS: ${{ matrix.arch }}
157161
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}

INSTALL.md

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

398+
This repo contains python type stubs which are generated from `pybind11` signatures.
399+
The workflow for releasing new stubs is as follows:
400+
401+
- Install [`uv`](https://docs.astral.sh/uv/getting-started/installation/)
402+
- Run `make pystubs` locally to generate updated stubs in `src/python/stubs/__init__.pyi`
403+
- Run `make test-pystubs` locally to use mypy to test the stubs against the code in
404+
the python testsuite.
405+
- Commit the new stubs and push to Github
406+
- In CI, the stubs will be included in the wheels built by `cibuildwheel`, as defined in `.github/wheel.yml`
407+
- In CI, one of the `cibuildwheel` Github actions will rebuild the stubs to a
408+
temp location and verify that they match what has been committed to the repo.
409+
This step ensures that if changes to the C++ source code and bindings results
410+
in a change to the stubs, developers are notified of the need to regenerate
411+
the stubs, so that changes can be reviewed and the rules in `generate_stubs.py`
412+
can be updated, if necessary.
398413

399414
Test Images
400415
-----------

Makefile

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

246+
# generate python stubs and add them to the repo
247+
pystubs: config
248+
@ ( cd ${build_dir} ; \
249+
${CMAKE} --build . --config ${CMAKE_BUILD_TYPE} --target pystubs \
250+
)
251+
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+
246258
# 'make install' builds everthing and installs it in 'dist'.
247259
# Suppress pointless output from docs installation.
248260
install: build

pyproject.toml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,31 @@ 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 & validation of the python stubs for certain platforms.
129+
# The test command acts as a kind of "post-build" callback where it's possible
130+
# for the stub-generator to import the freshly-built wheel.
131+
# There are two entry-points which are designed to call generate_stubs.py through
132+
# this test command:
133+
# - `make pystubs` is called during local development to generate the
134+
# stubs and copy them into the git repo to be committed and reviewed.
135+
# - in CI, the cibuildwheel action is used to validate that the stubs match what
136+
# has been committed to the repo.
137+
test-requires = "mypy~=1.15.0 stubgenlib~=0.1.0"
138+
# Note: the python version here must be kept in sync with src/python/stubs/CMakeLists.txt
139+
select = "cp311-manylinux_*64"
140+
inherit.test-command = "append"
141+
test-command = [
142+
"python {project}/src/python/stubs/generate_stubs.py --out-path '/output' --validate-path '{project}/src/python/stubs/OpenImageIO/__init__.pyi'",
143+
]
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ 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 DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
159+
install (FILES __init__.py stubs/OpenImageIO/__init__.pyi stubs/OpenImageIO/py.typed
160+
DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
160161

161162
endmacro ()
162163

src/python/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# SPDX-License-Identifier: Apache-2.0
33
# https://github.com/AcademySoftwareFoundation/OpenImageIO
44

5-
5+
add_subdirectory (stubs)
66

77
file (GLOB python_srcs *.cpp)
88
setup_python_module (TARGET PyOpenImageIO
@@ -19,3 +19,4 @@ set_target_properties(PyOpenImageIO PROPERTIES
1919
UNITY_BUILD_BATCH_SIZE ${UNITY_SMALL_BATCH_SIZE})
2020
set_source_files_properties(${python_srcs} PROPERTIES
2121
UNITY_GROUP PyOpenImageIO)
22+

src/python/stubs/CMakeLists.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
# Setup the pystub target, which is not built by default.
3+
4+
set (_stub_file "${CMAKE_SOURCE_DIR}/src/python/stubs/OpenImageIO/__init__.pyi")
5+
6+
# Note: the python version must be kept in sync with `[[tool.cibuildwheel.overrides]]` in pyproject.toml.
7+
# The stubs are generated within a container so the version of python does not need to match
8+
# the version of python that OpenImageIO is being built against.
9+
# Note: the version of cibuildwheel should be kept in sync with .github/workflows/wheel.yml
10+
add_custom_command (COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/python/stubs/generate_stubs_local.py
11+
--repo-root ${CMAKE_SOURCE_DIR} --python-version="3.11" --cibuildwheel-version="2.21.1"
12+
--output-dir "${CMAKE_BINARY_DIR}/wheelhouse"
13+
OUTPUT "${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
14+
DEPENDS "${CMAKE_SOURCE_DIR}/src/python/stubs/generate_stubs.py"
15+
DEPENDS "${CMAKE_SOURCE_DIR}/src/python/stubs/generate_stubs_local.py"
16+
COMMENT "pystubs: Generating python stubs"
17+
)
18+
19+
add_custom_command (COMMAND ${CMAKE_COMMAND} -E copy
20+
"${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
21+
${_stub_file}
22+
OUTPUT ${_stub_file}
23+
DEPENDS "${CMAKE_BINARY_DIR}/wheelhouse/OpenImageIO/__init__.pyi"
24+
COMMENT "pystubs: Copying generated stubs to source"
25+
)
26+
27+
add_custom_target (pystubs DEPENDS ${_stub_file})

0 commit comments

Comments
 (0)