Skip to content

Commit 69005cb

Browse files
authored
clib: Improve docstrings, comments, type hints and codes of some Session methods (#3327)
1 parent 1f4ce5e commit 69005cb

File tree

1 file changed

+79
-91
lines changed

1 file changed

+79
-91
lines changed

pygmt/clib/session.py

Lines changed: 79 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import pathlib
1111
import sys
1212
import warnings
13+
from collections.abc import Generator
1314
from typing import Literal
1415

1516
import numpy as np
@@ -262,7 +263,7 @@ def __getitem__(self, name: str) -> int:
262263

263264
def get_enum(self, name: str) -> int:
264265
"""
265-
Get the value of a GMT constant (C enum) from gmt_resources.h.
266+
Get the value of a GMT constant (C enum) from ``gmt_resources.h``.
266267
267268
Used to set configuration values for other API calls. Wraps ``GMT_Get_Enum``.
268269
@@ -374,7 +375,7 @@ def create(self, name):
374375
# destroyed
375376
raise GMTCLibError(
376377
"Failed to create a GMT API session: There is a currently open session."
377-
" Must destroy it fist."
378+
" Must destroy it first."
378379
)
379380
# If the exception is raised, this means that there is no open session
380381
# and we're free to create a new one.
@@ -518,43 +519,41 @@ def get_default(self, name: str) -> str:
518519
raise GMTCLibError(msg)
519520
return value.value.decode()
520521

521-
def get_common(self, option):
522+
def get_common(self, option: str) -> bool | int | float | np.ndarray:
522523
"""
523524
Inquire if a GMT common option has been set and return its current value if
524525
possible.
525526
526527
Parameters
527528
----------
528-
option : str
529-
The GMT common option to check. Valid options are ``"B"``, ``"I"``,
530-
``"J"``, ``"R"``, ``"U"``, ``"V"``, ``"X"``, ``"Y"``, ``"a"``,
531-
``"b"``, ``"f"``, ``"g"``, ``"h"``, ``"i"``, ``"n"``, ``"o"``,
532-
``"p"``, ``"r"``, ``"s"``, ``"t"``, and ``":"``.
529+
option
530+
The GMT common option to check. Valid options are ``"B"``, ``"I"``, ``"J"``,
531+
``"R"``, ``"U"``, ``"V"``, ``"X"``, ``"Y"``, ``"a"``, ``"b"``, ``"f"``,
532+
``"g"``, ``"h"``, ``"i"``, ``"n"``, ``"o"``, ``"p"``, ``"r"``, ``"s"``,
533+
``"t"``, and ``":"``.
533534
534535
Returns
535536
-------
536-
value : bool, int, float, or numpy.ndarray
537-
Whether the option was set or its value.
538-
539-
If the option was not set, return ``False``. Otherwise,
540-
the return value depends on the choice of the option.
537+
value
538+
Whether the option was set or its value. If the option was not set, return
539+
``False``. Otherwise, the return value depends on the choice of the option.
541540
542-
- options ``"B"``, ``"J"``, ``"U"``, ``"g"``, ``"n"``, ``"p"``,
543-
and ``"s"``: return ``True`` if set, else ``False`` (bool)
541+
- options ``"B"``, ``"J"``, ``"U"``, ``"g"``, ``"n"``, ``"p"``, and ``"s"``:
542+
return ``True`` if set, else ``False`` (bool)
544543
- ``"I"``: 2-element array for the increments (float)
545544
- ``"R"``: 4-element array for the region (float)
546545
- ``"V"``: the verbose level (int)
547546
- ``"X"``: the xshift (float)
548547
- ``"Y"``: the yshift (float)
549548
- ``"a"``: geometry of the dataset (int)
550-
- ``"b"``: return 0 if `-bi` was set and 1 if `-bo` was set (int)
551-
- ``"f"``: return 0 if `-fi` was set and 1 if `-fo` was set (int)
549+
- ``"b"``: return 0 if ``-bi`` was set and 1 if ``-bo`` was set (int)
550+
- ``"f"``: return 0 if ``-fi`` was set and 1 if ``-fo`` was set (int)
552551
- ``"h"``: whether to delete existing header records (int)
553552
- ``"i"``: number of input columns (int)
554553
- ``"o"``: number of output columns (int)
555554
- ``"r"``: registration type (int)
556555
- ``"t"``: 2-element array for the transparency (float)
557-
- ``":"``: return 0 if `-:i` was set and 1 if `-:o` was set (int)
556+
- ``":"``: return 0 if ``-:i`` was set and 1 if ``-:o`` was set (int)
558557
559558
Examples
560559
--------
@@ -586,28 +585,28 @@ def get_common(self, option):
586585
argtypes=[ctp.c_void_p, ctp.c_uint, ctp.POINTER(ctp.c_double)],
587586
restype=ctp.c_int,
588587
)
589-
value = np.empty(6) # numpy array to store the value of the option
588+
value = np.empty(6, np.float64) # numpy array to store the value of the option
590589
status = c_get_common(
591590
self.session_pointer,
592591
ord(option),
593592
value.ctypes.data_as(ctp.POINTER(ctp.c_double)),
594593
)
595594

596-
# GMT_NOTSET (-1) means the option is not set
597-
if status == self["GMT_NOTSET"]:
595+
if status == self["GMT_NOTSET"]: # GMT_NOTSET (-1) means the option is not set
598596
return False
599-
# option is set and no other value is returned
600-
if status == 0:
597+
if status == 0: # Option is set and no other value is returned.
601598
return True
602-
# option is set and option values (in double type) are returned via the
603-
# 'value' array. 'status' is number of valid values in the array.
604-
if option in "IRt":
605-
return value[:status]
606-
if option in "XY": # only one valid element in the array
607-
return value[0]
608-
# option is set and the option value (in integer type) is returned via
609-
# the function return value (i.e., 'status')
610-
return status
599+
600+
# Otherwise, option is set and values are returned.
601+
match option:
602+
case "I" | "R" | "t":
603+
# Option values (in double type) are returned via the 'value' array.
604+
# 'status' is number of valid values in the array.
605+
return value[:status]
606+
case "X" | "Y": # Only one valid element in the array.
607+
return value[0]
608+
case _: # 'status' is the option value (in integer type).
609+
return status
611610

612611
def call_module(self, module: str, args: str | list[str]):
613612
"""
@@ -1698,7 +1697,7 @@ def virtualfile_from_data(
16981697
@contextlib.contextmanager
16991698
def virtualfile_out(
17001699
self, kind: Literal["dataset", "grid"] = "dataset", fname: str | None = None
1701-
):
1700+
) -> Generator[str, None, None]:
17021701
r"""
17031702
Create a virtual file or an actual file for storing output data.
17041703
@@ -1718,7 +1717,7 @@ def virtualfile_out(
17181717
17191718
Yields
17201719
------
1721-
vfile : str
1720+
vfile
17221721
Name of the virtual file or the actual file.
17231722
17241723
Examples
@@ -1803,6 +1802,12 @@ def read_virtualfile(
18031802
Cast the data into a GMT data container. Valid values are ``"dataset"``,
18041803
``"grid"`` and ``None``. If ``None``, will return a ctypes void pointer.
18051804
1805+
Returns
1806+
-------
1807+
pointer
1808+
Pointer to the GMT data container. If ``kind`` is ``None``, returns a ctypes
1809+
void pointer instead.
1810+
18061811
Examples
18071812
--------
18081813
>>> from pathlib import Path
@@ -1833,10 +1838,6 @@ def read_virtualfile(
18331838
... data_pointer = lib.read_virtualfile(voutgrd, kind="grid")
18341839
... assert isinstance(data_pointer, ctp.POINTER(_GMT_GRID))
18351840
1836-
Returns
1837-
-------
1838-
Pointer to the GMT data container. If ``kind`` is None, returns a ctypes void
1839-
pointer instead.
18401841
"""
18411842
c_read_virtualfile = self.get_libgmt_func(
18421843
"GMT_Read_VirtualFile",
@@ -1871,8 +1872,7 @@ def virtualfile_to_dataset(
18711872
Parameters
18721873
----------
18731874
vfname
1874-
The virtual file name that stores the result data. Required for ``"pandas"``
1875-
and ``"numpy"`` output type.
1875+
The virtual file name that stores the result data.
18761876
output_type
18771877
Desired output type of the result data.
18781878
@@ -1929,44 +1929,37 @@ def virtualfile_to_dataset(
19291929
... assert result is None
19301930
... assert Path(outtmp.name).stat().st_size > 0
19311931
...
1932-
... # strings output
1932+
... # strings, numpy and pandas outputs
19331933
... with Session() as lib:
19341934
... with lib.virtualfile_out(kind="dataset") as vouttbl:
19351935
... lib.call_module("read", [tmpfile.name, vouttbl, "-Td"])
1936+
...
1937+
... # strings output
19361938
... outstr = lib.virtualfile_to_dataset(
19371939
... vfname=vouttbl, output_type="strings"
19381940
... )
1939-
... assert isinstance(outstr, np.ndarray)
1940-
... assert outstr.dtype.kind in ("S", "U")
1941+
... assert isinstance(outstr, np.ndarray)
1942+
... assert outstr.dtype.kind in ("S", "U")
19411943
...
1942-
... # numpy output
1943-
... with Session() as lib:
1944-
... with lib.virtualfile_out(kind="dataset") as vouttbl:
1945-
... lib.call_module("read", [tmpfile.name, vouttbl, "-Td"])
1944+
... # numpy output
19461945
... outnp = lib.virtualfile_to_dataset(
19471946
... vfname=vouttbl, output_type="numpy"
19481947
... )
1949-
... assert isinstance(outnp, np.ndarray)
1948+
... assert isinstance(outnp, np.ndarray)
19501949
...
1951-
... # pandas output
1952-
... with Session() as lib:
1953-
... with lib.virtualfile_out(kind="dataset") as vouttbl:
1954-
... lib.call_module("read", [tmpfile.name, vouttbl, "-Td"])
1950+
... # pandas output
19551951
... outpd = lib.virtualfile_to_dataset(
19561952
... vfname=vouttbl, output_type="pandas"
19571953
... )
1958-
... assert isinstance(outpd, pd.DataFrame)
1954+
... assert isinstance(outpd, pd.DataFrame)
19591955
...
1960-
... # pandas output with specified column names
1961-
... with Session() as lib:
1962-
... with lib.virtualfile_out(kind="dataset") as vouttbl:
1963-
... lib.call_module("read", [tmpfile.name, vouttbl, "-Td"])
1956+
... # pandas output with specified column names
19641957
... outpd2 = lib.virtualfile_to_dataset(
19651958
... vfname=vouttbl,
19661959
... output_type="pandas",
19671960
... column_names=["col1", "col2", "col3", "coltext"],
19681961
... )
1969-
... assert isinstance(outpd2, pd.DataFrame)
1962+
... assert isinstance(outpd2, pd.DataFrame)
19701963
>>> outstr
19711964
array(['TEXT1 TEXT23', 'TEXT4 TEXT567', 'TEXT8 TEXT90',
19721965
'TEXT123 TEXT456789'], dtype='<U18')
@@ -2057,7 +2050,7 @@ def virtualfile_to_raster(
20572050
... result = lib.virtualfile_to_raster(vfname=voutgrd, outgrid=outgrid)
20582051
... assert isinstance(result, xr.DataArray)
20592052
"""
2060-
if outgrid is not None:
2053+
if outgrid is not None: # Already written to file, so return None
20612054
return None
20622055
if kind is None: # Inquire the data family from the virtualfile
20632056
family = self.inquire_virtualfile(vfname)
@@ -2068,54 +2061,49 @@ def virtualfile_to_raster(
20682061
}[family]
20692062
return self.read_virtualfile(vfname, kind=kind).contents.to_dataarray()
20702063

2071-
def extract_region(self):
2064+
def extract_region(self) -> np.ndarray:
20722065
"""
2073-
Extract the WESN bounding box of the currently active figure.
2066+
Extract the region of the currently active figure.
20742067
2075-
Retrieves the information from the PostScript file, so it works for
2076-
country codes as well.
2068+
Retrieves the information from the PostScript file, so it works for country
2069+
codes as well.
20772070
20782071
Returns
20792072
-------
2080-
* wesn : 1-D array
2081-
A numpy 1-D array with the west, east, south, and north dimensions
2082-
of the current figure.
2073+
region
2074+
A numpy 1-D array with the west, east, south, and north dimensions of the
2075+
current figure.
20832076
20842077
Examples
20852078
--------
2086-
20872079
>>> import pygmt
20882080
>>> fig = pygmt.Figure()
20892081
>>> fig.coast(
2090-
... region=[0, 10, -20, -10],
2091-
... projection="M6i",
2092-
... frame=True,
2093-
... land="black",
2082+
... region=[0, 10, -20, -10], projection="M12c", frame=True, land="black"
20942083
... )
20952084
>>> with Session() as lib:
2096-
... wesn = lib.extract_region()
2097-
>>> print(", ".join([f"{x:.2f}" for x in wesn]))
2085+
... region = lib.extract_region()
2086+
>>> print(", ".join([f"{x:.2f}" for x in region]))
20982087
0.00, 10.00, -20.00, -10.00
20992088
2100-
Using ISO country codes for the regions (for example ``"US.HI"`` for
2101-
Hawaiʻi):
2089+
Using ISO country codes for the regions (for example ``"US.HI"`` for Hawaiʻi):
21022090
21032091
>>> fig = pygmt.Figure()
2104-
>>> fig.coast(region="US.HI", projection="M6i", frame=True, land="black")
2092+
>>> fig.coast(region="US.HI", projection="M12c", frame=True, land="black")
21052093
>>> with Session() as lib:
2106-
... wesn = lib.extract_region()
2107-
>>> print(", ".join([f"{x:.2f}" for x in wesn]))
2094+
... region = lib.extract_region()
2095+
>>> print(", ".join([f"{x:.2f}" for x in region]))
21082096
-164.71, -154.81, 18.91, 23.58
21092097
2110-
The country codes can have an extra argument that rounds the region a
2111-
multiple of the argument (for example, ``"US.HI+r5"`` will round the
2112-
region to multiples of 5):
2098+
The country codes can have an extra argument that rounds the region to multiples
2099+
of the argument (for example, ``"US.HI+r5"`` will round the region to multiples
2100+
of 5):
21132101
21142102
>>> fig = pygmt.Figure()
2115-
>>> fig.coast(region="US.HI+r5", projection="M6i", frame=True, land="black")
2103+
>>> fig.coast(region="US.HI+r5", projection="M12c", frame=True, land="black")
21162104
>>> with Session() as lib:
2117-
... wesn = lib.extract_region()
2118-
>>> print(", ".join([f"{x:.2f}" for x in wesn]))
2105+
... region = lib.extract_region()
2106+
>>> print(", ".join([f"{x:.2f}" for x in region]))
21192107
-165.00, -150.00, 15.00, 25.00
21202108
""" # noqa: RUF002
21212109
c_extract_region = self.get_libgmt_func(
@@ -2124,12 +2112,12 @@ def extract_region(self):
21242112
restype=ctp.c_int,
21252113
)
21262114

2127-
wesn = np.empty(4, dtype=np.float64)
2128-
wesn_pointer = wesn.ctypes.data_as(ctp.POINTER(ctp.c_double))
2129-
# The second argument to GMT_Extract_Region is a file pointer to a
2130-
# PostScript file. It's only valid in classic mode. Use None to get a
2131-
# NULL pointer instead.
2132-
status = c_extract_region(self.session_pointer, None, wesn_pointer)
2115+
region = np.empty(4, dtype=np.float64)
2116+
status = c_extract_region(
2117+
self.session_pointer,
2118+
None, # File pointer to a PostScript file. Must be None in modern mode.
2119+
region.ctypes.data_as(ctp.POINTER(ctp.c_double)),
2120+
)
21332121
if status != 0:
21342122
raise GMTCLibError("Failed to extract region from current figure.")
2135-
return wesn
2123+
return region

0 commit comments

Comments
 (0)