diff --git a/pygmt/params/pattern.py b/pygmt/params/pattern.py index 76b43224945..c33d80fd8df 100644 --- a/pygmt/params/pattern.py +++ b/pygmt/params/pattern.py @@ -3,6 +3,7 @@ """ import dataclasses +from numbers import Integral from pygmt._typing import PathLike from pygmt.alias import Alias @@ -80,7 +81,11 @@ def _validate(self): # Integer pattern number must be in the range 1-90. if not ( isinstance(self.pattern, PathLike) - or (isinstance(self.pattern, int) and 1 <= self.pattern <= 90) + or ( + isinstance(self.pattern, Integral) + and not isinstance(self.pattern, bool) + and 1 <= self.pattern <= 90 + ) ): raise GMTValueError( self.pattern, diff --git a/pygmt/src/binstats.py b/pygmt/src/binstats.py index b418ae6bca8..994571c3fcd 100644 --- a/pygmt/src/binstats.py +++ b/pygmt/src/binstats.py @@ -3,6 +3,7 @@ """ from collections.abc import Sequence +from numbers import Real from typing import Literal import xarray as xr @@ -168,7 +169,7 @@ def binstats( ) aliasdict.merge(kwargs) if statistic == "quantile": - if not isinstance(quantile_value, (int, float)): + if not isinstance(quantile_value, Real) or isinstance(quantile_value, bool): raise GMTTypeError( quantile_value, reason="quantile_value must be an 'int' or 'float'." ) diff --git a/pygmt/src/text.py b/pygmt/src/text.py index 98caf41b35c..ef0ec29508b 100644 --- a/pygmt/src/text.py +++ b/pygmt/src/text.py @@ -3,6 +3,7 @@ """ from collections.abc import Sequence +from numbers import Real from typing import Literal import numpy as np @@ -272,7 +273,7 @@ def text( # ruff: ignore[too-many-branches, too-many-statements] kwargs["F"] += flag elif arg is False: pass - elif isinstance(arg, int | float | str): + elif isinstance(arg, Real | str): kwargs["F"] += f"{flag}{arg}" confdict = {} diff --git a/pygmt/tests/test_binstats.py b/pygmt/tests/test_binstats.py index 9135a8d08d4..1f8d0ff1f54 100644 --- a/pygmt/tests/test_binstats.py +++ b/pygmt/tests/test_binstats.py @@ -4,6 +4,7 @@ from pathlib import Path +import numpy as np import numpy.testing as npt import pytest from pygmt import binstats @@ -91,3 +92,22 @@ def test_binstats_quantile(): npt.assert_allclose(temp_grid.min(), 53) npt.assert_allclose(temp_grid.median(), 543664.5) npt.assert_allclose(temp_grid.mean(), 1661363.6) + + +def test_binstats_quantile_value_numpy_scalars(): + """ + Test that numpy scalars are accepted as quantile_value but booleans are not. + """ + data = np.random.default_rng(0).random((100, 3)) * 10 + kwargs = { + "data": data, + "region": [0, 10, 0, 10], + "spacing": 5, + "search_radius": 5, + "statistic": "quantile", + } + expected = binstats(quantile_value=50, **kwargs) + for value in (np.int64(50), np.float32(50.0), np.float64(50.0)): + npt.assert_allclose(binstats(quantile_value=value, **kwargs), expected) + with pytest.raises(GMTTypeError): + binstats(quantile_value=True, **kwargs) diff --git a/pygmt/tests/test_params_pattern.py b/pygmt/tests/test_params_pattern.py index 51d768693ea..3a31dca880c 100644 --- a/pygmt/tests/test_params_pattern.py +++ b/pygmt/tests/test_params_pattern.py @@ -2,6 +2,7 @@ Test the Pattern class. """ +import numpy as np import pytest from pygmt.exceptions import GMTValueError from pygmt.params import Pattern @@ -51,3 +52,13 @@ def test_pattern_invalid_colors(): """ with pytest.raises(GMTValueError): _ = str(Pattern(10, fgcolor="", bgcolor="")) + + +def test_pattern_numpy_integer_and_bool(): + """ + Test that numpy integers are valid pattern numbers but booleans are not. + """ + assert str(Pattern(np.int64(15))) == "p15" + assert str(Pattern(np.int32(90), invert=True)) == "P90" + with pytest.raises(GMTValueError): + Pattern(True) diff --git a/pygmt/tests/test_text.py b/pygmt/tests/test_text.py index be2f7c5095b..f442077b85d 100644 --- a/pygmt/tests/test_text.py +++ b/pygmt/tests/test_text.py @@ -3,11 +3,13 @@ """ from pathlib import Path +from unittest.mock import patch import numpy as np import pytest from packaging.version import Version from pygmt import Figure, config +from pygmt.clib import Session from pygmt.clib.session import __gmt_version__ from pygmt.exceptions import GMTCLibError, GMTParameterError, GMTTypeError from pygmt.helpers import GMTTempFile @@ -517,3 +519,19 @@ def test_text_nonascii_iso8859(): fig.text(x=1, y=1, text="xytext:1éęëė2") fig.text(x=[5, 5], y=[3, 5], text=["xytext1:ųúûüũūαζ∆❡", "xytext2:íîī∑π∇✉"]) return fig + + +def test_text_numpy_scalar_angle(): + """ + Test that a numpy integer angle is not silently dropped. + """ + + def get_text_args(**kwargs): + fig = Figure() + fig.basemap(region=[0, 10, 0, 10], projection="X5c", frame=True) + with patch.object(Session, "call_module", autospec=True) as mock_call: + fig.text(x=1, y=1, text="hi", **kwargs) + return mock_call.call_args.kwargs.get("args") or mock_call.call_args.args[-1] + + assert "-F+a30" in get_text_args(angle=np.int64(30)) + assert "-F+a30" in get_text_args(angle=30)