Skip to content
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
53 changes: 41 additions & 12 deletions pygmt/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
def _to_string(
value: Any,
prefix: str = "", # Default to an empty string to simplify the code logic.
suffix: str = "", # Default to an empty string to simplify the code logic.
mapping: Mapping | None = None,
sep: Literal["/", ","] | None = None,
size: int | Sequence[int] | None = None,
Expand All @@ -38,20 +39,22 @@ def _to_string(
GMT's short-form argument ``"h"``).

An optional prefix (e.g., `"+o"`) can be added to the beginning of the converted
string.
string, and an optional suffix (e.g., `"+l"`) can be added to the end.

To avoid extra overhead, this function does not validate parameter combinations. For
example, if ``value`` is a sequence but ``sep`` is not specified, the function will
return a sequence of strings. In this case, ``prefix`` has no effect, but the
function does not check for such inconsistencies. The maintainer should ensure that
the parameter combinations are valid.
return a sequence of strings. In this case, ``prefix`` and ``suffix`` have no
effect, but the function does not check for such inconsistencies. The maintainer
should ensure that the parameter combinations are valid.

Parameters
----------
value
The value to convert.
prefix
The string to add as a prefix to the returned value.
suffix
The string to add as a suffix to the returned value.
mapping
A mapping dictionary to map PyGMT's long-form arguments to GMT's short-form.
sep
Expand Down Expand Up @@ -90,6 +93,13 @@ def _to_string(
>>> _to_string(False, prefix="+a")
>>> _to_string(None, prefix="+a")

>>> _to_string("blue", suffix="+l")
'blue+l'
>>> _to_string("red", suffix="+r")
'red+r'
>>> _to_string(True, suffix="+l")
'+l'

>>> _to_string("mean", mapping={"mean": "a", "mad": "d", "full": "g"})
'a'
>>> _to_string("invalid", mapping={"mean": "a", "mad": "d", "full": "g"})
Expand Down Expand Up @@ -135,9 +145,9 @@ def _to_string(
# None and False are converted to None.
if value is None or value is False:
return None
# True is converted to an empty string with the optional prefix.
# True is converted to an empty string with the optional prefix and suffix.
if value is True:
return f"{prefix}"
return f"{prefix}{suffix}"
# Any non-sequence value is converted to a string.
if not is_nonstr_iter(value):
if mapping:
Expand All @@ -148,16 +158,16 @@ def _to_string(
choices=mapping.keys(),
)
value = mapping.get(value, value)
return f"{prefix}{value}"
return f"{prefix}{value}{suffix}"

# Return the sequence if separator is not specified for options like '-B'.
# True in a sequence will be converted to an empty string.
if sep is None:
return [str(item) if item is not True else "" for item in value]
# Join the sequence of values with the separator.
# "prefix" and "mapping" are ignored. We can enable them when needed.
# "prefix", "suffix", and "mapping" are ignored. We can enable them when needed.
_value = sequence_join(value, sep=sep, size=size, ndim=ndim, name=name)
return _value if is_nonstr_iter(_value) else f"{prefix}{_value}"
return _value if is_nonstr_iter(_value) else f"{prefix}{_value}{suffix}"


class Alias:
Expand All @@ -172,6 +182,8 @@ class Alias:
The name of the parameter to be used in the error message.
prefix
The string to add as a prefix to the returned value.
suffix
The string to add as a suffix to the returned value.
mapping
A mapping dictionary to map PyGMT's long-form arguments to GMT's short-form.
sep
Expand All @@ -189,6 +201,10 @@ class Alias:
>>> par._value
'+o3.0/3.0'

>>> par = Alias("blue", suffix="+l")
>>> par._value
'blue+l'

>>> par = Alias("mean", mapping={"mean": "a", "mad": "d", "full": "g"})
>>> par._value
'a'
Expand All @@ -203,17 +219,20 @@ def __init__(
value: Any,
name: str | None = None,
prefix: str = "",
suffix: str = "",
mapping: Mapping | None = None,
sep: Literal["/", ","] | None = None,
size: int | Sequence[int] | None = None,
ndim: int = 1,
):
self.name = name
self.prefix = prefix
self.suffix = suffix
self._value = _to_string(
value=value,
name=name,
prefix=prefix,
suffix=suffix,
mapping=mapping,
sep=sep,
size=size,
Expand Down Expand Up @@ -283,8 +302,12 @@ def __init__(self, **kwargs):

# The value of each key in kwargs is an Alias object or a sequence of Alias
# objects. If it is a single Alias object, we will use its _value property. If
# it is a sequence of Alias objects, we will concatenate their _value properties
# into a single string.
# it is a sequence of Alias objects, we will check if any have suffix:
#
# - If any Alias has a suffix, return a list of values, for repeated GMT options
# like -Cblue+l -Cred+r
# - Otherwise, concatenate into a single string for combined modifiers like
# -BWSen+ttitle+gblue.
Copy link
Member Author

Choose a reason for hiding this comment

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

        G=[
            Alias(fillpositive, name="fillpositive", suffix="+p"),
            Alias(fillnegative, name="fillnegative", suffix="+n"),
        ],

Taking wiggle's -G option as an example. In the previous version, the values will be concatenated into a single string, so fillpositive="blue", fillnegative="red" will be -Gblue+pred+n, which is invalid.

I've updated the logic of AliasSystem, so that it will return a list of values, rather than concatenating them, when any Alias has a suffix.

#
# Note that alias._value is converted by the _to_string method and can only be
# None, string or sequence of strings.
Expand All @@ -296,7 +319,13 @@ def __init__(self, **kwargs):
if isinstance(aliases, Sequence): # A sequence of Alias objects.
values = [alias._value for alias in aliases if alias._value is not None]
if values:
kwdict[option] = "".join(values)
# Check if any alias has suffix - if so, return as list
has_suffix = any(alias.suffix for alias in aliases)
# If has suffix and multiple values, return as list;
# else concatenate into a single string.
kwdict[option] = (
values if has_suffix and len(values) > 1 else "".join(values)
)
elif aliases._value is not None: # A single Alias object and not None.
kwdict[option] = aliases._value
super().__init__(kwdict)
Expand Down
34 changes: 4 additions & 30 deletions pygmt/src/wiggle.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,6 @@
from pygmt.helpers import build_arg_list, fmt_docstring, use_alias


def _parse_fills(fillpositive, fillnegative):
"""
Parse the fillpositive and fillnegative parameters.

>>> _parse_fills("red", "blue")
['red+p', 'blue+n']
>>> _parse_fills(None, "blue")
'blue+n'
>>> _parse_fills("red", None)
'red+p'
>>> _parse_fills(None, None)
"""
_fills = []
if fillpositive is not None:
_fills.append(fillpositive + "+p")
if fillnegative is not None:
_fills.append(fillnegative + "+n")

match len(_fills):
case 0:
return None
case 1:
return _fills[0]
case 2:
return _fills


@fmt_docstring
@use_alias(
D="position",
Expand Down Expand Up @@ -138,10 +111,11 @@ def wiggle( # noqa: PLR0913
"""
self._activate_figure()

_fills = _parse_fills(fillpositive, fillnegative)

aliasdict = AliasSystem(
G=Alias(_fills, name="fillpositive/fillnegative"),
G=[
Alias(fillpositive, name="fillpositive", suffix="+p"),
Alias(fillnegative, name="fillnegative", suffix="+n"),
],
).add_common(
B=frame,
J=projection,
Expand Down