Skip to content

Commit 886355f

Browse files
authored
_parse_position: Refactor for new functions that do no need to check conflicting parameters (#4360)
1 parent 32abbf0 commit 886355f

File tree

7 files changed

+57
-27
lines changed

7 files changed

+57
-27
lines changed

pygmt/src/_common.py

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ def from_params(
248248

249249
def _parse_position(
250250
position: Position | Sequence[float | str] | str | None,
251-
kwdict: dict[str, Any],
252-
default: Position | None,
251+
default: Position | None = None,
252+
kwdict: dict[str, Any] | None = None,
253253
) -> Position | str | None:
254254
"""
255255
Parse the "position" parameter for embellishment-plotting functions.
@@ -264,12 +264,14 @@ def _parse_position(
264264
- A 2-character justification code
265265
- A raw GMT command string (for backward compatibility)
266266
- ``None``, in which case the default position is used
267-
kwdict
268-
The keyword arguments dictionary that conflicts with ``position`` if
269-
``position`` is given as a raw GMT command string.
270267
default
271268
The default Position object to use if ``position`` is ``None``. If ``default``
272269
is ``None``, use the GMT default.
270+
kwdict
271+
The keyword arguments dictionary that conflicts with ``position`` if
272+
``position`` is given as a raw GMT command string. This is used for backward
273+
compatibility to raise an exception if there are conflicting parameters. If
274+
``kwdict`` is ``None``, no conflict checking is performed (for new functions).
273275
274276
Returns
275277
-------
@@ -281,76 +283,104 @@ def _parse_position(
281283
>>> from pygmt.params import Position
282284
>>> _parse_position(
283285
... Position((3, 3), cstype="mapcoords"),
284-
... kwdict={"width": None, "height": None},
285286
... default=Position((0, 0), cstype="plotcoords"),
287+
... kwdict={"width": None, "height": None},
286288
... )
287289
Position(refpoint=(3, 3), cstype='mapcoords')
288290
289291
>>> _parse_position(
290292
... (3, 3),
291-
... kwdict={"width": None, "height": None},
292293
... default=Position((0, 0), cstype="plotcoords"),
294+
... kwdict={"width": None, "height": None},
293295
... )
294296
Position(refpoint=(3, 3), cstype='plotcoords')
295297
>>> _parse_position(
296298
... "TL",
297-
... kwdict={"width": None, "height": None},
298299
... default=Position((0, 0), cstype="plotcoords"),
300+
... kwdict={"width": None, "height": None},
299301
... )
300302
Position(refpoint='TL', cstype='inside')
301303
302304
>>> _parse_position(
303305
... None,
304-
... kwdict={"width": None, "height": None},
305306
... default=Position((0, 0), cstype="plotcoords"),
307+
... kwdict={"width": None, "height": None},
306308
... )
307309
Position(refpoint=(0, 0), cstype='plotcoords')
308310
309311
>>> _parse_position(
310312
... None,
311-
... kwdict={"width": None, "height": None},
312313
... default=None,
314+
... kwdict={"width": None, "height": None},
313315
... )
314316
315317
>>> _parse_position(
316318
... "x3c/4c+w2c",
317-
... kwdict={"width": None, "height": None},
318319
... default=Position((0, 0), cstype="plotcoords"),
320+
... kwdict={"width": None, "height": None},
319321
... )
320322
'x3c/4c+w2c'
321323
322324
>>> _parse_position(
323325
... "x3c/4c+w2c",
324-
... kwdict={"width": 2, "height": None},
325326
... default=Position((0, 0), cstype="plotcoords"),
327+
... kwdict={"width": 2, "height": None},
326328
... )
327329
Traceback (most recent call last):
328330
...
329331
pygmt.exceptions.GMTInvalidInput: Parameter 'position' is given with a raw GMT...
330332
331333
>>> _parse_position(
332334
... 123,
333-
... kwdict={"width": None, "height": None},
334335
... default=Position((0, 0), cstype="plotcoords"),
336+
... kwdict={"width": None, "height": None},
335337
... )
336338
Traceback (most recent call last):
337339
...
338340
pygmt.exceptions.GMTInvalidInput: Invalid type for parameter 'position':...
341+
342+
>>> # Below are examples without kwdict (for new functions).
343+
>>> _parse_position(
344+
... "BL",
345+
... default=Position((0, 0), cstype="plotcoords"),
346+
... )
347+
Position(refpoint='BL', cstype='inside')
348+
349+
>>> _parse_position(
350+
... "invalid",
351+
... default=Position((0, 0), cstype="plotcoords"),
352+
... )
353+
Traceback (most recent call last):
354+
...
355+
pygmt.exceptions.GMTValueError: Invalid position: 'invalid'...
339356
"""
340357

341358
_valid_anchors = {f"{h}{v}" for v in "TMB" for h in "LCR"} | {
342359
f"{v}{h}" for v in "TMB" for h in "LCR"
343360
}
344361
match position:
345-
case str() if position in _valid_anchors: # Anchor code
346-
position = Position(position, cstype="inside")
347-
case str(): # Raw GMT command string.
348-
if any(v is not None and v is not False for v in kwdict.values()):
349-
msg = (
350-
"Parameter 'position' is given with a raw GMT command string, and "
351-
f"conflicts with parameters {', '.join(repr(c) for c in kwdict)}."
362+
case str(): # String for anchor code or raw GMT command.
363+
if position in _valid_anchors: # Anchor code
364+
position = Position(position, cstype="inside")
365+
elif kwdict is not None: # Raw GMT command string with potential conflicts.
366+
if any(v is not None and v is not False for v in kwdict.values()):
367+
msg = (
368+
"Parameter 'position' is given with a raw GMT command string, "
369+
"and conflicts with parameters "
370+
f"{', '.join(repr(c) for c in kwdict)}."
371+
)
372+
raise GMTInvalidInput(msg)
373+
else:
374+
# No conflicting parameters to check, indicating it's a new function.
375+
# The string must be an anchor code.
376+
raise GMTValueError(
377+
position,
378+
description="position",
379+
reason=(
380+
"Parameter 'position' must be a two-character anchor code, "
381+
"a coordinate, or a Position object."
382+
),
352383
)
353-
raise GMTInvalidInput(msg)
354384
case Sequence() if len(position) == 2: # A sequence of x and y coordinates.
355385
position = Position(position, cstype="plotcoords")
356386
case Position(): # Already a Position object.

pygmt/src/colorbar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ def colorbar( # noqa: PLR0913
296296

297297
position = _parse_position(
298298
position,
299+
default=None, # Use GMT's default behavior if position is not provided.
299300
kwdict={
300301
"length": length,
301302
"width": width,
@@ -309,7 +310,6 @@ def colorbar( # noqa: PLR0913
309310
"move_text": move_text,
310311
"label_as_column": label_as_column,
311312
},
312-
default=None, # Use GMT's default behavior if position is not provided.
313313
)
314314

315315
aliasdict = AliasSystem(

pygmt/src/image.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ def image( # noqa: PLR0913
136136

137137
position = _parse_position(
138138
position,
139-
kwdict={"width": width, "height": height, "dpi": dpi, "replicate": replicate},
140139
default=Position((0, 0), cstype="plotcoords"), # Default to (0,0) in plotcoords
140+
kwdict={"width": width, "height": height, "dpi": dpi, "replicate": replicate},
141141
)
142142

143143
# width is required when only height is given.

pygmt/src/inset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ def inset(
129129

130130
position = _parse_position(
131131
position,
132-
kwdict={"width": width, "height": height},
133132
default=Position((0, 0), cstype="plotcoords"), # Default to (0,0) in plotcoords
133+
kwdict={"width": width, "height": height},
134134
)
135135

136136
# width is mandatory unless both projection and region are given.

pygmt/src/legend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ def legend( # noqa: PLR0913
123123

124124
position = _parse_position(
125125
position,
126-
kwdict={"width": width, "height": height, "line_spacing": line_spacing},
127126
default=Position("TR", offset=0.2), # Default to TR with 0.2-cm offset.
127+
kwdict={"width": width, "height": height, "line_spacing": line_spacing},
128128
)
129129

130130
# Set width to 0 (auto calculated) if height is given but width is not.

pygmt/src/logo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ def logo( # noqa: PLR0913
9999

100100
position = _parse_position(
101101
position,
102-
kwdict={"width": width, "height": height},
103102
default=Position((0, 0), cstype="plotcoords"), # Default to (0,0) in plotcoords
103+
kwdict={"width": width, "height": height},
104104
)
105105

106106
# width and height are mutually exclusive.

pygmt/src/wiggle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ def wiggle( # noqa: PLR0913
170170

171171
position = _parse_position(
172172
position,
173-
kwdict={"length": length, "label": label, "label_alignment": label_alignment},
174173
default=Position("BL", offset=0.2), # Default to BL with 0.2-cm offset.
174+
kwdict={"length": length, "label": label, "label_alignment": label_alignment},
175175
)
176176

177177
_fills = _parse_fills(positive_fill, negative_fill)

0 commit comments

Comments
 (0)