Source code for colour.utilities.requirements

"""
Requirements Utilities
======================

Define the requirements utilities objects.
"""

from __future__ import annotations

import functools
import shutil
import subprocess

from colour.hints import (
    Any,
    Callable,
    Literal,
)
from colour.utilities import CanonicalMapping

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = [
    "is_ctlrender_installed",
    "is_matplotlib_installed",
    "is_networkx_installed",
    "is_opencolorio_installed",
    "is_openimageio_installed",
    "is_pandas_installed",
    "is_pydot_installed",
    "is_tqdm_installed",
    "is_trimesh_installed",
    "is_xxhash_installed",
    "REQUIREMENTS_TO_CALLABLE",
    "required",
]


[docs] def is_ctlrender_installed(raise_exception: bool = False) -> bool: """ Return whether *ctlrender* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *ctlrender* is unavailable. Returns ------- :class:`bool` Whether *ctlrender* is installed. Raises ------ :class:`ImportError` If *ctlrender* is not installed. """ try: # pragma: no cover stdout = subprocess.run( ["ctlrender", "-help"], # noqa: S603, S607 capture_output=True, check=False, ).stdout.decode("utf-8") if "transforms an image using one or more CTL scripts" not in stdout: raise FileNotFoundError # noqa: TRY301 return True except FileNotFoundError as error: # pragma: no cover if raise_exception: raise FileNotFoundError( '"ctlrender" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_matplotlib_installed(raise_exception: bool = False) -> bool: """ Return whether *Matplotlib* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *Matplotlib* is unavailable. Returns ------- :class:`bool` Whether *Matplotlib* is installed. Raises ------ :class:`ImportError` If *Matplotlib* is not installed. """ try: # pragma: no cover import matplotlib as mpl # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"Matplotlib" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_networkx_installed(raise_exception: bool = False) -> bool: """ Return whether *NetworkX* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *NetworkX* is unavailable. Returns ------- :class:`bool` Whether *NetworkX* is installed. Raises ------ :class:`ImportError` If *NetworkX* is not installed. """ try: # pragma: no cover import networkx as nx # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"NetworkX" related API features, e.g., the automatic colour ' f'conversion graph, are not available: "{error}".\nPlease refer ' "to the installation guide for more information: " "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_opencolorio_installed(raise_exception: bool = False) -> bool: """ Return whether *OpenColorIO* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *OpenColorIO* is unavailable. Returns ------- :class:`bool` Whether *OpenColorIO* is installed. Raises ------ :class:`ImportError` If *OpenColorIO* is not installed. """ try: # pragma: no cover import PyOpenColorIO # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"OpenColorIO" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_openimageio_installed(raise_exception: bool = False) -> bool: """ Return whether *OpenImageIO* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *OpenImageIO* is unavailable. Returns ------- :class:`bool` Whether *OpenImageIO* is installed. Raises ------ :class:`ImportError` If *OpenImageIO* is not installed. """ try: # pragma: no cover import OpenImageIO # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"OpenImageIO" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_pandas_installed(raise_exception: bool = False) -> bool: """ Return whether *Pandas* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *Pandas* is unavailable. Returns ------- :class:`bool` Whether *Pandas* is installed. Raises ------ :class:`ImportError` If *Pandas* is not installed. """ try: # pragma: no cover import pandas # noqa: F401, ICN001 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( f'"Pandas" related API features are not available: "{error}".\n' "See the installation guide for more information: " "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_pydot_installed(raise_exception: bool = False) -> bool: """ Return whether *Pydot* is installed and available. The presence of *Graphviz* will also be tested. Parameters ---------- raise_exception Whether to raise an exception if *Pydot* is unavailable. Returns ------- :class:`bool` Whether *Pydot* is installed. Raises ------ :class:`ImportError` If *Pydot* is not installed. """ try: # pragma: no cover import pydot # noqa: F401 except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"Pydot" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error if shutil.which("fdp") is not None: return True else: if raise_exception: raise RuntimeError( '"Graphviz" is not installed, "Pydot" related API features ' "are not available!" "\nSee the installation guide for more information: " "https://www.colour-science.org/installation-guide/" ) return False
[docs] def is_tqdm_installed(raise_exception: bool = False) -> bool: """ Return whether *tqdm* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *tqdm* is unavailable. Returns ------- :class:`bool` Whether *tqdm* is installed. Raises ------ :class:`ImportError` If *tqdm* is not installed. """ try: # pragma: no cover import tqdm # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( f'"tqdm" related API features are not available: "{error}".\n' "See the installation guide for more information: " "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_trimesh_installed(raise_exception: bool = False) -> bool: """ Return whether *Trimesh* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *Trimesh* is unavailable. Returns ------- :class:`bool` Whether *Trimesh* is installed. Raises ------ :class:`ImportError` If *Trimesh* is not installed. """ try: # pragma: no cover import trimesh # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"Trimesh" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
[docs] def is_xxhash_installed(raise_exception: bool = False) -> bool: """ Return whether *xxhash* is installed and available. Parameters ---------- raise_exception Whether to raise an exception if *xxhash* is unavailable. Returns ------- :class:`bool` Whether *xxhash* is installed. Raises ------ :class:`ImportError` If *xxhash* is not installed. """ try: # pragma: no cover import xxhash # noqa: F401 return True except ImportError as error: # pragma: no cover if raise_exception: raise ImportError( '"xxhash" related API features are not available: ' f'"{error}".\nSee the installation guide for more information: ' "https://www.colour-science.org/installation-guide/" ) from error return False
REQUIREMENTS_TO_CALLABLE: CanonicalMapping = CanonicalMapping( { "ctlrender": is_ctlrender_installed, "Matplotlib": is_matplotlib_installed, "NetworkX": is_networkx_installed, "OpenColorIO": is_opencolorio_installed, "OpenImageIO": is_openimageio_installed, "Pandas": is_pandas_installed, "Pydot": is_pydot_installed, "tqdm": is_tqdm_installed, "trimesh": is_trimesh_installed, "xxhash": is_xxhash_installed, } ) """ Mapping of requirements to their respective callables. """
[docs] def required( *requirements: Literal[ "ctlrender", "Matplotlib", "NetworkX", "OpenColorIO", "OpenImageIO", "Pandas", "Pydot", "tqdm", "trimesh", "xxhash", ], ) -> Callable: """ Decorate a function to check whether various ancillary package requirements are satisfied. Other Parameters ---------------- requirements Requirements to check whether they are satisfied. Returns ------- Callable """ def wrapper(function: Callable) -> Callable: """Wrap given function wrapper.""" @functools.wraps(function) def wrapped(*args: Any, **kwargs: Any) -> Any: """Wrap given function.""" for requirement in requirements: REQUIREMENTS_TO_CALLABLE[requirement](raise_exception=True) return function(*args, **kwargs) return wrapped return wrapper