Diff of /dosma/utils/env.py [000000] .. [030aeb]

Switch to side-by-side view

--- a
+++ b/dosma/utils/env.py
@@ -0,0 +1,150 @@
+import logging
+import os
+from importlib import util
+
+_SUPPORTED_PACKAGES = {}
+
+_FILE_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
+
+__all__ = ["debug", "get_version", "package_available"]
+
+
+def package_available(name: str):
+    """Returns if package is available.
+
+    Args:
+        name (str): Name of the package.
+
+    Returns:
+        bool: Whether module exists in environment.
+    """
+    global _SUPPORTED_PACKAGES
+    if name not in _SUPPORTED_PACKAGES:
+        _SUPPORTED_PACKAGES[name] = util.find_spec(name) is not None
+    return _SUPPORTED_PACKAGES[name]
+
+
+def get_version(package_or_name) -> str:
+    """Returns package version.
+
+    Args:
+        package_or_name (``module`` or ``str``): Module or name of module.
+            This package must have the version accessible through ``<module>.__version__``.
+
+    Returns:
+        str: The package version.
+
+    Examples:
+        >>> get_version("numpy")
+        "1.20.0"
+    """
+    if isinstance(package_or_name, str):
+        if not package_available(package_or_name):
+            raise ValueError(f"Package {package_or_name} not available")
+        spec = util.find_spec(package_or_name)
+        package_or_name = util.module_from_spec(spec)
+        spec.loader.exec_module(package_or_name)
+    version = package_or_name.__version__
+    return version
+
+
+def debug(value: bool = None) -> bool:
+    """Return (and optionally set) debug mode.
+
+    Args:
+        value (bool, optional): If specified, sets the debug status.
+            If not specified, debug mode is not set, only returned.
+
+    Returns:
+        bool: If ``True``, debug mode is active.
+
+    Raises:
+        ValueError: If ``value`` is not a supported value.
+
+    Note:
+        Changing the debug state changes the stream handler logging level
+        for the default dosma logger. If debug state is turned off, logging
+        level is set to ``logging.INFO``. If debug state is turned on,
+        logging level is set to ``logging.DEBUG``.
+
+    Examples:
+        >>> debug()  # get debug status, defaults to False
+        False
+        >>> debug(True)  # turn on debug mode
+        True
+        >>> debug()  # get debug status
+        True
+    """
+
+    def _is_debug():
+        return os.environ.get("DOSMA_DEBUG", "") in ["True", "true"]
+
+    def _toggle_debug(_old_value, _new_value):
+        from dosma.defaults import preferences
+
+        # TODO: Toggle dosma logging to debug mode.
+        if _old_value == _new_value:
+            return
+
+        _dm_logger = logging.getLogger("dosma")
+        if _new_value:
+            preferences.set("nipype", value="stream", prefix="logging")
+            _dm_logger.setLevel(logging.DEBUG)
+            for h in _dm_logger.handlers:
+                h.setLevel(logging.DEBUG)
+        else:
+            preferences.set("nipype", value="file_stderr", prefix="logging")
+            _dm_logger.setLevel(logging.DEBUG)  # the root logger should always log at DEBUG level
+            for h in _dm_logger.handlers:
+                if isinstance(h, logging.StreamHandler):
+                    h.setLevel(logging.INFO)
+
+    if value is not None:
+        old_value = _is_debug()
+        if isinstance(value, bool):
+            os.environ["DOSMA_DEBUG"] = str(value)
+        elif isinstance(value, str) and value.lower() in ("true", "false", ""):
+            os.environ["DOSMA_DEBUG"] = value
+        else:
+            raise ValueError(f"Unknown value for debug: '{value}'")
+
+        _toggle_debug(old_value, _is_debug())
+
+    return _is_debug()
+
+
+def sitk_available():
+    return package_available("SimpleITK")
+
+
+def cupy_available():
+    if "cupy" not in _SUPPORTED_PACKAGES:
+        try:
+            import cupy  # noqa
+        except ImportError:
+            _SUPPORTED_PACKAGES["cupy"] = False
+    return package_available("cupy")
+
+
+def sigpy_available():
+    return package_available("sigpy")
+
+
+def torch_available():
+    return package_available("torch")
+
+
+def resources_dir() -> str:
+    return os.path.abspath(os.path.join(_FILE_DIRECTORY, "../resources"))
+
+
+def output_dir() -> str:
+    return os.path.abspath(os.path.join(_FILE_DIRECTORY, "../../.dosma"))
+
+
+def temp_dir() -> str:
+    return os.path.join(output_dir(), "temp")
+
+
+def log_file_path() -> str:
+    return os.path.join(output_dir(), "dosma.log")