From e182af6cfbd3cc12123423fc393e6bed4c5f84c8 Mon Sep 17 00:00:00 2001 From: Angela Liu Date: Thu, 6 Feb 2025 16:46:21 -0800 Subject: [PATCH] informative import error messages (#25) --- DEVELOPMENT.md | 13 +++++++++++++ pyproject.toml | 2 +- src/cleanlab_codex/codex_tool.py | 26 ++++++++++++++++++++++---- src/cleanlab_codex/utils/errors.py | 15 +++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 src/cleanlab_codex/utils/errors.py diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index e26968a..298566f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -95,3 +95,16 @@ Automated releases are handled by the [release workflow][release-workflow] which Testing, type checking, and formatting/linting is [checked in CI][ci]. [ci]: .github/workflows/ci.yml + +## Style guide + +### Adding integrations with external libraries + +When adding integrations with external libraries, always use a lazy import. The external dependency should not be required to use the `cleanlab-codex` library. Wrap the lazy import in a `try`/`except` block to catch the `ImportError` and raise a `MissingDependencyError` with a helpful message. See [codex_tool.py](src/cleanlab_codex/codex_tool.py) file for examples one of which is shown below: + +```python +try: + from cleanlab_codex.utils.smolagents import CodexTool as SmolagentsCodexTool +except ImportError as e: + raise MissingDependencyError("smolagents", "https://github.com/huggingface/smolagents") from e +``` diff --git a/pyproject.toml b/pyproject.toml index cb2cba3..b0da1bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,4 +101,4 @@ html = "coverage html" xml = "coverage xml" [tool.ruff.lint] -ignore = ["FA100", "UP007", "UP006"] +ignore = ["FA100", "UP007", "UP006", "EM101"] diff --git a/src/cleanlab_codex/codex_tool.py b/src/cleanlab_codex/codex_tool.py index 8c78c1b..e03051b 100644 --- a/src/cleanlab_codex/codex_tool.py +++ b/src/cleanlab_codex/codex_tool.py @@ -7,7 +7,11 @@ from typing_extensions import Annotated from cleanlab_codex.project import Project -from cleanlab_codex.utils.function import pydantic_model_from_function, required_properties_from_model +from cleanlab_codex.utils.errors import MissingDependencyError +from cleanlab_codex.utils.function import ( + pydantic_model_from_function, + required_properties_from_model, +) class CodexTool: @@ -119,7 +123,10 @@ def to_smolagents_tool(self) -> Any: Note: You must have the [`smolagents` library installed](https://github.com/huggingface/smolagents) to use this method. """ - from cleanlab_codex.utils.smolagents import CodexTool as SmolagentsCodexTool + try: + from cleanlab_codex.utils.smolagents import CodexTool as SmolagentsCodexTool + except ImportError as e: + raise MissingDependencyError("smolagents", "https://github.com/huggingface/smolagents") from e return SmolagentsCodexTool( query=self.query, @@ -133,7 +140,14 @@ def to_llamaindex_tool(self) -> Any: Note: You must have the [`llama-index` library installed](https://docs.llamaindex.ai/en/stable/getting_started/installation/) to use this method. """ - from llama_index.core.tools import FunctionTool + try: + from llama_index.core.tools import FunctionTool + + except ImportError as e: + raise MissingDependencyError( + "llama-index-core", + "https://docs.llamaindex.ai/en/stable/getting_started/installation/", + ) from e return FunctionTool.from_defaults( fn=self.query, @@ -147,7 +161,11 @@ def to_langchain_tool(self) -> Any: Note: You must have the [`langchain` library installed](https://python.langchain.com/docs/concepts/architecture/) to use this method. """ - from langchain_core.tools.structured import StructuredTool + try: + from langchain_core.tools.structured import StructuredTool + + except ImportError as e: + raise MissingDependencyError("langchain", "https://pypi.org/project/langchain/") from e return StructuredTool.from_function( func=self.query, diff --git a/src/cleanlab_codex/utils/errors.py b/src/cleanlab_codex/utils/errors.py new file mode 100644 index 0000000..ee6a45d --- /dev/null +++ b/src/cleanlab_codex/utils/errors.py @@ -0,0 +1,15 @@ +from __future__ import annotations + + +class MissingDependencyError(Exception): + """Raised when a lazy import is missing.""" + + def __init__(self, import_name: str, package_url: str | None = None) -> None: + self.import_name = import_name + self.package_url = package_url + + def __str__(self) -> str: + message = f"Failed to import {self.import_name}. Please install the package using `pip install {self.import_name}` and try again." + if self.package_url: + message += f" For more information, see {self.package_url}." + return message