Skip to content

Commit 9c6cd8e

Browse files
Griger5slayoo
andauthored
Switch PyPartMC's binding system from pybind11 to nanobind (#431)
Co-authored-by: Sylwester Arabas <[email protected]>
1 parent 1da8001 commit 9c6cd8e

34 files changed

+388
-467
lines changed

.github/workflows/buildwheels.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
name: Build wheels on ${{ matrix.os }}
4040
runs-on: ${{ matrix.os }}
4141
strategy:
42+
fail-fast: false
4243
matrix:
4344
os: [ubuntu-latest, ubuntu-24.04-arm, macos-13, macos-latest, windows-latest]
4445

.github/workflows/codecov.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ jobs:
1414
with:
1515
submodules: recursive
1616
fetch-depth: 0
17+
- uses: actions/setup-python@v1
18+
with:
19+
python-version: "3.12"
1720
- run: sudo apt install -y lcov
1821
- run: |
1922
set -xe
20-
pip install "pip<23" setuptools==65.2
21-
CMAKE_ARGS="-DCMAKE_CXX_FLAGS=--coverage -DCMAKE_C_FLAGS=--coverage -DCMAKE_Fortran_FLAGS=--coverage" pip install -e .[tests]
23+
CMAKE_ARGS="-DCMAKE_CXX_FLAGS=--coverage -DCMAKE_C_FLAGS=--coverage -DCMAKE_Fortran_FLAGS=--coverage" pip install --config-settings=build-dir=build -e .[tests]
2224
pytest -We tests
23-
lcov --capture --directory ./build/*/_PyPartMC/CMakeFiles/_PyPartMC.dir/ --output-file coverage.info --no-external
25+
lcov --capture --directory ./build/CMakeFiles/_PyPartMC.dir/ --output-file coverage.info --no-external
2426
- run: find -name \*.gcno | grep -v pypartmc.cpp | grep -v _PyPartMC.dir | xargs rm
2527
- uses: codecov/[email protected]
2628
with:

.github/workflows/readme_listings.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ jobs:
132132
- run: python -c "import pytest_codeblocks; code=pytest_codeblocks.extract_from_file('README.md'); f=open('readme.m', 'w'); f.writelines(block.code for block in code if block.syntax=='Matlab'); f.close()"
133133
- run: cat -n readme.m
134134

135-
# see https://github.com/pybind/cmake_example/pull/164
136-
- run: |
137-
echo "pybind11_type=type" > pybind11_builtins.py
138-
echo "PYTHONPATH=." >> $GITHUB_ENV
139-
140135
- uses: matlab-actions/setup-matlab@v0
141136
with:
142137
release: R2022a

.gitmodules

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
[submodule "pybind11"]
2-
path = gitmodules/pybind11
3-
url = https://github.com/pybind/pybind11
4-
shallow = true
51
[submodule "partmc"]
62
path = gitmodules/partmc
73
url = https://github.com/compdyn/partmc
84
shallow = true
9-
[submodule "pybind11_json"]
10-
path = gitmodules/pybind11_json
11-
url = https://github.com/pybind/pybind11_json
12-
shallow = true
135
[submodule "json"]
146
path = gitmodules/json
157
url = https://github.com/nlohmann/json
@@ -58,3 +50,13 @@
5850
path = gitmodules/hdf5
5951
url = https://github.com/HDFGroup/hdf5.git
6052
shallow = true
53+
[submodule "gitmodules/nanobind"]
54+
path = gitmodules/nanobind
55+
url = https://github.com/wjakob/nanobind
56+
[submodule "gitmodules/nanobind_json"]
57+
path = gitmodules/nanobind_json
58+
url = https://github.com/Griger5/nanobind_json
59+
[submodule "."]
60+
branch = pypartmc
61+
[submodule "nanobind_json"]
62+
branch = pypartmc

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
files: '.py'
22
exclude: '.git'
3-
default_stages: [commit]
3+
default_stages: [pre-commit]
44

55
repos:
66
- repo: https://github.com/psf/black
7-
rev: 23.1.0
7+
rev: 25.1.0
88
hooks:
99
- id: black
1010

1111
- repo: https://github.com/timothycrosley/isort
12-
rev: 5.12.0
12+
rev: 6.0.1
1313
hooks:
1414
- id: isort
1515
args: ["--profile", "black"]
1616

1717
- repo: https://github.com/pre-commit/pre-commit-hooks
18-
rev: v4.4.0
18+
rev: v5.0.0
1919
hooks:
2020
- id: trailing-whitespace
2121
- id: end-of-file-fixer

CMakeLists.txt

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Author: Sylwester Arabas #
55
####################################################################################################
66

7-
cmake_minimum_required(VERSION 3.8) # CXX17
7+
cmake_minimum_required(VERSION 3.15) # CXX17
88

99
file(GLOB mods RELATIVE ${CMAKE_SOURCE_DIR} gitmodules/*)
1010
foreach(mod ${mods})
@@ -16,7 +16,10 @@ endforeach()
1616

1717
project(_PyPartMC LANGUAGES C CXX Fortran)
1818

19-
find_package(PythonInterp REQUIRED)
19+
find_package(Python 3.8
20+
REQUIRED COMPONENTS Interpreter Development.Module
21+
OPTIONAL_COMPONENTS Development.SABIModule)
22+
message(STATUS "Python_EXECUTABLE= ${Python_EXECUTABLE}")
2023

2124
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
2225

@@ -33,6 +36,12 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
3336
add_compile_options($<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:DEBUG>>:-fcheck=bounds>)
3437
endif()
3538

39+
# Shadow the CMake intrinsic install function so that included CMake code from dependency submodules does not
40+
# interfere with "make install" issued by scikit-build (note that our intentional install() call is replaced with
41+
# _install() below - following https://cmake.org/pipermail/cmake/2011-March/043320.html)
42+
macro(install)
43+
endmacro(install)
44+
3645
macro(add_prefix prefix rootlist)
3746
set(outlist)
3847
foreach(root ${${rootlist}})
@@ -511,15 +520,17 @@ if(DEFINED ENV{MOSAIC_HOME})
511520
)
512521
endif()
513522

514-
### PYBIND11 & PyPartMC ############################################################################
523+
### NANOBIND & PyPartMC ############################################################################
524+
find_package(nanobind CONFIG REQUIRED)
515525

516-
add_subdirectory(gitmodules/pybind11)
517-
pybind11_add_module(_PyPartMC ${PyPartMC_sources})
526+
add_subdirectory(gitmodules/nanobind)
527+
nanobind_add_module(_PyPartMC STABLE_ABI ${PyPartMC_sources})
518528
add_dependencies(_PyPartMC partmclib)
519529
set(PYPARTMC_INCLUDE_DIRS
520530
"${CMAKE_BINARY_DIR}/include;"
521531
"${CMAKE_SOURCE_DIR}/gitmodules/json/include;"
522-
"${CMAKE_SOURCE_DIR}/gitmodules/pybind11_json/include;"
532+
"${CMAKE_SOURCE_DIR}/gitmodules/nanobind/include;"
533+
"${CMAKE_SOURCE_DIR}/gitmodules/nanobind_json/include;"
523534
"${CMAKE_SOURCE_DIR}/gitmodules/span/include;"
524535
"${CMAKE_SOURCE_DIR}/gitmodules/string_view-standalone/include;"
525536
"${CMAKE_SOURCE_DIR}/gitmodules/optional/include;"
@@ -545,7 +556,7 @@ endif()
545556
foreach(target _PyPartMC)
546557
target_compile_options(${target} PRIVATE
547558
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
548-
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
559+
# $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
549560
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-unused-parameter>
550561
)
551562
endforeach()
@@ -555,31 +566,32 @@ file(GLOB PyPartMC_headers ${CMAKE_SOURCE_DIR}/src/*.hpp)
555566
if (NOT "${CMAKE_REQUIRED_INCLUDES}" STREQUAL "")
556567
message("CMAKE_REQUIRED_INCLUDES not empty! (${CMAKE_REQUIRED_INCLUDES})")
557568
endif()
558-
foreach(file ${PyPartMC_headers})
559-
set(CMAKE_REQUIRED_INCLUDES "${PYPARTMC_INCLUDE_DIRS};${pybind11_INCLUDE_DIRS}")
560-
set(CMAKE_REQUIRED_FLAGS "-Werror")
561-
string(REGEX REPLACE "[\-./:]" "_" file_var ${file})
562-
check_cxx_source_compiles("
563-
// https://github.com/nlohmann/json/issues/1408
564-
#if defined(_WIN32) || defined(_WIN64)
565-
# define HAVE_SNPRINTF
566-
#endif
567-
#include \"${file}\"
568-
int main() { return 0;}
569-
"
570-
_header_self_contained_${file_var}
571-
)
572-
unset(CMAKE_REQUIRED_INCLUDES)
573-
unset(CMAKE_REQUIRED_FLAGS)
574-
if (NOT _header_self_contained_${file_var})
575-
message(SEND_ERROR "non-self-contained header: ${file}")
576-
if (${CMAKE_VERSION} VERSION_LESS "3.26.0")
577-
file(READ "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log" tmp)
578-
else()
579-
file(READ "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeConfigureLog.yaml" tmp)
580-
endif()
581-
message(FATAL_ERROR ${tmp})
582-
endif()
583-
unset(file_var)
584-
endforeach()
585-
569+
# foreach(file ${PyPartMC_headers})
570+
# set(CMAKE_REQUIRED_INCLUDES "${PYPARTMC_INCLUDE_DIRS};${pybind11_INCLUDE_DIRS}")
571+
# set(CMAKE_REQUIRED_FLAGS "-Werror")
572+
# string(REGEX REPLACE "[\-./:]" "_" file_var ${file})
573+
# check_cxx_source_compiles("
574+
# // https://github.com/nlohmann/json/issues/1408
575+
# #if defined(_WIN32) || defined(_WIN64)
576+
# # define HAVE_SNPRINTF
577+
# #endif
578+
# #include \"${file}\"
579+
# int main() { return 0;}
580+
# "
581+
# _header_self_contained_${file_var}
582+
# )
583+
# unset(CMAKE_REQUIRED_INCLUDES)
584+
# unset(CMAKE_REQUIRED_FLAGS)
585+
# if (NOT _header_self_contained_${file_var})
586+
# message(SEND_ERROR "non-self-contained header: ${file}")
587+
# if (${CMAKE_VERSION} VERSION_LESS "3.26.0")
588+
# file(READ "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log" tmp)
589+
# else()
590+
# file(READ "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeConfigureLog.yaml" tmp)
591+
# endif()
592+
# message(FATAL_ERROR ${tmp})
593+
# endif()
594+
# unset(file_var)
595+
# endforeach()
596+
597+
_install(TARGETS _PyPartMC LIBRARY DESTINATION PyPartMC)

CONTRIBUTING.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
## Implementation outline
66

7-
- PyPartMC is written in C++, Fortran and uses [pybind11](https://pybind11.readthedocs.io/en/stable/) and [CMake](https://cmake.org/).
8-
- JSON support is handled with [nlohmann::json](https://github.com/nlohmann/json) and [pybind11_json](https://github.com/pybind/pybind11_json)
7+
- PyPartMC is written in C++, Fortran and uses [nanobind](https://nanobind.readthedocs.io/) and [CMake](https://cmake.org/).
8+
- JSON support is handled with [nlohmann::json](https://github.com/nlohmann/json) and [nanobind_json](https://github.com/ianhbell/nanobind_json)
99
- PartMC and selected parts of SUNDIALS are statically linked (and compiled in during `pip install` or `python -m build`)
10-
- C (SUNDIALS, netCDF), C++ (pybind11, ...) and Fortran (PartMC, CAMP, netCDF-fortran) dependencies are linked through [git submodules](https://github.com/open-atmos/PyPartMC/blob/main/.gitmodules)
10+
- C (SUNDIALS, netCDF), C++ (nanobind, ...) and Fortran (PartMC, CAMP, netCDF-fortran) dependencies are linked through [git submodules](https://github.com/open-atmos/PyPartMC/blob/main/.gitmodules)
1111
- MOSAIC dependency is optionally linked through setting the environmental variable `MOSAIC_HOME`
1212
- a [drop-in replacement of the PartMC spec file routines](https://github.com/open-atmos/PyPartMC/blob/main/src/spec_file_pypartmc.F90) is used for i/o from/to JSON
1313

@@ -23,15 +23,15 @@ flowchart TD
2323
end
2424
subgraph P ["Python"]
2525
python_user_code -.-> NumPy
26-
python_user_code["Python user code"] ---> PyPartMC["pubind11-generated\nPyPartMC module"]
26+
python_user_code["Python user code"] ---> PyPartMC["nanobind-generated\nPyPartMC module"]
2727
matlab_python --> PyPartMC
2828
PyCall.jl --> PyPartMC
2929
end
3030
subgraph Cpp ["C++"]
3131
cpp_user_code["C++ user code"] ----> ppmc_cpp
3232
PyPartMC --> ppmc_cpp["PyPartMC-C++"]
33-
ppmc_cpp --> pybind11_json
34-
pybind11_json ---> nlohmann::JSON
33+
ppmc_cpp --> nanobind_json
34+
nanobind_json ---> nlohmann::JSON
3535
spec_file_pypartmc_cpp --> nlohmann::JSON
3636
end
3737
subgraph C ["C"]

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ print(aero_state.masses()'aero_state.num_concs, "# kg/m3")
208208
notes (see the [PyPartMC Matlab CI workflow](https://github.com/open-atmos/PyPartMC/blob/main/.github/workflows/readme_listings.yml) for an example on how to achieve it on Ubuntu 20):
209209
- Matlab ships with convenience copies of C, C++ and Fortran runtime libraries which are `dlopened()` by default; one way to make PyPartMC OK with it is to [pip-]install by compiling from source using the very same version of GCC that Matlab borrowed these libraries from (e.g., [GCC 9 for Matlab R2022a, etc](https://www.mathworks.com/support/requirements/supported-compilers-linux.html));
210210
- Matlab needs to [use the same Python interpretter/venv](https://www.mathworks.com/support/requirements/python-compatibility.html) as the pip invocation used to install PyPartMC;
211-
- a single-line `pybind11_builtins.py` file with just `pybind11_type=type` inside needs to be placed within Matlab's `PYTHONPATH` to sort out a [Matlab-pybind11 incompatibility](https://github.com/pybind/pybind11/issues/3945).
212211

213212
````Matlab
214213
ppmc = py.importlib.import_module('PyPartMC');

gitmodules/nanobind

Submodule nanobind added at d4b245a

gitmodules/nanobind_json

Submodule nanobind_json added at 6e9e15f

0 commit comments

Comments
 (0)