diff --git a/pygmt/clib/session.py b/pygmt/clib/session.py index cfabf84c1bc..d70be62f076 100644 --- a/pygmt/clib/session.py +++ b/pygmt/clib/session.py @@ -87,6 +87,9 @@ np.datetime64: "GMT_DATETIME", } +# Load the GMT library outside the Session class to avoid repeated loading. +_libgmt = load_libgmt() + class Session: """ @@ -308,7 +311,7 @@ def get_libgmt_func(self, name, argtypes=None, restype=None): ._FuncPtr'> """ if not hasattr(self, "_libgmt"): - self._libgmt = load_libgmt() + self._libgmt = _libgmt function = getattr(self._libgmt, name) if argtypes is not None: function.argtypes = argtypes diff --git a/pygmt/tests/test_clib_loading.py b/pygmt/tests/test_clib_loading.py index 3b104afb786..33d1cf2081d 100644 --- a/pygmt/tests/test_clib_loading.py +++ b/pygmt/tests/test_clib_loading.py @@ -11,6 +11,7 @@ import pytest from pygmt.clib.loading import check_libgmt, clib_full_names, clib_names, load_libgmt +from pygmt.clib.session import Session from pygmt.exceptions import GMTCLibError, GMTCLibNotFoundError, GMTOSError @@ -207,6 +208,44 @@ def test_brokenlib_brokenlib_workinglib(self): assert check_libgmt(load_libgmt(lib_fullnames=lib_fullnames)) is None +class TestLibgmtCount: + """ + Test that the GMT library is not repeatedly loaded in every session. + """ + + loaded_libgmt = load_libgmt() # Load the GMT library and reuse it when necessary + counter = 0 # Global counter for how many times ctypes.CDLL is called + + def _mock_ctypes_cdll_return(self, libname): # noqa: ARG002 + """ + Mock ctypes.CDLL to count how many times the function is called. + + If ctypes.CDLL is called, the counter increases by one. + """ + self.counter += 1 # Increase the counter + return self.loaded_libgmt + + def test_libgmt_load_counter(self, monkeypatch): + """ + Make sure that the GMT library is not loaded in every session. + """ + # Monkeypatch the ctypes.CDLL function + monkeypatch.setattr(ctypes, "CDLL", self._mock_ctypes_cdll_return) + + # Create two sessions and check the global counter + with Session() as lib: + _ = lib + with Session() as lib: + _ = lib + assert self.counter == 0 # ctypes.CDLL is not called after two sessions. + + # Explicitly calling load_libgmt to make sure the mock function is correct + load_libgmt() + assert self.counter == 1 + load_libgmt() + assert self.counter == 2 + + ############################################################################### # Test clib_full_names @pytest.fixture(scope="module", name="gmt_lib_names")