diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 662935de..00000000 --- a/.coveragerc +++ /dev/null @@ -1,5 +0,0 @@ -[run] -omit = - */h3/api/basic_int/__init__.py - */h3/api/memview_int/__init__.py - */h3/api/numpy_int/__init__.py diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 0ad48bab..328dc449 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -22,8 +22,6 @@ jobs: - name: Install dependencies run: | pip install --upgrade pip setuptools wheel - pip install -r requirements.in - pip install -r requirements-dev.txt pip install .[all] - name: Build the book diff --git a/.github/workflows/coverage-lint.yml b/.github/workflows/coverage-lint.yml index 24ec82e1..08021693 100644 --- a/.github/workflows/coverage-lint.yml +++ b/.github/workflows/coverage-lint.yml @@ -23,10 +23,10 @@ jobs: - name: Install from source run: | pip install --upgrade pip setuptools wheel - pip install .[all] + pip install .[test] - name: Lint - run: flake8 src/h3 setup.py tests + run: flake8 src/h3 tests - name: Pylint # As a test for visibility of API bindings, we want to ensure that pylint has no @@ -35,9 +35,9 @@ jobs: - name: Coverage run: | - pip install Cython - cythonize -i tests/test_cython/cython_example.pyx - pytest --cov=h3 --full-trace --cov-report=xml + pip install cython + cythonize tests/test_cython/cython_example.pyx + pytest --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v4.3.0 @@ -45,14 +45,3 @@ jobs: file: ./coverage.xml fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} - - - name: Copy Cython annotations to project dir - run: | - mkdir annotations - cp _skbuild/*/cmake-build/src/h3/_cy/*.html ./annotations - - - name: Upload artifacts to GitHub - uses: actions/upload-artifact@v4.3.1 - with: - name: annotations - path: ./annotations diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ee175036..8075b013 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,10 +39,10 @@ jobs: - name: Install from source run: | pip install --upgrade pip setuptools wheel - pip install .[all] + pip install .[test] - name: Tests run: | - pip install Cython - cythonize -i tests/test_cython/cython_example.pyx - pytest tests --cov=h3 --full-trace + pip install cython + cythonize tests/test_cython/cython_example.pyx + pytest diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 91a9e447..659a1939 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -41,12 +41,11 @@ jobs: - name: Install from sdist run: | pip install --upgrade pip setuptools wheel - pip install pytest cp dist/h3-*.tar.gz h3.tar.gz - pip install h3.tar.gz[all] + pip install h3.tar.gz[test] - name: Test sdist - run: pytest --cov=h3 --full-trace + run: pytest - name: Upload artifacts to GitHub uses: actions/upload-artifact@v4.3.1 @@ -76,12 +75,6 @@ jobs: build: 'cp*-manylinux_x86_64' name: Linux Intel glibc 64-bit - - os: ubuntu-22.04 - build: 'cp36-musllinux_x86_64' - name: Linux Intel musl 64-bit 3.6 - - os: ubuntu-22.04 - build: 'cp37-musllinux_x86_64' - name: Linux Intel musl 64-bit 3.7 - os: ubuntu-22.04 build: 'cp38-musllinux_x86_64' name: Linux Intel musl 64-bit 3.8 @@ -98,12 +91,6 @@ jobs: build: 'cp312-musllinux_x86_64' name: Linux Intel musl 64-bit 3.12 - - os: ubuntu-22.04 - build: 'cp36-manylinux_aarch64' - name: Linux Aarch64 3.6 - - os: ubuntu-22.04 - build: 'cp37-manylinux_aarch64' - name: Linux Aarch64 3.7 - os: ubuntu-22.04 build: 'cp38-manylinux_aarch64' name: Linux Aarch64 3.8 @@ -143,7 +130,7 @@ jobs: - uses: pypa/cibuildwheel@v2.17.0 env: - CIBW_TEST_REQUIRES: pytest numpy + CIBW_TEST_REQUIRES: pytest pytest-cov numpy CIBW_TEST_COMMAND: pytest {project}/tests CIBW_ARCHS_LINUX: auto aarch64 CIBW_BUILD: ${{ matrix.build }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 85ea71cc..40ce38e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,16 @@ -cmake_minimum_required(VERSION 3.7.2) +cmake_minimum_required(VERSION 3.15...3.26) -project(h3) +project(${SKBUILD_PROJECT_NAME} LANGUAGES C) set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Always make a release build set(CMAKE_BUILD_TYPE Release) +find_package( + Python + COMPONENTS Interpreter Development.Module + REQUIRED) + # Avoid building tooling we won't need for release # See all options with `cmake -LA` in an `h3/build` directory, # or at https://h3geo.org/docs/next/core-library/compilation-options/ @@ -37,4 +42,4 @@ install( FILES "${CMAKE_CURRENT_BINARY_DIR}/src/h3lib/src/h3lib/include/h3api.h" DESTINATION - src/h3/_cy) + ${SKBUILD_PROJECT_NAME}/_cy) diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 6ccde1f6..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,24 +0,0 @@ -# MANIFEST.in commands specified here: -# https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands - -include CHANGELOG.md -include CMakeLists.txt -include LICENSE -include makefile -include pyproject.toml -include readme.md -include requirements.in -include setup.py - -graft src/h3/ - -prune src/h3lib/ - -include src/h3lib/LICENSE -include src/h3lib/README.md -include src/h3lib/VERSION -include src/h3lib/CMakeLists.txt - -graft src/h3lib/cmake -graft src/h3lib/src/h3lib -exclude MANIFEST.in diff --git a/makefile b/makefile index 629caa7f..a838caa0 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ PYTHON=$(shell command -v python || command -v python3) build-docs: - ./env/bin/pip install -r requirements-dev.txt + ./env/bin/pip install .[all] ./env/bin/jupyter-book build docs/ --warningiserror --keep-going --all open: @@ -14,39 +14,35 @@ init: purge git submodule update --init $(PYTHON) -m venv env ./env/bin/pip install --upgrade pip wheel setuptools - ./env/bin/pip install .[all] - ./env/bin/pip install -r requirements.in + ./env/bin/pip install .[test] clear: -./env/bin/pip uninstall -y h3 -@rm -rf MANIFEST - -@rm -rf annotations - -@rm -rf .pytest_cache _skbuild dist .coverage build docs/_build + -@rm -rf .pytest_cache _skbuild dist .coverage build docs/_build .ruff_cache -@find . -type d -name '__pycache__' | xargs rm -r -@find . -type d -name '*.egg-info' | xargs rm -r -@find . -type f -name '*.pyc' | xargs rm -r -@find . -type f -name '*.so' | xargs rm -r -@find . -type d -name '*.ipynb_checkpoints' | xargs rm -r -@find ./tests -type f -name '*.c' | xargs rm -r + -@find ./tests -type f -name '*.html' | xargs rm -r rebuild: clear - ./env/bin/pip install .[all] + ./env/bin/pip install .[test] purge: clear -@rm -rf env -annotations: rebuild - mkdir -p annotations - cp _skbuild/*/cmake-build/src/h3/_cy/*.html ./annotations - test: - ./env/bin/cythonize -i tests/test_cython/cython_example.pyx - ./env/bin/pytest tests --cov=h3 --cov-report term-missing --durations=10 + ./env/bin/pip install cython + ./env/bin/cythonize tests/test_cython/cython_example.pyx + ./env/bin/pytest lint: - ./env/bin/flake8 src/h3 setup.py tests + ./env/bin/flake8 src/h3 tests ./env/bin/pylint --disable=all --enable=import-error tests/ lab: - ./env/bin/pip install -r requirements-dev.txt + ./env/bin/pip install .[all] ./env/bin/jupyter lab diff --git a/pyproject.toml b/pyproject.toml index ce6867f8..c6a6ddbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,93 @@ [build-system] -requires = [ - 'scikit-build', - 'cython', - 'cmake', +requires = ['scikit-build-core', 'cython'] +build-backend = 'scikit_build_core.build' + +[project] +name = 'h3' +version = '4.0.0b6' +description = "Uber's hierarchical hexagonal geospatial indexing system" +readme = 'readme.md' +license = {file = 'LICENSE'} +authors = [ + { name = 'Uber Technologies', email = 'ajfriend@gmail.com' }, +] +maintainers = [ + { name = 'AJ Friend', email = 'ajfriend@gmail.com' }, +] +requires-python = '>=3.8' + +dependencies = [] + +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: C', + 'Programming Language :: Cython', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft :: Windows', + 'Topic :: Scientific/Engineering :: GIS', +] + +[project.urls] +Homepage = 'https://github.com/organization/package' +Documentation = 'https://package.readthedocs.io/' +'Bug Tracker' = 'https://github.com/organization/package/issues' +Discussions = 'https://github.com/organization/package/discussions' +Changelog = 'https://package.readthedocs.io/en/latest/changelog.html' + + +[project.optional-dependencies] +numpy = ['numpy'] +test = ['pytest', 'pytest-cov', 'flake8', 'pylint', 'numpy'] +all = [ + 'jupyter-book', + 'flake8', + 'sphinx>=7.3.3', # https://github.com/sphinx-doc/sphinx/issues/12290 + 'jupyterlab', + 'jupyterlab-geojson', + 'geopandas', + 'geodatasets', + 'matplotlib', + 'contextily', + 'cartopy', + 'geoviews', + 'numpy', + 'pytest', + 'pytest-cov', + 'pylint', +] + +[tool.pytest.ini_options] +addopts = "--cov=h3 --cov=tests --cov-report=term-missing --durations=10" + +[tool.coverage.run] +omit = [ + '*/h3/api/basic_int/__init__.py', + '*/h3/api/memview_int/__init__.py', + '*/h3/api/numpy_int/__init__.py' +] + + +[tool.scikit-build] +sdist.exclude = [ + 'src/h3lib', + 'docs', + 'dev_notes.md', + 'makefile', +] +sdist.include = [ + 'src/h3lib/LICENSE', + 'src/h3lib/README.md', + 'src/h3lib/VERSION', + 'src/h3lib/CMakeLists.txt', + 'src/h3lib/cmake/*', + 'src/h3lib/src/h3lib/*' ] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index c6ffe776..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,8 +0,0 @@ -jupyterlab -jupyterlab-geojson -geopandas -geodatasets -matplotlib -contextily -cartopy -geoviews diff --git a/requirements.in b/requirements.in deleted file mode 100644 index dc355b97..00000000 --- a/requirements.in +++ /dev/null @@ -1,15 +0,0 @@ -# Required for pip-compile until -# https://github.com/jazzband/pip-tools/issues/1047 is resolved. -# -# In the meantime, this information is duplicated from -# pyproject.toml - -scikit-build -cython -cmake - -# for docs - -jupyter-book -flake8 -sphinx>=7.3.3 # https://github.com/sphinx-doc/sphinx/issues/12290 diff --git a/setup.py b/setup.py deleted file mode 100644 index 23353b18..00000000 --- a/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -from setuptools import find_packages -from skbuild import setup - -here = os.path.abspath(os.path.dirname(__file__)) - -about = {} -with open(os.path.join(here, 'src', 'h3', '_version.py')) as f: - exec(f.read(), about) - - -def long_desc(): - here = os.path.abspath(os.path.dirname(__file__)) - fname = os.path.join(here, 'readme.md') - with open(fname) as f: - long_description = f.read() - - return long_description - - -setup( - name = 'h3', - version = about['__version__'], - description = about['__description__'], - long_description = long_desc(), - long_description_content_type = 'text/markdown', - license = about['__license__'], - author = about['__author__'], - author_email = about['__author_email__'], - url = about['__url__'], - classifiers = about['__classifiers__'], - include_package_data=True, - packages = find_packages( - 'src', - exclude = ["*.tests", "*.tests.*", "tests.*", "tests"], - ), - package_dir = {'': 'src'}, - cmake_languages = ('C'), - extras_require={ - 'numpy': ['numpy'], - 'test': ['pytest', 'pytest-cov', 'flake8', 'pylint'], - 'all': ['numpy', 'pytest', 'pytest-cov', 'flake8', 'pylint'], - }, -) diff --git a/src/h3/_cy/CMakeLists.txt b/src/h3/_cy/CMakeLists.txt index 52e01561..2cb2b6f3 100644 --- a/src/h3/_cy/CMakeLists.txt +++ b/src/h3/_cy/CMakeLists.txt @@ -1,31 +1,35 @@ -find_package(Cython MODULE REQUIRED) -find_package(PythonExtensions MODULE REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -# Build Cython annotations -set(CYTHON_ANNOTATE TRUE) - macro(add_cython_file filename) - add_cython_target(${filename} C PY3) - add_library(${filename} MODULE ${filename} ${LIB_SOURCE_FILES} ${CONFIGURED_API_HEADER}) - python_extension_module(${filename}) + add_custom_command( + OUTPUT "${filename}.c" + COMMENT + "Making ${CMAKE_CURRENT_BINARY_DIR}/${filename}.c from ${CMAKE_CURRENT_SOURCE_DIR}/${filename}.pyx" + COMMAND Python::Interpreter -m cython + "${CMAKE_CURRENT_SOURCE_DIR}/${filename}.pyx" --output-file "${filename}.c" -I ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS "${filename}.pyx" + VERBATIM) + + python_add_library(${filename} MODULE "${filename}.c" WITH_SOABI) + set_property(TARGET ${filename} PROPERTY C_STANDARD 99) - target_link_libraries(${filename} h3) - install(TARGETS ${filename} LIBRARY DESTINATION src/h3/_cy) + target_link_libraries(${filename} PRIVATE h3) + install(TARGETS ${filename} LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME}/_cy) endmacro() # GLOB pattern is recommended against # https://cmake.org/cmake/help/v3.14/command/file.html?highlight=file#filesystem -add_cython_file(util) -add_cython_file(latlng) add_cython_file(cells) add_cython_file(edges) -add_cython_file(to_multipoly) add_cython_file(error_system) +add_cython_file(latlng) add_cython_file(memory) add_cython_file(vertex) +add_cython_file(to_multipoly) +add_cython_file(util) # Include pyx and pxd files in distribution for use by Cython API install( @@ -34,16 +38,16 @@ install( cells.pyx edges.pxd edges.pyx + error_system.pyx + h3lib.pxd latlng.pxd latlng.pyx - h3lib.pxd - util.pxd - util.pyx - error_system.pyx memory.pxd memory.pyx + util.pxd + util.pyx vertex.pxd vertex.pyx DESTINATION - src/h3/_cy + ${SKBUILD_PROJECT_NAME}/_cy ) diff --git a/src/h3/_version.py b/src/h3/_version.py index 41bf4d04..712565a5 100644 --- a/src/h3/_version.py +++ b/src/h3/_version.py @@ -1,24 +1,3 @@ -__version__ = '4.0.0b6' -__description__ = 'Hierarchical hexagonal geospatial indexing system' -__url__ = 'https://github.com/uber/h3-py' -__license__ = 'Apache 2.0 License' -__author__ = 'Uber Technologies' -__author_email__ = 'AJ Friend ' -__classifiers__ = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: C', - 'Programming Language :: Cython', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX :: Linux', - 'Operating System :: Microsoft :: Windows', - 'Topic :: Scientific/Engineering :: GIS', -] +from importlib import metadata + +__version__ = metadata.version(__package__ or __name__) diff --git a/tests/polyfill/test_polyfill.py b/tests/polyfill/test_polyfill.py index b78e8751..1431b876 100644 --- a/tests/polyfill/test_polyfill.py +++ b/tests/polyfill/test_polyfill.py @@ -135,12 +135,10 @@ def test_invalid_polygon(): some `cdef` functions. """ with pytest.raises(TypeError): - poly = h3.LatLngPoly([1, 2, 3]) - h3.h3shape_to_cells(poly, 4) + h3.LatLngPoly([1, 2, 3]) with pytest.raises(ValueError): - poly = h3.LatLngPoly([[1, 2, 3]]) - h3.h3shape_to_cells(poly, 4) + h3.LatLngPoly([[1, 2, 3]]) def test_bad_geo_input(): diff --git a/tests/polyfill/test_polygon_class.py b/tests/polyfill/test_polygon_class.py index c38505dc..3996d302 100644 --- a/tests/polyfill/test_polygon_class.py +++ b/tests/polyfill/test_polygon_class.py @@ -39,8 +39,10 @@ def test_LatLngPoly_len(): def test_bad_subclass(): class H3Shoop(h3.H3Shape): - def __geo_interface__(): - pass + def __geo_interface__(self): + return 'foo' + shoop = H3Shoop() + shoop.__geo_interface__() with pytest.raises(ValueError): - h3.h3shape_to_cells(H3Shoop(), res=9) + h3.h3shape_to_cells(shoop, res=9) diff --git a/tests/test_cython/test_cython.py b/tests/test_cython/test_cython.py index 89aa0646..1833de3f 100644 --- a/tests/test_cython/test_cython.py +++ b/tests/test_cython/test_cython.py @@ -12,7 +12,7 @@ def test_cython_api(): if latlng_to_cell_vect is None: - print("Not running Cython test because cython example was not compiled") + print('Not running Cython test because cython example was not compiled') return N = 100000 @@ -22,7 +22,7 @@ def test_cython_api(): lats = np.array(lats, dtype=np.float64) lngs = np.array(lngs, dtype=np.float64) - out = np.zeros(len(lats), dtype="uint64") + out = np.zeros(len(lats), dtype='uint64') latlng_to_cell_vect(lats, lngs, res, out) assert out[0] == 617284541015654399 diff --git a/tests/test_h3.py b/tests/test_h3.py index 567ba1fa..051480a6 100644 --- a/tests/test_h3.py +++ b/tests/test_h3.py @@ -6,6 +6,10 @@ from . import util as u +def test_approx_helper(): + assert not u.approx2('abc', 'abdc') + + def test_is_valid_cell(): assert h3.is_valid_cell('85283473fffffff') assert h3.is_valid_cell('850dab63fffffff')