Skip to content
92 changes: 68 additions & 24 deletions pygmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ def show_versions(file=sys.stdout):
- System information (Python version, Operating System)
- Core dependency versions (NumPy, Pandas, Xarray, etc)
- GMT library information

It also warns users if the installed Ghostscript version has serious bugs or is
incompatible with the installed GMT version.
"""

import importlib
Expand All @@ -110,8 +113,18 @@ def show_versions(file=sys.stdout):
import subprocess

from packaging.requirements import Requirement
from packaging.version import Version

def _get_clib_info() -> dict:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pygmt.print_clib_info function was initially added in #176 but has been replaced by pygmt.show_versions in #466 (first appear in v0.1.2).

In this PR, a new private function _get_clib_info is added which returns a dictionary instead of printing a long string.

It's time to retire the print_clib_info function. Since it's rarely used, I think we can just remove it without a deprecation warning. Of course, we should do it in a separate PR so that at least we have a deprecation entry in the v0.13.0 changelog.

"""
Return information about the GMT shared library.
"""
from pygmt.clib import Session

def _get_module_version(modname):
with Session() as ses:
return ses.info

def _get_module_version(modname: str) -> str | None:
"""
Get version information of a Python module.
"""
Expand All @@ -128,17 +141,19 @@ def _get_module_version(modname):
except ImportError:
return None

def _get_ghostscript_version():
def _get_ghostscript_version() -> str | None:
"""
Get ghostscript version.
Get Ghostscript version.
"""
os_name = sys.platform
if os_name.startswith(("linux", "freebsd", "darwin")):
cmds = ["gs"]
elif os_name == "win32":
cmds = ["gswin64c.exe", "gswin32c.exe"]
else:
return None
match sys.platform:
case "linux" | "darwin":
cmds = ["gs"]
case os_name if os_name.startswith("freebsd"):
cmds = ["gs"]
case "win32":
cmds = ["gswin64c.exe", "gswin32c.exe"]
case _:
return None

for gs_cmd in cmds:
if (gsfullpath := shutil.which(gs_cmd)) is not None:
Expand All @@ -147,24 +162,53 @@ def _get_ghostscript_version():
).strip()
return None

def _check_ghostscript_version(gs_version: str) -> str | None:
"""
Check if the Ghostscript version is compatible with GMT versions.
"""
match Version(gs_version):
case v if v < Version("9.53"):
return (
f"Ghostscript v{gs_version} is too old and may have serious bugs. "
"Please consider upgrading your Ghostscript."
)
case v if Version("10.00") <= v < Version("10.02"):
return (
f"Ghostscript v{gs_version} has known bugs. "
"Please consider upgrading to version v10.02 or later."
)
case v if v >= Version("10.02"):
from pygmt.clib import __gmt_version__

if Version(__gmt_version__) < Version("6.5.0"):
return (
f"GMT v{__gmt_version__} doesn't support Ghostscript "
"v{gs_version}. Please consider upgrading to GMT>=6.5.0 or "
"downgrading to Ghostscript v9.56."
)
return None

sys_info = {
"python": sys.version.replace("\n", " "),
"executable": sys.executable,
"machine": platform.platform(),
}

deps = [Requirement(v).name for v in importlib.metadata.requires("pygmt")]
gs_version = _get_ghostscript_version()

lines = []
lines.append("PyGMT information:")
lines.append(f" version: {__version__}")
lines.append("System information:")
lines.extend([f" {key}: {val}" for key, val in sys_info.items()])
lines.append("Dependency information:")
lines.extend([f" {modname}: {_get_module_version(modname)}" for modname in deps])
lines.append(f" ghostscript: {gs_version}")
lines.append("GMT library information:")
lines.extend([f" {key}: {val}" for key, val in _get_clib_info().items()])

if warnmsg := _check_ghostscript_version(gs_version):
lines.append("WARNING:")
lines.append(f" {warnmsg}")

print("PyGMT information:", file=file)
print(f" version: {__version__}", file=file)

print("System information:", file=file)
for key, val in sys_info.items():
print(f" {key}: {val}", file=file)

print("Dependency information:", file=file)
for modname in deps:
print(f" {modname}: {_get_module_version(modname)}", file=file)
print(f" ghostscript: {_get_ghostscript_version()}", file=file)

print_clib_info(file=file)
print("\n".join(lines), file=file)