Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0105bb6
add conflict parameter for GMTParameterError
Chuan1937 Feb 4, 2026
d8c15e7
update test file
Chuan1937 Feb 4, 2026
7b6d102
Merge branch 'main' into feature/conflict
Chuan1937 Feb 4, 2026
406eb7b
Update pygmt/src/_common.py
Chuan1937 Feb 4, 2026
b28747d
Update pygmt/src/_common.py
Chuan1937 Feb 4, 2026
c15f3f3
Update pygmt/src/grdview.py
Chuan1937 Feb 4, 2026
25bad04
fix check
Chuan1937 Feb 4, 2026
7d9bd89
Update pygmt/src/grdview.py
Chuan1937 Feb 4, 2026
fc029ad
Update pygmt/src/_common.py
Chuan1937 Feb 4, 2026
47f3e1e
Update pygmt/src/_common.py
Chuan1937 Feb 4, 2026
11991fe
update alias
Chuan1937 Feb 4, 2026
3fef364
simplify the error message
Chuan1937 Feb 4, 2026
350f67d
update the error message
Chuan1937 Feb 4, 2026
e4f040f
Update pygmt/exceptions.py
Chuan1937 Feb 5, 2026
f73fcdb
Replace long-form parameter(s) with short-form parameter(s)
Chuan1937 Feb 5, 2026
1ed8e05
Merge branch 'feature/conflict' of github.com:Chuan1937/pygmt into fe…
Chuan1937 Feb 5, 2026
5a5b87d
Merge branch 'main' into feature/conflict
Chuan1937 Feb 5, 2026
a096843
fix test
Chuan1937 Feb 5, 2026
5cbcbe3
Update pygmt/alias.py
Chuan1937 Feb 5, 2026
af21ef2
fix error
Chuan1937 Feb 5, 2026
ce3c9cc
Merge branch 'main' into feature/conflict
Chuan1937 Feb 5, 2026
3c94d17
fix sort error in alias system
Chuan1937 Feb 5, 2026
7c454ba
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
355ba29
Update pygmt/exceptions.py
Chuan1937 Feb 6, 2026
c260d4a
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
b3c4ba5
simplify the code
Chuan1937 Feb 6, 2026
368a102
Update pygmt/exceptions.py
Chuan1937 Feb 6, 2026
35ebe3c
Update pygmt/exceptions.py
Chuan1937 Feb 6, 2026
e8380d4
Update pygmt/exceptions.py
Chuan1937 Feb 6, 2026
fdeba84
Update pygmt/src/_common.py
Chuan1937 Feb 6, 2026
0deddd2
Update pygmt/src/grdview.py
Chuan1937 Feb 6, 2026
6e951b3
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
c3bf105
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
28e0582
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
e33a11a
update test_alias_system.py
Chuan1937 Feb 6, 2026
3743e11
fix test
Chuan1937 Feb 6, 2026
f2194bf
Update pygmt/alias.py
Chuan1937 Feb 6, 2026
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
37 changes: 19 additions & 18 deletions pygmt/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from collections.abc import Mapping, Sequence
from typing import Any, Literal

from pygmt.exceptions import GMTInvalidInput, GMTValueError
from pygmt.exceptions import GMTParameterError, GMTValueError
from pygmt.helpers.utils import is_nonstr_iter, sequence_join


Expand Down Expand Up @@ -404,27 +404,28 @@ def merge(self, kwargs: Mapping[str, Any]):

# Long-form parameters exist.
aliases = self.aliasdict.get(short_param)
if not isinstance(aliases, Sequence): # Single Alias object.
_msg_long = f"Use long-form parameter {aliases.name!r} instead."
else: # Sequence of Alias objects.
_params = [f"{v.name!r}" for v in aliases if not v.prefix]
_modifiers = [f"{v.name!r} ({v.prefix})" for v in aliases if v.prefix]
_msg_long = (
f"Use long-form parameters {', '.join(_params)}, "
f"with optional parameters {', '.join(_modifiers)} instead."
)
if not isinstance(aliases, Sequence):
aliases = [aliases]

long_params = {v.name for v in aliases}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
long_params = {v.name for v in aliases}
long_params = [v.name for v in aliases]

long_params_text = ", ".join(
[
f"{v.name!r} ({v.prefix})"
if v.prefix.startswith("+")
else f"{v.name!r}"
for v in aliases
]
)
msg = (
f"Short-form parameter {short_param!r} is not recommended. "
f"Use long-form parameter(s) {long_params_text} instead."
)

# Long-form parameters are already specified.
if long_param_given:
msg = (
f"Short-form parameter {short_param!r} conflicts with long-form "
f"parameters and is not recommended. {_msg_long}"
raise GMTParameterError(
conflicts_with=(short_param, long_params), reason=msg
)
raise GMTInvalidInput(msg)

# Long-form parameters are not specified.
msg = (
f"Short-form parameter {short_param!r} is not recommended. {_msg_long}"
)
warnings.warn(msg, category=SyntaxWarning, stacklevel=2)
return self
10 changes: 10 additions & 0 deletions pygmt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ class GMTParameterError(GMTError):
at_most_one
A set of mutually exclusive parameter names, of which at most one can be
specified.
conflicts_with
A tuple with the parameter name and a set of conflicting parameter names,
Copy link
Member

@seisman seisman Feb 6, 2026

Choose a reason for hiding this comment

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

Suggested change
A tuple with the parameter name and a set of conflicting parameter names,
A tuple with the parameter name and a collection of conflicting parameter names,

indicating which parameters cannot be used together.
reason
Detailed reason why the parameters are invalid.
"""
Expand All @@ -155,6 +158,7 @@ def __init__(
required: str | set[str] | None = None,
at_least_one: set[str] | None = None,
at_most_one: set[str] | None = None,
conflicts_with: tuple[str, set[str]] | None = None,
Copy link
Member

@seisman seisman Feb 6, 2026

Choose a reason for hiding this comment

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

Suggested change
conflicts_with: tuple[str, set[str]] | None = None,
conflicts_with: tuple[str, Iterable[str]] | None = None,

reason: str | None = None,
):
msg = []
Expand All @@ -177,6 +181,12 @@ def __init__(
f"{', '.join(repr(par) for par in at_most_one)}. "
"Specify at most one of them."
)
if conflicts_with:
param, conflicts = conflicts_with
msg.append(
f"Conflicting parameters: {param!r} cannot be used with "
f"{', '.join(repr(c) for c in conflicts)}."
)
if reason:
msg.append(reason)
super().__init__(" ".join(msg))
12 changes: 5 additions & 7 deletions pygmt/src/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from typing import Any, ClassVar, Literal

from pygmt.exceptions import GMTInvalidInput, GMTTypeError, GMTValueError
from pygmt.exceptions import GMTParameterError, GMTTypeError, GMTValueError
from pygmt.params.position import Position
from pygmt.src.which import which

Expand Down Expand Up @@ -328,7 +328,7 @@ def _parse_position(
... )
Traceback (most recent call last):
...
pygmt.exceptions.GMTInvalidInput: Parameter 'position' is given with a raw GMT...
pygmt.exceptions.GMTParameterError: ...

>>> _parse_position(
... 123,
Expand Down Expand Up @@ -364,12 +364,10 @@ def _parse_position(
position = Position(position, cstype="inside")
elif kwdict is not None: # Raw GMT command string with potential conflicts.
if any(v is not None and v is not False for v in kwdict.values()):
msg = (
"Parameter 'position' is given with a raw GMT command string, "
"and conflicts with parameters "
f"{', '.join(repr(c) for c in kwdict)}."
raise GMTParameterError(
conflicts_with=("position", set(kwdict)),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
conflicts_with=("position", set(kwdict)),
conflicts_with=("position", kwdict.keys()),

reason="'position' is specified using the unrecommended GMT command string syntax.",
)
raise GMTInvalidInput(msg)
else:
# No conflicting parameters to check, indicating it's a new function.
# The string must be an anchor code.
Expand Down
12 changes: 7 additions & 5 deletions pygmt/src/grdview.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from pygmt._typing import PathLike
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session, __gmt_version__
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTInvalidInput, GMTParameterError
from pygmt.helpers import build_arg_list, deprecate_parameter, fmt_docstring, use_alias
from pygmt.src.grdinfo import grdinfo

Expand Down Expand Up @@ -71,11 +71,13 @@ def _alias_option_Q( # noqa: N802
v is not None and v is not False
for v in (dpi, mesh_fill, monochrome, nan_transparent)
):
msg = (
"Parameter 'surftype' is given with a raw GMT command string, and conflicts "
"with parameters 'dpi', 'mesh_fill', 'monochrome', or 'nan_transparent'."
raise GMTParameterError(
conflicts_with=(
"surftype",
{"dpi", "mesh_fill", "monochrome", "nan_transparent"},
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
{"dpi", "mesh_fill", "monochrome", "nan_transparent"},
["dpi", "mesh_fill", "monochrome", "nan_transparent"],

),
reason="'surftype' is specified using the unrecommended GMT command string syntax.",
)
raise GMTInvalidInput(msg)

if dpi is not None and surftype != "image":
msg = "Parameter 'dpi' can only be used when 'surftype' is 'image'."
Expand Down
27 changes: 12 additions & 15 deletions pygmt/tests/test_alias_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt.alias import Alias, AliasSystem
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.helpers import build_arg_list


Expand Down Expand Up @@ -65,21 +65,17 @@ def test_alias_system_one_alias_short_form():
"""
Test that the alias system works when short-form parameters coexist.
"""
_msg_conflict = "Conflicting parameters: 'J' cannot be used with 'projection'."
_msg_reason = r"Short-form parameter 'J' is not recommended. Use long-form parameter\(s\) 'projection' instead."
# Long-form does not exist.
assert func(A="abc") == ["-Aabc"]

# Long-form exists but is not given, and short-form is given.
with pytest.warns(
SyntaxWarning,
match=r"Short-form parameter 'J' is not recommended. Use long-form parameter 'projection' instead.",
):
with pytest.warns(SyntaxWarning, match=_msg_reason):
assert func(J="X10c") == ["-JX10c"]

# Coexistence of long-form and short-form parameters.
with pytest.raises(
GMTInvalidInput,
match=r"Short-form parameter 'J' conflicts with long-form parameters and is not recommended. Use long-form parameter 'projection' instead.",
):
with pytest.raises(GMTParameterError, match=rf"{_msg_conflict} {_msg_reason}"):
func(projection="X10c", J="H10c")


Expand All @@ -88,18 +84,19 @@ def test_alias_system_multiple_aliases_short_form():
Test that the alias system works with multiple aliases when short-form parameters
are used.
"""
_msg_long = r"Use long-form parameters 'label', with optional parameters 'text' \(\+t\), 'offset' \(\+o\) instead."
_msg_conflict = (
"Conflicting parameters: 'U' cannot be used with 'label', 'text', 'offset'."
)
_msg_reason = r"Short-form parameter 'U' is not recommended. Use long-form parameter\(s\) 'label', 'text' \(\+t\), 'offset' \(\+o\) instead."
# Long-form exists but is not given, and short-form is given.
msg = rf"Short-form parameter 'U' is not recommended. {_msg_long}"
with pytest.warns(SyntaxWarning, match=msg):
with pytest.warns(SyntaxWarning, match=_msg_reason):
assert func(U="abcd+tefg") == ["-Uabcd+tefg"]

# Coexistence of long-form and short-form parameters.
msg = rf"Short-form parameter 'U' conflicts with long-form parameters and is not recommended. {_msg_long}"
with pytest.raises(GMTInvalidInput, match=msg):
with pytest.raises(GMTParameterError, match=rf"{_msg_conflict} {_msg_reason}"):
func(label="abcd", U="efg")

with pytest.raises(GMTInvalidInput, match=msg):
with pytest.raises(GMTParameterError, match=rf"{_msg_conflict} {_msg_reason}"):
func(text="efg", U="efg")


Expand Down
4 changes: 2 additions & 2 deletions pygmt/tests/test_coast.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput, GMTParameterError
from pygmt.exceptions import GMTParameterError


@pytest.mark.benchmark
Expand Down Expand Up @@ -82,7 +82,7 @@ def test_coast_resolution_long_short_form_conflict():
using the long form.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.coast(
region=[-180, 180, -80, 80],
projection="M15c",
Expand Down
18 changes: 9 additions & 9 deletions pygmt/tests/test_colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.params.position import Position


Expand Down Expand Up @@ -50,21 +50,21 @@ def test_image_position_mixed_syntax():
Test that mixing deprecated GMT CLI syntax string with new parameters.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", length="4c")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", width="0.5c")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", orientation="horizontal")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", reverse=True)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", nan=True)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(
cmap="gmt/rainbow", position="x0/0", fg_triangle=True, bg_triangle=True
)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", move_text="label")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.colorbar(cmap="gmt/rainbow", position="x0/0", label_as_column=True)
10 changes: 5 additions & 5 deletions pygmt/tests/test_grdview.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure, grdcut
from pygmt.exceptions import GMTInvalidInput, GMTTypeError
from pygmt.exceptions import GMTInvalidInput, GMTParameterError, GMTTypeError
from pygmt.helpers import GMTTempFile
from pygmt.helpers.testing import load_static_earth_relief

Expand Down Expand Up @@ -343,13 +343,13 @@ def test_grdview_mixed_syntax(gridfile):
Run grdview using grid as a file and drapegrid as an xarray.DataArray.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.grdview(grid=gridfile, cmap="SCM/oleron", surftype="i", dpi=300)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.grdview(grid=gridfile, cmap="SCM/oleron", surftype="m", mesh_fill="red")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.grdview(grid=gridfile, cmap="SCM/oleron", surftype="s", monochrome=True)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.grdview(
grid=gridfile, cmap="SCM/oleron", surftype="i", nan_transparent=True
)
10 changes: 5 additions & 5 deletions pygmt/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput
from pygmt.exceptions import GMTParameterError
from pygmt.params import Box, Position


Expand Down Expand Up @@ -66,11 +66,11 @@ def test_image_position_mixed_syntax():
and conflicts with other parameters.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.image(imagefile="@circuit.png", position="x0/0", width="4c")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.image(imagefile="@circuit.png", position="x0/0", height="3c")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.image(imagefile="@circuit.png", position="x0/0", dpi="300")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.image(imagefile="@circuit.png", position="x0/0", replicate=(2, 1))
4 changes: 2 additions & 2 deletions pygmt/tests/test_inset.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput, GMTParameterError
from pygmt.exceptions import GMTParameterError
from pygmt.params import Box, Position


Expand Down Expand Up @@ -94,6 +94,6 @@ def test_inset_invalid_inputs():
with fig.inset(position=Position("TL"), height="5c"):
pass
# Old position syntax conflicts with width/height
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
with fig.inset(position="jTL+w3.5c", width="3.5c"):
pass
8 changes: 4 additions & 4 deletions pygmt/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput, GMTTypeError
from pygmt.exceptions import GMTParameterError, GMTTypeError
from pygmt.helpers import GMTTempFile


Expand Down Expand Up @@ -174,9 +174,9 @@ def test_legend_position_mixed_syntax(legend_spec):
spec = io.StringIO(legend_spec)
fig = Figure()
fig.basemap(projection="x6i", region=[0, 1, 0, 1], frame=True)
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.legend(spec, position="jTL", width="5i")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.legend(spec, position="jTL", height="5i")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.legend(spec, position="jTL", line_spacing=2.0)
6 changes: 3 additions & 3 deletions pygmt/tests/test_logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput, GMTParameterError
from pygmt.exceptions import GMTParameterError
from pygmt.params import Position


Expand Down Expand Up @@ -66,7 +66,7 @@ def test_logo_position_mixed_syntax():
Test that an error is raised when mixing new and deprecated syntax in 'position'.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.logo(position="jTL", width="5c")
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
fig.logo(position="jTL", height="6c")
4 changes: 2 additions & 2 deletions pygmt/tests/test_solar.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest
from pygmt import Figure
from pygmt.exceptions import GMTInvalidInput, GMTValueError
from pygmt.exceptions import GMTParameterError, GMTValueError


@pytest.mark.mpl_image_compare
Expand Down Expand Up @@ -84,7 +84,7 @@ def test_invalid_parameter():
arguments for 'terminator' and 'terminator_datetime'.
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
with pytest.raises(GMTParameterError):
# Use single-letter parameter 'T' for testing
fig.solar(
region="d", projection="W0/15c", frame="a", T="d+d1990-02-17T04:25:00"
Expand Down
Loading
Loading