From 1abd1f2188e1deed029c035ad99cf80f5c10ff54 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 16 Sep 2025 22:15:11 +0800 Subject: [PATCH 1/7] SPEC 0: Bump minimum supported versions to NumPy 2.0 --- .github/workflows/ci_tests.yaml | 2 +- .github/workflows/ci_tests_legacy.yaml | 2 +- environment.yml | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 812fd667eab..71ffbc67912 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -72,7 +72,7 @@ jobs: include: # Python 3.11 + core packages (minimum supported versions) + optional packages (minimum supported versions if any) - python-version: '3.11' - numpy-version: '1.26' + numpy-version: '2.0' pandas-version: '=2.1' xarray-version: '=2023.07' optional-packages: ' contextily geopandas ipython pyarrow-core rioxarray netCDF4 sphinx-gallery' diff --git a/.github/workflows/ci_tests_legacy.yaml b/.github/workflows/ci_tests_legacy.yaml index b5fa4860af5..6b75cd47da0 100644 --- a/.github/workflows/ci_tests_legacy.yaml +++ b/.github/workflows/ci_tests_legacy.yaml @@ -61,7 +61,7 @@ jobs: python=3.11 gmt=${{ matrix.gmt_version }} ghostscript<10 - numpy=1.26 + numpy=2.0 pandas xarray packaging=24.2 diff --git a/environment.yml b/environment.yml index 7cde2e70015..2951bf6be56 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,7 @@ dependencies: # Required dependencies - gmt=6.5.0 - ghostscript=10.04.0 - - numpy>=1.26 + - numpy>=2.0 - pandas>=2.1 - xarray>=2023.07 - packaging>=24.2 diff --git a/pyproject.toml b/pyproject.toml index e391433947f..cae2165a832 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "numpy>=1.26", + "numpy>=2.0", "pandas>=2.1", "xarray>=2023.07", "packaging>=24.2", diff --git a/requirements.txt b/requirements.txt index e17f53add57..701c9ca5a4b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Required packages -numpy>=1.26 +numpy>=2.0 pandas>=2.1 xarray>=2023.07 packaging>=24.2 From d1d79d2d04edb27826e63891b7bf8680362d939e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 16 Sep 2025 22:17:04 +0800 Subject: [PATCH 2/7] Remove workarounds for NumPy < 2.0 --- pygmt/conftest.py | 11 ----------- pygmt/tests/test_clib_to_numpy.py | 10 +--------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 pygmt/conftest.py diff --git a/pygmt/conftest.py b/pygmt/conftest.py deleted file mode 100644 index ee491b7f8bc..00000000000 --- a/pygmt/conftest.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -conftest.py for pytest. -""" - -import numpy as np -from packaging.version import Version - -# TODO(NumPy>=2.0): Remove the conftest.py file. -# Address https://github.com/GenericMappingTools/pygmt/issues/2628. -if Version(np.__version__) >= Version("2.0.0.dev0+git20230726"): - np.set_printoptions(legacy="1.25") # type: ignore[arg-type] diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 9ff86c84f9b..4e267a9bb99 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -3,7 +3,6 @@ """ import datetime -import sys import numpy as np import numpy.testing as npt @@ -52,14 +51,7 @@ def _check_result(result, expected_dtype): @pytest.mark.parametrize( ("data", "expected_dtype"), [ - # TODO(NumPy>=2.0): Remove the if-else statement after NumPy>=2.0. - pytest.param( - [1, 2, 3], - np.int32 - if sys.platform == "win32" and Version(np.__version__) < Version("2.0") - else np.int64, - id="int", - ), + pytest.param([1, 2, 3], np.int64, id="int"), pytest.param([1.0, 2.0, 3.0], np.float64, id="float"), pytest.param( [complex(+1), complex(-2j), complex("-Infinity+NaNj")], From 5ce69a24219739e8afed4a705c500ec58a810010 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 16 Sep 2025 22:37:41 +0800 Subject: [PATCH 3/7] Fix two tests due to the removal of conftest.py --- pygmt/datatypes/image.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pygmt/datatypes/image.py b/pygmt/datatypes/image.py index 6188e6c88e0..177c00fcccb 100644 --- a/pygmt/datatypes/image.py +++ b/pygmt/datatypes/image.py @@ -70,7 +70,7 @@ class _GMT_IMAGE(ctp.Structure): # noqa: N801 >>> data.shape (180, 360, 3) >>> data.min(), data.max() - (10, 255) + (np.uint8(10), np.uint8(255)) """ _fields_: ClassVar = [ @@ -139,17 +139,18 @@ def to_xarray(self) -> xr.DataArray: ..., [177, 179, 179, ..., 178, 177, 177], [185, 187, 187, ..., 187, 186, 185], - [189, 191, 191, ..., 191, 191, 189]]], dtype=uint8) + [189, 191, 191, ..., 191, 191, 189]]], + shape=(3, 180, 360), dtype=uint8) Coordinates: * y (y) float64... 89.5 88.5 87.5 86.5 ... -86.5 -87.5 -88.5 -89.5 * x (x) float64... -179.5 -178.5 -177.5 -176.5 ... 177.5 178.5 179.5 * band (band) uint8... 1 2 3 Attributes: - long_name: z + long_name: z >>> da.coords["x"] # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS ... - array([-179.5, -178.5, -177.5, ..., 177.5, 178.5, 179.5]) + array([-179.5, -178.5, -177.5, ..., 177.5, 178.5, 179.5], shape=(360,)) Coordinates: * x (x) float64... -179.5 -178.5 -177.5 -176.5 ... 177.5 178.5 179.5 Attributes: From 1b97f25f528c8af3947db02eacd1ddb26054f902 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 8 Oct 2025 22:38:53 +0800 Subject: [PATCH 4/7] Bump to xarray 2024.5 --- .github/workflows/ci_tests.yaml | 2 +- .github/workflows/ci_tests_legacy.yaml | 2 +- environment.yml | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index 3cd74b7ccfe..7fc228f58a2 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -74,7 +74,7 @@ jobs: - python-version: '3.11' numpy-version: '2.0' pandas-version: '=2.2' - xarray-version: '=2023.10' + xarray-version: '=2024.5' optional-packages: ' contextily geopandas ipython pyarrow-core rioxarray netCDF4 sphinx-gallery' # Python 3.13 + core packages (latest versions) + optional packages - python-version: '3.13' diff --git a/.github/workflows/ci_tests_legacy.yaml b/.github/workflows/ci_tests_legacy.yaml index 62692983221..99e961ed181 100644 --- a/.github/workflows/ci_tests_legacy.yaml +++ b/.github/workflows/ci_tests_legacy.yaml @@ -63,7 +63,7 @@ jobs: ghostscript numpy=2.0 pandas=2.2 - xarray=2023.10 + xarray=2024.5 packaging=24.2 contextily=1.5 geopandas=1.0 diff --git a/environment.yml b/environment.yml index 8f87a0948e6..bc4722dcf09 100644 --- a/environment.yml +++ b/environment.yml @@ -9,7 +9,7 @@ dependencies: - ghostscript=10.06.0 - numpy>=2.0 - pandas>=2.2 - - xarray>=2023.10 + - xarray>=2024.5 - packaging>=24.2 # Optional dependencies - contextily>=1.5 diff --git a/pyproject.toml b/pyproject.toml index 48dcc38015b..8a03f58b1a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ classifiers = [ dependencies = [ "numpy>=2.0", "pandas>=2.2", - "xarray>=2023.10", + "xarray>=2024.5", "packaging>=24.2", ] dynamic = ["version"] diff --git a/requirements.txt b/requirements.txt index f52e9f9fd42..804519b48c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Required packages numpy>=2.0 pandas>=2.2 -xarray>=2023.10 +xarray>=2024.5 packaging>=24.2 From 7d3c1d19166fc0f6c9ef56d61d385e06ba00c90b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Oct 2025 10:31:46 +0800 Subject: [PATCH 5/7] Update to xarray repr for xarray>=2024.02 --- pygmt/datasets/tile_map.py | 10 +++++----- pygmt/datatypes/grid.py | 20 ++++++++++---------- pygmt/datatypes/image.py | 10 +++++----- pygmt/helpers/decorators.py | 4 +--- pygmt/xarray/backend.py | 16 ++++++++-------- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/pygmt/datasets/tile_map.py b/pygmt/datasets/tile_map.py index 08520396099..1dffaf58e20 100644 --- a/pygmt/datasets/tile_map.py +++ b/pygmt/datasets/tile_map.py @@ -122,12 +122,12 @@ def load_tile_map( ... ) >>> raster.sizes Frozen({'band': 3, 'y': 256, 'x': 512}) - >>> raster.coords # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + >>> raster.coords Coordinates: - * band (band) uint8... 1 2 3 - * y (y) float64... -7.081e-10 -7.858e+04 ... -1.996e+07 -2.004e+07 - * x (x) float64... -2.004e+07 -1.996e+07 ... 1.996e+07 2.004e+07 - spatial_ref int... 0 + * band (band) uint8 1 2 3 + * y (y) float64 -7.081e-10 -7.858e+04 ... -1.996e+07 -2.004e+07 + * x (x) float64 -2.004e+07 -1.996e+07 ... 1.996e+07 2.004e+07 + spatial_ref int 0 >>> # CRS is set only if rioxarray is available >>> if hasattr(raster, "rio"): ... raster.rio.crs.to_string() diff --git a/pygmt/datatypes/grid.py b/pygmt/datatypes/grid.py index 0c2632b1eb4..080bf3810cb 100644 --- a/pygmt/datatypes/grid.py +++ b/pygmt/datatypes/grid.py @@ -114,8 +114,8 @@ def to_xarray(self) -> xr.DataArray: ... grid = lib.read_virtualfile(voutgrd, kind="grid") ... # Convert to xarray.DataArray and use it later ... da = grid.contents.to_xarray() - >>> da # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS - ... + >>> da + Size: 448B array([[347.5, 344.5, 386. , 640.5, 617. , 579. , 646.5, 671. ], [383. , 284.5, 344.5, 394. , 491. , 556.5, 578.5, 618.5], [373. , 367.5, 349. , 352.5, 419.5, 428. , 570. , 667.5], @@ -132,8 +132,8 @@ def to_xarray(self) -> xr.DataArray: [347.5, 331.5, 309. , 282. , 190. , 208. , 299.5, 348. ]], dtype=float32) Coordinates: - * lat (lat) float64... -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 - * lon (lon) float64... -54.5 -53.5 -52.5 -51.5 -50.5 -49.5 -48.5 -47.5 + * lat (lat) float64 112B -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 + * lon (lon) float64 64B -54.5 -53.5 -52.5 -51.5 -50.5 -49.5 -48.5 -47.5 Attributes: Conventions: CF-1.7 title: Produced by grdcut @@ -141,23 +141,23 @@ def to_xarray(self) -> xr.DataArray: description: Reduced by Gaussian Cartesian filtering (111.2 km fullwi... actual_range: [190. 981.] long_name: elevation (m) - >>> da.coords["lon"] # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS - ... + >>> da.coords["lon"] + Size: 64B array([-54.5, -53.5, -52.5, -51.5, -50.5, -49.5, -48.5, -47.5]) Coordinates: - * lon (lon) float64... -54.5 -53.5 -52.5 -51.5 -50.5 -49.5 -48.5 -47.5 + * lon (lon) float64 64B -54.5 -53.5 -52.5 -51.5 -50.5 -49.5 -48.5 -47.5 Attributes: long_name: longitude units: degrees_east standard_name: longitude axis: X actual_range: [-55. -47.] - >>> da.coords["lat"] # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS - ... + >>> da.coords["lat"] + Size: 64B array([-23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, -15.5, -14.5, -13.5, -12.5, -11.5, -10.5]) Coordinates: - * lat (lat) float64... -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 + * lat (lat) float64 64B -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 Attributes: long_name: latitude units: degrees_north diff --git a/pygmt/datatypes/image.py b/pygmt/datatypes/image.py index 3978e50ac36..75f63871f73 100644 --- a/pygmt/datatypes/image.py +++ b/pygmt/datatypes/image.py @@ -67,9 +67,9 @@ class _GMT_IMAGE(ctp.Structure): # noqa: N801 [2, 2, 2, 2] b'BRPa' 0.5 1 0 - >>> x # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS + >>> x array([-179.5, -178.5, ..., 178.5, 179.5]) - >>> y # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS + >>> y array([ 89.5, 88.5, ..., -88.5, -89.5]) >>> data.dtype dtype('uint8') @@ -121,7 +121,7 @@ def to_xarray(self) -> xr.DataArray: ... image = lib.read_virtualfile(voutimg, kind="image") ... # Convert to xarray.DataArray and use it later ... da = image.contents.to_xarray() - >>> da # doctest: +NORMALIZE_WHITESPACE + >>> da Size: 194kB array([[[ 10, 10, 10, ..., 10, 10, 10], [ 10, 10, 10, ..., 10, 10, 10], @@ -154,7 +154,7 @@ def to_xarray(self) -> xr.DataArray: Attributes: long_name: z - >>> da.coords["x"] # doctest: +NORMALIZE_WHITESPACE + >>> da.coords["x"] Size: 3kB array([-179.5, -178.5, -177.5, ..., 177.5, 178.5, 179.5], shape=(360,)) Coordinates: @@ -163,7 +163,7 @@ def to_xarray(self) -> xr.DataArray: long_name: x axis: X actual_range: [-180. 180.] - >>> da.coords["y"] # doctest: +NORMALIZE_WHITESPACE + >>> da.coords["y"] Size: 1kB array([ 89.5, 88.5, 87.5, 86.5, ..., -87.5, -88.5, -89.5]) Coordinates: diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 62cb37198c1..50fcefaf0b5 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -514,9 +514,7 @@ def use_alias(**aliases): R = bla J = meh >>> my_module(region="bla", projection="meh") R = bla J = meh - >>> my_module( - ... region="bla", projection="meh", J="bla" - ... ) # doctest: +NORMALIZE_WHITESPACE + >>> my_module(region="bla", projection="meh", J="bla") Traceback (most recent call last): ... pygmt.exceptions.GMTInvalidInput: diff --git a/pygmt/xarray/backend.py b/pygmt/xarray/backend.py index 26700b8dec6..79a6e1f0c66 100644 --- a/pygmt/xarray/backend.py +++ b/pygmt/xarray/backend.py @@ -50,13 +50,13 @@ class GMTBackendEntrypoint(BackendEntrypoint): >>> da_grid = xr.open_dataarray( ... "@static_earth_relief.nc", engine="gmt", raster_kind="grid" ... ) - >>> da_grid # doctest: +NORMALIZE_WHITESPACE + >>> da_grid Size: 448B [112 values with dtype=float32] Coordinates: * lat (lat) float64 112B -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 * lon (lon) float64 64B -54.5 -53.5 -52.5 -51.5 -50.5 -49.5 -48.5 -47.5 - Attributes:... + Attributes: Conventions: CF-1.7 title: Produced by grdcut history: grdcut @earth_relief_01d_p -R-55/-47/-24/-10 -Gstatic_eart... @@ -69,14 +69,14 @@ class GMTBackendEntrypoint(BackendEntrypoint): >>> da_image = xr.open_dataarray( ... "@earth_night_01d", engine="gmt", raster_kind="image" ... ) - >>> da_image # doctest: +NORMALIZE_WHITESPACE + >>> da_image Size: 194kB [194400 values with dtype=uint8] Coordinates: - * band (band) uint8... 1 2 3 - * y (y) float64... 89.5 88.5 87.5 86.5 ... -86.5 -87.5 -88.5 -89.5 - * x (x) float64... -179.5 -178.5 -177.5 -176.5 ... 177.5 178.5 179.5 - Attributes:... + * band (band) uint8 3B 1 2 3 + * y (y) float64 1kB 89.5 88.5 87.5 86.5 ... -86.5 -87.5 -88.5 -89.5 + * x (x) float64 3kB -179.5 -178.5 -177.5 -176.5 ... 177.5 178.5 179.5 + Attributes: long_name: z Load a single-band netCDF file using ``raster_kind="grid"`` over a bounding box @@ -95,7 +95,7 @@ class GMTBackendEntrypoint(BackendEntrypoint): Coordinates: * lat (lat) float64 104B 32.0 32.08 32.17 32.25 ... 32.83 32.92 33.0 * lon (lon) float64 200B -64.0 -63.92 -63.83 ... -62.17 -62.08 -62.0 - Attributes:... + Attributes: Conventions: CF-1.7 title: ETOPO5 global topography history: grdreformat -fg bermuda.grd bermuda.nc=ns From e1a0e4e6eebd6099dfb1946f8752d5faabff627c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Oct 2025 10:38:37 +0800 Subject: [PATCH 6/7] Fix doctests --- pygmt/datasets/tile_map.py | 8 ++++---- pygmt/datatypes/grid.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pygmt/datasets/tile_map.py b/pygmt/datasets/tile_map.py index 1dffaf58e20..bc34f5b8151 100644 --- a/pygmt/datasets/tile_map.py +++ b/pygmt/datasets/tile_map.py @@ -124,10 +124,10 @@ def load_tile_map( Frozen({'band': 3, 'y': 256, 'x': 512}) >>> raster.coords Coordinates: - * band (band) uint8 1 2 3 - * y (y) float64 -7.081e-10 -7.858e+04 ... -1.996e+07 -2.004e+07 - * x (x) float64 -2.004e+07 -1.996e+07 ... 1.996e+07 2.004e+07 - spatial_ref int 0 + * band (band) uint8 3B 1 2 3 + * y (y) float64 2kB -7.081e-10 -7.858e+04 ... -1.996e+07 -2.004e+07 + * x (x) float64 4kB -2.004e+07 -1.996e+07 ... 1.996e+07 2.004e+07 + spatial_ref int 8B 0 >>> # CRS is set only if rioxarray is available >>> if hasattr(raster, "rio"): ... raster.rio.crs.to_string() diff --git a/pygmt/datatypes/grid.py b/pygmt/datatypes/grid.py index 080bf3810cb..a5b0d2feff7 100644 --- a/pygmt/datatypes/grid.py +++ b/pygmt/datatypes/grid.py @@ -153,11 +153,11 @@ def to_xarray(self) -> xr.DataArray: axis: X actual_range: [-55. -47.] >>> da.coords["lat"] - Size: 64B + Size: 112B array([-23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, -15.5, -14.5, - -13.5, -12.5, -11.5, -10.5]) + -13.5, -12.5, -11.5, -10.5]) Coordinates: - * lat (lat) float64 64B -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 + * lat (lat) float64 112B -23.5 -22.5 -21.5 -20.5 ... -12.5 -11.5 -10.5 Attributes: long_name: latitude units: degrees_north From 0bc407b7a5c6af32f67bee0595ef71406e583acb Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Oct 2025 10:44:23 +0800 Subject: [PATCH 7/7] Fix int to int64, but it may not work on Windows --- pygmt/datasets/tile_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/datasets/tile_map.py b/pygmt/datasets/tile_map.py index bc34f5b8151..758354ebf5c 100644 --- a/pygmt/datasets/tile_map.py +++ b/pygmt/datasets/tile_map.py @@ -127,7 +127,7 @@ def load_tile_map( * band (band) uint8 3B 1 2 3 * y (y) float64 2kB -7.081e-10 -7.858e+04 ... -1.996e+07 -2.004e+07 * x (x) float64 4kB -2.004e+07 -1.996e+07 ... 1.996e+07 2.004e+07 - spatial_ref int 8B 0 + spatial_ref int64 8B 0 >>> # CRS is set only if rioxarray is available >>> if hasattr(raster, "rio"): ... raster.rio.crs.to_string()