From c968a29c062d36c7de9f1aa60029c195770fbb5a Mon Sep 17 00:00:00 2001 From: decitre <590094+decitre@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:19:28 +0100 Subject: [PATCH] Refactor build/test harness Add CONTRIBUTING.md Replace README.rst with README.md Add examples to README.md --- .bumpversion.cfg | 20 -- .coveragerc | 1 + .github/workflows/github-actions.yml | 187 ------------------ .github/workflows/release.yml | 28 +++ .github/workflows/test.yml | 54 +++++ .pre-commit-config.yaml | 39 ++-- CONTRIBUTING.md | 51 +++++ LICENSE | 2 +- MANIFEST.in | 24 --- README.md | 57 ++++++ README.rst | 63 ------ ci/bootstrap.py | 93 --------- ci/requirements.txt | 6 - .../.github/workflows/github-actions.yml | 110 ----------- pyproject.toml | 81 +++++++- requirements.txt | 1 - requirements_test.txt | 1 - setup.cfg | 7 - setup.py | 150 ++------------ src/cymorton/__init__.py | 5 +- src/cymorton/cli.py | 9 +- src/cymorton/codec.pxd | 1 - src/cymorton/codec.pyx | 1 - tests/test_cymorton.py | 11 +- tox.ini | 71 +++---- 25 files changed, 345 insertions(+), 728 deletions(-) delete mode 100644 .bumpversion.cfg delete mode 100644 .github/workflows/github-actions.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 CONTRIBUTING.md delete mode 100644 MANIFEST.in create mode 100644 README.md delete mode 100644 README.rst delete mode 100755 ci/bootstrap.py delete mode 100644 ci/requirements.txt delete mode 100644 ci/templates/.github/workflows/github-actions.yml delete mode 100644 requirements.txt delete mode 100644 requirements_test.txt delete mode 100644 setup.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 9bdd4b5..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[bumpversion] -current_version = 0.0.2 -commit = True -tag = True - -[bumpversion:file:setup.py] -search = version='{current_version}' -replace = version='{new_version}' - -[bumpversion:file (badge):README.rst] -search = /v{current_version}.svg -replace = /v{new_version}.svg - -[bumpversion:file (link):README.rst] -search = /v{current_version}...master -replace = /v{new_version}...master - -[bumpversion:file:src/cymorton/__init__.py] -search = __version__ = '{current_version}' -replace = __version__ = '{new_version}' diff --git a/.coveragerc b/.coveragerc index 770e36e..1cf5b3b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,7 @@ source = src [run] +plugins = Cython.Coverage branch = true source = src diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml deleted file mode 100644 index dbf2276..0000000 --- a/.github/workflows/github-actions.yml +++ /dev/null @@ -1,187 +0,0 @@ -name: build -on: [push, pull_request] -jobs: - test: - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - - name: 'check' - python: '3.9' - toxpython: 'python3.9' - tox_env: 'check' - os: 'ubuntu-latest' - - name: 'py36-cover (ubuntu/x86_64)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'ubuntu-latest' - - name: 'py36-cover (windows/AMD64)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-cover' - cibw_arch: 'AMD64' - cibw_build: false - os: 'windows-latest' - - name: 'py36-cover (macos/x86_64)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'macos-latest' - - name: 'py37-cover (ubuntu/x86_64)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'ubuntu-latest' - - name: 'py37-cover (windows/AMD64)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-cover' - cibw_arch: 'AMD64' - cibw_build: false - os: 'windows-latest' - - name: 'py37-cover (macos/x86_64)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'macos-latest' - - name: 'py38-cover (ubuntu/x86_64)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'ubuntu-latest' - - name: 'py38-cover (windows/AMD64)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-cover' - cibw_arch: 'AMD64' - cibw_build: false - os: 'windows-latest' - - name: 'py38-cover (macos/x86_64)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'macos-latest' - - name: 'py39-cover (ubuntu/x86_64)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'ubuntu-latest' - - name: 'py39-cover (windows/AMD64)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-cover' - cibw_arch: 'AMD64' - cibw_build: false - os: 'windows-latest' - - name: 'py39-cover (macos/x86_64)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'macos-latest' - - name: 'py310-cover (ubuntu/x86_64)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'ubuntu-latest' - - name: 'py310-cover (windows/AMD64)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-cover' - cibw_arch: 'AMD64' - cibw_build: false - os: 'windows-latest' - - name: 'py310-cover (macos/x86_64)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-cover' - cibw_arch: 'x86_64' - cibw_build: false - os: 'macos-latest' - steps: - - uses: docker/setup-qemu-action@v1 - if: matrix.cibw_arch == 'aarch64' - with: - platforms: arm64 - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - architecture: ${{ matrix.python_arch }} - - name: install dependencies - run: | - python -mpip install --progress-bar=off cibuildwheel -r ci/requirements.txt - virtualenv --version - pip --version - tox --version - pip list --format=freeze - - name: cibw build and test - if: matrix.cibw_build - run: cibuildwheel - env: - TOXPYTHON: '${{ matrix.toxpython }}' - CIBW_ARCHS: '${{ matrix.cibw_arch }}' - CIBW_BUILD: '${{ matrix.cibw_build }}' - CIBW_BUILD_VERBOSITY: '3' - CIBW_TEST_REQUIRES: > - tox - tox-direct - CIBW_TEST_COMMAND: > - cd {project} && - tox --skip-pkg-install --direct-yolo -e ${{ matrix.tox_env }} -v - CIBW_TEST_COMMAND_WINDOWS: > - cd /d {project} && - tox --skip-pkg-install --direct-yolo -e ${{ matrix.tox_env }} -v - - name: regular build and test - env: - TOXPYTHON: '${{ matrix.toxpython }}' - if: > - !matrix.cibw_build - run: > - tox -e ${{ matrix.tox_env }} -v - - name: check wheel - if: matrix.cibw_build - run: twine check wheelhouse/*.whl - - name: upload wheel - uses: actions/upload-artifact@v2 - if: matrix.cibw_build - with: - path: wheelhouse/*.whl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c22504e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Publish to PyPI + +on: + release: + types: [published] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Install tools + run: | + python -m pip install --upgrade pip + pip install '.[dev]' + - name: Build + run: | + python -m build + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + # https://github.com/pypa/gh-action-pypi-publish + with: + user: __token__ + password: ${{ secrets.PYPI_TOKEN }} + verbose: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ed8458c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,54 @@ +name: test +on: + push: + pull_request: + schedule: + - cron: "0 8 * * *" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e '.[dev]' + - name: Format with Black + run: black . + - name: Lint with Ruff + run: ruff . + + py_3x: + strategy: + fail-fast: false + matrix: + # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs + os: [ubuntu-latest, macos-latest, windows-latest] + python: [ '3.8', '3.9', '3.10', '3.11', '3.x'] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + # https://github.com/actions/checkout + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + # https://github.com/actions/setup-python + with: + python-version: ${{ matrix.python }} + architecture: x64 + cache: pip + - name: install dependencies + run: | + pip install --upgrade pip + pip install -e '.[ci]' + pip freeze + - name: Setup test suite + run: tox -vv --notest + - name: Run test suite + run: tox --skip-pkg-install diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83e54d8..38fec37 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,27 +2,28 @@ # pre-commit install # To update the pre-commit hooks run: # pre-commit install-hooks -exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' +exclude: '^(\.tox|\.bumpversion\.cfg)(/|$)' repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: master - hooks: - - id: trailing-whitespace - exclude_types: - - c - - id: end-of-file-fixer - exclude_types: - - c - - id: debug-statements - - repo: https://github.com/timothycrosley/isort - rev: master - hooks: - - id: isort - repo: https://github.com/psf/black - rev: main + rev: 24.1.1 hooks: - id: black - - repo: https://gitlab.com/pycqa/flake8 - rev: master + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.2.1" + hooks: + - id: ruff + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.0 hooks: - - id: flake8 + - id: pyupgrade + args: [ --py38-plus ] + - repo: https://github.com/mgedmin/check-python-versions + rev: 0.22.0 + hooks: + - id: check-python-versions + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: debug-statements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c463434 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +## Linting & Testing + + pip install -e '.[dev]' + pre-commit autoupdate + pre-commit install + pre-commit run --all-files + tox + +`tox` uses by default the installed `protoc` compiler. The gh repo `test` workflow considers `protoc` in various versions (`3.20.3`, `21.12`, `25.x`). +Contributors using a macOS workstation shall have a look at `tests/tox_mac.sh` to test against `protobuf`, `protobuf@21` and `protobuf@3` bottles. + +## Releasing + +If dev branch `test` workflow succeeds, a new version can be released. + +1. Version + + Use `bumpver` on dev branches. First, with `--dry`, then without :) + + Breaking changes: + + bumpver update --major --dry + + Additional features: + + bumpver update --minor --dry + + Other: + + bumpver update --patch --dry + +2. Merge + + To `main` + +3. Publish + + Publishing to PyPi is done through the creation of a release in the [Github UI](https://github.com/decitre/cymorton/releases): + - "Draft a new release" + - Choose the tag created in step 1 + - Use it to name the release + - Add the changes to the description field + - "Publish release" + - Check the `release` [action](https://github.com/decitre/cymorton/actions/workflows/release.yml) + +4. Install published package + + In a dedicated `venv`, do: + + pip install cymorton== + python -c "import cymorton.codec as cc; print(cc.convert_xy_level_to_code(5, 2, 3))" diff --git a/LICENSE b/LICENSE index 9492d62..29b9dc9 100644 --- a/LICENSE +++ b/LICENSE @@ -6,4 +6,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index df3f0c8..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,24 +0,0 @@ -graft docs -graft src -graft src/cymorton -graft ci -graft tests - -include .bumpversion.cfg -include .coveragerc -include tox.ini -include .github/workflows/github-actions.yml -include .pre-commit-config.yaml -include pytest.ini -include tox.ini -include LICENSE -include README.rst -include requirements.txt -include requirements_test.txt -include src/cymorton/__codec.cpp -include src/cymorton/__codec.hpp -include src/cymorton/codec.pxd -include src/cymorton/codec.pyx -exclude src/cymorton/codec.cpp - -global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec935c1 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +[![test][test_badge]][test_target] +[![version][version_badge]][pypi] +[![wheel][wheel_badge]][pypi] +[![python version][python_versions_badge]][pypi] +[![python implementation][python_implementation_badge]][pypi] +[![commits_since][commits_since_badge]][pypi] + + +A morton code codec in c++/cython + + +## Installation + + pip install cymorton + + +## Usage + +Library: + +```python +>>> from cymorton.codec import convert_lat_lon_level_to_code +>>> convert_lat_lon_level_to_code(9.165507, 105.219986, 12) == 24145105 +True +``` + +```python +>>> from cymorton.codec import test_convert_xy_level_to_code +>>>convert_xy_level_to_code(5, 2, 3) == 0b1011001 +True +``` + +CLI: + +```shell +$ cymorton --help +Usage: cymorton [OPTIONS] LAT LON Z + + Prints the Morton code for LAT and LON at Z level + +Options: + --version Show the version and exit. + --help Show this message and exit. +``` + +```shell +$ cymorton 9.165507 105.219986 12 +24145105 +``` + +[pypi]: https://pypi.org/project/cymorton +[test_badge]: https://github.com/decitre/python-cymorton/actions/workflows/test.yml/badge.svg +[test_target]: https://github.com/decitre/python-cymorton/actions +[version_badge]: https://img.shields.io/pypi/v/cymorton.svg +[wheel_badge]: https://img.shields.io/pypi/wheel/cymorton.svg +[python_versions_badge]: https://img.shields.io/pypi/pyversions/cymorton.svg +[python_implementation_badge]: https://img.shields.io/pypi/implementation/cymorton.svg diff --git a/README.rst b/README.rst deleted file mode 100644 index 9902357..0000000 --- a/README.rst +++ /dev/null @@ -1,63 +0,0 @@ -======== -Overview -======== - -.. start-badges - -.. list-table:: - :stub-columns: 1 - - * - tests - - | |github-actions| |requires| - | - * - package - - | |version| |wheel| |supported-versions| |supported-implementations| - | |commits-since| - -.. |github-actions| image:: https://github.com/decitre/python-cymorton/actions/workflows/github-actions.yml/badge.svg - :alt: GitHub Actions Build Status - :target: https://github.com/decitre/python-cymorton/actions - -.. |requires| image:: https://requires.io/github/decitre/python-cymorton/requirements.svg?branch=master - :alt: Requirements Status - :target: https://requires.io/github/decitre/python-cymorton/requirements/?branch=master - -.. |version| image:: https://img.shields.io/pypi/v/cymorton.svg - :alt: PyPI Package latest release - :target: https://pypi.org/project/cymorton - -.. |wheel| image:: https://img.shields.io/pypi/wheel/cymorton.svg - :alt: PyPI Wheel - :target: https://pypi.org/project/cymorton - -.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/cymorton.svg - :alt: Supported versions - :target: https://pypi.org/project/cymorton - -.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/cymorton.svg - :alt: Supported implementations - :target: https://pypi.org/project/cymorton - -.. |commits-since| image:: https://img.shields.io/github/commits-since/decitre/python-cymorton/v0.0.2.svg - :alt: Commits since latest release - :target: https://github.com/decitre/python-cymorton/compare/v0.0.2...master - - - -.. end-badges - -A morton code codec in c++/cython - -* Free software: MIT license - -Installation -============ - -:: - - pip install cymorton - -You can also install the in-development version with:: - - pip install https://github.com/decitre/python-cymorton/archive/master.zip - diff --git a/ci/bootstrap.py b/ci/bootstrap.py deleted file mode 100755 index 3ca06b7..0000000 --- a/ci/bootstrap.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals - -import os -import subprocess -import sys -from os.path import abspath -from os.path import dirname -from os.path import exists -from os.path import join -from os.path import relpath - -base_path = dirname(dirname(abspath(__file__))) -templates_path = join(base_path, "ci", "templates") - - -def check_call(args): - print("+", *args) - subprocess.check_call(args) - - -def exec_in_env(): - env_path = join(base_path, ".tox", "bootstrap") - if sys.platform == "win32": - bin_path = join(env_path, "Scripts") - else: - bin_path = join(env_path, "bin") - if not exists(env_path): - import subprocess - - print("Making bootstrap env in: {0} ...".format(env_path)) - try: - check_call([sys.executable, "-m", "venv", env_path]) - except subprocess.CalledProcessError: - try: - check_call([sys.executable, "-m", "virtualenv", env_path]) - except subprocess.CalledProcessError: - check_call(["virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment...") - check_call([join(bin_path, "pip"), "install", "jinja2", "tox"]) - python_executable = join(bin_path, "python") - if not os.path.exists(python_executable): - python_executable += '.exe' - - print("Re-executing with: {0}".format(python_executable)) - print("+ exec", python_executable, __file__, "--no-env") - os.execv(python_executable, [python_executable, __file__, "--no-env"]) - - -def main(): - import jinja2 - - print("Project path: {0}".format(base_path)) - - jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(templates_path), - trim_blocks=True, - lstrip_blocks=True, - keep_trailing_newline=True, - ) - - tox_environments = [ - line.strip() - # 'tox' need not be installed globally, but must be importable - # by the Python that is running this script. - # This uses sys.executable the same way that the call in - # cookiecutter-pylibrary/hooks/post_gen_project.py - # invokes this bootstrap.py itself. - for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() - ] - tox_environments = [line for line in tox_environments if line.startswith('py')] - - for root, _, files in os.walk(templates_path): - for name in files: - relative = relpath(root, templates_path) - with open(join(base_path, relative, name), "w") as fh: - fh.write(jinja.get_template(join(relative, name)).render(tox_environments=tox_environments)) - print("Wrote {}".format(name)) - print("DONE.") - - -if __name__ == "__main__": - args = sys.argv[1:] - if args == ["--no-env"]: - main() - elif not args: - exec_in_env() - else: - print("Unexpected arguments {0}".format(args), file=sys.stderr) - sys.exit(1) diff --git a/ci/requirements.txt b/ci/requirements.txt deleted file mode 100644 index a1708f4..0000000 --- a/ci/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -virtualenv>=16.6.0 -pip>=19.1.1 -setuptools>=18.0.1 -six>=1.14.0 -tox -twine diff --git a/ci/templates/.github/workflows/github-actions.yml b/ci/templates/.github/workflows/github-actions.yml deleted file mode 100644 index 750985e..0000000 --- a/ci/templates/.github/workflows/github-actions.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: build -on: [push, pull_request] -jobs: - test: - name: {{ '${{ matrix.name }}' }} - runs-on: {{ '${{ matrix.os }}' }} - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - - name: 'check' - python: '3.9' - toxpython: 'python3.9' - tox_env: 'check' - os: 'ubuntu-latest' - - name: 'docs' - python: '3.9' - toxpython: 'python3.9' - tox_env: 'docs' - os: 'ubuntu-latest' -{% for env in tox_environments %} -{% set prefix = env.split('-')[0] -%} -{% if prefix.startswith('pypy') %} -{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} -{% set cpython %}pp{{ prefix[4:5] }}{% endset %} -{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} -{% else %} -{% set python %}{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} -{% set cpython %}cp{{ prefix[2:] }}{% endset %} -{% set toxpython %}python{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} -{% endif %} -{% for os, python_arch, cibw_arch, wheel_arch, include_cover in [ - ['ubuntu', 'x64', 'x86_64', '*manylinux*', True], - ['ubuntu', 'x64', 'x86_64', '*musllinux*', False], - ['ubuntu', 'x64', 'aarch64', '*manylinux*', False], - ['ubuntu', 'x64', 'aarch64', '*musllinux*', False], - ['windows', 'x64', 'AMD64', '*', True], - ['windows', 'x86', 'x86', '*', False], - ['macos', 'x64', 'x86_64', '*', True], -] %} -{% if include_cover or ('nocov' in env and not prefix.startswith('pypy')) %} -{% set wheel_suffix = 'nocov' in env and wheel_arch.strip('*') %} -{% set name_suffix = '/' + wheel_suffix if wheel_suffix else '' %} - - name: '{{ env }} ({{ os }}/{{ cibw_arch }}{{ name_suffix }})' - python: '{{ python }}' - toxpython: '{{ toxpython }}' - python_arch: '{{ python_arch }}' - tox_env: '{{ env }}{% if 'cover' in env %},codecov{% endif %}' - cibw_arch: '{{ cibw_arch }}' -{% if 'nocov' in env and not prefix.startswith('pypy') %} - cibw_build: '{{ cpython }}-{{ wheel_arch }}' -{% else %} - cibw_build: false -{% endif %} - os: '{{ os }}-latest' -{% endif %} -{% endfor %} -{% endfor %} - steps: - - uses: docker/setup-qemu-action@v1 - if: matrix.cibw_arch == 'aarch64' - with: - platforms: arm64 - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-python@v2 - with: - python-version: {{ '${{ matrix.python }}' }} - architecture: {{ '${{ matrix.python_arch }}' }} - - name: install dependencies - run: | - python -mpip install --progress-bar=off cibuildwheel -r ci/requirements.txt - virtualenv --version - pip --version - tox --version - pip list --format=freeze - - name: cibw build and test - if: matrix.cibw_build - run: cibuildwheel - env: - TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' - CIBW_ARCHS: '{{ '${{ matrix.cibw_arch }}' }}' - CIBW_BUILD: '{{ '${{ matrix.cibw_build }}' }}' - CIBW_BUILD_VERBOSITY: '3' - CIBW_TEST_REQUIRES: > - tox - tox-direct - CIBW_TEST_COMMAND: > - cd {project} && - tox --skip-pkg-install --direct-yolo -e {{ '${{ matrix.tox_env }}' }} -v - CIBW_TEST_COMMAND_WINDOWS: > - cd /d {project} && - tox --skip-pkg-install --direct-yolo -e {{ '${{ matrix.tox_env }}' }} -v - - name: regular build and test - env: - TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' - if: > - !matrix.cibw_build - run: > - tox -e {{ '${{ matrix.tox_env }}' }} -v - - name: check wheel - if: matrix.cibw_build - run: twine check wheelhouse/*.whl - - name: upload wheel - uses: actions/upload-artifact@v2 - if: matrix.cibw_build - with: - path: wheelhouse/*.whl diff --git a/pyproject.toml b/pyproject.toml index be1038c..2ca965a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,79 @@ [build-system] -requires = [ - "setuptools>=30.3.0", - "wheel", - "Cython" -] +requires = ["setuptools>=42", "wheel", "Cython"] build-backend = "setuptools.build_meta" +[project] +name = "cymorton" +version = "0.1.0" +description = "A morton code codec in c++/cython" +readme = "README.rst" +requires-python = ">=3.8" +license = {file = "LICENSE"} +classifiers = [ + "Intended Audience :: Developers", + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + "Programming Language :: Python :: 3 :: Only", + "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", + 'Programming Language :: Cython', + 'Programming Language :: Python :: Implementation :: CPython', + "Operating System :: OS Independent" +] +keywords=["morton", "z-curve"] +dependencies = ["Cython"] + +[project.scripts] +cymorton = "cymorton.cli:main" + +[project.urls] +homepage = "https://github.com/decitre/python-cymorton" + +[project.optional-dependencies] +dev = [ + "pytest", "pytest-recording", "urllib3<2", + "bumpver", "black", "ruff", "pre-commit", + "tox", "build", "twine" +] +ci = [ + "virtualenv>=16.6.0", "pip>=19.1.1", "setuptools>=18.0.1", + "six>=1.14.0", "tox>=4.12.1", "tox-gh>=1.2" +] + [tool.black] line-length = 88 -target-version = ['py39'] -skip-string-normalization = true +target-version = ['py311'] + +[tool.ruff] +line-length = 88 +exclude = [ + ".tox", ".eggs", "build", "dist", "__pycache__", + "docs/source/conf.py", "old", "env", +] +lint.ignore = ["COM812", "D103", "E501", "F401", "F403"] +lint.select = ["B", "B9", "C", "E", "F", "W", "I001"] + +[tool.ruff.lint.flake8-quotes] +inline-quotes = "double" + +[tool.ruff.lint.mccabe] +max-complexity = 18 + +[tool.bumpver] +current_version = "0.1.0" +version_pattern = "MAJOR.MINOR.PATCH" +commit = false +tag = false +push = false + +[tool.bumpver.file_patterns] +"pyproject.toml" = [ + 'version = "{version}"', +] + +[tool.tox] +# tox 4 supports pyproject.toml only through an inconveninent legacy_tox_ini string... +# So we still keep tox.ini diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 002d1b9..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Cython diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index e079f8a..0000000 --- a/requirements_test.txt +++ /dev/null @@ -1 +0,0 @@ -pytest diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 18a9f6a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[flake8] -max-line-length = 88 -ignore = C812, D103, E203, E266, E501, W503, F403, F401 -max-complexity = 18 -inline-quotes = double -select = B,C,E,F,W,T4,B9 -exclude = .tox,.eggs,ci/templates,build,dist,__pycache__,docs/source/conf.py,old,env diff --git a/setup.py b/setup.py index b219887..e72b243 100755 --- a/setup.py +++ b/setup.py @@ -1,140 +1,16 @@ -import io -import os -import re -from glob import glob -from os.path import basename -from os.path import dirname -from os.path import join -from os.path import relpath -from os.path import splitext -from typing import Set, Any, List, Dict +#!/usr/bin/env python +# Copyright (C) 2022 HERE Global B.V. and its affiliate(s). +# All rights reserved. + +# This software and other materials contain proprietary information +# controlled by HERE and are protected by applicable copyright legislation. +# Any use and utilization of this software and other materials and +# disclosure to any third parties is conditional upon having a separate +# agreement with HERE for the access, use, utilization or disclosure of this +# software. In the absence of such agreement, the use of the software is not +# allowed. -from setuptools import Extension -from setuptools import find_packages from setuptools import setup -from setuptools.dist import Distribution -from pathlib import Path -from Cython.Build import cythonize - -try: - # Allow installing package without any Cython available. This - # assumes you are going to include the .c files in your sdist. - import Cython -except ImportError: - Cython = None - - -class BinaryDistribution(Distribution): - """Distribution which almost always forces a binary package with platform name""" - - def has_ext_modules(self): - return super().has_ext_modules() or not os.environ.get('SETUPPY_ALLOW_PURE') - - -def get_property(prop, packages_path: str, packages: List[str]) -> Set[Any]: - """ - Searches and returns a property from all packages __init__.py files - :param prop: property searched - :param packages_path: root path of packages to search into - :param packages: array of packages paths - :return: an set of values - """ - results = set() - namespace: Dict[str, Any] = {} - for package in packages: - init_file = open(Path(packages_path, package, "__init__.py")).read() - exec(init_file, namespace) - if prop in namespace: - results.add(namespace[prop]) - return results - - -def get_requirements(file_path: str, no_precise_version: bool = False) -> List[str]: - _requirements = [] - try: - with open(file_path, "rt") as r: - for line in r.readlines(): - package = line.strip() - if not package or package.startswith("#"): - continue - if no_precise_version: - package = package.split("==")[0] - _requirements.append(package) - except FileExistsError: - pass - return _requirements - - -def read(*names, **kwargs): - with io.open( - join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') - ) as fh: - return fh.read() - - -project_name = "cymorton" -github_home = "https://github.com/decitre" - -if __name__ == '__main__': - _packages_path = "src" - _packages = find_packages(where=_packages_path) - main_package_path = { - Path(_packages_path, *package.split(".")) - for package in _packages - if package.endswith(project_name) - }.pop() - version = get_property("__version__", _packages_path, _packages).pop() - requirements = ["click"] - requirements.extend(get_requirements("requirements.txt", no_precise_version=True)) - requirements_test = get_requirements("requirements_test.txt") - - ext_modules = [ - Extension( - splitext(relpath(path, 'src').replace(os.sep, '.'))[0], - sources=[path], - include_dirs=[dirname(path)], - ) - for root, _, _ in os.walk('src') - for path in glob(join(root, '*.pyx')) - ] - - setup( - name=project_name, - version=version, - license='MIT', - description='A morton code codec in c++/cython', - long_description=re.compile( - '^.. start-badges.*^.. end-badges', re.M | re.S - ).sub('', read('README.rst')), - author='Emmanuel Decitre', - url=f'{github_home}/python-{project_name}', - packages=find_packages(_packages_path), - package_dir={'': _packages_path}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], - include_package_data=True, - zip_safe=False, - classifiers=[ - # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Cython', - 'Programming Language :: Python :: Implementation :: CPython', - ], - project_urls={ - 'Issue Tracker': f'{github_home}/python-{project_name}/issues', - }, - keywords=["morton", "z-curve"], - python_requires='>=3.6', - install_requires=requirements, - extras_require={"dev": requirements_test}, - setup_requires=['Cython'], - entry_points={'console_scripts': [f'{project_name} = {project_name}.cli:main']}, - ext_modules=cythonize(ext_modules, language_level="3"), - distclass=BinaryDistribution, - ) +if __name__ == "__main__": + setup() diff --git a/src/cymorton/__init__.py b/src/cymorton/__init__.py index 8487af4..c8872fd 100644 --- a/src/cymorton/__init__.py +++ b/src/cymorton/__init__.py @@ -1,2 +1,3 @@ -VERSION = 0, 0, 2 -__version__ = ".".join(map(str, VERSION)) +from importlib.metadata import version + +__version__ = version("cymorton") diff --git a/src/cymorton/cli.py b/src/cymorton/cli.py index 8f36bcf..02b8eb7 100644 --- a/src/cymorton/cli.py +++ b/src/cymorton/cli.py @@ -1,13 +1,14 @@ import click -from cymorton.codec import convert_lat_lon_level_to_code + from cymorton import __version__ +from cymorton.codec import convert_lat_lon_level_to_code @click.command() @click.version_option(__version__) -@click.argument('lat', type=click.FLOAT) -@click.argument('lon', type=click.FLOAT) -@click.argument('z', type=click.INT) +@click.argument("lat", type=click.FLOAT) +@click.argument("lon", type=click.FLOAT) +@click.argument("z", type=click.INT) def main(lat: float, lon: float, z: int): """ Prints the Morton code for LAT and LON at Z level diff --git a/src/cymorton/codec.pxd b/src/cymorton/codec.pxd index c2903d6..e6b30cf 100644 --- a/src/cymorton/codec.pxd +++ b/src/cymorton/codec.pxd @@ -25,4 +25,3 @@ cdef extern from '__codec.hpp' nogil: uint32_t getQuadY(uint64_t); unsigned char getQuadZ(uint64_t); uint8_t MAX_Z; - diff --git a/src/cymorton/codec.pyx b/src/cymorton/codec.pyx index 6c43095..a3bc8f6 100644 --- a/src/cymorton/codec.pyx +++ b/src/cymorton/codec.pyx @@ -44,4 +44,3 @@ def convert_xy_level_to_code(uint32_t x, uint32_t y, unsigned char z): def convert_lat_lon_level_to_code(double latitude, double longitude, unsigned char z): return convertLatLonLevelToCode(latitude, longitude, z) - diff --git a/tests/test_cymorton.py b/tests/test_cymorton.py index e3ce231..3d0ad79 100644 --- a/tests/test_cymorton.py +++ b/tests/test_cymorton.py @@ -1,15 +1,16 @@ from click.testing import CliRunner - -from cymorton.codec import interleave_to_number -from cymorton.codec import convert_xy_level_to_code -from cymorton.codec import convert_lat_lon_level_to_code from cymorton.cli import main +from cymorton.codec import ( + convert_lat_lon_level_to_code, + convert_xy_level_to_code, + interleave_to_number, +) def test_main(): runner = CliRunner() result = runner.invoke(main, ["9.165507", "105.219986", "12"]) - assert result.output == '24145105\n' + assert result.output == "24145105\n" assert result.exit_code == 0 diff --git a/tox.ini b/tox.ini index 718b372..1888993 100644 --- a/tox.ini +++ b/tox.ini @@ -1,67 +1,59 @@ -[testenv:bootstrap] -deps = - jinja2 - tox -skip_install = true -commands = - python ci/bootstrap.py --no-env -passenv = - * -; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist - [tox] envlist = clean, - check, - {py36,py37,py38,py39,py310}-cover, + lint, + {py38,py39,py310,py311,py312}, report ignore_basepython_conflict = true +[gh] +python = + 3.12 = py312 + 3.11 = py311 + 3.10 = py310 + 3.9 = py39 + 3.8 = py38 + [testenv] basepython = - py36: {env:TOXPYTHON:python3.6} - py37: {env:TOXPYTHON:python3.7} - py38: {env:TOXPYTHON:python3.8} - py39: {env:TOXPYTHON:python3.9} - py310: {env:TOXPYTHON:python3.10} - {bootstrap,clean,check,report}: {env:TOXPYTHON:python3} + 3.8: {env:TOXPYTHON:python3.8} + 3.9: {env:TOXPYTHON:python3.9} + 3.10: {env:TOXPYTHON:python3.10} + 3.11: {env:TOXPYTHON:python3.11} + 3.12: {env:TOXPYTHON:python3.12} + {clean,lint,report,codecov}: {env:TOXPYTHON:python3} setenv = - PYTHONPATH={toxinidir}/tests + PYTHONPATH={toxinidir}/src PYTHONUNBUFFERED=yes - cover: SETUPPY_EXT_COVERAGE=yes passenv = * -usedevelop = - cover: true +usedevelop = false deps = pytest - cover: pytest-cov + pytest-cov + click Cython commands = - cover: python setup.py clean --all build_ext --force --inplace - cover: {posargs:pytest --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv tests} -[testenv:check] +[testenv:cythonize] +basepython = {env:TOXPYTHON:python} deps = - docutils - check-manifest - flake8 - readme-renderer - pygments Cython skip_install = true commands = - python setup.py check --metadata --restructuredtext - check-manifest {toxinidir} - flake8 + {posargs:python setup.py clean --all build_ext --force} -[testenv:cythonize] -basepython = {env:TOXPYTHON:python} +[testenv:codecov] deps = - Cython + codecov skip_install = true commands = - {posargs:python setup.py clean --all build_ext --force} + codecov [] + +[testenv:lint] +deps = pre-commit +commands = pre-commit run --all-files [testenv:report] deps = @@ -71,6 +63,7 @@ skip_install = true commands = coverage report coverage html + coverage xml [testenv:clean] commands = coverage erase