diff --git a/pygmt/alias.py b/pygmt/alias.py index 06b5186d07d..4a770fe8076 100644 --- a/pygmt/alias.py +++ b/pygmt/alias.py @@ -8,7 +8,7 @@ from typing import Any, Literal from pygmt.exceptions import GMTParameterError, GMTValueError -from pygmt.helpers.utils import is_nonstr_iter, sequence_join +from pygmt.helpers.utils import is_given, is_nonstr_iter, sequence_join def _to_string( @@ -141,7 +141,7 @@ def _to_string( '2010-01-01/2010-03-01T00:00:00.000000/2015-01-01T12:00:00.123456/2005-01-01T08:00:00.000000000' """ # None and False are converted to None. - if value is None or value is False: + if not is_given(value): return None # True is converted to an empty string with the optional prefix and suffix. if value is True: 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..1854beee478 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -563,7 +563,7 @@ def build_arg_list( # noqa: PLR0912 if len(key) > 2: # Raise an exception for unrecognized options msg = f"Unrecognized parameter {key!r}." raise GMTInvalidInput(msg) - if value is None or value is False: # Exclude arguments that are None or False + if not is_given(value): pass elif value is True: gmt_args.append(f"-{key}") @@ -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/basemap.py b/pygmt/src/basemap.py index 4dbec079178..e3ec0af3d78 100644 --- a/pygmt/src/basemap.py +++ b/pygmt/src/basemap.py @@ -8,7 +8,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias +from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias from pygmt.params import Axis, Box, Frame @@ -138,7 +138,7 @@ def basemap( # noqa: PLR0913 ("compass", compass, "Figure.magnetic_rose"), ("rose", rose, "Figure.directional_rose"), ): - if value is not None and value is not False: + if is_given(value): warnings.warn( f"The {name!r} parameter has been deprecated since v0.19.0. Use {recommendation!r} instead.", category=FutureWarning, diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index b73ab072044..8b4e4d7d7a3 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -9,7 +9,7 @@ from pygmt.alias import Alias, AliasSystem from pygmt.clib import Session from pygmt.exceptions import GMTValueError -from pygmt.helpers import build_arg_list, fmt_docstring, use_alias +from pygmt.helpers import build_arg_list, fmt_docstring, is_given, use_alias from pygmt.helpers.utils import is_nonstr_iter from pygmt.params import Axis, Box, Frame, Position from pygmt.src._common import _parse_position @@ -63,11 +63,11 @@ def _build_frame( >>> _build_frame() # Passing no parameters returns None """ # Using the old 'frame' parameter. - if frame is not None and frame is not False: + if is_given(frame): return frame _xaxis_is_set = any( - v is not None and v is not False + is_given(v) for v in {annot, tick, grid, annot_angle, annot_prefix, annot_unit, label} ) _yaxis_is_set = unit is not None 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 75e4df009c9..55c8cb83e5a 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 ecfda37a582..5fae8666fec 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.params import Axis, Frame from pygmt.src.grdinfo import grdinfo @@ -69,8 +75,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 539c39727d3..2ed9321242b 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,