diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index ed61bbc..05de20a 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -1,14 +1,21 @@ name: pytest -# Only build PRs, the master branch, and releases. +# Only build PRs, the main branch, and releases. on: pull_request: push: branches: - - master + - main release: types: - published + schedule: + - cron: "14 14 20 * *" + +# Cancel any previous run of the test job. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: test: @@ -19,19 +26,13 @@ jobs: fail-fast: false matrix: os: [ubuntu, ] # macos, windows] # Only Linux currently. - python-version: [3.6, 3.7, 3.8] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - # Cancel any previous run of the test job; [pin v0.6.0] - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@ce177499ccf9fd2aded3b0426c97e5434c2e8a73 - with: - access_token: ${{ github.token }} - # Checks-out your repository under $GITHUB_WORKSPACE - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # Need to fetch more than the last commit so that setuptools_scm can # create the correct version string. If the number of commits since @@ -42,92 +43,64 @@ jobs: # to be able to push to GitHub. persist-credentials: false - # Need the tags so that setuptools_scm can form a valid version number - - name: Fetch git tags - run: git fetch origin 'refs/tags/*:refs/tags/*' - - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements-dev.txt - pip install -e . + python -m pip install .[tests] + + - name: Flake8 + run: flake8 src/ tests/ - name: Test with pytest - run: pytest --cov=fftlog --flake8 + run: pytest --cov=fftlog deploy: needs: test name: Deploy to PyPI runs-on: ubuntu-latest - # Only from the origin repository, not forks; only master. - if: github.repository_owner == 'prisae' && github.ref == 'refs/heads/master' + # Only from the origin repository, not forks; only main and tags. + if: github.repository_owner == 'emsig' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) steps: # Checks-out your repository under $GITHUB_WORKSPACE - name: Checkout - uses: actions/checkout@v2 - with: - # Need to fetch more than the last commit so that setuptools_scm can - # create the correct version string. If the number of commits since - # the last release is greater than this, the version will still be - # wrong. Increase if necessary. - fetch-depth: 100 - # The GitHub token is preserved by default but this job doesn't need - # to be able to push to GitHub. - persist-credentials: false - - # Need the tags so that setuptools_scm can form a valid version number - - name: Fetch git tags - run: git fetch origin 'refs/tags/*:refs/tags/*' + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.12" - name: Install dependencies run: | python -m pip install --upgrade pip - pip install wheel numpy - pip install -r requirements-dev.txt + python -m pip install build - name: Build source and wheel distributions + if: github.ref == 'refs/heads/main' run: | # Change setuptools-scm local_scheme to "no-local-version" so the # local part of the version isn't included, making the version string # compatible with Test PyPI. - sed --in-place "s/'root'/'local_scheme':'no-local-version','root'/g" setup.py + sed --in-place 's/version_file/local_scheme = "no-local-version"\nversion_file/g' pyproject.toml + + - name: Build source and wheel distributions + run: | # Build source and wheel packages - python setup.py sdist - # python setup.py bdist_wheel + python -m build echo "" echo "Generated files:" ls -lh dist/ - # # fftlog currently does not use setuptools_scm, hence the version - # # number has to be adjusted manually; too cumbersome. - # - name: Publish to Test PyPI - # if: success() - # # Hash corresponds to v1.4.1 - # uses: pypa/gh-action-pypi-publish@54b39fb9371c0b3a6f9f14bb8a67394defc7a806 - # with: - # user: __token__ - # password: ${{ secrets.TEST_PYPI_PASSWORD }} - # repository_url: https://test.pypi.org/legacy/ - # # Allow existing releases on test PyPI without errors. - # # NOT TO BE USED in PyPI! - # skip_existing: true - - name: Publish to PyPI # Only for releases if: success() && github.event_name == 'release' - # Hash corresponds to v1.4.1 - uses: pypa/gh-action-pypi-publish@54b39fb9371c0b3a6f9f14bb8a67394defc7a806 + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_PASSWORD }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index da55d7f..91bd377 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Changelog ######### + +v0.2.2 : Update home and build backend +-------------------------------------- + +No code changes. What changed: +- Moved from github.com/prisae to github.com/emsig. +- Ported build from distutils to meson (thanks @jokva). + + v0.2.1 : Fix packaging ---------------------- diff --git a/README.rst b/README.rst index 7641c67..3a99e66 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -.. image:: https://github.com/prisae/fftlog/workflows/pytest/badge.svg?branch=master - :target: https://github.com/prisae/fftlog/actions +.. image:: https://github.com/emsig/fftlog/workflows/pytest/badge.svg?branch=master + :target: https://github.com/emsig/fftlog/actions :alt: GitHub Actions .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3830534.svg :target: https://doi.org/10.5281/zenodo.3830534 @@ -13,8 +13,8 @@ This is a simple `f2py`-wrapper for the logarithmic FFT code *FFTLog* as presented in Appendix B of [Hami00]_ and published at `casa.colorado.edu/~ajsh/FFTLog `_. -A pure python version (`pyfftlog`) can be found on `github.com/prisae/pyfftlog -`_. Tests have shown that `fftlog` is a bit +A pure python version (`pyfftlog`) can be found on `github.com/emsig/pyfftlog +`_. Tests have shown that `fftlog` is a bit faster than `pyfftlog`, but `pyfftlog` is easier to implement, as you only need `NumPy` and `SciPy`, without the need to compile anything. @@ -28,7 +28,7 @@ test from the original code, and my use case, which is `pyfftlog.fftl` with (forward). Please let me know if you encounter any issues. - **Documentation**: https://pyfftlog.readthedocs.io -- **Source Code**: https://github.com/prisae/fftlog +- **Source Code**: https://github.com/emsig/fftlog **Note** that the documentation is for the pure python version `pyfftlog`, but equally applies to `fftlog`. diff --git a/fftlog/__init__.py b/fftlog/__init__.py deleted file mode 100644 index 4ede578..0000000 --- a/fftlog/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from fftlog._fftlog import fhti, fftl, fht, fhtq - -__all__ = ['fhti', 'fftl', 'fht', 'fhtq'] - -# Version -# Not using setuptools_scm, as in pyfftlog, because of numpy-setup. -# Has to be adjusted in setup.py too! -__version__ = '0.2.1' diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..c8e6458 --- /dev/null +++ b/meson.build @@ -0,0 +1,56 @@ +project('fftlog', 'c') +# fortran_std=legacy? +add_languages('fortran') + +if meson.get_compiler('fortran').get_id() == 'gcc' + # Allow argument mismatch, otherwise (newer, probably >= 10) gfortran throws + # Error: Type mismatch in argument ‘ifac’ at (1); passed REAL(8) to INTEGER(4) + add_global_arguments('-fallow-argument-mismatch', + language: ['fortran']) +endif + +py = import('python') +py3 = py.find_installation() + +f2py_name = '_fftlog' +f2py_module_c = f'@f2py_name@module.c' +f2py_wrapper_f = f'@f2py_name@-f2pywrappers.f' +f2py_dep = custom_target( + 'f2py wrappers', + input: 'src/fftlog.pyf', + output: [f2py_module_c, f2py_wrapper_f], + command: [py3, '-m', 'numpy.f2py', '@INPUT@', + '--build-dir', '@OUTDIR@', ], +) + +incdir_numpy = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'], + check : true +).stdout().strip() +incdir_f2py = run_command(py3, + ['-c', 'import os; os.chdir(".."); import numpy.f2py; print(numpy.f2py.get_include())'], + check : true +).stdout().strip() +inc_np = include_directories(incdir_numpy, incdir_f2py) + +py3.extension_module('_fftlog', + [f2py_dep, + incdir_f2py / 'fortranobject.c', + 'src/cdgamma.f', + 'src/drfftb.f', + 'src/drfftf.f', + 'src/drffti.f', + 'src/fftlog.f', + ], + link_language: 'fortran', + include_directories: inc_np, + install : true, + subdir: 'fftlog', +) + +py3.install_sources([ + 'src/__init__.py', + ], + pure: false, + subdir: 'fftlog', +) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1e3e991 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[build-system] +build-backend = "mesonpy" +requires = [ + "meson-python", + "numpy", + "wheel", + "charset_normalizer", +] + +[project] +name = "fftlog" +description = "A python wrapper for FFTLog" +readme = "README.rst" +version = "0.2.2dev1" # also in __init__.py +authors = [ + {name = "The emsig community", email = "info@emsig.xyz"}, +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", +] +dependencies = [ + "numpy", + "scipy", +] + +[project.license] +file = "LICENSE" + +[project.optional-dependencies] +tests = [ + "flake8", + "pytest", + "coveralls", + "pytest_cov", + "flake8-pyproject", +] +all = [ + "fftlog[tests]", +] + +[project.urls] +Repository = "https://github.com/prisae/fftlog" diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 02c654b..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,8 +0,0 @@ -# GLOBAL REQUIREMENTS --r requirements.txt - -# FOR TESTING -pytest -coveralls -pytest_cov -pytest_flake8 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9c558e3..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -. diff --git a/setup.py b/setup.py deleted file mode 100644 index a1e3c2a..0000000 --- a/setup.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -import re -import sys - -# Get README and remove badges. -readme = open('README.rst').read() -readme = re.sub('.*`fftlog` - A', '`fftlog` - A', readme, flags=re.DOTALL) - -metadata = dict( - name='fftlog', - version='0.2.1', # Adjust in fftlog/__init__.py too! - description='Logarithmic Fast Fourier Transform', - long_description=readme, - author='Dieter Werthmüller', - author_email='dieter@werthmuller.org', - url='https://github.com/prisae/fftlog', - license='CC0-1.0', - packages=['fftlog', ], - include_package_data=True, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication', - ], - install_requires=[ - 'scipy', - ], - # setup_requires=['numpy', ], -) - - -bdist = ('bdist_wheel', 'bdist_egg') -nonumpy = ('--help-commands', 'egg_info', '--version', 'clean') -argv2 = len(sys.argv) >= 2 -if argv2 and ('--help' in sys.argv[1:] or sys.argv[1] in nonumpy): - # For these actions, NumPy is not required. - # - # They are required to succeed without Numpy, for example when pip is - # used to install fftlog when Numpy is not yet present in the system. - from setuptools import setup -else: - if (argv2 >= 2 and sys.argv[1] in bdist) or ('develop' in sys.argv): - - # bdist_wheel/bdist_egg needs setuptools - import setuptools # noqa - - from numpy.distutils.core import setup, Extension - - metadata['ext_modules'] = [ - Extension( - name="fftlog._fftlog", - sources=['fftlog/fftlog.pyf', ] + - [ - 'fftlog/src/cdgamma.f', 'fftlog/src/drfftb.f', - 'fftlog/src/drfftf.f', 'fftlog/src/drffti.f', - 'fftlog/src/fftlog.f' - ], - ) - ] - -setup(**metadata) diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..ceca5c3 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,8 @@ +from fftlog._fftlog import fhti, fftl, fht, fhtq + +__all__ = ['fhti', 'fftl', 'fht', 'fhtq'] + +# Version +# Not currently using setuptools_scm, as in pyfftlog, because of numpy-setup. +# Has to be adjusted in pyproject.toml too! +__version__ = '0.2.2dev1' diff --git a/fftlog/src/cdgamma.f b/src/cdgamma.f similarity index 100% rename from fftlog/src/cdgamma.f rename to src/cdgamma.f diff --git a/fftlog/src/drfftb.f b/src/drfftb.f similarity index 100% rename from fftlog/src/drfftb.f rename to src/drfftb.f diff --git a/fftlog/src/drfftf.f b/src/drfftf.f similarity index 100% rename from fftlog/src/drfftf.f rename to src/drfftf.f diff --git a/fftlog/src/drffti.f b/src/drffti.f similarity index 100% rename from fftlog/src/drffti.f rename to src/drffti.f diff --git a/fftlog/src/fftlog.f b/src/fftlog.f similarity index 100% rename from fftlog/src/fftlog.f rename to src/fftlog.f diff --git a/fftlog/fftlog.pyf b/src/fftlog.pyf similarity index 94% rename from fftlog/fftlog.pyf rename to src/fftlog.pyf index 77ae082..729cb2b 100644 --- a/fftlog/fftlog.pyf +++ b/src/fftlog.pyf @@ -19,8 +19,8 @@ python module _fftlog ! in real*8 intent(in) :: dlnr real*8 optional, intent(in, out) :: kr=1 integer optional, intent(in) :: kropt=1 - real*8 dimension(2*n+3*(n/2)+19), intent(out) :: wsave - logical intent(out) :: ok + real*8 dimension(2*n+3*(n/2)+19), intent(hide, out) :: wsave + logical intent(hide, out) :: ok end subroutine fhti subroutine fftl(n,a,rk,dir,wsave) ! in :fftlog:src/fftlog.f integer, optional, intent(hide), depend(a) :: n=len(a) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_pyfftlog.py b/tests/test_fftlog.py similarity index 100% rename from tests/test_pyfftlog.py rename to tests/test_fftlog.py