Report missing optional dependencies when a model is initialized.
A few imodels estimators rely on packages that are not installed by default
(the optional extra in pyproject.toml). Importing those packages lazily
keeps import imodels working without them, but the failure then surfaces
deep inside fit as an opaque ImportError – or, worse, as a silent
fallback to a different model. The helpers here move the message up to
__init__, where it can name the model, the missing package, and the
command that installs it.
Expand source code
"""Report missing optional dependencies when a model is initialized.
A few imodels estimators rely on packages that are not installed by default
(the ``optional`` extra in pyproject.toml). Importing those packages lazily
keeps ``import imodels`` working without them, but the failure then surfaces
deep inside ``fit`` as an opaque ImportError -- or, worse, as a silent
fallback to a different model. The helpers here move the message up to
``__init__``, where it can name the model, the missing package, and the
command that installs it.
"""
import warnings
from importlib.util import find_spec
__all__ = ['is_installed', 'require_optional_dependency',
'warn_optional_dependency']
def is_installed(package: str) -> bool:
"""Whether `package` can be imported, without importing it."""
try:
return find_spec(package) is not None
except (ImportError, ValueError):
# a parent package is missing, or the module is in a broken state
return False
def _install_hint(package: str) -> str:
return (f"Install it with `pip install {package}` "
f"(or `pip install imodels[optional]` for all optional deps).")
def require_optional_dependency(package: str, model_name: str, purpose: str = ''):
"""Raise ImportError if `package` is missing.
For models that cannot run at all without the dependency.
Parameters
----------
package: str
name of the package to import, e.g. 'interpret'
model_name: str
name of the model requiring it, used in the message
purpose: str
optional clause explaining what the package is used for
"""
if is_installed(package):
return
because = f' ({purpose})' if purpose else ''
raise ImportError(f"{model_name} requires the optional dependency "
f"'{package}'{because}, which is not installed. "
f"{_install_hint(package)}")
def warn_optional_dependency(package: str, model_name: str, fallback: str,
purpose: str = ''):
"""Warn if `package` is missing.
For models that still work without the dependency, but behave differently.
Parameters
----------
package: str
name of the package to import, e.g. 'cvxpy'
model_name: str
name of the model that would use it, used in the message
fallback: str
what the model does instead, e.g. 'rounding the coefficients of a Lasso fit'
purpose: str
optional clause explaining what the package is used for
"""
if is_installed(package):
return
because = f' ({purpose})' if purpose else ''
warnings.warn(f"{model_name} uses the optional dependency "
f"'{package}'{because}, which is not installed, so it will "
f"fall back to {fallback}. {_install_hint(package)}")
Functions
def is_installed(package: str) ‑> bool-
Whether
packagecan be imported, without importing it.Expand source code
def is_installed(package: str) -> bool: """Whether `package` can be imported, without importing it.""" try: return find_spec(package) is not None except (ImportError, ValueError): # a parent package is missing, or the module is in a broken state return False def require_optional_dependency(package: str, model_name: str, purpose: str = '')-
Raise ImportError if
packageis missing.For models that cannot run at all without the dependency.
Parameters
package:str- name of the package to import, e.g. 'interpret'
model_name:str- name of the model requiring it, used in the message
purpose:str- optional clause explaining what the package is used for
Expand source code
def require_optional_dependency(package: str, model_name: str, purpose: str = ''): """Raise ImportError if `package` is missing. For models that cannot run at all without the dependency. Parameters ---------- package: str name of the package to import, e.g. 'interpret' model_name: str name of the model requiring it, used in the message purpose: str optional clause explaining what the package is used for """ if is_installed(package): return because = f' ({purpose})' if purpose else '' raise ImportError(f"{model_name} requires the optional dependency " f"'{package}'{because}, which is not installed. " f"{_install_hint(package)}") def warn_optional_dependency(package: str, model_name: str, fallback: str, purpose: str = '')-
Warn if
packageis missing.For models that still work without the dependency, but behave differently.
Parameters
package:str- name of the package to import, e.g. 'cvxpy'
model_name:str- name of the model that would use it, used in the message
fallback:str- what the model does instead, e.g. 'rounding the coefficients of a Lasso fit'
purpose:str- optional clause explaining what the package is used for
Expand source code
def warn_optional_dependency(package: str, model_name: str, fallback: str, purpose: str = ''): """Warn if `package` is missing. For models that still work without the dependency, but behave differently. Parameters ---------- package: str name of the package to import, e.g. 'cvxpy' model_name: str name of the model that would use it, used in the message fallback: str what the model does instead, e.g. 'rounding the coefficients of a Lasso fit' purpose: str optional clause explaining what the package is used for """ if is_installed(package): return because = f' ({purpose})' if purpose else '' warnings.warn(f"{model_name} uses the optional dependency " f"'{package}'{because}, which is not installed, so it will " f"fall back to {fallback}. {_install_hint(package)}")