Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python packaging #1720

Merged
merged 45 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
03b7de3
pip package for gt4py to depend on
Jul 14, 2022
2e13579
move pip package into subdir
Jul 14, 2022
43a1b10
Update pylibgt/setup.py
havogt Jul 22, 2022
51b7fd4
Update pylibgt/setup.py
havogt Jul 22, 2022
f81f6bb
remove print statements
Aug 2, 2022
6c3f99b
add test and CI
Aug 3, 2022
be6f794
fix piptest CI
Aug 3, 2022
37f68ff
set python version for pylib CI to 3.10
Aug 3, 2022
6d00ba2
use python instead of python3 in nox-ci
Aug 3, 2022
06333b8
add python setup step to nox-ci
Aug 3, 2022
2cea7bf
add workdir to nox-ci
Aug 3, 2022
e37f977
add tests
Aug 3, 2022
7662b29
add test for wheel build
Nov 11, 2022
41fa89a
fix headers for all py files
Nov 11, 2022
5fe6a99
fix licence header also for tests
Nov 11, 2022
35ef68f
add get_include_dir method and test
Nov 11, 2022
c4bb261
remove duplicate setup keys from cfg
Nov 11, 2022
8a9a674
renamed to .python_package
Nov 11, 2022
e2f1047
rename test module
Nov 11, 2022
0628d7f
update CI with new python package dir
Nov 14, 2022
2e1b28e
reusable build_wheel session with caching
Nov 15, 2022
ccaf5f8
rename python package github action
Nov 18, 2022
8e8be70
Update .python_package/py_src/gridtools/__init__.py
havogt Nov 21, 2022
d2289e9
clean up noxfile
Nov 22, 2022
c77d8ee
update minimum compatible python version to 3.4
Nov 22, 2022
23cc4eb
remove MANIFEST.in and `include_package_data=True`
Nov 22, 2022
1085e5b
add cmake build/install to nox instead of setup.py
Nov 23, 2022
c6155bf
change distribution name
Nov 23, 2022
7ddd678
update dist name in noxfile
Nov 24, 2022
6e2fec7
try testing wheel on matrix of python versions
Nov 24, 2022
e04a3b4
add dependency between build and test jobs
Nov 24, 2022
d17034e
set cwd of wheel upload step
Nov 24, 2022
393649a
change artifact path instead of pwd
Nov 24, 2022
006c084
download wheel to the right path
Nov 24, 2022
1483de8
cleanup and add package metadata
Nov 25, 2022
01eab36
do not rely on external `make` being present
Nov 25, 2022
2a96235
add description to metadata
Nov 25, 2022
be0095f
fix description metadata
Nov 25, 2022
5ac7494
add README, pkg name -> gridtools_cpp, setup.cfg -> setup.cfg.in
Nov 25, 2022
72c54b7
remove cmake install requirement
Nov 25, 2022
863c647
update usage section in README
Nov 25, 2022
eb9f344
update tests with new package name
Nov 25, 2022
353dc1d
update package name in preparation step
Nov 25, 2022
38b1596
change README.md
Dec 1, 2022
001b1ff
Merge branch 'master' into make-pip-installable
Dec 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/python-package-tests.yml
Original file line number Diff line number Diff line change
@@ -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"]
DropD marked this conversation as resolved.
Show resolved Hide resolved
compiler: [gcc-10]
DropD marked this conversation as resolved.
Show resolved Hide resolved
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 }}
73 changes: 73 additions & 0 deletions .python_package/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Python package for GridTools headers and CMake files

## Usage

Use the following when compiling C++ code agains GridTools programatically from Python.
DropD marked this conversation as resolved.
Show resolved Hide resolved
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
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

To delete all generated files run

```bash
# nox -s clean clean_cache
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

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
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

### Testing

Using nox, the tests will be carried out in isolated python environments for you:

```bash
# nox -s test_src
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

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
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

### 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
DropD marked this conversation as resolved.
Show resolved Hide resolved
```

### Building for distribution

Uses (`build`)[https://pypa-build.readthedocs.io/en/latest/], follow the link for available options.

```bash
# nox -s build -- <build options>
DropD marked this conversation as resolved.
Show resolved Hide resolved
```
126 changes: 126 additions & 0 deletions .python_package/noxfile.py
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably you're already aware, but configparser removes all comments and formatting from the original file. It's not very important in this case, though, but it is not nice. The only alternative I know which preserves format is ConfigUpdate (https://pypi.org/project/ConfigUpdater/) but is a third-party project

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]")
Copy link
Contributor

@egparedes egparedes Nov 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a comment, maybe it would be good to add all packages used or installed here (build, cmake, ...) to the list of build_system.requires in pyproject.toml to make the dependencies explicit.

Copy link
Contributor Author

@DropD DropD Nov 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add them for documentation purposes, even they are not build requirements anymore. They are pre-build requirements. In the PEP-517 conformant isolated build environment, they don't have to be present.

A compromise might be to add them commented-out, I guess?

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)
3 changes: 3 additions & 0 deletions .python_package/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools>=64", "wheel"]
build-backend = "setuptools.build_meta"
44 changes: 44 additions & 0 deletions .python_package/setup.cfg.in
Original file line number Diff line number Diff line change
@@ -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

20 changes: 20 additions & 0 deletions .python_package/src/gridtools_cpp/__init__.py
Original file line number Diff line number Diff line change
@@ -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"
21 changes: 21 additions & 0 deletions .python_package/tests/test_dirs.py
Original file line number Diff line number Diff line change
@@ -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