diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index c7d713647ef..61bcbc189ea 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -12,7 +12,7 @@ import pandas as pd import xarray as xr from packaging.version import Version -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError def dataarray_to_matrix( @@ -49,7 +49,7 @@ def dataarray_to_matrix( Raises ------ - GMTInvalidInput + GMTValueError If the grid has more than two dimensions or variable grid spacing. Examples @@ -92,8 +92,11 @@ def dataarray_to_matrix( [2.0, 2.0] """ if len(grid.dims) != 2: - msg = f"Invalid number of grid dimensions 'len({grid.dims})'. Must be 2." - raise GMTInvalidInput(msg) + raise GMTValueError( + len(grid.dims), + description="number of grid dimensions", + reason="The grid must be 2-D.", + ) # Extract region and inc from the grid region, inc = [], [] @@ -113,8 +116,11 @@ def dataarray_to_matrix( ) warnings.warn(msg, category=RuntimeWarning, stacklevel=2) if coord_inc == 0: - msg = f"Grid has a zero increment in the '{dim}' dimension." - raise GMTInvalidInput(msg) + raise GMTValueError( + coord_inc, + description="grid increment", + reason=f"Grid has a zero increment in the '{dim}' dimension.", + ) region.extend( [ coord.min() - coord_inc / 2 * grid.gmt.registration, diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index 4d2e89f2399..df48ea0e71a 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -913,9 +913,10 @@ def _check_dtype_and_dim(self, array: np.ndarray, ndim: int) -> int: Raises ------ + GMTValueError + If the array has the wrong number of dimensions. GMTInvalidInput - If the array has the wrong number of dimensions or is an unsupported data - type. + If the array is an unsupported data type. Examples -------- @@ -933,8 +934,11 @@ def _check_dtype_and_dim(self, array: np.ndarray, ndim: int) -> int: """ # Check that the array has the given number of dimensions. if array.ndim != ndim: - msg = f"Expected a numpy {ndim}-D array, got {array.ndim}-D." - raise GMTInvalidInput(msg) + raise GMTValueError( + array.ndim, + description="array dimension", + reason=f"Expected a numpy {ndim}-D array, got {array.ndim}-D.", + ) # 1-D arrays can be numeric or text, 2-D arrays can only be numeric. valid_dtypes = DTYPES if ndim == 1 else DTYPES_NUMERIC diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index dac58eb160c..3d70917f152 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -18,7 +18,7 @@ import xarray as xr from pygmt._typing import PathLike from pygmt.encodings import charset -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError # Type hints for the list of encodings supported by PyGMT. Encoding = Literal[ @@ -597,8 +597,7 @@ def build_arg_list( # noqa: PLR0912 or os.fspath(outfile) in {"", ".", ".."} or os.fspath(outfile).endswith(("/", "\\")) ): - msg = f"Invalid output file name '{outfile}'." - raise GMTInvalidInput(msg) + raise GMTValueError(outfile, description="output file name") gmt_args.append(f"->{os.fspath(outfile)}") return gmt_args diff --git a/pygmt/helpers/validators.py b/pygmt/helpers/validators.py index 524dfe33092..34a8e85e1b7 100644 --- a/pygmt/helpers/validators.py +++ b/pygmt/helpers/validators.py @@ -6,7 +6,7 @@ from typing import Literal from pygmt._typing import PathLike -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError def validate_output_table_type( @@ -39,7 +39,7 @@ def validate_output_table_type( >>> validate_output_table_type(output_type="invalid-type") Traceback (most recent call last): ... - pygmt.exceptions.GMTInvalidInput: Must specify 'output_type' either as 'file', ... + pygmt....GMTValueError: ...: 'invalid-type'. Expected one of: ... >>> validate_output_table_type("file", outfile=None) Traceback (most recent call last): ... @@ -49,9 +49,13 @@ def validate_output_table_type( ... assert len(w) == 1 'file' """ - if output_type not in {"file", "numpy", "pandas"}: - msg = "Must specify 'output_type' either as 'file', 'numpy', or 'pandas'." - raise GMTInvalidInput(msg) + _valids = {"file", "numpy", "pandas"} + if output_type not in _valids: + raise GMTValueError( + output_type, + description="value for parameter 'output_type'", + choices=_valids, + ) if output_type == "file" and outfile is None: msg = "Must specify 'outfile' for output_type='file'." raise GMTInvalidInput(msg) diff --git a/pygmt/src/_common.py b/pygmt/src/_common.py index f0b74ff715d..378376415e4 100644 --- a/pygmt/src/_common.py +++ b/pygmt/src/_common.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any, ClassVar, Literal -from pygmt.exceptions import GMTInvalidInput, GMTValueError +from pygmt.exceptions import GMTValueError from pygmt.src.which import which @@ -125,7 +125,7 @@ class _FocalMechanismConvention: >>> _FocalMechanismConvention.from_params(["strike", "dip", "rake"]) Traceback (most recent call last): ... - pygmt.exceptions.GMTInvalidInput: Fail to determine focal mechanism convention... + pygmt.exceptions.GMTValueError: Invalid focal mechanism parameters: ... """ # Mapping of focal mechanism conventions to their parameters. @@ -236,18 +236,14 @@ def from_params( Raises ------ - GMTInvalidInput + GMTValueError If the focal mechanism convention cannot be determined from the given parameters. """ for convention, param_list in cls._params.items(): if set(param_list).issubset(set(params)): return cls(convention, component=component) - msg = ( - "Fail to determine focal mechanism convention from the given parameters: " - f"{', '.join(params)}." - ) - raise GMTInvalidInput(msg) + raise GMTValueError(params, description="focal mechanism parameters") def _parse_coastline_resolution( diff --git a/pygmt/src/grd2xyz.py b/pygmt/src/grd2xyz.py index f77d6a6955d..57cf1ccf152 100644 --- a/pygmt/src/grd2xyz.py +++ b/pygmt/src/grd2xyz.py @@ -9,7 +9,7 @@ import xarray as xr from pygmt._typing import PathLike from pygmt.clib import Session -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError from pygmt.helpers import ( build_arg_list, fmt_docstring, @@ -145,10 +145,11 @@ def grd2xyz( output_type = validate_output_table_type(output_type, outfile=outfile) if kwargs.get("o") is not None and output_type == "pandas": - msg = ( - "If 'outcols' is specified, 'output_type' must be either 'numpy' or 'file'." + raise GMTValueError( + output_type, + description="value for parameter 'output_type'", + reason="Expected one of: 'numpy', 'file' if 'outcols' is specified.", ) - raise GMTInvalidInput(msg) # Set the default column names for the pandas DataFrame header. column_names: list[str] = ["x", "y", "z"] # Let output pandas column names match input DataArray dimension names diff --git a/pygmt/src/hlines.py b/pygmt/src/hlines.py index 7ee5bb3b5cc..285112b6a1c 100644 --- a/pygmt/src/hlines.py +++ b/pygmt/src/hlines.py @@ -5,7 +5,7 @@ from collections.abc import Sequence import numpy as np -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError __doctest_skip__ = ["hlines"] @@ -99,11 +99,12 @@ def hlines( # Check if xmin/xmax are scalars or have the expected length. if _xmin.size not in {1, nlines} or _xmax.size not in {1, nlines}: - msg = ( - f"'xmin' and 'xmax' are expected to be scalars or have lengths '{nlines}', " - f"but lengths '{_xmin.size}' and '{_xmax.size}' are given." + _value = f"{_xmin.size}, {_xmax.size}" + raise GMTValueError( + _value, + description="size for 'xmin'/'xmax'", + reason=f"'xmin'/'xmax' are expected to be scalars or have lengths {nlines!r}.", ) - raise GMTInvalidInput(msg) # Repeat xmin/xmax to match the length of y if they are scalars. if nlines != 1: diff --git a/pygmt/src/meca.py b/pygmt/src/meca.py index 0b4cbc3f23a..629911f9097 100644 --- a/pygmt/src/meca.py +++ b/pygmt/src/meca.py @@ -9,7 +9,7 @@ import pandas as pd from pygmt._typing import PathLike, TableLike from pygmt.clib import Session -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import ( build_arg_list, data_kind, @@ -66,8 +66,11 @@ def _preprocess_spec(spec, colnames, override_cols): } ndiff = spec.shape[1] - len(colnames) if ndiff not in extra_cols: - msg = f"Input array must have {len(colnames)} or two/three more columns." - raise GMTInvalidInput(msg) + raise GMTValueError( + spec.shape[1], + description="input array shape", + reason=f"Input array must have {len(colnames)} or two/three more columns.", + ) spec = dict(zip([*colnames, *extra_cols[ndiff]], spec.T, strict=False)) # Now, the input data is a dict or an ASCII file. diff --git a/pygmt/src/vlines.py b/pygmt/src/vlines.py index 1e889a40cd6..0da815833fe 100644 --- a/pygmt/src/vlines.py +++ b/pygmt/src/vlines.py @@ -5,7 +5,7 @@ from collections.abc import Sequence import numpy as np -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError __doctest_skip__ = ["vlines"] @@ -99,11 +99,12 @@ def vlines( # Check if ymin/ymax are scalars or have the expected length. if _ymin.size not in {1, nlines} or _ymax.size not in {1, nlines}: - msg = ( - f"'ymin' and 'ymax' are expected to be scalars or have lengths '{nlines}', " - f"but lengths '{_ymin.size}' and '{_ymax.size}' are given." + _value = f"{_ymin.size}, {_ymax.size}" + raise GMTValueError( + _value, + description="size for 'ymin'/'ymax'", + reason=f"'ymin'/'ymax' are expected to be scalars or have lengths {nlines!r}.", ) - raise GMTInvalidInput(msg) # Repeat ymin/ymax to match the length of x if they are scalars. if nlines != 1: diff --git a/pygmt/tests/test_clib_dataarray_to_matrix.py b/pygmt/tests/test_clib_dataarray_to_matrix.py index 7dfaf2df81a..1ade6c8e987 100644 --- a/pygmt/tests/test_clib_dataarray_to_matrix.py +++ b/pygmt/tests/test_clib_dataarray_to_matrix.py @@ -7,7 +7,7 @@ import pytest import xarray as xr from pygmt.clib.conversion import dataarray_to_matrix -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError @pytest.mark.benchmark @@ -81,7 +81,7 @@ def test_dataarray_to_matrix_dims_fails(): y = np.arange(12) z = np.arange(10) grid = xr.DataArray(data, coords=[("z", z), ("y", y), ("x", x)]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): dataarray_to_matrix(grid) @@ -107,11 +107,11 @@ def test_dataarray_to_matrix_zero_inc_fails(): x = np.linspace(0, 1, 5) y = np.zeros_like(x) grid = xr.DataArray(data, coords=[("y", y), ("x", x)]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): dataarray_to_matrix(grid) y = np.linspace(0, 1, 5) x = np.zeros_like(x) grid = xr.DataArray(data, coords=[("y", y), ("x", x)]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): dataarray_to_matrix(grid) diff --git a/pygmt/tests/test_clib_put_vector.py b/pygmt/tests/test_clib_put_vector.py index cad77ba59cf..f93506ec61c 100644 --- a/pygmt/tests/test_clib_put_vector.py +++ b/pygmt/tests/test_clib_put_vector.py @@ -10,7 +10,7 @@ import pytest from pygmt import clib from pygmt.clib.session import DTYPES_NUMERIC -from pygmt.exceptions import GMTCLibError, GMTInvalidInput +from pygmt.exceptions import GMTCLibError, GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile @@ -257,5 +257,5 @@ def test_put_vector_2d_fails(): dim=[1, 6, 0, 0], # ncolumns, nrows, dtype, unused ) data = np.array([[37, 12, 556], [37, 12, 556]], dtype=np.int32) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): lib.put_vector(dataset, column=0, vector=data) diff --git a/pygmt/tests/test_grd2cpt.py b/pygmt/tests/test_grd2cpt.py index c2d9a257cc1..3fe5a0a390d 100644 --- a/pygmt/tests/test_grd2cpt.py +++ b/pygmt/tests/test_grd2cpt.py @@ -6,7 +6,7 @@ import pytest from pygmt import Figure, grd2cpt -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile from pygmt.helpers.testing import load_static_earth_relief @@ -37,7 +37,7 @@ def test_grd2cpt_blank_output(grid): """ Use incorrect setting by passing in blank file name to output parameter. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): grd2cpt(grid=grid, output="") @@ -45,7 +45,7 @@ def test_grd2cpt_invalid_output(grid): """ Use incorrect setting by passing in invalid type to output parameter. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): grd2cpt(grid=grid, output=["some.cpt"]) diff --git a/pygmt/tests/test_grd2xyz.py b/pygmt/tests/test_grd2xyz.py index ab3feccf80c..a2eb91fd770 100644 --- a/pygmt/tests/test_grd2xyz.py +++ b/pygmt/tests/test_grd2xyz.py @@ -6,7 +6,7 @@ import pandas as pd import pytest from pygmt import grd2xyz -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError from pygmt.helpers.testing import load_static_earth_relief @@ -38,5 +38,5 @@ def test_grd2xyz_pandas_output_with_o(grid): """ Test that grd2xyz fails when outcols is set and output_type is set to 'pandas'. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): grd2xyz(grid=grid, output_type="pandas", outcols="2") diff --git a/pygmt/tests/test_grdhisteq.py b/pygmt/tests/test_grdhisteq.py index 35ca1cf9de7..486fbc7116e 100644 --- a/pygmt/tests/test_grdhisteq.py +++ b/pygmt/tests/test_grdhisteq.py @@ -10,7 +10,7 @@ import xarray as xr from pygmt import grdhisteq from pygmt.enums import GridRegistration, GridType -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile from pygmt.helpers.testing import load_static_earth_relief @@ -136,7 +136,7 @@ def test_compute_bins_invalid_format(grid): """ Test that compute_bins fails with incorrect format. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): grdhisteq.compute_bins(grid=grid, output_type=1) with pytest.raises(GMTInvalidInput): grdhisteq.compute_bins(grid=grid, output_type="pandas", header="o+c") diff --git a/pygmt/tests/test_helpers.py b/pygmt/tests/test_helpers.py index 157eb51f334..5d13428b0b7 100644 --- a/pygmt/tests/test_helpers.py +++ b/pygmt/tests/test_helpers.py @@ -10,7 +10,7 @@ import pytest import xarray as xr from pygmt import Figure -from pygmt.exceptions import GMTInvalidInput, GMTValueError +from pygmt.exceptions import GMTValueError from pygmt.helpers import ( GMTTempFile, args_in_kwargs, @@ -129,7 +129,7 @@ def test_build_arg_list_invalid_output(outfile): """ Test that build_arg_list raises an exception when output file name is invalid. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): build_arg_list({}, outfile=outfile) diff --git a/pygmt/tests/test_hlines.py b/pygmt/tests/test_hlines.py index aaaddad4f08..c8d8ab4abb5 100644 --- a/pygmt/tests/test_hlines.py +++ b/pygmt/tests/test_hlines.py @@ -4,7 +4,7 @@ import pytest from pygmt import Figure -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError @pytest.mark.mpl_image_compare @@ -109,13 +109,13 @@ def test_hlines_invalid_input(): """ fig = Figure() fig.basemap(region=[0, 10, 0, 6], projection="X10c/6c", frame=True) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.hlines(1, xmin=2, xmax=[3, 4]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.hlines(1, xmin=[2, 3], xmax=4) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.hlines(1, xmin=[2, 3], xmax=[4, 5]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.hlines([1, 2], xmin=[2, 3, 4], xmax=3) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.hlines([1, 2], xmin=[2, 3], xmax=[4, 5, 6]) diff --git a/pygmt/tests/test_makecpt.py b/pygmt/tests/test_makecpt.py index 39733c7cfc5..6b55c21b27c 100644 --- a/pygmt/tests/test_makecpt.py +++ b/pygmt/tests/test_makecpt.py @@ -7,7 +7,7 @@ import numpy as np import pytest from pygmt import Figure, makecpt -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile POINTS_DATA = Path(__file__).parent / "data" / "points.txt" @@ -83,7 +83,7 @@ def test_makecpt_blank_output(): """ Use incorrect setting by passing in blank file name to output parameter. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): makecpt(output="") @@ -91,7 +91,7 @@ def test_makecpt_invalid_output(): """ Use incorrect setting by passing in invalid type to output parameter. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): makecpt(output=["some.cpt"]) diff --git a/pygmt/tests/test_meca.py b/pygmt/tests/test_meca.py index 424486ac408..eaa9213fd5e 100644 --- a/pygmt/tests/test_meca.py +++ b/pygmt/tests/test_meca.py @@ -10,7 +10,7 @@ from packaging.version import Version from pygmt import Figure from pygmt.clib import __gmt_version__ -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile @@ -318,14 +318,14 @@ def test_meca_spec_ndarray_mismatched_columns(): """ fig = Figure() fig.basemap(region=[-125, -122, 47, 49], projection="M6c", frame=True) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.meca( spec=np.array([[-124, 48, 12.0, 330, 30, 90]]), convention="aki", scale="1c" ) fig = Figure() fig.basemap(region=[-125, -122, 47, 49], projection="M6c", frame=True) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.meca( spec=np.array([[-124, 48, 12.0, 330, 30, 90, 3, -124.5, 47.5, 30.0, 50.0]]), convention="aki", diff --git a/pygmt/tests/test_triangulate.py b/pygmt/tests/test_triangulate.py index 4e4ba794240..e0dba0ac43b 100644 --- a/pygmt/tests/test_triangulate.py +++ b/pygmt/tests/test_triangulate.py @@ -10,7 +10,7 @@ import xarray as xr from pygmt import triangulate, which from pygmt.enums import GridRegistration, GridType -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTInvalidInput, GMTValueError from pygmt.helpers import GMTTempFile @@ -127,7 +127,7 @@ def test_delaunay_triples_invalid_format(dataframe): """ Test that triangulate.delaunay_triples fails with incorrect format. """ - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): triangulate.delaunay_triples(data=dataframe, output_type=1) diff --git a/pygmt/tests/test_vlines.py b/pygmt/tests/test_vlines.py index 21aff1c06d5..8f5108f1398 100644 --- a/pygmt/tests/test_vlines.py +++ b/pygmt/tests/test_vlines.py @@ -4,7 +4,7 @@ import pytest from pygmt import Figure -from pygmt.exceptions import GMTInvalidInput +from pygmt.exceptions import GMTValueError @pytest.mark.mpl_image_compare @@ -90,13 +90,13 @@ def test_vlines_invalid_input(): """ fig = Figure() fig.basemap(region=[0, 10, 0, 6], projection="X10c/6c", frame=True) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.vlines(1, ymin=2, ymax=[3, 4]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.vlines(1, ymin=[2, 3], ymax=4) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.vlines(1, ymin=[2, 3], ymax=[4, 5]) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.vlines([1, 2], ymin=[2, 3, 4], ymax=3) - with pytest.raises(GMTInvalidInput): + with pytest.raises(GMTValueError): fig.vlines([1, 2], ymin=[2, 3], ymax=[4, 5, 6])