diff --git a/.github/workflows/cache_data.yaml b/.github/workflows/cache_data.yaml index c2a3ef28abe..0630262b483 100644 --- a/.github/workflows/cache_data.yaml +++ b/.github/workflows/cache_data.yaml @@ -80,7 +80,7 @@ jobs: with: name: gmt-cache path: | - ~/.gmt/cache - ~/.gmt/server - ~/.gmt/gmt_data_server.txt - ~/.gmt/gmt_hash_server.txt + ~/.gmt/cache + ~/.gmt/server + ~/.gmt/gmt_data_server.txt + ~/.gmt/gmt_hash_server.txt diff --git a/pygmt/datatypes/dataset.py b/pygmt/datatypes/dataset.py index 7d0b1d469db..daf0073aefe 100644 --- a/pygmt/datatypes/dataset.py +++ b/pygmt/datatypes/dataset.py @@ -3,6 +3,7 @@ """ import ctypes as ctp +import warnings from collections.abc import Mapping from typing import Any, ClassVar @@ -153,6 +154,17 @@ def to_strings(self) -> np.ndarray[Any, np.dtype[np.str_]]: for segment in table.contents.segment[: table.contents.n_segments]: if segment.contents.text: textvector.extend(segment.contents.text[: segment.contents.n_rows]) + if None in textvector: + # Workaround for upstream GMT bug reported in + # https://github.com/GenericMappingTools/pygmt/issues/3170. + msg = ( + "The trailing text column contains `None' values and has been replaced" + "with empty strings to avoid TypeError exceptions. " + "It's likely caused by an upstream GMT API bug. " + "Please consider reporting to us." + ) + warnings.warn(msg, category=RuntimeWarning, stacklevel=1) + textvector = [item if item is not None else b"" for item in textvector] return np.char.decode(textvector) if textvector else np.array([], dtype=str) def to_dataframe( diff --git a/pygmt/tests/test_datatypes_dataset.py b/pygmt/tests/test_datatypes_dataset.py index 7861b6b3119..e78782ada37 100644 --- a/pygmt/tests/test_datatypes_dataset.py +++ b/pygmt/tests/test_datatypes_dataset.py @@ -6,6 +6,7 @@ import pandas as pd import pytest +from pygmt import which from pygmt.clib import Session from pygmt.helpers import GMTTempFile @@ -81,3 +82,28 @@ def test_dataset_empty(): assert df.empty # Empty DataFrame expected_df = dataframe_from_pandas(tmpfile.name) pd.testing.assert_frame_equal(df, expected_df) + + +def test_dataset_to_strings_with_none_values(): + """ + Test that None values in the trailing text doesn't raise an excetion. + + Due to a likely upstream bug, the trailing texts sometimes can be ``None`` when + downloading tiled grids. The temporary workaround is to replace any None values with + an empty string. + + See the bug report at https://github.com/GenericMappingTools/pygmt/issues/3170. + """ + tiles = ["@N30W120.earth_relief_15s_p.nc", "@N00E000.earth_relief_15s_p.nc"] + paths = which(fname=tiles, download="a") + assert len(paths) == 2 + # 'paths' may contain an empty string or not, depending on if the tiles are cached. + if "" not in paths: # Contains two valid paths. + # Delete the cached tiles and try again. + for path in paths: + Path(path).unlink() + with pytest.warns(expected_warning=RuntimeWarning) as record: + paths = which(fname=tiles, download="a") + assert len(record) == 1 + assert len(paths) == 2 + assert "" in paths