diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fc5e6eb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "Bot" \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ff5ccb8 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,49 @@ +name: Documentation + +on: + pull_request: + push: + branches: + - main + release: + types: + - published + +jobs: + build-docs: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Micromamba + uses: mamba-org/setup-micromamba@v1 + with: + environment-name: DOCS + init-shell: bash + create-args: >- + python=3 pip iris numpy sphinx sphinx-rtd-theme --channel conda-forge + + - name: Install gridfill + shell: bash -l {0} + run: | + python -m pip install -e . --no-deps --force-reinstall + + - name: Build documentation + shell: bash -l {0} + run: | + set -e + micromamba activate DOCS + pushd doc + make clean html linkcheck + popd + + - name: Deploy + if: success() && github.event_name == 'release' + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: doc/_build/html \ No newline at end of file diff --git a/.github/workflows/tests_iris.yml b/.github/workflows/tests_iris.yml new file mode 100644 index 0000000..9b8638a --- /dev/null +++ b/.github/workflows/tests_iris.yml @@ -0,0 +1,37 @@ +name: Tests (with iris) + +on: + pull_request: + push: + branches: [main] + +jobs: + run: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11"] + os: [windows-latest, ubuntu-latest, macos-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v4 + + - name: Setup Micromamba Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v1 + with: + environment-name: TEST + init-shell: bash + create-args: >- + python=${{ matrix.python-version }} pip pytest iris numpy --channel conda-forge + + + - name: Install gridfill + shell: bash -l {0} + run: | + python -m pip install -e . --no-deps --force-reinstall + + - name: Tests + shell: bash -l {0} + run: | + python -m pytest -vrsx gridfill/tests \ No newline at end of file diff --git a/.github/workflows/tests_numpy.yml b/.github/workflows/tests_numpy.yml new file mode 100644 index 0000000..dc9e646 --- /dev/null +++ b/.github/workflows/tests_numpy.yml @@ -0,0 +1,37 @@ +name: Tests (numpy only) + +on: + pull_request: + push: + branches: [main] + +jobs: + run: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + os: [windows-latest, ubuntu-latest, macos-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v4 + + - name: Setup Micromamba Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v1 + with: + environment-name: TEST + init-shell: bash + create-args: >- + python=${{ matrix.python-version }} pip pytest numpy --channel conda-forge + + + - name: Install gridfill + shell: bash -l {0} + run: | + python -m pip install -e . --no-deps --force-reinstall + + - name: Tests + shell: bash -l {0} + run: | + python -m pytest -vrsx gridfill/tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index d6c31a0..dd66cce 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ dist ## Ignore build output gridfill/_gridfill.c *.so +gridfill/_version.py \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 70a01f3..0000000 --- a/.travis.yml +++ /dev/null @@ -1,53 +0,0 @@ -language: python - -sudo: false - -env: - global: - - PACKAGES_STANDARD="setuptools cython numpy nose" - matrix: - - NAME="Python 2.7 (standard)" - PYTHON_VERSION=2.7 - PACKAGES="${PACKAGES_STANDARD}" - - NAME="Python 2.7 (extras)" - PYTHON_VERSION=2.7 - PACKAGES="${PACKAGES_STANDARD} iris" - - NAME="Python 3.5 (standard)" - PYTHON_VERSION=3.5 - PACKAGES="${PACKAGES_STANDARD}" - - NAME="Python 3.5 (extras)" - PYTHON_VERSION=3.5 - PACKAGES="${PACKAGES_STANDARD} iris" - - NAME="Python 3.6 (standard)" - PYTHON_VERSION=3.6 - PACKAGES="${PACKAGES_STANDARD}" - - NAME="Python 3.6 (extras)" - PYTHON_VERSION=3.6 - PACKAGES="${PACKAGES_STANDARD} iris" - -install: - # Install Miniconda so we can use it to manage dependencies: - - wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - # Set-up extra channels for dependencies: - - conda config --set always_yes yes --set changeps1 no - - conda config --add channels conda-forge - - conda update conda - - conda info -a - # Create a test environment with the required Python version: - - conda create -n test-environment python=$PYTHON_VERSION - - source activate test-environment - # Install required packages: - - conda install ${PACKAGES} - # Install the package to test: - - python setup.py install - -script: - - mkdir ../test_directory - - cd ../test_directory - - nosetests gridfill --verbosity=2 - -notifications: - email: false diff --git a/MANIFEST.in b/MANIFEST.in index 3edea60..7425102 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include COPYING -include README.rst -recursive-include lib *.py -recursive-include lib *.pyx +include COPYING README.rst +recursive-include gridfill *.py *.pyx *.npy +recursive-include doc *.py *.rst Makefile diff --git a/README.rst b/README.rst index 1886610..4e95b45 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,22 @@ gridfill can also operate on `iris` cubes, which requires the iris_ package to be installed. -.. _conda: http://conda.pydata.org +Developers +---------- + +For development the pytest package is required in order to run tests. In order +to run all tests you should also install iris_. + +Tests can be run in-place but you must first compile the extension module:: + + python setup.py build_ext --inplace + python -m pytest + +Tests can also be run against the installed version:: + + python -m pytest --pyargs gridfill.tests + + +.. _conda: https://docs.conda.io/en/latest/ .. _Github: https://github.com/ajdawson/gridfill -.. _iris: http://scitools.org.uk/iris +.. _iris: https://scitools-iris.readthedocs.io/en/stable/ diff --git a/doc/conf.py b/doc/conf.py index 52bffd5..422120e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -73,7 +73,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -147,7 +147,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -299,5 +299,5 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'numpy': ('http://docs.scipy.org/doc/numpy', None), - 'iris': ('http://scitools.org.uk/iris/docs/latest', None), + 'iris': ('https://scitools-iris.readthedocs.io/en/latest/', None), } diff --git a/doc/index.rst b/doc/index.rst index 147ad71..0bd7bad 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -13,7 +13,7 @@ Gridfill Gridfill is a Python package for filling in missing values in gridded data. Missing values are filled by solving Poisson's equation using an iterative relaxation scheme at the missing values, resulting in a smooth filling. -The core package runs on Python 2 or 3, on Linux and OSX (maybe on Windows too), and requires numpy_. +The core package runs on Python 3, on Linux and OSX (maybe on Windows too), and requires numpy_. Gridfill has an optional interface for working with iris_ cubes. @@ -64,10 +64,10 @@ Bug reports and feature requests should be made using the repository issues_ pag Pull requests for new features and bug fixes are welcome! -.. _numpy: http://www.numpy.org -.. _iris: http://scitools.org.uk/iris -.. _conda: http://conda.pydata.org/docs/ -.. _setuptools: https://pypi.python.org/pypi/setuptools -.. _cython: http://cython.org -.. _Github: http://github.com/ajdawson/gridfill -.. _issues: http://github.com/ajdawson/gridfill/issues +.. _numpy: https://numpy.org +.. _iris: https://scitools-iris.readthedocs.io/en/stable/ +.. _conda: https://docs.conda.io/en/latest/ +.. _setuptools: https://setuptools.pypa.io/en/latest/ +.. _cython: https://cython.org +.. _Github: https://github.com/ajdawson/gridfill +.. _issues: https://github.com/ajdawson/gridfill/issues diff --git a/gridfill/__init__.py b/gridfill/__init__.py index 8f8496f..6745d52 100644 --- a/gridfill/__init__.py +++ b/gridfill/__init__.py @@ -22,9 +22,10 @@ from .gridfill import fill, fill_cube - -# Define a version string. -__version__ = '1.0.dev2' +try: + from ._version import __version__ +except ImportError: + __version__ == "unknown" # Define the objects to be imported by imports of the form: # from gridfill import * diff --git a/gridfill/_gridfill.pyx b/gridfill/_gridfill.pyx index 415d273..6ff46a3 100644 --- a/gridfill/_gridfill.pyx +++ b/gridfill/_gridfill.pyx @@ -1,4 +1,4 @@ -#cython: boundscheck=False, wraparound=False, initializedcheck=False +#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False """Fill missing values in grids using an iterative relaxation scheme.""" # Copyright (c) 2016 Andrew Dawson # diff --git a/gridfill/tests/test_fill.py b/gridfill/tests/test_fill.py index cbdbc72..962910b 100644 --- a/gridfill/tests/test_fill.py +++ b/gridfill/tests/test_fill.py @@ -20,7 +20,7 @@ # THE SOFTWARE. import os -from nose.tools import raises +import pytest import numpy as np import numpy.ma as ma @@ -72,12 +72,17 @@ def test_multi_grid(self): cyclic=self.cyclic, verbose=False) self.assert_array_almost_equal(filled, self.soln) - @raises(TypeError) def test_not_masked(self): - filled, c = fill(self.grid.filled(fill_value=np.nan), self.eps, - relax=self.relax, itermax=self.itermax, - initzonal=self.initzonal, cyclic=self.cyclic, - verbose=False) + with pytest.raises(TypeError): + fill( + self.grid.filled(fill_value=np.nan), + self.eps, + relax=self.relax, + itermax=self.itermax, + initzonal=self.initzonal, + cyclic=self.cyclic, + verbose=False, + ) def assert_array_almost_equal(self, a, b): np.testing.assert_array_almost_equal(a, b) diff --git a/gridfill/tests/test_fill_cube.py b/gridfill/tests/test_fill_cube.py index 2c42373..02f1511 100644 --- a/gridfill/tests/test_fill_cube.py +++ b/gridfill/tests/test_fill_cube.py @@ -20,13 +20,12 @@ # THE SOFTWARE. import warnings -from nose import SkipTest -from nose.tools import raises - +import pytest try: - import iris + import iris.coords + import iris.cube except ImportError: - raise SkipTest('Cannot import iris, fill_cube() will not be tested') + pytest.skip('Cannot import iris, fill_cube() will not be tested', allow_module_level=True) import numpy as np from gridfill import fill_cube @@ -83,11 +82,13 @@ def test_multi_grid_inplace(self): assert not hasattr(filled.data, 'mask') assert not hasattr(cube.data, 'mask') - @raises(TypeError) def test_not_masked(self): cube = self.cube.copy(data=self.cube.data.filled(fill_value=np.nan)) - fill_cube(cube, self.eps, relax=self.relax, itermax=self.itermax, - initzonal=self.initzonal, verbose=False) + with pytest.raises(TypeError): + fill_cube( + cube, self.eps, relax=self.relax, itermax=self.itermax, + initzonal=self.initzonal, verbose=False + ) def test_not_converged_warning(self): with warnings.catch_warnings(record=True) as w: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7d2af6d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = [ + "setuptools>=60", + "setuptools-scm>=8.0", + "wheel", + "Cython>=3.0", + "numpy", +] + +[project] +name = "gridfill" +description = "Fill missing values in grids using iterative relaxation" +license = {text = "MIT"} +authors = [{name = "Andrew Dawson"}] +requires-python = ">=3.8" +dependencies = ["numpy"] +dynamic = ["readme", "version"] +classifiers = [ + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Mathematics', + 'Topic :: Scientific/Engineering :: Physics', + 'Topic :: Scientific/Engineering :: Atmospheric Science', +] + +[tool.setuptools] +license-files = ["COPYING"] +include-package-data = true + +[tool.setuptools.package-data] +gridfill_tests = ["data/*.npy"] + +[tool.setuptools.dynamic] +readme = {file = "README.rst", content-type = "text/x-rst"} + +[tool.setuptools_scm] +version_file = "gridfill/_version.py" diff --git a/setup.py b/setup.py index 6b9f71f..5575817 100644 --- a/setup.py +++ b/setup.py @@ -18,79 +18,21 @@ # 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. - -import setuptools from setuptools import setup, Extension +from Cython.Build import cythonize +import numpy as np - -try: - from Cython.Distutils import build_ext -except ImportError: - raise ImportError('Cython 0.15.1+ is required to install gridfill') -try: - import numpy as np -except ImportError: - raise ImportError('NumPy 1.6+ is required to install gridfill') - -# Define the required dependencies: -install_requires = [ - 'numpy>=1.6', - 'Cython>=0.15.1', - 'setuptools>=0.7.2', -] - -# Get the library version: -for line in open('gridfill/__init__.py').readlines(): - if line.startswith('__version__'): - exec(line.strip()) - -# Define packages and package data: -packages = [ - 'gridfill', - 'gridfill.tests', -] -package_data = { - 'gridfill.tests': ['data/*.npy'], -} - -# Define extension modules: -ext_modules = [ +extensions = [ #The core implemented as a Cython extension: Extension( 'gridfill._gridfill', ['gridfill/_gridfill.pyx'], include_dirs=[np.get_include()], + define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], ), ] -classifiers = [ - 'License :: OSI Approved :: MIT License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Mathematics', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Scientific/Engineering :: Atmospheric Science', -] - setup( name='gridfill', - version=__version__, - author="Andrew Dawson", - url='https://github.com/ajdawson/gridfill', - description='Fill missing values in grids using iterative relaxation', - license='MIT', - install_requires=install_requires, - packages=packages, - package_data=package_data, - ext_modules=ext_modules, - cmdclass={'build_ext': build_ext}, - classifiers=classifiers, + ext_modules=cythonize(extensions), )