diff --git a/pygmt/helpers/__init__.py b/pygmt/helpers/__init__.py index 8cebc43cb77..8e746687f40 100644 --- a/pygmt/helpers/__init__.py +++ b/pygmt/helpers/__init__.py @@ -20,6 +20,7 @@ args_in_kwargs, build_arg_list, data_kind, + is_given, is_nonstr_iter, launch_external_viewer, non_ascii_to_octal, diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index c26b1df85f7..5d6c312c660 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -603,6 +603,44 @@ def build_arg_list( # noqa: PLR0912 return gmt_args +def is_given(value: Any) -> bool: + """ + Check if a parameter is given (not None and not False). + + In PyGMT, most parameters default to ``False`` (for boolean-only parameters) or + ``None`` (for non-boolean parameters), which means the parameters are not given and + should not be used in building the CLI option string. + + Parameters + ---------- + value + The value to check. + + Returns + ------- + bool + ``True`` if the value is not ``None`` and not ``False``, otherwise ``False``. + + Examples + -------- + >>> is_given(None) + False + >>> is_given(False) + False + >>> is_given(True) + True + >>> is_given(0) + True + >>> is_given("") + True + >>> is_given([]) + True + >>> is_given("value") + True + """ + return value is not None and value is not False + + def is_nonstr_iter(value: Any) -> bool: """ Check if the value is iterable (e.g., list, tuple, array) but not a string or a 0-D @@ -720,9 +758,7 @@ def args_in_kwargs(args: Sequence[str], kwargs: dict[str, Any]) -> bool: >>> args_in_kwargs(args=["A", "B"], kwargs={"B": 0}) True """ - return any( - kwargs.get(arg) is not None and kwargs.get(arg) is not False for arg in args - ) + return any(is_given(kwargs.get(arg)) for arg in args) def sequence_join( diff --git a/pygmt/params/base.py b/pygmt/params/base.py index b85591f58f3..79d680dcedb 100644 --- a/pygmt/params/base.py +++ b/pygmt/params/base.py @@ -4,6 +4,8 @@ from abc import ABC, abstractmethod +from pygmt.helpers import is_given + class BaseParam(ABC): """ @@ -89,9 +91,5 @@ def __repr__(self): """ String representation of the object. """ - params = ", ".join( - f"{k}={v!r}" - for k, v in vars(self).items() - if v is not None and v is not False - ) + params = ", ".join(f"{k}={v!r}" for k, v in vars(self).items() if is_given(v)) return f"{self.__class__.__name__}({params})" diff --git a/pygmt/src/_common.py b/pygmt/src/_common.py index c5b0c01c752..8fff5e8e976 100644 --- a/pygmt/src/_common.py +++ b/pygmt/src/_common.py @@ -8,6 +8,7 @@ from typing import Any, ClassVar, Literal from pygmt.exceptions import GMTParameterError, GMTTypeError, GMTValueError +from pygmt.helpers import is_given from pygmt.params.position import Position from pygmt.src.which import which @@ -363,7 +364,7 @@ def _parse_position( if position in _valid_anchors: # Anchor code 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()): + if any(is_given(v) for v in kwdict.values()): raise GMTParameterError( conflicts_with=("position", kwdict.keys()), reason="'position' is specified using the unrecommended GMT command string syntax.", diff --git a/pygmt/src/grdfill.py b/pygmt/src/grdfill.py index b665e682d4e..7fe8219d8b2 100644 --- a/pygmt/src/grdfill.py +++ b/pygmt/src/grdfill.py @@ -11,7 +11,13 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.exceptions import GMTParameterError -from pygmt.helpers import build_arg_list, deprecate_parameter, fmt_docstring, use_alias +from pygmt.helpers import ( + build_arg_list, + deprecate_parameter, + fmt_docstring, + is_given, + use_alias, +) __doctest_skip__ = ["grdfill"] @@ -125,7 +131,7 @@ def grdfill( "spline_fill": spline_fill, "inquire": inquire, } - n_given = sum(v is not None and v is not False for v in params.values()) + n_given = sum(is_given(v) for v in params.values()) if n_given == 0: raise GMTParameterError(at_least_one=params) if n_given > 1: diff --git a/pygmt/src/grdfilter.py b/pygmt/src/grdfilter.py index 352e40e0673..626c242224c 100644 --- a/pygmt/src/grdfilter.py +++ b/pygmt/src/grdfilter.py @@ -10,7 +10,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.exceptions import GMTParameterError -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias +from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias __doctest_skip__ = ["grdfilter"] @@ -48,7 +48,7 @@ def _alias_option_F( # noqa: N802 if _old_filter_syntax: kwdict = {"width": width, "highpass": highpass} - if any(v is not None and v is not False for v in kwdict.values()): + if any(is_given(v) for v in kwdict.values()): raise GMTParameterError( conflicts_with=("filter", kwdict.keys()), reason="'filter' is specified using the unrecommended GMT command string syntax.", diff --git a/pygmt/src/grdgradient.py b/pygmt/src/grdgradient.py index ba269ab5c06..ed66aefb91c 100644 --- a/pygmt/src/grdgradient.py +++ b/pygmt/src/grdgradient.py @@ -10,7 +10,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.exceptions import GMTParameterError -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias +from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias __doctest_skip__ = ["grdgradient"] @@ -50,10 +50,7 @@ def _alias_option_N( # noqa: N802 _normalize_mapping = {"laplace": "e", "cauchy": "t"} # Check for old syntax for normalize if isinstance(normalize, str) and normalize not in _normalize_mapping: - if any( - v is not None and v is not False - for v in [norm_amp, norm_ambient, norm_sigma, norm_offset] - ): + if any(is_given(v) for v in [norm_amp, norm_ambient, norm_sigma, norm_offset]): raise GMTParameterError( conflicts_with=( "normalize", diff --git a/pygmt/src/grdproject.py b/pygmt/src/grdproject.py index a0271403344..3ef3bd64c86 100644 --- a/pygmt/src/grdproject.py +++ b/pygmt/src/grdproject.py @@ -10,7 +10,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.exceptions import GMTParameterError -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias +from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias __doctest_skip__ = ["grdproject"] @@ -120,7 +120,7 @@ def grdproject( # noqa: PLR0913 if kwargs.get("J", projection) is None: raise GMTParameterError(required="projection") - if kwargs.get("M", unit) is not None and kwargs.get("F", scaling) is not False: + if is_given(kwargs.get("M", unit)) and is_given(kwargs.get("F", scaling)): raise GMTParameterError(at_most_one=["unit", "scaling"]) aliasdict = AliasSystem( diff --git a/pygmt/src/grdview.py b/pygmt/src/grdview.py index 1a13f4ef6e3..aee5d6269e2 100644 --- a/pygmt/src/grdview.py +++ b/pygmt/src/grdview.py @@ -11,7 +11,13 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session, __gmt_version__ from pygmt.exceptions import GMTParameterError -from pygmt.helpers import build_arg_list, deprecate_parameter, fmt_docstring, use_alias +from pygmt.helpers import ( + build_arg_list, + deprecate_parameter, + fmt_docstring, + is_given, + use_alias, +) from pygmt.src.grdinfo import grdinfo __doctest_skip__ = ["grdview"] @@ -68,8 +74,7 @@ def _alias_option_Q( # noqa: N802 _old_surftype_syntax = surftype is not None and surftype not in _surftype_mapping if _old_surftype_syntax and any( - v is not None and v is not False - for v in (dpi, mesh_fill, monochrome, nan_transparent) + is_given(v) for v in (dpi, mesh_fill, monochrome, nan_transparent) ): raise GMTParameterError( conflicts_with=( diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 9b54961195d..847a5d7e487 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -14,6 +14,7 @@ build_arg_list, deprecate_parameter, fmt_docstring, + is_given, kwargs_to_strings, use_alias, ) @@ -62,7 +63,7 @@ def _alias_option_A( # noqa: N802 # Check conflicts with deprecated 'autolabel' parameter. if autolabel: if any( - v is not None and v is not False + is_given(v) for v in [tag, tag_position, tag_box, tag_number_style, tag_orientation] ): raise GMTParameterError( @@ -83,8 +84,7 @@ def _alias_option_A( # noqa: N802 # Validate tag_box if provided. if tag_box: if any( - v is not None and v is not False - for v in {tag_box.inner_pen, tag_box.inner_gap, tag_box.radius} + is_given(v) for v in {tag_box.inner_pen, tag_box.inner_gap, tag_box.radius} ): raise GMTValueError( tag_box,