Skip to content

Commit b2edd1f

Browse files
seismanweiji14
andauthored
Session.virtualfile_in: Deprecate the parameter 'extra_arrays'. Prepare and pass a dictionary of arrays instead (will be removed in v0.20.0) (#3823)
Co-authored-by: Wei Ji <[email protected]>
1 parent 662f97e commit b2edd1f

File tree

7 files changed

+108
-47
lines changed

7 files changed

+108
-47
lines changed

pygmt/clib/session.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,16 +1748,17 @@ def virtualfile_from_stringio(
17481748
seg.header = None
17491749
seg.text = None
17501750

1751+
# TODO(PyGMT>=0.20.0): Remove the deprecated parameter 'extra_arrays'.
17511752
def virtualfile_in(
17521753
self,
17531754
check_kind=None,
17541755
data=None,
17551756
x=None,
17561757
y=None,
17571758
z=None,
1758-
extra_arrays=None,
17591759
required_z=False,
17601760
required_data=True,
1761+
extra_arrays=None,
17611762
):
17621763
"""
17631764
Store any data inside a virtual file.
@@ -1771,20 +1772,25 @@ def virtualfile_in(
17711772
check_kind : str or None
17721773
Used to validate the type of data that can be passed in. Choose
17731774
from 'raster', 'vector', or None. Default is None (no validation).
1774-
data : str or pathlib.Path or xarray.DataArray or {table-like} or None
1775+
data : str or pathlib.Path or xarray.DataArray or {table-like} or dict or None
17751776
Any raster or vector data format. This could be a file name or
17761777
path, a raster grid, a vector matrix/arrays, or other supported
17771778
data input.
17781779
x/y/z : 1-D arrays or None
17791780
x, y, and z columns as numpy arrays.
1780-
extra_arrays : list of 1-D arrays
1781-
Optional. A list of numpy arrays in addition to x, y, and z.
1782-
All of these arrays must be of the same size as the x/y/z arrays.
17831781
required_z : bool
17841782
State whether the 'z' column is required.
17851783
required_data : bool
17861784
Set to True when 'data' is required, or False when dealing with
17871785
optional virtual files. [Default is True].
1786+
extra_arrays : list of 1-D arrays
1787+
A list of numpy arrays in addition to x, y, and z. All of these arrays must
1788+
be of the same size as the x/y/z arrays.
1789+
1790+
.. deprecated:: v0.16.0
1791+
The parameter 'extra_arrays' will be removed in v0.20.0. Prepare and pass
1792+
a dictionary of arrays instead to the `data` parameter. E.g.,
1793+
``data={"x": x, "y": y, "size": size}``.
17881794
17891795
Returns
17901796
-------
@@ -1863,10 +1869,16 @@ def virtualfile_in(
18631869
if z is not None:
18641870
_data.append(z)
18651871
if extra_arrays:
1872+
msg = (
1873+
"The parameter 'extra_arrays' will be removed in v0.20.0. "
1874+
"Prepare and pass a dictionary of arrays instead to the `data` "
1875+
"parameter. E.g., `data={'x': x, 'y': y, 'size': size}`"
1876+
)
1877+
warnings.warn(message=msg, category=FutureWarning, stacklevel=1)
18661878
_data.extend(extra_arrays)
18671879
case "vectors":
18681880
if hasattr(data, "items") and not hasattr(data, "to_frame"):
1869-
# pandas.DataFrame or xarray.Dataset types.
1881+
# Dictionary, pandas.DataFrame or xarray.Dataset types.
18701882
# pandas.Series will be handled below like a 1-D numpy.ndarray.
18711883
_data = [array for _, array in data.items()]
18721884
else:

pygmt/helpers/utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import time
1313
import webbrowser
1414
from collections.abc import Iterable, Mapping, Sequence
15+
from itertools import islice
1516
from pathlib import Path
1617
from typing import Any, Literal
1718

@@ -41,7 +42,7 @@
4142
]
4243

4344

44-
def _validate_data_input(
45+
def _validate_data_input( # noqa: PLR0912
4546
data=None, x=None, y=None, z=None, required_z=False, required_data=True, kind=None
4647
) -> None:
4748
"""
@@ -143,6 +144,15 @@ def _validate_data_input(
143144
raise GMTInvalidInput(msg)
144145
if hasattr(data, "data_vars") and len(data.data_vars) < 3: # xr.Dataset
145146
raise GMTInvalidInput(msg)
147+
if kind == "vectors" and isinstance(data, dict):
148+
# Iterator over the up-to-3 first elements.
149+
arrays = list(islice(data.values(), 3))
150+
if len(arrays) < 2 or any(v is None for v in arrays[:2]): # Check x/y
151+
msg = "Must provide x and y."
152+
raise GMTInvalidInput(msg)
153+
if required_z and (len(arrays) < 3 or arrays[2] is None): # Check z
154+
msg = "Must provide x, y, and z."
155+
raise GMTInvalidInput(msg)
146156

147157

148158
def _is_printable_ascii(argstr: str) -> bool:

pygmt/src/plot.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
w="wrap",
5151
)
5252
@kwargs_to_strings(R="sequence", c="sequence_comma", i="sequence_comma", p="sequence")
53-
def plot(
53+
def plot( # noqa: PLR0912
5454
self,
5555
data=None,
5656
x=None,
@@ -232,34 +232,36 @@ def plot(
232232
kwargs = self._preprocess(**kwargs)
233233

234234
kind = data_kind(data)
235-
extra_arrays = []
236-
if kind == "empty": # Add more columns for vectors input
235+
if kind == "empty": # Data is given via a series of vectors.
236+
data = {"x": x, "y": y}
237237
# Parameters for vector styles
238238
if (
239239
isinstance(kwargs.get("S"), str)
240240
and len(kwargs["S"]) >= 1
241241
and kwargs["S"][0] in "vV"
242242
and is_nonstr_iter(direction)
243243
):
244-
extra_arrays.extend(direction)
244+
data.update({"x2": direction[0], "y2": direction[1]})
245245
# Fill
246246
if is_nonstr_iter(kwargs.get("G")):
247-
extra_arrays.append(kwargs.get("G"))
248-
del kwargs["G"]
247+
data["fill"] = kwargs.pop("G")
249248
# Size
250249
if is_nonstr_iter(size):
251-
extra_arrays.append(size)
250+
data["size"] = size
252251
# Intensity and transparency
253-
for flag in ["I", "t"]:
252+
for flag, name in [("I", "intensity"), ("t", "transparency")]:
254253
if is_nonstr_iter(kwargs.get(flag)):
255-
extra_arrays.append(kwargs.get(flag))
254+
data[name] = kwargs[flag]
256255
kwargs[flag] = ""
257256
# Symbol must be at the last column
258257
if is_nonstr_iter(symbol):
259258
if "S" not in kwargs:
260259
kwargs["S"] = True
261-
extra_arrays.append(symbol)
260+
data["symbol"] = symbol
262261
else:
262+
if any(v is not None for v in (x, y)):
263+
msg = "Too much data. Use either data or x/y/z."
264+
raise GMTInvalidInput(msg)
263265
for name, value in [
264266
("direction", direction),
265267
("fill", kwargs.get("G")),
@@ -277,7 +279,5 @@ def plot(
277279
kwargs["S"] = "s0.2c"
278280

279281
with Session() as lib:
280-
with lib.virtualfile_in(
281-
check_kind="vector", data=data, x=x, y=y, extra_arrays=extra_arrays
282-
) as vintbl:
282+
with lib.virtualfile_in(check_kind="vector", data=data) as vintbl:
283283
lib.call_module(module="plot", args=build_arg_list(kwargs, infile=vintbl))

pygmt/src/plot3d.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
w="wrap",
5252
)
5353
@kwargs_to_strings(R="sequence", c="sequence_comma", i="sequence_comma", p="sequence")
54-
def plot3d(
54+
def plot3d( # noqa: PLR0912
5555
self,
5656
data=None,
5757
x=None,
@@ -210,35 +210,37 @@ def plot3d(
210210
kwargs = self._preprocess(**kwargs)
211211

212212
kind = data_kind(data)
213-
extra_arrays = []
214-
215-
if kind == "empty": # Add more columns for vectors input
213+
if kind == "empty": # Data is given via a series of vectors.
214+
data = {"x": x, "y": y, "z": z}
216215
# Parameters for vector styles
217216
if (
218217
isinstance(kwargs.get("S"), str)
219218
and len(kwargs["S"]) >= 1
220219
and kwargs["S"][0] in "vV"
221220
and is_nonstr_iter(direction)
222221
):
223-
extra_arrays.extend(direction)
222+
data.update({"x2": direction[0], "y2": direction[1]})
224223
# Fill
225224
if is_nonstr_iter(kwargs.get("G")):
226-
extra_arrays.append(kwargs.get("G"))
227-
del kwargs["G"]
225+
data["fill"] = kwargs.pop("G")
228226
# Size
229227
if is_nonstr_iter(size):
230-
extra_arrays.append(size)
228+
data["size"] = size
231229
# Intensity and transparency
232-
for flag in ["I", "t"]:
230+
for flag, name in [("I", "intensity"), ("t", "transparency")]:
233231
if is_nonstr_iter(kwargs.get(flag)):
234-
extra_arrays.append(kwargs.get(flag))
232+
data[name] = kwargs[flag]
235233
kwargs[flag] = ""
236234
# Symbol must be at the last column
237235
if is_nonstr_iter(symbol):
238236
if "S" not in kwargs:
239237
kwargs["S"] = True
240-
extra_arrays.append(symbol)
238+
data["symbol"] = symbol
241239
else:
240+
if any(v is not None for v in (x, y, z)):
241+
msg = "Too much data. Use either data or x/y/z."
242+
raise GMTInvalidInput(msg)
243+
242244
for name, value in [
243245
("direction", direction),
244246
("fill", kwargs.get("G")),
@@ -257,12 +259,6 @@ def plot3d(
257259

258260
with Session() as lib:
259261
with lib.virtualfile_in(
260-
check_kind="vector",
261-
data=data,
262-
x=x,
263-
y=y,
264-
z=z,
265-
extra_arrays=extra_arrays,
266-
required_z=True,
262+
check_kind="vector", data=data, required_z=True
267263
) as vintbl:
268264
lib.call_module(module="plot3d", args=build_arg_list(kwargs, infile=vintbl))

pygmt/src/text.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,22 +222,24 @@ def text_( # noqa: PLR0912
222222
elif isinstance(arg, int | float | str):
223223
kwargs["F"] += f"{flag}{arg}"
224224

225-
extra_arrays = []
226225
confdict = {}
226+
data = None
227227
if kind == "empty":
228+
data = {"x": x, "y": y}
229+
228230
for arg, flag, name in array_args:
229231
if is_nonstr_iter(arg):
230232
kwargs["F"] += flag
231233
# angle is numeric type and font/justify are str type.
232234
if name == "angle":
233-
extra_arrays.append(arg)
235+
data["angle"] = arg
234236
else:
235-
extra_arrays.append(np.asarray(arg, dtype=np.str_))
237+
data[name] = np.asarray(arg, dtype=np.str_)
236238

237239
# If an array of transparency is given, GMT will read it from the last numerical
238240
# column per data record.
239241
if is_nonstr_iter(kwargs.get("t")):
240-
extra_arrays.append(kwargs["t"])
242+
data["transparency"] = kwargs["t"]
241243
kwargs["t"] = True
242244

243245
# Append text to the last column. Text must be passed in as str type.
@@ -247,7 +249,7 @@ def text_( # noqa: PLR0912
247249
text, encoding=encoding
248250
)
249251
confdict["PS_CHAR_ENCODING"] = encoding
250-
extra_arrays.append(text)
252+
data["text"] = text
251253
else:
252254
if isinstance(position, str):
253255
kwargs["F"] += f"+c{position}+t{text}"
@@ -260,10 +262,7 @@ def text_( # noqa: PLR0912
260262
with Session() as lib:
261263
with lib.virtualfile_in(
262264
check_kind="vector",
263-
data=textfiles,
264-
x=x,
265-
y=y,
266-
extra_arrays=extra_arrays,
265+
data=textfiles or data,
267266
required_data=required_data,
268267
) as vintbl:
269268
lib.call_module(

pygmt/tests/test_clib_virtualfile_in.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,32 @@ def test_virtualfile_in_matrix_string_dtype():
128128
assert output == "347.5 348.5 -30.5 -30\n"
129129
# Should check that lib.virtualfile_from_vectors is called once,
130130
# not lib.virtualfile_from_matrix, but it's technically complicated.
131+
132+
133+
# TODO(PyGMT>=0.20.0): Remove the test related to deprecated parameter 'extra_arrays'.
134+
def test_virtualfile_in_extra_arrays(data):
135+
"""
136+
Test that the extra_arrays parameter is deprecated.
137+
"""
138+
with clib.Session() as lib:
139+
# Call the method twice to ensure only one statement in the with block.
140+
# Test that a FutureWarning is raised when extra_arrays is used.
141+
with pytest.warns(FutureWarning):
142+
with lib.virtualfile_in(
143+
check_kind="vector",
144+
x=data[:, 0],
145+
y=data[:, 1],
146+
extra_arrays=[data[:, 2]],
147+
) as vfile:
148+
pass
149+
# Test that the output is correct.
150+
with GMTTempFile() as outfile:
151+
with lib.virtualfile_in(
152+
check_kind="vector",
153+
x=data[:, 0],
154+
y=data[:, 1],
155+
extra_arrays=[data[:, 2]],
156+
) as vfile:
157+
lib.call_module("info", [vfile, "-C", f"->{outfile.name}"])
158+
output = outfile.read(keep_tabs=False)
159+
assert output == "11.5309 61.7074 -2.9289 7.8648 0.1412 0.9338\n"

pygmt/tests/test_plot3d.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@ def test_plot3d_fail_1d_array_with_data(data, region):
8888
fig.plot3d(style="cc", fill="red", transparency=data[:, 2] * 100, **kwargs)
8989

9090

91+
def test_plot3d_fail_no_data(data, region):
92+
"""
93+
Should raise an exception if data is not enough or too much.
94+
"""
95+
fig = Figure()
96+
with pytest.raises(GMTInvalidInput):
97+
fig.plot3d(
98+
style="c0.2c", x=data[0], y=data[1], region=region, projection="X10c"
99+
)
100+
with pytest.raises(GMTInvalidInput):
101+
fig.plot3d(
102+
style="c0.2c", data=data, x=data[0], region=region, projection="X10c"
103+
)
104+
105+
91106
@pytest.mark.mpl_image_compare
92107
def test_plot3d_projection(data, region):
93108
"""

0 commit comments

Comments
 (0)