diff --git a/.github/workflows/python-bindings.yml b/.github/workflows/python-bindings.yml index 32c32d6..15eb4cc 100644 --- a/.github/workflows/python-bindings.yml +++ b/.github/workflows/python-bindings.yml @@ -11,47 +11,48 @@ concurrency: cancel-in-progress: true jobs: python-tests: - name: "Test python bindings" + name: "Test Python bindings" strategy: matrix: - on: [ 'ubuntu-24.04', 'macos-15-intel' , 'macos-26' ] + on: [ 'ubuntu-24.04', 'macos-15-intel', 'macos-26' ] python: [ '3.10', '3.11', '3.12', '3.13' ] - runs-on: ${{matrix.on}} + runs-on: ${{ matrix.on }} env: INSTALL_PREFIX: "/usr/local" steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v6 + - name: "Checkout repository" + uses: actions/checkout@v4 + + - name: "Set up Python ${{ matrix.python }}" + uses: actions/setup-python@v6 with: - python-version: ${{matrix.python}} - - name: "Install packages ubuntu" - if: ${{ startsWith(matrix.on, 'ubuntu-') }} - run: sudo apt install -y ninja-build g++ + python-version: ${{ matrix.python }} + + # Install platform build dependencies + - name: "Install system packages (Ubuntu)" + if: startsWith(matrix.on, 'ubuntu-') + run: sudo apt-get update && sudo apt-get install -y ninja-build g++ cmake - - name: "Setup macos runner" - if: ${{ startsWith(matrix.on, 'macos-') }} + - name: "Setup Homebrew (macOS)" + if: startsWith(matrix.on, 'macos-') uses: Homebrew/actions/setup-homebrew@main - - name: "Install macos packages" - if: ${{ startsWith(matrix.on, 'macos-') }} + - name: "Install system packages (macOS)" + if: startsWith(matrix.on, 'macos-') run: brew install ninja gcc cmake - - name: "Run CMake" + # Install Python build dependencies + - name: "Build and Install Python wheel" run: | - cmake -DCMAKE_BUILD_TYPE=Release \ - -G Ninja \ - -B ../build \ - -S ${GITHUB_WORKSPACE} - cmake --build ../build -j $(nproc) - sudo cmake --install ../build --prefix $INSTALL_PREFIX - - - name: "Install python test environment" - run: python -m pip install -r ${GITHUB_WORKSPACE}/test-requirement.txt + python -m pip install --upgrade pip + python -m pip install -r build-requirements.txt + python -m build + pip install dist/*.whl - - name: "Run python tests" + # Run unit tests + - name: "Run Python tests" run: | - export CAPIO_CL_PY_BINDING_PATH=$(realpath $INSTALL_PREFIX/lib/python/py_capio_cl*) - echo "Python module path: $CAPIO_CL_PY_BINDING_PATH" - pytest ${GITHUB_WORKSPACE}/tests/python/test_* \ No newline at end of file + python -m pip install -r test-requirements.txt + pytest -v tests/python/test_* diff --git a/.gitignore b/.gitignore index 5fafb18..ec1a7cb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ cmake-build-*/** .venv/** __pycache__/ *.pyc +dist/** # Prerequisites *.d diff --git a/CMakeLists.txt b/CMakeLists.txt index c5112a6..8097fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ install(TARGETS ${TARGET_NAME} ##################################### if (BUILD_PYTHON_BINDINGS) - set(PYTHON_BIND_NAME py_capio_cl) + set(PYTHON_BIND_NAME _py_capio_cl) pybind11_add_module(${PYTHON_BIND_NAME} bindings/python_bindings.cpp @@ -97,11 +97,7 @@ if (BUILD_PYTHON_BINDINGS) ${CMAKE_CURRENT_SOURCE_DIR} ) - install(TARGETS ${PYTHON_BIND_NAME} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/python - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/python - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) + install(TARGETS _py_capio_cl DESTINATION py_capio_cl) endif () diff --git a/README.md b/README.md index 831070e..dcc7a1f 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,15 @@ These bindings expose the core C++ APIs—such as `Engine`, `Parser`, directly to Python, allowing the CAPIO-CL logic to be used within python projects. ### 🔧 Building the Bindings -To include Python support when building CAPIO-CL: +You can build and install the Python bindings directly from the CAPIO-CL source tree using: ```bash -cmake -DBUILD_PYTHON_BINDINGS=ON .. -cmake --build . --target py_capio_cl +pip install --upgrade pip +pip install -r build-requirements.txt +python -m build +pip install dst/*.whl ``` +Now you will be able to directly import the package **py_capio_cl** in your project! --- diff --git a/bindings/python_bindings.cpp b/bindings/python_bindings.cpp index c612bba..9a0f50e 100644 --- a/bindings/python_bindings.cpp +++ b/bindings/python_bindings.cpp @@ -5,7 +5,7 @@ namespace py = pybind11; -PYBIND11_MODULE(py_capio_cl, m) { +PYBIND11_MODULE(_py_capio_cl, m) { m.doc() = "CAPIO-CL: Cross Application Programmable I/O - Coordination Language python bindings."; py::class_( diff --git a/build-requirements.txt b/build-requirements.txt new file mode 100644 index 0000000..09d7689 --- /dev/null +++ b/build-requirements.txt @@ -0,0 +1 @@ +build==1.3.0 \ No newline at end of file diff --git a/py_capio_cl/__init__.py b/py_capio_cl/__init__.py new file mode 100644 index 0000000..428aaff --- /dev/null +++ b/py_capio_cl/__init__.py @@ -0,0 +1 @@ +from ._py_capio_cl import * \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..711b17b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = [ + "scikit-build-core[pyproject]==0.11.6", + "pybind11==3.0.1", + "setuptools==80.9.0", + "wheel==0.45.1" +] +build-backend = "scikit_build_core.build" + +[project] +name = "py_capio_cl" +dynamic = ["version"] +description = "Python bindings for the CAPIO-CL coordination language" +readme = "README.md" +license = { text = "MIT" } +requires-python = ">=3.10" +authors = [ + { name = "Marco Edoardo Santimaria", email = "marcoedoardo.santimaria@unito.it" } +] + +[project.urls] +Homepage = "https://capio.hpc4ai.it" +Documentation = "https://capio.hpc4ai.it/docs/coord-language/" +Repository = "https://github.com/High-Performance-IO/CAPIO-CL" +Issues = "https://github.com/High-Performance-IO/CAPIO-CL/issues" + + +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.regex" +input = "CMakeLists.txt" +regex = "(?m)VERSION\\s+(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" +result = "{major}.{minor}.{patch}" + +[tool.scikit-build] +cmake.version = ">=3.18" +ninja.version = ">=1.10" diff --git a/test-requirement.txt b/test-requirements.txt similarity index 100% rename from test-requirement.txt rename to test-requirements.txt diff --git a/tests/python/test_engine.py b/tests/python/test_engine.py index dab0f94..48c8375 100644 --- a/tests/python/test_engine.py +++ b/tests/python/test_engine.py @@ -1,28 +1,4 @@ -''' -TEST(testpy_capio_clEngine, testAddFileDefault) { - py_capio_cl::Engine engine; -EXPECT_EQ(engine.size(), 0); -engine.newFile("test.dat"); -EXPECT_EQ(engine.size(), 1); -EXPECT_EQ(engine.getCommitRule("test.dat"), py_capio_cl::COMMITTED_ON_TERMINATION); -EXPECT_EQ(engine.getFireRule("test.dat"), py_capio_cl::MODE_UPDATE); -EXPECT_TRUE(engine.getConsumers("test.dat").empty()); -EXPECT_TRUE(engine.getProducers("test.dat").empty()); -EXPECT_FALSE(engine.isPermanent("test.dat")); -EXPECT_FALSE(engine.isExcluded("test.dat")); -EXPECT_TRUE(engine.isFile("test.dat")); -EXPECT_FALSE(engine.isDirectory("test.dat")); -EXPECT_EQ(engine.getDirectoryFileCount("test.dat"), 0); -EXPECT_FALSE(engine.isStoredInMemory("test.dat")); -} -''' - -import os -import importlib.util - -spec = importlib.util.spec_from_file_location("py_capio_cl", os.getenv("CAPIO_CL_PY_BINDING_PATH")) -py_capio_cl = importlib.util.module_from_spec(spec) -spec.loader.exec_module(py_capio_cl) +import py_capio_cl def test_instantiation():