From 13652e592a06a7fc7aaed4d886e86e6d29c3178f Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 4 Jan 2024 12:42:09 +0800 Subject: [PATCH] clib: Refactor to check the GMT version only once --- .../ISSUE_TEMPLATE/5-bump_gmt_checklist.md | 2 +- doc/conf.py | 5 +-- pygmt/clib/__init__.py | 15 ++++++-- pygmt/clib/loading.py | 27 ++++++++++++++ pygmt/clib/session.py | 37 +++---------------- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/5-bump_gmt_checklist.md b/.github/ISSUE_TEMPLATE/5-bump_gmt_checklist.md index a4591f12847..b2034a57d83 100644 --- a/.github/ISSUE_TEMPLATE/5-bump_gmt_checklist.md +++ b/.github/ISSUE_TEMPLATE/5-bump_gmt_checklist.md @@ -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` diff --git a/doc/conf.py b/doc/conf.py index b8a6c6ce4fc..ca2d2385c5a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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", diff --git a/pygmt/clib/__init__.py b/pygmt/clib/__init__.py index 868616f2345..b11163ea3e7 100644 --- a/pygmt/clib/__init__.py +++ b/pygmt/clib/__init__.py @@ -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 = ( + f"Using an incompatible GMT version {__gmt_version__}. " + f"Must be equal or newer than {required_version}." + ) + raise GMTVersionError(msg) diff --git a/pygmt/clib/loading.py b/pygmt/clib/loading.py index 7bcf576b9b6..c683849b797 100644 --- a/pygmt/clib/loading.py +++ b/pygmt/clib/loading.py @@ -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. diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index 5648bf00c6e..8d54a5d1893 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -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 @@ -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: @@ -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): """ @@ -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):