# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Module with guards for optional libraries, that cannot be listed in `requirements.txt`. Provides helper constants and decorators to check if the library is available in the system. """ __all__ = ["KENLM_AVAILABLE", "K2_AVAILABLE", "TRITON_AVAILABLE", "kenlm_required", "k2_required", "triton_required"] import importlib.util from functools import wraps from nemo.core.utils.k2_utils import K2_INSTALLATION_MESSAGE def is_lib_available(name: str) -> bool: """ Checks if the library/package with `name` is available in the system NB: try/catch with importlib.import_module(name) requires importing the library, which can be slow. So, `find_spec` should be preferred """ return importlib.util.find_spec(name) is not None KENLM_AVAILABLE = is_lib_available("kenlm") KENLM_INSTALLATION_MESSAGE = "Try installing kenlm with `pip install kenlm`" TRITON_AVAILABLE = is_lib_available("triton") TRITON_INSTALLATION_MESSAGE = "Try installing triton with `pip install triton`" try: from nemo.core.utils.k2_guard import k2 as _ # noqa: F401 K2_AVAILABLE = True except (ImportError, ModuleNotFoundError): K2_AVAILABLE = False def identity_decorator(f): """Identity decorator for further using in conditional decorators""" return f def _lib_required(is_available: bool, name: str, message: str | None = None): """ Decorator factory. Returns identity decorator if lib `is_available`, otherwise returns a decorator which returns a function that raises an error when called. Such decorator can be used for conditional checks for optional libraries in functions and methods with zero computational overhead. """ if is_available: return identity_decorator # return wrapper that will raise an error when the function is called def function_stub_with_error_decorator(f): """Decorator that replaces the function and raises an error when called""" @wraps(f) def wrapper(*args, **kwargs): error_msg = f"Module {name} required for the function {f.__name__} is not found." if message: error_msg += f" {message}" raise ModuleNotFoundError(error_msg) return wrapper return function_stub_with_error_decorator kenlm_required = _lib_required(is_available=KENLM_AVAILABLE, name="kenlm", message=KENLM_INSTALLATION_MESSAGE) triton_required = _lib_required(is_available=TRITON_AVAILABLE, name="triton", message=TRITON_INSTALLATION_MESSAGE) k2_required = _lib_required(is_available=K2_AVAILABLE, name="k2", message=K2_INSTALLATION_MESSAGE)