[ENH]: Implement a command to print the running environment details #33
|
|
@ -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))
|
||||
|
|
|
|||
89
junifer/api/tests/test_api_utils.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
"""Provide tests for utils."""
|
||||
|
||||
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
||||
# 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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
132
junifer/api/utils.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
"""Provide utility functions for the api sub-package."""
|
||||
|
Ignored built-in modules as it doesn't make sense. Ignored built-in modules as it doesn't make sense.
Can we read this from the pyproject.toml file? Or we need to manually hardcode here? I have the impression that it will end-up like the Can we read this from the pyproject.toml file?
Or we need to manually hardcode here?
I have the impression that it will end-up like the `_version.py` file, which is created on build/install
The only reason I want to avoid reading from I don't really get the analogy with the The only reason I want to avoid reading from `pyproject.toml` is because of the extra I/O. The dependencies are not going to change frequently so hardcoding it doesn't seem like a bad idea.
I don't really get the analogy with the `_version.py` though.
On build/install time, a file Soon, we will start having "conditional" dependencies in which some specific markers will require some dependencies. I'm just trying to avoid having always in mind that we need to manage dependencies in two different sites. On build/install time, a file `_dependencies.py` is created with an array that contains the dependencies.
Soon, we will start having "conditional" dependencies in which some specific markers will require some dependencies. I'm just trying to avoid having always in mind that we need to manage dependencies in two different sites.
e.g.: e.g.: `pip install junifer[mri]` `pip install junifer[eeg]` `pip install junifer[surf]` `pip install junifer[dmri]` ... etc
I don't remember seeing > On build/install time, a file `_dependencies.py` is created with an array that contains the dependencies.
>
> Soon, we will start having "conditional" dependencies in which some specific markers will require some dependencies. I'm just trying to avoid having always in mind that we need to manage dependencies in two different sites.
I don't remember seeing `_dependencies.py` but will check it.
I see what you mean. These conditional dependencies would go in the > e.g.: `pip install junifer[mri]` `pip install junifer[eeg]` `pip install junifer[surf]` `pip install junifer[dmri]` ... etc
I see what you mean. These conditional dependencies would go in the `[project.optional-dependencies]` block of the `pyproject.toml` like we have now for `dev` and `docs`. So, the "base" dependencies for example, `numpy` or `nilearn` stay the same and we can check for them. But, I agree that maintaining dependencies in two places is a bit troublesome. Let me see if I can find a way to hook into the setuptools or something similar to extract information without requiring extra I/O.
Now implemented with an improved regex search. Now implemented with an improved regex search.
|
||||
|
||||
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
||||
# 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
|
||||
I would go full environment versions here, not only dependencies.