Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/crewai-tools/src/crewai_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,28 @@
]

__version__ = "1.1.0"


def __getattr__(name: str):
"""
Catch common typos and provide helpful error messages.

This function is called when an attribute is not found in the module.
It helps users who make common typos when importing tools.
"""
if name == "PGSearchTool":
raise NotImplementedError(
f"'{name}' is currently under development and not yet available. "
f"Please check the CrewAI documentation for updates on when this tool will be released."
)

if name.endswith("tool") and not name.endswith("Tool"):
correct_name = name[:-4] + "Tool"
if correct_name in __all__ or correct_name == "PGSearchTool":
raise ImportError(
f"Cannot import name '{name}' from 'crewai_tools'. "
f"Did you mean '{correct_name}'? "
f"Note: Tool names use capital 'T' in 'Tool'."
)

raise AttributeError(f"module 'crewai_tools' has no attribute '{name}'")
54 changes: 54 additions & 0 deletions lib/crewai-tools/tests/test_import_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import pytest
import crewai_tools


def test_typo_in_tool_name_lowercase_t():
"""Test that accessing a tool with lowercase 't' in 'tool' provides a helpful error message."""
with pytest.raises(ImportError) as exc_info:
getattr(crewai_tools, "CSVSearchtool")

assert "Cannot import name 'CSVSearchtool' from 'crewai_tools'" in str(exc_info.value)
assert "Did you mean 'CSVSearchTool'?" in str(exc_info.value)
assert "Tool names use capital 'T' in 'Tool'" in str(exc_info.value)


def test_typo_pgsearchtool_lowercase_t():
"""Test that accessing PGSearchtool (with lowercase 't') provides a helpful error message."""
with pytest.raises(ImportError) as exc_info:
getattr(crewai_tools, "PGSearchtool")

assert "Cannot import name 'PGSearchtool' from 'crewai_tools'" in str(exc_info.value)
assert "Did you mean 'PGSearchTool'?" in str(exc_info.value)


def test_pgsearchtool_not_implemented():
"""Test that accessing PGSearchTool (correct spelling) shows it's not yet implemented."""
with pytest.raises(NotImplementedError) as exc_info:
getattr(crewai_tools, "PGSearchTool")

assert "'PGSearchTool' is currently under development" in str(exc_info.value)
assert "not yet available" in str(exc_info.value)


def test_nonexistent_tool():
"""Test that accessing a completely nonexistent tool gives a standard AttributeError."""
with pytest.raises(AttributeError) as exc_info:
getattr(crewai_tools, "NonExistentTool")

assert "module 'crewai_tools' has no attribute 'NonExistentTool'" in str(exc_info.value)


def test_multiple_typos():
"""Test multiple common typos to ensure they all get helpful messages."""
typos = [
("FileReadtool", "FileReadTool"),
("PDFSearchtool", "PDFSearchTool"),
("MySQLSearchtool", "MySQLSearchTool"),
]

for typo, correct in typos:
with pytest.raises(ImportError) as exc_info:
getattr(crewai_tools, typo)

assert f"Cannot import name '{typo}' from 'crewai_tools'" in str(exc_info.value)
assert f"Did you mean '{correct}'?" in str(exc_info.value)