Skip to content

Commit

Permalink
clib: Refactor to check the GMT version only once
Browse files Browse the repository at this point in the history
  • Loading branch information
seisman committed May 22, 2024
1 parent 4862bf6 commit 13652e5
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/5-bump_gmt_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ using the following command:
**To-Do for bumping the minimum required GMT version**:

- [ ] Bump the minimum required GMT version (1 PR)
- [ ] Update `required_version` in `pygmt/clib/session.py`
- [ ] Update `required_version` in `pygmt/clib/__init__.py`
- [ ] Update `test_get_default` in `pygmt/tests/test_clib.py`
- [ ] Update minimum required versions in `doc/minversions.md`
- [ ] Remove unsupported GMT version from `.github/workflows/ci_tests_legacy.yaml`
Expand Down
5 changes: 2 additions & 3 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@

# ruff: isort: off
from sphinx_gallery.sorting import ExplicitOrder, ExampleTitleSortKey
import pygmt
from pygmt.clib import required_version
from pygmt import __commit__, __version__
from pygmt.sphinx_gallery import PyGMTScraper

# ruff: isort: on

requires_python = metadata("pygmt")["Requires-Python"]
with pygmt.clib.Session() as lib:
requires_gmt = f">={lib.required_version}"
requires_gmt = f">={required_version}"

extensions = [
"myst_parser",
Expand Down
15 changes: 12 additions & 3 deletions pygmt/clib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@
interface. Access to the C library is done through ctypes.
"""

from pygmt.clib.session import Session
from packaging.version import Version
from pygmt.clib.session import Session, __gmt_version__
from pygmt.exceptions import GMTVersionError

with Session() as lib:
__gmt_version__ = lib.info["version"]
required_version = "6.3.0"

# Check if the GMT version is older than the required version.
if Version(__gmt_version__) < Version(required_version):
msg = (

Check warning on line 16 in pygmt/clib/__init__.py

View check run for this annotation

Codecov / codecov/patch

pygmt/clib/__init__.py#L16

Added line #L16 was not covered by tests
f"Using an incompatible GMT version {__gmt_version__}. "
f"Must be equal or newer than {required_version}."
)
raise GMTVersionError(msg)

Check warning on line 20 in pygmt/clib/__init__.py

View check run for this annotation

Codecov / codecov/patch

pygmt/clib/__init__.py#L20

Added line #L20 was not covered by tests
27 changes: 27 additions & 0 deletions pygmt/clib/loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,33 @@ def load_libgmt(lib_fullnames: Iterator[str] | None = None) -> ctypes.CDLL:
return libgmt


def get_gmt_version(libgmt: ctypes.CDLL) -> str:
"""
Get the GMT version string for the GMT library.
Parameters
----------
libgmt
The GMT shared library.
Returns
-------
The GMT version string in *major.minor.patch* format.
"""
func = libgmt.GMT_Get_Version
func.argtypes = (
ctypes.c_void_p, # Unsed parameter, so it can be None.
ctypes.POINTER(ctypes.c_uint), # major
ctypes.POINTER(ctypes.c_uint), # minor
ctypes.POINTER(ctypes.c_uint), # patch
)
# The function return value is the current lib verison as a float, e.g., 6.5.
func.restype = ctypes.c_float
major, minor, patch = ctypes.c_uint(0), ctypes.c_uint(0), ctypes.c_uint(0)
func(None, major, minor, patch)
return f"{major.value}.{minor.value}.{patch.value}"


def clib_names(os_name: str) -> list[str]:
"""
Return the name(s) of GMT's shared library for the current operating system.
Expand Down
37 changes: 5 additions & 32 deletions pygmt/clib/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,10 @@
strings_to_ctypes_array,
vectors_to_arrays,
)
from pygmt.clib.loading import load_libgmt
from pygmt.clib.loading import get_gmt_version, load_libgmt
from pygmt.datatypes import _GMT_DATASET, _GMT_GRID
from pygmt.exceptions import (
GMTCLibError,
GMTCLibNoSessionError,
GMTInvalidInput,
GMTVersionError,
)
from pygmt.helpers import (
data_kind,
tempfile_from_geojson,
tempfile_from_image,
)
from pygmt.exceptions import GMTCLibError, GMTCLibNoSessionError, GMTInvalidInput
from pygmt.helpers import data_kind, tempfile_from_geojson, tempfile_from_image

FAMILIES = [
"GMT_IS_DATASET", # Entity is a data table
Expand Down Expand Up @@ -94,6 +85,7 @@

# Load the GMT library outside the Session class to avoid repeated loading.
_libgmt = load_libgmt()
__gmt_version__ = get_gmt_version(_libgmt)


class Session:
Expand Down Expand Up @@ -151,9 +143,6 @@ class Session:
-55 -47 -24 -10 190 981 1 1 8 14 1 1
"""

# The minimum supported GMT version.
required_version = "6.3.0"

@property
def session_pointer(self):
"""
Expand Down Expand Up @@ -208,27 +197,11 @@ def info(self):

def __enter__(self):
"""
Create a GMT API session and check the libgmt version.
Create a GMT API session.
Calls :meth:`pygmt.clib.Session.create`.
Raises
------
GMTVersionError
If the version reported by libgmt is less than
``Session.required_version``. Will destroy the session before
raising the exception.
"""
self.create("pygmt-session")
# Need to store the version info because 'get_default' won't work after
# the session is destroyed.
version = self.info["version"]
if Version(version) < Version(self.required_version):
self.destroy()
raise GMTVersionError(
f"Using an incompatible GMT version {version}. "
f"Must be equal or newer than {self.required_version}."
)
return self

def __exit__(self, exc_type, exc_value, traceback):
Expand Down

0 comments on commit 13652e5

Please sign in to comment.