diff --git a/junifer/api/cli.py b/junifer/api/cli.py index 2402de4b9..a5888ccbd 100644 --- a/junifer/api/cli.py +++ b/junifer/api/cli.py @@ -8,12 +8,20 @@ import pathlib from typing import Dict, List, Union import click +import yaml from ..utils.logging import configure_logging, logger, warn_with_log from .functions import collect as api_collect from .functions import queue as api_queue from .functions import run as api_run from .parser import parse_yaml +from .utils import ( + _get_dependency_information, + _get_environment_information, + _get_junifer_version, + _get_python_information, + _get_system_information, +) def _parse_elements(element: str, config: Dict) -> Union[List, None]: @@ -190,6 +198,21 @@ def queue( @cli.command() -def selftest() -> None: - """Selftest command for CLI.""" - pass +@click.option("--long", "long_", is_flag=True) +def wtf(long_: bool) -> None: + """Wtf command for CLI. + + Parameters + ---------- + long_ : bool + Whether to report long version or not. + + """ + report = { + "junifer": _get_junifer_version(), + "python": _get_python_information(), + "dependencies": _get_dependency_information(long_=long_), + "system": _get_system_information(), + "environment": _get_environment_information(long_=long_), + } + click.echo(yaml.dump(report, sort_keys=False)) diff --git a/junifer/api/tests/test_api_utils.py b/junifer/api/tests/test_api_utils.py new file mode 100644 index 000000000..33dd4db42 --- /dev/null +++ b/junifer/api/tests/test_api_utils.py @@ -0,0 +1,89 @@ +"""Provide tests for utils.""" + +# Authors: Synchon Mandal +# License: AGPL + +import platform as pl + +import pytest + +from junifer._version import __version__ +from junifer.api.utils import ( + _get_dependency_information, + _get_environment_information, + _get_junifer_version, + _get_python_information, + _get_system_information, +) + + +def test_get_junifer_version() -> None: + """Test _get_junifer_version().""" + assert _get_junifer_version()["version"] == __version__ + + +def test_get_python_information() -> None: + """Test _get_python_information().""" + python_information = _get_python_information() + assert python_information["version"] == pl.python_version() + assert python_information["implementation"] == pl.python_implementation() + + +def test_get_dependency_information_short() -> None: + """Test short version of _get_dependency_information().""" + dependency_information = _get_dependency_information(long_=False) + assert [key for key in dependency_information.keys()] == [ + "click", + "numpy", + "datalad", + "pandas", + "nibabel", + "nilearn", + "sqlalchemy", + "yaml", + ] + + +def test_get_dependency_information_long() -> None: + """Test long version of _get_dependency_information().""" + dependency_information = _get_dependency_information(long_=True) + dependency_information_keys = [ + key for key in dependency_information.keys() + ] + for key in [ + "click", + "numpy", + "datalad", + "pandas", + "nibabel", + "nilearn", + "sqlalchemy", + "yaml", + ]: + assert key in dependency_information_keys + + +def test_get_system_information() -> None: + """Test _get_system_information().""" + system_information = _get_system_information() + assert system_information["platform"] == pl.platform() + + +@pytest.mark.parametrize( + "format_", + [ + "short", + "long", + ], +) +def test_get_environment_information(format_: str) -> None: + """Test _get_environment_information(). + + Parameters + ---------- + format_ : str + The parametrized report version. + + """ + environment_information = _get_environment_information(long_=format_) + assert "PATH" in environment_information.keys() diff --git a/junifer/api/tests/test_cli.py b/junifer/api/tests/test_cli.py index 380196f12..e2259c581 100644 --- a/junifer/api/tests/test_cli.py +++ b/junifer/api/tests/test_cli.py @@ -11,7 +11,7 @@ import pytest import yaml from click.testing import CliRunner -from junifer.api.cli import collect, run +from junifer.api.cli import collect, run, wtf # Create click test runner @@ -68,3 +68,19 @@ def test_run_and_collect_commands( collect_result = runner.invoke(collect, collect_args) # Check assert collect_result.exit_code == 0 + + +def test_wtf_short() -> None: + """Test short version of wtf command.""" + # Invoke wtf command + wtf_result = runner.invoke(wtf) + # Check + assert wtf_result.exit_code == 0 + + +def test_wtf_long() -> None: + """Test long version of wtf command.""" + # Invoke wtf command + wtf_result = runner.invoke(wtf, "--long") + # Check + assert wtf_result.exit_code == 0 diff --git a/junifer/api/utils.py b/junifer/api/utils.py new file mode 100644 index 000000000..0cd5552a8 --- /dev/null +++ b/junifer/api/utils.py @@ -0,0 +1,132 @@ +"""Provide utility functions for the api sub-package.""" + +# Authors: Synchon Mandal +# License: AGPL + +import os +import platform as pl +import re +from importlib.metadata import distribution +from typing import Dict + +from .._version import __version__ +from ..utils.logging import get_versions + + +def _get_junifer_version() -> Dict[str, str]: + """Get junifer version information. + + Returns + ------- + dict + A dictionary containing junifer version. + + """ + return { + "version": __version__, + } + + +def _get_python_information() -> Dict[str, str]: + """Get installed Python information. + + Parameters + ---------- + dict + A dictionary containing Python information. + + """ + return { + "version": pl.python_version(), + "implementation": pl.python_implementation(), + } + + +def _get_dependency_information(long_: bool) -> Dict[str, str]: + """Get Python environment dependency information. + + Parameters + ---------- + long_ : bool + Whether to report long version. + + Returns + ------- + dict + A dictionary containing Python dependency information. + + """ + dependency_versions = get_versions() + + pruned_dependency_versions = {} + # Report long version + if long_: + for key, value in dependency_versions.items(): + # Ignore built-in modules and self + if value != "None" and key != "junifer": + pruned_dependency_versions[key] = value + + # Report short version + else: + # Get dependencies for junifer + dist = distribution("junifer") + # Compile regex pattern + re_pattern = re.compile("[a-z-]+") + + for pkg_with_version in dist.requires: + # Perform regex search + matches = re.findall(pattern=re_pattern, string=pkg_with_version) + # Fix issue with PyYAML name registration + if matches[0] == "pyyaml": + key = "yaml" + else: + key = matches[0] + + if key in dependency_versions.keys(): + # Check if pkg part of optional dependencies + if "extra" not in matches: + pruned_dependency_versions[key] = dependency_versions[key] + + return pruned_dependency_versions + + +def _get_system_information() -> Dict[str, str]: + """Get system information. + + Returns + ------- + dict + A dictionary containing system information. + + """ + return { + "platform": pl.platform(), + } + + +def _get_environment_information(long_: bool) -> Dict[str, str]: + """Get system environment information. + + Parameters + ---------- + long_ : bool + Whether to report long version. + + Returns + ------- + dict + A dictionary containing system environment information. + + """ + environment_values = {} + # Report long version + if long_: + for key, value in os.environ.items(): + environment_values[key] = value + # Report short version + else: + for key in ["LC_CTYPE", "LC_TERMINAL", "LC_TERMINAL_VERSION", "PATH"]: + if key in os.environ.keys(): + environment_values[key] = os.environ[key] + + return environment_values