Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test against numpy 2.0 #166

Merged
merged 14 commits into from
Apr 30, 2024
48 changes: 48 additions & 0 deletions .github/workflows/test_code_generation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Test code generation

on:
pull_request:
push:
branches: [main]

jobs:
code-generation:
runs-on: ubuntu-latest
defaults:
run:
shell: bash -l {0}

steps:
- name: checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Micromamba
uses: mamba-org/setup-micromamba@v1
with:
environment-name: TEST
init-shell: bash
create-args: >-
python=3 pip
--file requirements-dev.txt
--channel conda-forge

- name: Install nightly version of numpy
run: |
python -m pip install --pre --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple --extra-index-url https://pypi.org/simple numpy scipy pandas -U

- name: Test Code Generation
run: >
git clone https://github.com/TEOS-10/GSW-C.git ../GSW-C
&& git clone https://github.com/TEOS-10/GSW-Matlab.git ../GSW-Matlab
&& python tools/copy_from_GSW-C.py
&& python tools/mat2npz.py
&& python tools/make_ufuncs.py
&& python tools/make_wrapped_ufuncs.py
&& python tools/fix_wrapped_ufunc_typos.py

- name: Install gsw
run: >
python -m pip install -v -e . --no-deps --no-build-isolation --force-reinstall
&& python -m pytest -s -rxs -v gsw/tests
29 changes: 20 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,21 @@ jobs:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
os: [windows-latest, ubuntu-latest, macos-latest]
experimental: [false]
# Oldest one based on NEP-29 and latest one.
# See https://numpy.org/neps/nep-0029-deprecation_policy.html
numpy-version: ["1.23", "1.26"]
exclude:
- python-version: "3.12"
numpy-version: "1.23"
include:
- python-version: "3.12"
os: "ubuntu-latest"
experimental: true
fail-fast: false
defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@v4
Expand All @@ -31,18 +39,21 @@ jobs:
create-args: >-
python=${{ matrix.python-version }} python-build numpy=${{ matrix.numpy-version }} --file requirements-dev.txt --channel conda-forge

- name: Install gsw
shell: bash -l {0}
- name: Install unstable dependencies
if: matrix.experimental == true
run: |
python -m pip install -e . --no-deps --force-reinstall
python -m pip install \
--index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/ \
--trusted-host pypi.anaconda.org \
--no-deps --pre --upgrade \
numpy scipy pandas;
python -m pip install -v -e . --no-deps --no-build-isolation --force-reinstall

- name: Debug
shell: bash -l {0}
- name: Install gsw
if: matrix.experimental != true
run: |
python -c "import numpy; print(f'Running numpy {numpy.__version__}')"
python -m pip install -e . --no-deps --force-reinstall

- name: Tests
shell: bash -l {0}
run: |
micromamba activate TEST
pytest -s -rxs -v gsw/tests
python -m pytest -s -rxs -v gsw/tests
2 changes: 1 addition & 1 deletion gsw/tests/test_check_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
mfuncnames = [mf.name for mf in mfuncs]


@pytest.fixture(params=[-360, 0, 360])
@pytest.fixture(params=[-360., 0., 360.])
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardest part was setting up the np2.0 test! This was the only failure we had due to https://numpy.org/devdocs/numpy_2_0_migration_guide.html#changes-to-numpy-data-type-promotion

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@efiring this is the only code change in this PR. Everything else is to be able to set a testing matrix with numpy 2.0. This PR also adds a test for the code generation step that serves both as test and developer breadcrumbs in case we need to do that locally.

def lonshift(request):
return request.param

Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
build-backend = "setuptools.build_meta"
requires = [
"build",
"oldest-supported-numpy",
'numpy<3,>=2.0.0rc1; python_version >= "3.9"',
'oldest-supported-numpy; python_version < "3.9"',
"pip>9.0.1",
"setuptools>=42",
"setuptools_scm[toml]>=3.4",
Expand Down Expand Up @@ -60,18 +61,19 @@ write_to_template = "__version__ = '{version}'"
tag_regex = "^(?P<prefix>v)?(?P<version>[^\\+]+)(?P<suffix>.*)?$"

[tool.ruff]
select = [
lint.select = [
"A", # flake8-builtins
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"F", # flakes
"I", # import sorting
"UP", # upgrade
"NPY201", # numpy 2.0
]
target-version = "py38"
line-length = 105

ignore = [
lint.ignore = [
"F401", # module imported but unused
"E501", # line too long
"E713", # test for membership should be 'not in'
Expand All @@ -81,7 +83,7 @@ exclude = [
"tools",
]

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"docs/conf.py" = [
"A001", # variable is shadowing a python builtin
]
Expand Down
6 changes: 4 additions & 2 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
check-manifest
dask
numpydoc
pandas>=2
pytest
scipy
setuptools_scm
sphinx
sphinx_rtd_theme
twine
pandas>=2
dask
xarray
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import shutil
import sys

import pkg_resources
import numpy
from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext as _build_ext

Expand All @@ -21,7 +21,7 @@ def read(*parts):
class build_ext(_build_ext):
# Extension builder from pandas without the cython stuff
def build_extensions(self):
numpy_incl = pkg_resources.resource_filename("numpy", "core/include")
numpy_incl = numpy.get_include()

for ext in self.extensions:
if hasattr(ext, "include_dirs") and not numpy_incl in ext.include_dirs:
Expand Down
2 changes: 1 addition & 1 deletion tools/c_header_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np


basedir = Path('..').resolve()
basedir = Path(__file__).parent.parent

def get_signatures(strip_extern=True, srcdir='src'):
"""
Expand Down
25 changes: 15 additions & 10 deletions tools/copy_from_GSW-C.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@
Copy all relevant .c and .h files from Python-C, if they are newer.

This is a simple utility, to be run from this directory. It assumes that
an up-to-date GSW-C github repo and the present GSW-Python repo are
an up-to-date GSW-C GitHub repo and the present GSW-Python repo are
siblings in the directory tree.
"""

import sys
import shutil
from pathlib import Path

current = Path(__file__).parent
srcdir = Path(current.parent.parent, "GSW-C")

fnames = ['gsw_oceanographic_toolbox.c',
'gsw_saar.c',
'gsw_saar_data.h',
'gsw_internal_const.h',
'gswteos-10.h']
destdir = Path(current.parent, "src", "c_gsw")

fnames = [
"gsw_oceanographic_toolbox.c",
"gsw_saar.c",
"gsw_saar_data.h",
"gsw_internal_const.h",
"gswteos-10.h",
]

srcdir = Path('..', '..', 'GSW-C')
destdir = Path('..', 'src', 'c_gsw')

if not srcdir.exists():
raise IOError(
f"Could not find the GSW-C source code in {srcdir}."
f"Could not find the GSW-C source code in {srcdir}. "
"Please read the development notes to find how to setup your GSW-Python development environment."
)

Expand All @@ -31,4 +36,4 @@
dest = destdir.joinpath(fname)
if src.stat().st_mtime > dest.stat().st_mtime:
shutil.copyfile(str(src), str(dest))
print('copied %s to %s' % (src, dest))
print(f"copied {src} to {dest}")
2 changes: 1 addition & 1 deletion tools/fix_wrapped_ufunc_typos.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path
import shutil

basedir = Path('..').resolve()
basedir = Path(__file__).parent.parent
wrapmod = basedir.joinpath('gsw', '_wrapped_ufuncs.py')

orig = wrapmod.with_suffix('.orig')
Expand Down
10 changes: 5 additions & 5 deletions tools/make_ufuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

blacklist = ['add_barrier']

basedir = Path('..').resolve()
basedir = Path(__file__).parent.parent

modfile_head_top = """
/*
Expand Down Expand Up @@ -358,19 +358,19 @@ def write_modfile(modfile_name, srcdir):
f.write(''.join(chunks))

funcnamelist1.sort()
with open(srcdir + '_ufuncs1.list', 'w') as f:
with open(srcdir.joinpath('_ufuncs1.list'), 'w') as f:
f.write('\n'.join(funcnamelist1))

funcnamelist2.sort()
with open(srcdir + '_ufuncs2.list', 'w') as f:
with open(srcdir.joinpath('_ufuncs2.list'), 'w') as f:
f.write('\n'.join(funcnamelist2))

funcnamelist = funcnamelist1 + funcnamelist2 + funcnamelist3
funcnamelist.sort()
with open(srcdir + '_ufuncs.list', 'w') as f:
with open(srcdir.joinpath('_ufuncs.list'), 'w') as f:
f.write('\n'.join(funcnamelist))

if __name__ == '__main__':
srcdir = 'src'
srcdir = basedir.joinpath('src')
modfile_name = basedir.joinpath(srcdir, '_ufuncs.c')
write_modfile(modfile_name, srcdir=srcdir)
8 changes: 4 additions & 4 deletions tools/make_wrapped_ufuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
fix_outputs_doc,
docstring_from_sections)

basedir = Path('..').resolve()
basedir = Path(__file__).parent.parent


# Functions that are Matlab subroutines, or exclusive to
Expand Down Expand Up @@ -205,8 +205,8 @@ def uf_wrapper(ufname):
return wrapper_template % subs

if __name__ == '__main__':
srcdir = 'src'
with open(srcdir + '_ufuncs.list') as f:
srcdir = basedir.joinpath('src')
with open(srcdir.joinpath('_ufuncs.list')) as f:
ufunclist = [name.strip() for name in f.readlines()]
ufunclist = [name for name in ufunclist if name not in blacklist]

Expand All @@ -232,5 +232,5 @@ def uf_wrapper(ufname):
f.write(wrapped)
wrapped_ufnames.append(ufname)
wrapped_ufnames.sort()
with open(srcdir + '_wrapped_ufuncs.list', 'w') as f:
with open(srcdir.joinpath('_wrapped_ufuncs.list'), 'w') as f:
f.write('\n'.join(wrapped_ufnames) + '\n')
12 changes: 9 additions & 3 deletions tools/mat2npz.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,14 @@ def loadmatdict(fname):

# The following relative path will depend on the directory layout for
# whoever is running this utility.
gsw_data_file = Path('..', '..', '..', 'gsw_matlab_%s' % mat_zip_ver,
'library', 'gsw_data_%s.mat' % data_ver)
basedir = Path(__file__).parent.parent
gsw_data_file = Path(
basedir.parent,
"GSW-Matlab",
"Toolbox",
"library",
f"gsw_data_{data_ver}.mat",
)
print(gsw_data_file)

gsw_data = loadmatdict(gsw_data_file)
Expand All @@ -55,5 +61,5 @@ def loadmatdict(fname):
cv_vars = gsw_data['gsw_cv']
cv_vars['gsw_data_file'] = str(gsw_data_file)
cv_vars['mat_zip_ver'] = mat_zip_ver
fname = Path('..', 'gsw', 'tests', 'gsw_cv_%s' % data_ver)
fname = Path(basedir, "gsw", "tests", f"gsw_cv_{data_ver}")
np.savez(str(fname), **cv_vars)
3 changes: 1 addition & 2 deletions tools/matlab_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import re
from pathlib import Path


basedir = Path('..').resolve()
basedir = Path(__file__).parent.parent

gsw_matlab_dir = basedir.joinpath('..', 'GSW-Matlab', 'Toolbox').resolve()
if not gsw_matlab_dir.exists():
Expand Down