From 0a47ff763fa4194009067f2ee7ee45614e95971a Mon Sep 17 00:00:00 2001 From: styfenschaer <79762922+styfenschaer@users.noreply.github.com> Date: Thu, 25 Dec 2025 10:57:21 +0100 Subject: [PATCH 1/2] Fix packaging and PyPI publishing --- .github/workflows/run-tests.yml | 33 ++++--- .github/workflows/upload-to-pypi.yml | 105 +++++++++++++------- .github/workflows/upload-to-test-pypi.yml | 115 ++++++++++++++-------- MANIFEST.in | 7 ++ README.md | 2 +- pyproject.toml | 65 +++++++++++- rocket_fft/py.typed | 0 setup.py | 98 +++++------------- 8 files changed, 255 insertions(+), 170 deletions(-) create mode 100644 MANIFEST.in create mode 100644 rocket_fft/py.typed diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1a83497..441144f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -4,25 +4,30 @@ on: [push, pull_request] jobs: tests: - if: contains(github.event.head_commit.message, '[run-tests]') + if: | + github.event_name == 'push' && contains(github.event.head_commit.message, '[run-tests]') || + github.event_name == 'pull_request' strategy: + fail-fast: false matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install .[dev] - - name: Install - run: | - python -m pip install --upgrade pip - python -m pip install -e .[dev] - - - name: Run tests - run: | - python tests/runtests.py + - name: Run tests + run: | + python tests/runtests.py diff --git a/.github/workflows/upload-to-pypi.yml b/.github/workflows/upload-to-pypi.yml index 384d46f..4707162 100644 --- a/.github/workflows/upload-to-pypi.yml +++ b/.github/workflows/upload-to-pypi.yml @@ -9,75 +9,106 @@ on: jobs: make_sdist: if: contains(github.event.head_commit.message, '[upload-to-pypi]') - name: Make SDist + name: Build sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Install deps - run: python -m pip install build twine + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - - name: Build SDist - run: python -m build --sdist + - name: Build sdist + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade build + python -m build --sdist - - uses: actions/upload-artifact@v2 - with: - path: dist/*.tar.gz - - - name: Check metadata - run: twine check dist/* + - name: Upload sdist artifact + uses: actions/upload-artifact@v4 + with: + name: dist-sdist + path: dist/*.tar.gz build_wheels: if: contains(github.event.head_commit.message, '[upload-to-pypi]') - name: Build wheels on ${{ matrix.os }} + name: Build wheels (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - # Allows building non-amd64 wheels on Linux. - - name: Set up QEMU + - name: Set up QEMU (Linux only) if: runner.os == 'Linux' uses: docker/setup-qemu-action@v3 with: platforms: all - - - uses: actions/setup-python@v2 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" - name: Install cibuildwheel - run: python -m pip install cibuildwheel + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade cibuildwheel - name: Build wheels env: - CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" - CIBW_ARCHS_MACOS: "x86_64 arm64" + + CIBW_PRERELEASE_PYTHONS: "1" + CIBW_ARCHS_LINUX: "auto aarch64" - run: python -m cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v2 + CIBW_ARCHS_MACOS: "arm64" + run: | + python -m cibuildwheel --output-dir dist + + - name: Upload wheel artifacts + uses: actions/upload-artifact@v4 with: - path: wheelhouse/*.whl + name: dist-wheels-${{ matrix.os }} + path: dist/*.whl upload_all: if: contains(github.event.head_commit.message, '[upload-to-pypi]') - needs: [build_wheels, make_sdist] + name: Upload to PyPI runs-on: ubuntu-latest + needs: [make_sdist, build_wheels] + steps: + - name: Download all dists + uses: actions/download-artifact@v4 + with: + pattern: dist-* + merge-multiple: true + path: dist + + - name: Install modern twine (avoid metadata parser issues) + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade "twine>=6.2.0" "packaging>=24.2" "pkginfo>=1.12.0" + python -m twine --version + python -m pip show twine packaging pkginfo - - uses: actions/download-artifact@v2 - with: - name: artifact - path: dist + - name: Check metadata + run: | + ls -lah dist + python -m twine check dist/* - - uses: pypa/gh-action-pypi-publish@v1.4.1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} + - name: Upload to PyPI (twine) + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + python -m twine upload \ + --non-interactive \ + dist/* diff --git a/.github/workflows/upload-to-test-pypi.yml b/.github/workflows/upload-to-test-pypi.yml index 2ba5232..d752fea 100644 --- a/.github/workflows/upload-to-test-pypi.yml +++ b/.github/workflows/upload-to-test-pypi.yml @@ -1,82 +1,117 @@ -name: Upload package to Pypi +name: Upload package to TestPyPI on: push: - branches: - - '**' + branches: + - main + - 'release*' + - dev jobs: make_sdist: if: contains(github.event.head_commit.message, '[upload-to-test-pypi]') - name: Make SDist + name: Build sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - - name: Install deps - run: python -m pip install build twine + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - - name: Build SDist - run: python -m build --sdist + - name: Build sdist + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade build + python -m build --sdist - - uses: actions/upload-artifact@v2 - with: - path: dist/*.tar.gz - - - name: Check metadata - run: twine check dist/* + - name: Upload sdist artifact + uses: actions/upload-artifact@v4 + with: + name: dist-sdist + path: dist/*.tar.gz build_wheels: if: contains(github.event.head_commit.message, '[upload-to-test-pypi]') - name: Build wheels on ${{ matrix.os }} + name: Build wheels (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up QEMU + - name: Set up QEMU (Linux only) if: runner.os == 'Linux' uses: docker/setup-qemu-action@v3 with: platforms: all - - - uses: actions/setup-python@v2 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" - name: Install cibuildwheel - run: python -m pip install cibuildwheel + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade cibuildwheel - name: Build wheels env: - CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" - CIBW_ARCHS_MACOS: "x86_64 arm64" + + CIBW_PRERELEASE_PYTHONS: "1" + CIBW_ARCHS_LINUX: "auto aarch64" - run: python -m cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v2 + CIBW_ARCHS_MACOS: "arm64" + run: | + python -m cibuildwheel --output-dir dist + + - name: Upload wheel artifacts + uses: actions/upload-artifact@v4 with: - path: wheelhouse/*.whl + name: dist-wheels-${{ matrix.os }} + path: dist/*.whl upload_all: if: contains(github.event.head_commit.message, '[upload-to-test-pypi]') - needs: [build_wheels, make_sdist] + name: Upload to TestPyPI runs-on: ubuntu-latest + needs: [make_sdist, build_wheels] + steps: + - name: Download all dists + uses: actions/download-artifact@v4 + with: + pattern: dist-* + merge-multiple: true + path: dist + + - name: Install modern twine (avoid metadata parser issues) + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade "twine>=6.2.0" "packaging>=24.2" "pkginfo>=1.12.0" + python -m twine --version + python -m pip show twine packaging pkginfo - - uses: actions/download-artifact@v2 - with: - name: artifact - path: dist + - name: Check metadata + run: | + ls -lah dist + python -m twine check dist/* - - uses: pypa/gh-action-pypi-publish@v1.4.1 - with: - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ + - name: Upload to TestPyPI (twine) + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: | + python -m twine upload \ + --repository-url https://test.pypi.org/legacy/ \ + --non-interactive \ + --skip-existing \ + dist/* diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..df08d15 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE +include README.md +include pyproject.toml +include setup.py + +recursive-include rocket_fft *.py *.pyi *.h *.cpp py.typed +recursive-include tests *.py diff --git a/README.md b/README.md index 99f9470..2cc3e59 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![status](https://img.shields.io/pypi/status/rocket-fft?color=%2376519B)](https://pypi.org/project/rocket-fft/) [![downloads](https://img.shields.io/pypi/dm/rocket-fft?color=%2376519B)](https://pypi.org/project/rocket-fft/) -![](https://raw.githubusercontent.com/styfenschaer/rocket-fft/release0.2.5/assets/fourier.gif) +![](https://raw.githubusercontent.com/styfenschaer/rocket-fft/release0.3.0/assets/fourier.gif) Rocket-FFT makes [Numba](https://numba.pydata.org/) aware of `numpy.fft` and `scipy.fft`. It takes its name from the [PocketFFT](https://github.com/mreineck/pocketfft) Fast Fourier Transformation library that powers it, and Numba's goal of making your scientific Python code blazingly fast - like a rocket. diff --git a/pyproject.toml b/pyproject.toml index 15c6d7e..4a740ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,64 @@ [build-system] -requires = ["setuptools>=80.9.0", "numba>=0.60.0"] -build-backend = "setuptools.build_meta" \ No newline at end of file +# Build isolation needs these because setup.py imports numpy+numba to locate headers. +requires = [ + "setuptools>=80", + "wheel", + "numpy>=2.0.0", + "numba>=0.60.0", +] +build-backend = "setuptools.build_meta" + +[project] +name = "rocket-fft" +dynamic = ["version"] +description = "Rocket-FFT extends Numba by scipy.fft and numpy.fft" +readme = { file = "README.md", content-type = "text/markdown" } +license = { text = "BSD-3-Clause" } +authors = [{ name = "Styfen Schär", email = "styfen.schaer.blog@gmail.com" }] +requires-python = ">=3.10" +keywords = ["FFT", "Fourier", "Numba", "SciPy", "NumPy"] + +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Topic :: Scientific/Engineering", + "Topic :: Software Development", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] + +dependencies = [ + "numba>=0.60.0", +] + +[project.urls] +Homepage = "https://github.com/styfenschaer/rocket-fft" +Repository = "https://github.com/styfenschaer/rocket-fft" + +[project.entry-points."numba_extensions"] +init = "rocket_fft:_init_extension" + +[project.optional-dependencies] +dev = ["scipy>=1.13.1", "pytest>=8.4.2"] + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +# If you keep your package at repo root (rocket_fft/), this is fine. +where = ["."] +include = ["rocket_fft*"] + +[tool.setuptools.dynamic] +version = { attr = "rocket_fft._version.__version__" } + +[tool.setuptools.package-data] +rocket_fft = ["*.pyi", "py.typed", "*.h"] diff --git a/rocket_fft/py.typed b/rocket_fft/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py index b64b546..cfab0bf 100644 --- a/setup.py +++ b/setup.py @@ -1,28 +1,20 @@ import platform -import re from pathlib import Path from tempfile import NamedTemporaryFile -from setuptools import Extension, find_packages, setup +from setuptools import Extension, setup from setuptools.command.build_ext import build_ext from setuptools.errors import CompileError -def get_version(rel_path): - with open(Path(__file__).parent / rel_path) as file: - return re.search(r'__version__ = "(.*?)"', file.read())[1] - - def numpy_get_include(): import numpy as np - return np.get_include() def numba_get_include(): import numba as nb - - return Path(nb.__file__).parent + return str(Path(nb.__file__).parent) class build_ext_with_pthreads(build_ext): @@ -30,14 +22,12 @@ def build_extensions(self): if platform.system() != "Windows" and self._pthread_available(): for ext in self.extensions: ext.define_macros.append(("POCKETFFT_PTHREADS", None)) - super().build_extensions() def _pthread_available(self): with NamedTemporaryFile(mode="w", suffix=".c", delete=False) as f: f.write("#include \nint main(void){return 0;}") fname = f.name - try: self.compiler.compile([fname]) return True @@ -45,10 +35,6 @@ def _pthread_available(self): return False -with open("README.md") as file: - long_description = file.read() - - define_macros = [ ("POCKETFFT_NO_SANITYCHECK", None), ("POCKETFFT_CACHE_SIZE", "16"), @@ -59,65 +45,25 @@ def _pthread_available(self): else: extra_compile_args = ["-std=c++11", "-O3", "-Wall"] +include_dirs = [numpy_get_include(), numba_get_include()] + +ext_modules = [ + Extension( + "rocket_fft._pocketfft_numba", + sources=["rocket_fft/_pocketfft_numba.cpp"], + define_macros=define_macros, + extra_compile_args=extra_compile_args, + include_dirs=include_dirs, + ), + Extension( + "rocket_fft._special_helpers", + sources=["rocket_fft/_special_helpers.cpp"], + extra_compile_args=extra_compile_args, + include_dirs=include_dirs, + ), +] setup( - name="rocket-fft", - version=get_version("rocket_fft/_version.py"), - description="Rocket-FFT extends Numba by scipy.fft and numpy.fft", - long_description_content_type="text/markdown", - long_description=long_description, - author="Styfen Schär", - author_email="styfen.schaer.blog@gmail.com", - url="https://github.com/styfenschaer/rocket-fft", - download_url="https://github.com/styfenschaer/rocket-fft", - packages=find_packages(), - include_package_data=True, - package_data={"rocket_fft": ["*.pyi"]}, - entry_points={ - "numba_extensions": [ - "init = rocket_fft:_init_extension", - ], - }, - install_requires=["numba>=0.60.0"], - license="BSD", - ext_modules=[ - Extension( - "rocket_fft._pocketfft_numba", - sources=["rocket_fft/_pocketfft_numba.cpp"], - define_macros=define_macros, - extra_compile_args=extra_compile_args, - ), - Extension( - "rocket_fft._special_helpers", - sources=["rocket_fft/_special_helpers.cpp"], - extra_compile_args=extra_compile_args, - ), - ], - include_dirs=[ - numpy_get_include(), - numba_get_include(), - ], - cmdclass={ - "build_ext": build_ext_with_pthreads, - }, - classifiers=[ - "Development Status :: 4 - Beta", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Topic :: Scientific/Engineering", - "Topic :: Software Development", - "Intended Audience :: Science/Research", - "Intended Audience :: Developers", - "Intended Audience :: Education", - ], - keywords=["FFT", "Fourier", "Numba", "SciPy", "NumPy"], - extras_require={ - "dev": ["scipy>=1.13.1", "pytest>=8.4.2"], - }, -) \ No newline at end of file + ext_modules=ext_modules, + cmdclass={"build_ext": build_ext_with_pthreads}, +) From 6d3976c38a99a2a3409e5c2583c83e8aed437fb3 Mon Sep 17 00:00:00 2001 From: styfenschaer <79762922+styfenschaer@users.noreply.github.com> Date: Thu, 25 Dec 2025 11:22:40 +0100 Subject: [PATCH 2/2] Add support for Python 3.9 --- .github/workflows/upload-to-pypi.yml | 2 +- .github/workflows/upload-to-test-pypi.yml | 2 +- pyproject.toml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/upload-to-pypi.yml b/.github/workflows/upload-to-pypi.yml index 4707162..b06b712 100644 --- a/.github/workflows/upload-to-pypi.yml +++ b/.github/workflows/upload-to-pypi.yml @@ -60,7 +60,7 @@ jobs: - name: Build wheels env: - CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*" CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" diff --git a/.github/workflows/upload-to-test-pypi.yml b/.github/workflows/upload-to-test-pypi.yml index d752fea..419ca25 100644 --- a/.github/workflows/upload-to-test-pypi.yml +++ b/.github/workflows/upload-to-test-pypi.yml @@ -61,7 +61,7 @@ jobs: - name: Build wheels env: - CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*" CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" diff --git a/pyproject.toml b/pyproject.toml index 4a740ba..b890027 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ description = "Rocket-FFT extends Numba by scipy.fft and numpy.fft" readme = { file = "README.md", content-type = "text/markdown" } license = { text = "BSD-3-Clause" } authors = [{ name = "Styfen Schär", email = "styfen.schaer.blog@gmail.com" }] -requires-python = ">=3.10" +requires-python = ">=3.9" keywords = ["FFT", "Fourier", "Numba", "SciPy", "NumPy"] classifiers = [ @@ -28,6 +28,7 @@ classifiers = [ "Intended Audience :: Developers", "Intended Audience :: Education", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12",