Skip to content

Add new exception GMTParameterError for invalid parameters #4003

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

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ All custom exceptions are derived from :class:`pygmt.exceptions.GMTError`.
exceptions.GMTCLibNotFoundError
exceptions.GMTTypeError
exceptions.GMTValueError

exceptions.GMTTypeError
exceptions.GMTParameterError

.. currentmodule:: pygmt

Expand Down
9 changes: 4 additions & 5 deletions pygmt/datasets/load_remote_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Any, Literal, NamedTuple

import xarray as xr
from pygmt.exceptions import GMTInvalidInput, GMTValueError
from pygmt.exceptions import GMTParameterError, GMTValueError

with contextlib.suppress(ImportError):
# rioxarray is needed to register the rio accessor
Expand Down Expand Up @@ -572,11 +572,10 @@ def _load_remote_dataset(
reg = registration[0]

if resinfo.tiled and region is None:
msg = (
f"The 'region' parameter is required for {dataset.description} "
f"resolution '{resolution}'."
raise GMTParameterError(
required={"region"},
reason=f"Required for {dataset.description} resolution {resolution!r} with tiled grids.",
)
raise GMTInvalidInput(msg)

fname = f"@{prefix}_{resolution}_{reg}"
grid = xr.load_dataarray(
Expand Down
50 changes: 49 additions & 1 deletion pygmt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
All exceptions derive from GMTError.
"""

from collections.abc import Iterable
from collections.abc import Iterable, Set
from typing import Any


Expand Down Expand Up @@ -130,3 +130,51 @@ def __init__(self, dtype: object, /, reason: str | None = None):
if reason:
msg += f" {reason}"
super().__init__(msg)


class GMTParameterError(GMTError):
"""
Raised when parameters are missing or invalid.

Parameters
----------
required
Names of required parameters.
require_any
Names of parameters where at least one must be specified.
exclusive
Names of mutually exclusive parameters.
reason
Detailed reason why the parameters are invalid.
"""

def __init__(
self,
*,
required: Set[str] | None = None,
require_any: Set[str] | None = None,
exclusive: Set[str] | None = None,
reason: str | None = None,
):
msg = []
if required:
msg.append(
"Required parameter(s) are missing: "
f"{', '.join(repr(par) for par in required)}."
)

if require_any:
msg.append(
"At least one of the following parameters must be specified: "
f"{', '.join(repr(par) for par in require_any)}."
)

if exclusive:
msg.append(
"Mutually exclusive parameter(s) are specified: "
f"{', '.join(repr(par) for par in exclusive)}."
)

if reason:
msg.append(reason)
super().__init__(" ".join(msg))
17 changes: 12 additions & 5 deletions pygmt/src/coast.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
args_in_kwargs,
build_arg_list,
Expand Down Expand Up @@ -207,11 +207,18 @@ def coast(
"""
self._activate_figure()
if not args_in_kwargs(args=["C", "G", "S", "I", "N", "E", "Q", "W"], kwargs=kwargs):
msg = (
"At least one of the following parameters must be specified: "
"lakes, land, water, rivers, borders, dcw, Q, or shorelines."
raise GMTParameterError(
require_any={
"lakes",
"land",
"water",
"rivers",
"borders",
"dcw",
"Q",
"shorelines",
}
)
raise GMTInvalidInput(msg)

aliasdict = AliasSystem(
D=Alias(
Expand Down
8 changes: 2 additions & 6 deletions pygmt/src/dimfilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import xarray as xr
from pygmt._typing import PathLike
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias

__doctest_skip__ = ["dimfilter"]
Expand Down Expand Up @@ -138,11 +138,7 @@ def dimfilter(
... )
"""
if not all(arg in kwargs for arg in ["D", "F", "N"]) and "Q" not in kwargs:
msg = (
"At least one of the following parameters must be specified: "
"distance, filters, or sectors."
)
raise GMTInvalidInput(msg)
raise GMTParameterError(require_any={"distance", "filter", "sectors"})
with Session() as lib:
with (
lib.virtualfile_in(check_kind="raster", data=grid) as vingrd,
Expand Down
5 changes: 2 additions & 3 deletions pygmt/src/filter1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,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 GMTParameterError
from pygmt.helpers import (
build_arg_list,
fmt_docstring,
Expand Down Expand Up @@ -112,8 +112,7 @@ def filter1d(
(depends on ``output_type``)
"""
if kwargs.get("F") is None:
msg = "Pass a required argument to 'filter_type'."
raise GMTInvalidInput(msg)
raise GMTParameterError(required={"filter_type"})

output_type = validate_output_table_type(output_type, outfile=outfile)

Expand Down
5 changes: 2 additions & 3 deletions pygmt/src/grd2cpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import xarray as xr
from pygmt._typing import PathLike
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias

__doctest_skip__ = ["grd2cpt"]
Expand Down Expand Up @@ -184,8 +184,7 @@ def grd2cpt(grid: PathLike | xr.DataArray, **kwargs):
>>> fig.show()
"""
if kwargs.get("W") is not None and kwargs.get("Ww") is not None:
msg = "Set only 'categorical' or 'cyclic' to True, not both."
raise GMTInvalidInput(msg)
raise GMTParameterError(exclusive={"categorical", "cyclic"})

if (output := kwargs.pop("H", None)) is not None:
kwargs["H"] = True
Expand Down
8 changes: 2 additions & 6 deletions pygmt/src/grdclip.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
build_arg_list,
deprecate_parameter,
Expand Down Expand Up @@ -109,11 +109,7 @@ def grdclip(
[0.0, 10000.0]
"""
if all(v is None for v in (above, below, between, replace)):
msg = (
"Must specify at least one of the following parameters: ",
"'above', 'below', 'between', or 'replace'.",
)
raise GMTInvalidInput(msg)
raise GMTParameterError(require_any={"above", "below", "between", "replace"})

aliasdict = AliasSystem(
Sa=Alias(above, name="above", sep="/", size=2),
Expand Down
25 changes: 13 additions & 12 deletions pygmt/src/grdfill.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
build_arg_list,
deprecate_parameter,
Expand Down Expand Up @@ -38,22 +38,22 @@ def _validate_params(
>>> _validate_params(constantfill=20.0, gridfill="bggrid.nc")
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Parameters ... are mutually exclusive.
pygmt.exceptions.GMTParameterError: Mutually exclusive parameter...
>>> _validate_params(constantfill=20.0, inquire=True)
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Parameters ... are mutually exclusive.
pygmt.exceptions.GMTParameterError: Mutually exclusive parameter...
>>> _validate_params()
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Need to specify parameter ...
pygmt.exceptions.GMTParameterError: ...
"""
_fill_params = "'constantfill'/'gridfill'/'neighborfill'/'splinefill'"
_fill_params = {"constantfill", "gridfill", "neighborfill", "splinefill"}
# The deprecated 'mode' parameter is given.
if mode is not None:
msg = (
"The 'mode' parameter is deprecated since v0.15.0 and will be removed in "
f"v0.19.0. Use {_fill_params} instead."
f"v0.19.0. Use {', '.join(repr(par) for par in _fill_params)} instead."
)
warnings.warn(msg, FutureWarning, stacklevel=2)

Expand All @@ -62,14 +62,15 @@ def _validate_params(
for param in [constantfill, gridfill, neighborfill, splinefill, inquire, mode]
)
if n_given > 1: # More than one mutually exclusive parameter is given.
msg = f"Parameters {_fill_params}/'inquire'/'mode' are mutually exclusive."
raise GMTInvalidInput(msg)
raise GMTParameterError(exclusive=[*_fill_params, "inquire", "mode"])
if n_given == 0: # No parameters are given.
msg = (
f"Need to specify parameter {_fill_params} for filling holes or "
"'inquire' for inquiring the bounds of each hole."
raise GMTParameterError(
required=_fill_params,
reason=(
f"Need to specify parameter {_fill_params!r} for filling holes or "
"'inquire' for inquiring the bounds of each hole."
),
)
raise GMTInvalidInput(msg)


@fmt_docstring
Expand Down
15 changes: 7 additions & 8 deletions pygmt/src/grdgradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import xarray as xr
from pygmt._typing import PathLike
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
args_in_kwargs,
build_arg_list,
Expand Down Expand Up @@ -162,14 +162,13 @@ def grdgradient(
>>> new_grid = pygmt.grdgradient(grid=grid, azimuth=10)
"""
if kwargs.get("Q") is not None and kwargs.get("N") is None:
msg = "Must specify normalize if tiles is specified."
raise GMTInvalidInput(msg)
if not args_in_kwargs(args=["A", "D", "E"], kwargs=kwargs):
msg = (
"At least one of the following parameters must be specified: "
"azimuth, direction, or radiance."
raise GMTParameterError(
required={"normalize"},
reason="Must specify 'normalize' if 'tiles' is specified.",
)
raise GMTInvalidInput(msg)
if not args_in_kwargs(args=["A", "D", "E"], kwargs=kwargs):
raise GMTParameterError(require_any={"azimuth", "direction", "radiance"})

with Session() as lib:
with (
lib.virtualfile_in(check_kind="raster", data=grid) as vingrd,
Expand Down
3 changes: 1 addition & 2 deletions pygmt/src/grdimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import (
build_arg_list,
fmt_docstring,
Expand Down Expand Up @@ -165,7 +164,7 @@ def grdimage(self, grid: PathLike | xr.DataArray, projection=None, **kwargs):
"Parameter 'img_out'/'A' is not implemented. "
"Please consider submitting a feature request to us."
)
raise GMTInvalidInput(msg)
raise NotImplementedError(msg)

aliasdict = AliasSystem(
J=Alias(projection, name="projection"),
Expand Down
5 changes: 2 additions & 3 deletions pygmt/src/grdlandmask.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
build_arg_list,
fmt_docstring,
Expand Down Expand Up @@ -114,8 +114,7 @@ def grdlandmask(
>>> landmask = pygmt.grdlandmask(spacing=1, region=[125, 130, 30, 35])
"""
if kwargs.get("I") is None or kwargs.get("R") is None:
msg = "Both 'region' and 'spacing' must be specified."
raise GMTInvalidInput(msg)
raise GMTParameterError(required={"spacing", "region"})

aliasdict = AliasSystem(
D=Alias(
Expand Down
5 changes: 2 additions & 3 deletions pygmt/src/grdproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias

__doctest_skip__ = ["grdproject"]
Expand Down Expand Up @@ -110,8 +110,7 @@ def grdproject(
>>> new_grid = pygmt.grdproject(grid=grid, projection="M10c", region=region)
"""
if projection is None:
msg = "The projection must be specified."
raise GMTInvalidInput(msg)
raise GMTParameterError(required={"projection"})

aliasdict = AliasSystem(
J=Alias(projection, name="projection"),
Expand Down
14 changes: 7 additions & 7 deletions pygmt/src/grdtrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import xarray as xr
from pygmt._typing import PathLike, TableLike
from pygmt.clib import Session
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import (
build_arg_list,
fmt_docstring,
Expand Down Expand Up @@ -292,16 +292,16 @@ def grdtrack(
... )
"""
if points is not None and kwargs.get("E") is not None:
msg = "Can't set both 'points' and 'profile'."
raise GMTInvalidInput(msg)
raise GMTParameterError(exclusive={"points", "profile"})

if points is None and kwargs.get("E") is None:
msg = "Must give 'points' or set 'profile'."
raise GMTInvalidInput(msg)
raise GMTParameterError(require_any={"points", "profile"})

if hasattr(points, "columns") and newcolname is None:
msg = "Please pass in a str to 'newcolname'."
raise GMTInvalidInput(msg)
raise GMTParameterError(
required={"newcolname"},
reason="Parameter 'newcolname' is required when 'points' is a pandas.DataFrame object.",
)

output_type = validate_output_table_type(output_type, outfile=outfile)

Expand Down
Loading
Loading