diff --git a/satpy/tests/test_utils.py b/satpy/tests/test_utils.py index 61255c8006..c52006f1be 100644 --- a/satpy/tests/test_utils.py +++ b/satpy/tests/test_utils.py @@ -278,13 +278,38 @@ def test_specific_check_satpy(self): """Test 'check_satpy' with specific features provided.""" from satpy.utils import check_satpy with mock.patch("satpy.utils.print") as print_mock: - check_satpy(readers=["viirs_sdr"], extras=("cartopy", "__fake")) - checked_fake = False - for call in print_mock.mock_calls: - if len(call[1]) > 0 and "__fake" in call[1][0]: - assert "ok" not in call[1][1] - checked_fake = True - assert checked_fake, "Did not find __fake module mentioned in checks" + check_satpy(readers=["viirs_sdr"], packages=("cartopy", "__fake")) + checked_fake = any("__fake: not installed" in c[1] for c in print_mock.mock_calls if len(c[1])) + assert checked_fake, "Did not find __fake package mentioned in checks" + + +class TestShowVersions: + """Test the 'show_versions' function.""" + + def test_basic_show_versions(self): + """Test 'check_satpy' basic functionality.""" + from satpy.utils import show_versions + show_versions() + + def test_show_specific_version(self, capsys): + """Test 'show_version' works with installed package.""" + from satpy.utils import show_versions + show_versions(packages=["pytest"]) + out, _ = capsys.readouterr() + + pytest_mentioned = "pytest:" in out + pytest_installed = "pytest: not installed" not in out + check_pytest = pytest_mentioned and pytest_installed + assert check_pytest, "pytest with package version not in print output" + + def test_show_missing_specific_version(self, capsys): + """Test 'show_version' works with missing package.""" + from satpy.utils import show_versions + show_versions(packages=["__fake"]) + out, _ = capsys.readouterr() + + check_fake = "__fake: not installed" in out + assert check_fake, "Did not find '__fake: not installed' in print output" def test_debug_on(caplog): @@ -294,12 +319,7 @@ def test_debug_on(caplog): def depwarn(): logger = logging.getLogger("satpy.silly") logger.debug("But now it's just got SILLY.") - warnings.warn( - "Stop that! It's SILLY.", - DeprecationWarning, - stacklevel=2 - ) - + warnings.warn("Stop that! It's SILLY.", DeprecationWarning, stacklevel=2) warnings.filterwarnings("ignore", category=DeprecationWarning) debug_on(False) filts_before = warnings.filters.copy() diff --git a/satpy/utils.py b/satpy/utils.py index 77645a476a..f4d456d4f6 100644 --- a/satpy/utils.py +++ b/satpy/utils.py @@ -20,9 +20,11 @@ import contextlib import datetime +import importlib.metadata import logging import os import pathlib +import platform import warnings from contextlib import contextmanager from copy import deepcopy @@ -476,30 +478,76 @@ def _check_yaml_configs(configs, key): pass return diagnostic +def _check_package_version(package_name: str) -> Optional[str]: + """Check the version of `package_name`. -def _check_import(module_names): - """Import the specified modules and provide status.""" - diagnostics = {} - for module_name in module_names: - try: - __import__(module_name) - res = "ok" - except ImportError as err: - res = str(err) - diagnostics[module_name] = res - return diagnostics + Args: + package_name (str): the distribution package name. + + Returns: + the version number if available else `None`. + """ + try: + return importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + return None + + +def show_versions(packages=None): + """Shows version for system, python and common packages (if installed). + + Args: + packages (list or None): Limit packages to those specified. + + Returns: + None. + + """ + packages = ( + ( + "cartopy", + "geoviews", + "numpy", + "dask", + "xarray", + "gdal", + "rasterio", + "pyproj", + "netcdf4", + "h5py", + "pyhdf", + "h5netcdf", + "fsspec", + ) + if packages is None + else packages + ) + + print("Versions") # noqa: T201 + print("======") # noqa: T201 + print(f"platform: {platform.platform()}") # noqa: T201 + print(f"python: {platform.python_version()}") # noqa: T201 + print() # noqa: T201 + + for package_name in sorted(packages): + package_version = _check_package_version(package_name) + print( # noqa: T201 + f"{package_name}: {package_version if package_version else 'not installed'}" + ) + + print() # noqa: T201 -def check_satpy(readers=None, writers=None, extras=None): +def check_satpy(readers=None, writers=None, packages=None): """Check the satpy readers and writers for correct installation. Args: readers (list or None): Limit readers checked to those specified writers (list or None): Limit writers checked to those specified - extras (list or None): Limit extras checked to those specified + packages (list or None): Limit packages checked to those specified - Returns: bool - True if all specified features were successfully loaded. + Returns: + None """ from satpy.readers import configs_for_reader @@ -517,12 +565,7 @@ def check_satpy(readers=None, writers=None, extras=None): print(writer + ": ", res) # noqa: T201 print() # noqa: T201 - print("Extras") # noqa: T201 - print("======") # noqa: T201 - module_names = extras if extras is not None else ("cartopy", "geoviews") - for module_name, res in sorted(_check_import(module_names).items()): - print(module_name + ": ", res) # noqa: T201 - print() # noqa: T201 + show_versions(packages=packages) def unify_chunks(*data_arrays: xr.DataArray) -> tuple[xr.DataArray, ...]: