diff --git a/.github/workflows/python-package-tests.yml b/.github/workflows/python-package-tests.yml new file mode 100644 index 000000000..9a055ae27 --- /dev/null +++ b/.github/workflows/python-package-tests.yml @@ -0,0 +1,65 @@ +name: Python Package Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-wheel: + runs-on: ubuntu-latest + container: ghcr.io/gridtools/gridtools-base:${{ matrix.compiler }} + strategy: + matrix: + python-version: ["3.10"] + compiler: [gcc-10] + build_type: [release] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: install nox + run: | + python -m pip install --upgrade pip setuptools + python -m pip install nox + - name: run tests + working-directory: ./.python_package + run: nox -s test_src + - name: build and test wheel + working-directory: ./.python_package + run: nox -s build_wheel test_wheel_with_python-${{ matrix.python-version }} + - name: archive wheel + uses: actions/upload-artifact@v3 + with: + name: gridtools-cpp-wheel + path: .python_package/.nox/.cache/dist/gridtools_cpp-*.whl + + test-wheel: + needs: build-wheel + runs-on: ubuntu-latest + container: ghcr.io/gridtools/gridtools-base:${{ matrix.compiler }} + strategy: + matrix: + python-version: ["3.8", "3.9", "3.11"] + compiler: [gcc-10] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: install nox + run: | + python -m pip install --upgrade pip setuptools + python -m pip install nox + - name: download wheel + uses: actions/download-artifact@v3 + with: + name: gridtools-cpp-wheel + path: .python_package/.nox/.cache/dist + - name: test wheel + working-directory: ./.python_package + run: nox -s test_wheel_with_python-${{ matrix.python-version }} diff --git a/.python_package/README.md b/.python_package/README.md new file mode 100644 index 000000000..7f7716d60 --- /dev/null +++ b/.python_package/README.md @@ -0,0 +1,73 @@ +# Python package for GridTools headers and CMake files + +## Usage + +Use the following when compiling C++ code with GridTools programmatically from Python. +Either by calling a compiler directly or by generating a CMake project and calling CMake on it. + +```python +import gridtools_cpp +include_dir = gridtools_cpp.get_include_dir() # header files can be found here +cmake_dir = gridtools_cpp.get_cmake_dir() # cmake files can be found here +``` + +## Development + +In order to be able to work on this package, it is necessary to run a preparation step. +This will generate the `setup.cfg` file from `setup.cfg.in` and install the gridtools header distribution into the package data. +It will read the version number from the top-level `version.txt` and copy the `LICENSE` file to where packaging tools can find it. + +All of this requires `nox`, the preparation step runs in an isolated environment and installs additional requirements `cmake` and `ninja` at runtime. + +```bash +pip install nox +nox -s prepare +``` + +To delete all generated files run + +```bash +nox -s clean clean_cache +``` + +where `clean_cache` deletes chached files from Nox sessions like CMake builds and testing wheels (found in `.nox/.cache`), and `clean` deletes visible artifacts like `dist/`, `build/`, `.egg-info/`. +`setup.cfg` will not be deleted for convenience, to make sure tools keep functioning as expected. + +### Installing + +As always it is recommended to carry out the following steps in a virtual environment: + +```bash +nox -s build -- --wheel . +pip install dist/gridtools_cpp-2.2.0-py3-none-any.whl +``` + +### Testing + +Using nox, the tests will be carried out in isolated python environments for you: + +```bash +nox -s test_src +``` + +To test the wheel distribution specifically: + +```bash +nox -s build_wheel test_wheel_with_python-3.10 # replace 3.10 with the Python version you are running +``` + +### Advanced testing (all supported versions) + +The following requires you to have Python interpreters for Python 3.8, 3.9, 3.10 and 3.11 in your system path. + +```bash +nox +``` + +### Building for distribution + +Uses (`build`)[https://pypa-build.readthedocs.io/en/latest/], follow the link for available options. + +```bash +nox -s build -- +``` diff --git a/.python_package/noxfile.py b/.python_package/noxfile.py new file mode 100644 index 000000000..6cc2f52a9 --- /dev/null +++ b/.python_package/noxfile.py @@ -0,0 +1,126 @@ +# GridTools +# +# Copyright (c) 2014-2021, ETH Zurich +# All rights reserved. +# +# Please, refer to the LICENSE file in the root directory. +# SPDX-License-Identifier: BSD-3-Clause + +import configparser +import pathlib +import shutil + +import nox + + +_RELATIVE_DATA_DIR = pathlib.Path("src/gridtools_cpp/data") + + +nox.options.sessions = ["test_src", "test_wheel"] + + +@nox.session +def prepare(session: nox.Session): + session.install("cmake>=3.18.1") + session.install("ninja") + build_path = session.cache_dir.joinpath("build").absolute() + build_path.mkdir(exist_ok=True) + install_path = pathlib.Path(".").absolute() / _RELATIVE_DATA_DIR + source_path = pathlib.Path("..").absolute() + with session.chdir(build_path): + session.run( + "cmake", + "-DBUILD_TESTING=OFF", + "-DGT_INSTALL_EXAMPLES:BOOL=OFF", + f"-DCMAKE_INSTALL_PREFIX={install_path}", + "-GNinja", + str(source_path), + ) + session.run("cmake", "--install", ".") + session.log("installed gridttols sources") + version_path = source_path / "version.txt" + config = configparser.ConfigParser() + config.read("setup.cfg.in") + config["metadata"]["version"] = version_path.read_text().strip() + with open("setup.cfg", mode="w") as setup_fp: + config.write(setup_fp) + session.log("updated version metadata") + shutil.copy(source_path / "LICENSE", ".") + session.log("copied license file") + + +def get_wheel(session: nox.Session) -> pathlib.Path: + return list(session.cache_dir.joinpath("dist").glob("gridtools_cpp-*.whl"))[0] + + +@nox.session +def build_wheel(session: nox.Session): + prepare(session) + dist_path = session.cache_dir.joinpath("dist").absolute() + nox_workdir = pathlib.Path(".").absolute() + session.install("build[virtualenv]") + with session.chdir(session.cache_dir): + session.run( + "python", + "-m", + "build", + "--no-isolation", + "--wheel", + "-o", + str(dist_path), + str(nox_workdir), + ) + session.log(f"built wheel in {dist_path}") + session.log("\n".join(str(path) for path in dist_path.iterdir())) + + +@nox.session +def test_src(session: nox.Session): + prepare(session) + session.install(".") + session.install("pytest") + session.run("pytest", "tests", *session.posargs) + + +@nox.session +def test_wheel(session: nox.Session): + session.notify("build_wheel") + session.notify("test_wheel_with_python-3.8") + session.notify("test_wheel_with_python-3.9") + session.notify("test_wheel_with_python-3.10") + session.notify("test_wheel_with_python-3.11") + + +@nox.session(python=["3.8", "3.9", "3.10", "3.11"]) +def test_wheel_with_python(session: nox.Session): + wheel_path = get_wheel(session) + session.install("pytest") + session.install(str(wheel_path)) + session.run("pytest", "tests", *session.posargs) + + +@nox.session +def clean_cache(session: nox.Session): + for subtree in session.cache_dir.iterdir(): + shutil.rmtree(subtree, True) + + +@nox.session +def build(session: nox.Session): + prepare(session) + session.install("build[virtualenv]") + session.run("python", "-m", "build", "--no-isolation", *session.posargs) + + +@nox.session +def clean(session: nox.Session): + data_dir = _RELATIVE_DATA_DIR + session.log(f"rm -r {data_dir}") + shutil.rmtree(data_dir, True) + session.log("rm -r src/*.egg-info") + for egg_tree in pathlib.Path("src").glob("*.egg-info"): + shutil.rmtree(egg_tree, True) + session.log("rm -r dist") + shutil.rmtree("dist", True) + session.log("rm -r build") + shutil.rmtree("build", True) diff --git a/.python_package/pyproject.toml b/.python_package/pyproject.toml new file mode 100644 index 000000000..7b959378a --- /dev/null +++ b/.python_package/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=64", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/.python_package/setup.cfg.in b/.python_package/setup.cfg.in new file mode 100644 index 000000000..a0934e2ad --- /dev/null +++ b/.python_package/setup.cfg.in @@ -0,0 +1,44 @@ +[metadata] +name = gridtools-cpp +author = ETH Zurich +author_email = gridtools@cscs.ch +license_files = LICENSE # this file is automatically copied from the top level +long_description = file: README.md +long_description_content_type = text/markdown; charset=UTF-8 +url = https://gridtools.github.io +project_urls = + Source Code = https://github.com/GridTools/gridtools +platforms = any +version = # leave empty, automatically generated +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Operating System :: OS Independent + Programming Language :: Python :: 3 :: Only + Topic :: Scientific/Engineering :: Atmospheric Science + Topic :: Scientific/Engineering :: Mathematics + Topic :: Scientific/Engineering :: Physics + +[options] +packages = find_namespace: +package_dir = + = src +python_requires = >=3.8 + +[options.package_data] +* = + *.hpp + *.h + *.cmake + +[options.packages.find] +where = src +exclude = tests + +[tools:isort] +line_length = 100 + +[flake8] +max-line-length = 100 + diff --git a/.python_package/src/gridtools_cpp/__init__.py b/.python_package/src/gridtools_cpp/__init__.py new file mode 100644 index 000000000..6895d4475 --- /dev/null +++ b/.python_package/src/gridtools_cpp/__init__.py @@ -0,0 +1,20 @@ +# GridTools +# +# Copyright (c) 2014-2021, ETH Zurich +# All rights reserved. +# +# Please, refer to the LICENSE file in the root directory. +# SPDX-License-Identifier: BSD-3-Clause + +import pathlib + + +_file = pathlib.Path(__file__) + + +def get_cmake_dir() -> pathlib.Path: + return _file.parent / "data" / "lib" / "cmake" / "GridTools" + + +def get_include_dir() -> pathlib.Path: + return _file.parent / "data" / "include" diff --git a/.python_package/tests/test_dirs.py b/.python_package/tests/test_dirs.py new file mode 100644 index 000000000..1bb254b4e --- /dev/null +++ b/.python_package/tests/test_dirs.py @@ -0,0 +1,21 @@ +# GridTools +# +# Copyright (c) 2014-2021, ETH Zurich +# All rights reserved. +# +# Please, refer to the LICENSE file in the root directory. +# SPDX-License-Identifier: BSD-3-Clause + +import gridtools_cpp + + +def test_cmake_dir_contains_gridtools_cmake(): + main_config_file = gridtools_cpp.get_cmake_dir() / "GridToolsConfig.cmake" + assert main_config_file.exists() + assert main_config_file.read_text() + + +def test_include_dir_contains_headers(): + include_path = gridtools_cpp.get_include_dir() + assert include_path.exists() + assert len(list(include_path.rglob("*.hpp"))) > 0