Skip to content

Commit b7019d0

Browse files
committed
Add the _to_string function
1 parent de0eba8 commit b7019d0

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

pygmt/alias.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""
2+
The PyGMT alias system to convert PyGMT long-form arguments to GMT's short-form.
3+
"""
4+
5+
from collections.abc import Mapping, Sequence
6+
from typing import Any, Literal
7+
8+
from pygmt.exceptions import GMTInvalidInput
9+
from pygmt.helpers.utils import is_nonstr_iter, sequence_join
10+
11+
12+
def _to_string(
13+
value: Any,
14+
prefix: str = "", # Default to an empty string to simplify the code logic.
15+
mapping: bool | Mapping = False,
16+
separator: Literal["/", ","] | None = None,
17+
size: int | Sequence[int] | None = None,
18+
ndim: int = 1,
19+
name: str | None = None,
20+
) -> str | list[str] | None:
21+
"""
22+
Convert any value to a string, a sequence of strings or None.
23+
24+
The general rules are:
25+
26+
- ``None``/``False`` will be converted to ``None``.
27+
- ``True`` will be converted to an empty string.
28+
- A sequence will be joined by the separator if a separator is provided. Otherwise,
29+
each item in the sequence will be converted to a string and a sequence of strings
30+
will be returned.
31+
- Any other type of values will be converted to a string if possible.
32+
33+
If a mapping dictionary is provided, the value will be converted to the short-form
34+
string that GMT accepts (e.g., mapping PyGMT long-form argument ``"high"`` to GMT's
35+
short-form argument ``"h"``). If the value is not in the mapping dictionary, the
36+
original value will be returned. If ``mapping`` is set to ``True``, the first letter
37+
of the long-form argument will be used as the short-form argument.
38+
39+
An optional prefix (e.g., `"+o"`) can be added to the beginning of the converted
40+
string.
41+
42+
To avoid extra overhead, this function does not validate parameter combinations. For
43+
example, if ``value`` is a sequence but ``separator`` is not specified, the function
44+
will return a sequence of strings. In this case, ``prefix`` has no effect, but the
45+
function does not check for such inconsistencies. The maintaner should ensure that
46+
the parameter combinations are valid.
47+
48+
Parameters
49+
----------
50+
value
51+
The value to convert.
52+
prefix
53+
The string to add as a prefix to the returned value.
54+
mapping
55+
A mapping dictionary or ``True`` to map long-form arguments to GMT's short-form
56+
arguments. If ``True``, will use the first letter of the long-form arguments.
57+
separator
58+
The separator to use if the value is a sequence.
59+
60+
Returns
61+
-------
62+
ret
63+
The converted value.
64+
65+
Examples
66+
--------
67+
>>> _to_string("text")
68+
'text'
69+
>>> _to_string(12)
70+
'12'
71+
>>> _to_string(True)
72+
''
73+
>>> _to_string(False)
74+
>>> _to_string(None)
75+
76+
>>> _to_string("text", prefix="+a")
77+
'+atext'
78+
>>> _to_string(12, prefix="+a")
79+
'+a12'
80+
>>> _to_string(True, prefix="+a")
81+
'+a'
82+
>>> _to_string(False, prefix="+a")
83+
>>> _to_string(None, prefix="+a")
84+
85+
>>> _to_string("high", mapping=True)
86+
'h'
87+
>>> _to_string("mean", mapping={"mean": "a", "mad": "d", "full": "g"})
88+
'a'
89+
>>> _to_string("invalid", mapping={"mean": "a", "mad": "d", "full": "g"})
90+
Traceback (most recent call last):
91+
...
92+
pygmt...GMTInvalidInput: Invalid value: 'invalid'. Valid values are: mean, ...
93+
94+
>>> _to_string((12, 34), separator="/")
95+
'12/34'
96+
>>> _to_string(("12p", "34p"), separator=",")
97+
'12p,34p'
98+
>>> _to_string(("12p", "34p"), prefix="+o", separator="/")
99+
'+o12p/34p'
100+
101+
>>> _to_string(["xaf", "yaf", "WSen"])
102+
['xaf', 'yaf', 'WSen']
103+
"""
104+
# None and False are converted to None.
105+
if value is None or value is False:
106+
return None
107+
# True is converted to an empty string with the optional prefix.
108+
if value is True:
109+
return f"{prefix}"
110+
# Any non-sequence value is converted to a string.
111+
if not is_nonstr_iter(value):
112+
match mapping:
113+
case False:
114+
pass
115+
case True:
116+
value = value[0]
117+
case Mapping():
118+
if value not in mapping and value not in mapping.values():
119+
_name = f"Parameter {name!r}: " if name else ""
120+
msg = (
121+
f"{_name}Invalid value: {value!r}. "
122+
f"Valid values are: {', '.join(mapping)}."
123+
)
124+
raise GMTInvalidInput(msg)
125+
value = mapping.get(value, value)
126+
return f"{prefix}{value}"
127+
128+
# Return the sequence if separator is not specified for options like '-B'.
129+
# True in a sequence will be converted to an empty string.
130+
if separator is None:
131+
return [str(item) if item is not True else "" for item in value]
132+
# Join the sequence of values with the separator.
133+
# "prefix" and "mapping" are ignored. We can enable them when needed.
134+
_value = sequence_join(value, separator=separator, size=size, ndim=ndim, name=name)
135+
return f"{prefix}{_value}"

0 commit comments

Comments
 (0)