mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-10 00:28:31 +00:00
Add import utilities for optional dependencies (#3389)
This commit is contained in:
32
src/crewai/utilities/import_utils.py
Normal file
32
src/crewai/utilities/import_utils.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
"""Import utilities for optional dependencies."""
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
|
||||||
|
class OptionalDependencyError(ImportError):
|
||||||
|
"""Exception raised when an optional dependency is not installed."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def require(name: str, *, purpose: str) -> ModuleType:
|
||||||
|
"""Import a module, raising a helpful error if it's not installed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The module name to import.
|
||||||
|
purpose: Description of what requires this dependency.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The imported module.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
OptionalDependencyError: If the module is not installed.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return importlib.import_module(name)
|
||||||
|
except ImportError as exc:
|
||||||
|
raise OptionalDependencyError(
|
||||||
|
f"{purpose} requires the optional dependency '{name}'.\n"
|
||||||
|
f"Install it with: uv add {name}"
|
||||||
|
) from exc
|
||||||
42
tests/utilities/test_import_utils.py
Normal file
42
tests/utilities/test_import_utils.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""Tests for import utilities."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from crewai.utilities.import_utils import require, OptionalDependencyError
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequire:
|
||||||
|
"""Test the require function."""
|
||||||
|
|
||||||
|
def test_require_existing_module(self):
|
||||||
|
"""Test requiring a module that exists."""
|
||||||
|
module = require("json", purpose="testing")
|
||||||
|
assert module.__name__ == "json"
|
||||||
|
|
||||||
|
def test_require_missing_module(self):
|
||||||
|
"""Test requiring a module that doesn't exist."""
|
||||||
|
with pytest.raises(OptionalDependencyError) as exc_info:
|
||||||
|
require("nonexistent_module_xyz", purpose="testing missing module")
|
||||||
|
|
||||||
|
error_msg = str(exc_info.value)
|
||||||
|
assert (
|
||||||
|
"testing missing module requires the optional dependency 'nonexistent_module_xyz'"
|
||||||
|
in error_msg
|
||||||
|
)
|
||||||
|
assert "uv add nonexistent_module_xyz" in error_msg
|
||||||
|
|
||||||
|
def test_require_with_import_error(self):
|
||||||
|
"""Test that ImportError is properly chained."""
|
||||||
|
with patch("importlib.import_module") as mock_import:
|
||||||
|
mock_import.side_effect = ImportError("Module import failed")
|
||||||
|
|
||||||
|
with pytest.raises(OptionalDependencyError) as exc_info:
|
||||||
|
require("some_module", purpose="testing error handling")
|
||||||
|
|
||||||
|
assert isinstance(exc_info.value.__cause__, ImportError)
|
||||||
|
assert str(exc_info.value.__cause__) == "Module import failed"
|
||||||
|
|
||||||
|
def test_optional_dependency_error_is_import_error(self):
|
||||||
|
"""Test that OptionalDependencyError is a subclass of ImportError."""
|
||||||
|
assert issubclass(OptionalDependencyError, ImportError)
|
||||||
Reference in New Issue
Block a user