From f6399208fb46bfb41e3a985990e66419e92880b2 Mon Sep 17 00:00:00 2001 From: Lucas Gomide Date: Fri, 16 May 2025 13:09:53 -0300 Subject: [PATCH] refactor: renaming available_tools_classes by available_exports --- src/crewai/cli/plus_api.py | 4 ++-- src/crewai/cli/tools/main.py | 10 ++++----- src/crewai/cli/utils.py | 11 +++++----- tests/cli/test_plus_api.py | 4 ++-- tests/cli/test_utils.py | 41 ++++++++++++++++++------------------ tests/cli/tools/test_main.py | 20 +++++++++--------- 6 files changed, 46 insertions(+), 44 deletions(-) diff --git a/src/crewai/cli/plus_api.py b/src/crewai/cli/plus_api.py index 352ef0862..6961f886e 100644 --- a/src/crewai/cli/plus_api.py +++ b/src/crewai/cli/plus_api.py @@ -48,7 +48,7 @@ class PlusAPI: version: str, description: Optional[str], encoded_file: str, - available_tool_classes: Optional[List[str]] = None, + available_exports: Optional[List[str]] = None, ): params = { "handle": handle, @@ -56,7 +56,7 @@ class PlusAPI: "version": version, "file": encoded_file, "description": description, - "available_tool_classes": available_tool_classes, + "available_exports": available_exports, } return self._make_request("POST", f"{self.TOOLS_RESOURCE}", json=params) diff --git a/src/crewai/cli/tools/main.py b/src/crewai/cli/tools/main.py index 6e7f8dceb..2cb4821c3 100644 --- a/src/crewai/cli/tools/main.py +++ b/src/crewai/cli/tools/main.py @@ -11,7 +11,7 @@ from crewai.cli import git from crewai.cli.command import BaseCommand, PlusAPIMixin from crewai.cli.config import Settings from crewai.cli.utils import ( - extract_available_tools, + extract_available_exports, get_project_description, get_project_name, get_project_version, @@ -84,11 +84,11 @@ class ToolCommand(BaseCommand, PlusAPIMixin): encoded_tarball = None console.print("[bold blue]Discovering tools from your project...[/bold blue]") - available_tools = extract_available_tools() + available_exports = extract_available_exports() - if available_tools: + if available_exports: console.print( - f"[green]Found these tools to publish: {', '.join(available_tools)}[/green]" + f"[green]Found these tools to publish: {', '.join(available_exports)}[/green]" ) with tempfile.TemporaryDirectory() as temp_build_dir: @@ -121,7 +121,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin): version=project_version, description=project_description, encoded_file=f"data:application/x-gzip;base64,{encoded_tarball}", - available_tools=available_tools, + available_exports=available_exports, ) self._validate_response(publish_response) diff --git a/src/crewai/cli/utils.py b/src/crewai/cli/utils.py index 0a665b5d0..7ff2cf7bd 100644 --- a/src/crewai/cli/utils.py +++ b/src/crewai/cli/utils.py @@ -355,7 +355,7 @@ def is_valid_tool(obj): return isinstance(obj, Tool) -def extract_available_tools(dir_path: str = "src"): +def extract_available_exports(dir_path: str = "src"): """ Extract available tool classes from the project's __init__.py files. Only includes classes that inherit from BaseTool or functions decorated with @tool. @@ -365,17 +365,17 @@ def extract_available_tools(dir_path: str = "src"): """ try: init_files = Path(dir_path).glob("**/__init__.py") - available_tools = [] + available_exports = [] for init_file in init_files: tools = _load_tools_from_init(init_file) - available_tools.extend(tools) + available_exports.extend(tools) - if not available_tools: + if not available_exports: _print_no_tools_warning() raise SystemExit(1) - return available_tools + return available_exports except Exception as e: console.print(f"[red]Error: Could not extract tool classes: {str(e)}[/red]") @@ -406,6 +406,7 @@ def _load_tools_from_init(init_file: Path) -> list: ) raise SystemExit(1) + # TODO: Security check: prevent any inject malicious code, or stuff like that return [ name for name in module.__all__ diff --git a/tests/cli/test_plus_api.py b/tests/cli/test_plus_api.py index 3fbe688ad..da26ba35f 100644 --- a/tests/cli/test_plus_api.py +++ b/tests/cli/test_plus_api.py @@ -61,7 +61,7 @@ class TestPlusAPI(unittest.TestCase): "version": version, "file": encoded_file, "description": description, - "available_tool_classes": None, + "available_exports": None, } mock_make_request.assert_called_once_with( "POST", "/crewai_plus/api/v1/tools", json=params @@ -88,7 +88,7 @@ class TestPlusAPI(unittest.TestCase): "version": version, "file": encoded_file, "description": description, - "available_tool_classes": None, + "available_exports": None, } mock_make_request.assert_called_once_with( "POST", "/crewai_plus/api/v1/tools", json=params diff --git a/tests/cli/test_utils.py b/tests/cli/test_utils.py index 988310d59..887a080b3 100644 --- a/tests/cli/test_utils.py +++ b/tests/cli/test_utils.py @@ -2,6 +2,7 @@ import os import shutil import tempfile from pathlib import Path + import pytest from crewai.cli import utils @@ -113,45 +114,45 @@ def create_init_file(directory, content): return create_file(directory / "__init__.py", content) -def test_extract_available_tools_empty_project(temp_project_dir, capsys): +def test_extract_available_exports_empty_project(temp_project_dir, capsys): with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "No valid tools were exposed in your __init__.py file" in captured.out -def test_extract_available_tools_no_init_file(temp_project_dir, capsys): +def test_extract_available_exports_no_init_file(temp_project_dir, capsys): (temp_project_dir / "some_file.py").write_text("print('hello')") with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "No valid tools were exposed in your __init__.py file" in captured.out -def test_extract_available_tools_empty_init_file(temp_project_dir, capsys): +def test_extract_available_exports_empty_init_file(temp_project_dir, capsys): create_init_file(temp_project_dir, "") with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "Warning: No __all__ defined in" in captured.out -def test_extract_available_tools_no_all_variable(temp_project_dir, capsys): +def test_extract_available_exports_no_all_variable(temp_project_dir, capsys): create_init_file( temp_project_dir, "from crewai.tools import BaseTool\n\nclass MyTool(BaseTool):\n pass", ) with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "Warning: No __all__ defined in" in captured.out -def test_extract_available_tools_valid_base_tool_class(temp_project_dir): +def test_extract_available_exports_valid_base_tool_class(temp_project_dir): create_init_file( temp_project_dir, """from crewai.tools import BaseTool @@ -163,11 +164,11 @@ class MyTool(BaseTool): __all__ = ['MyTool'] """, ) - tools = utils.extract_available_tools(dir_path=temp_project_dir) + tools = utils.extract_available_exports(dir_path=temp_project_dir) assert ["MyTool"] == tools -def test_extract_available_tools_valid_tool_decorator(temp_project_dir): +def test_extract_available_exports_valid_tool_decorator(temp_project_dir): create_init_file( temp_project_dir, """from crewai.tools import tool @@ -180,11 +181,11 @@ def my_tool_function(text: str) -> str: __all__ = ['my_tool_function'] """, ) - tools = utils.extract_available_tools(dir_path=temp_project_dir) + tools = utils.extract_available_exports(dir_path=temp_project_dir) assert ["my_tool_function"] == tools -def test_extract_available_tools_multiple_valid_tools(temp_project_dir): +def test_extract_available_exports_multiple_valid_tools(temp_project_dir): create_init_file( temp_project_dir, """from crewai.tools import BaseTool, tool @@ -201,11 +202,11 @@ def my_tool_function(text: str) -> str: __all__ = ['MyTool', 'my_tool_function'] """, ) - tools = utils.extract_available_tools(dir_path=temp_project_dir) + tools = utils.extract_available_exports(dir_path=temp_project_dir) assert ["MyTool", "my_tool_function"] == tools -def test_extract_available_tools_with_invalid_tool_decorator(temp_project_dir): +def test_extract_available_exports_with_invalid_tool_decorator(temp_project_dir): create_init_file( temp_project_dir, """from crewai.tools import BaseTool @@ -220,11 +221,11 @@ def not_a_tool(): __all__ = ['MyTool', 'not_a_tool'] """, ) - tools = utils.extract_available_tools(dir_path=temp_project_dir) + tools = utils.extract_available_exports(dir_path=temp_project_dir) assert ["MyTool"] == tools -def test_extract_available_tools_import_error(temp_project_dir, capsys): +def test_extract_available_exports_import_error(temp_project_dir, capsys): create_init_file( temp_project_dir, """from nonexistent_module import something @@ -236,13 +237,13 @@ __all__ = ['MyTool'] """, ) with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "nonexistent_module" in captured.out -def test_extract_available_tools_syntax_error(temp_project_dir, capsys): +def test_extract_available_exports_syntax_error(temp_project_dir, capsys): create_init_file( temp_project_dir, """from crewai.tools import BaseTool @@ -256,7 +257,7 @@ __all__ = ['MyTool'] """, ) with pytest.raises(SystemExit): - utils.extract_available_tools(dir_path=temp_project_dir) + utils.extract_available_exports(dir_path=temp_project_dir) captured = capsys.readouterr() assert "was never closed" in captured.out diff --git a/tests/cli/tools/test_main.py b/tests/cli/tools/test_main.py index 218b9647a..f9fe871f3 100644 --- a/tests/cli/tools/test_main.py +++ b/tests/cli/tools/test_main.py @@ -135,9 +135,9 @@ def test_publish_when_not_in_sync(mock_is_synced, capsys, tool_command): ) @patch("crewai.cli.plus_api.PlusAPI.publish_tool") @patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=False) -@patch("crewai.cli.tools.main.extract_available_tools", return_value=["SampleTool"]) +@patch("crewai.cli.tools.main.available_exports", return_value=["SampleTool"]) def test_publish_when_not_in_sync_and_force( - mock_extract_available_tools, + mock_available_exports, mock_is_synced, mock_publish, mock_open, @@ -170,7 +170,7 @@ def test_publish_when_not_in_sync_and_force( version="1.0.0", description="A sample tool", encoded_file=unittest.mock.ANY, - available_tools=["SampleTool"], + available_exports=["SampleTool"], ) @@ -186,9 +186,9 @@ def test_publish_when_not_in_sync_and_force( ) @patch("crewai.cli.plus_api.PlusAPI.publish_tool") @patch("crewai.cli.tools.main.git.Repository.is_synced", return_value=True) -@patch("crewai.cli.tools.main.extract_available_tools", return_value=["SampleTool"]) +@patch("crewai.cli.tools.main.available_exports", return_value=["SampleTool"]) def test_publish_success( - mock_extract_available_tools, + mock_available_exports, mock_is_synced, mock_publish, mock_open, @@ -221,7 +221,7 @@ def test_publish_success( version="1.0.0", description="A sample tool", encoded_file=unittest.mock.ANY, - available_tools=["SampleTool"], + available_exports=["SampleTool"], ) @@ -236,9 +236,9 @@ def test_publish_success( read_data=b"sample tarball content", ) @patch("crewai.cli.plus_api.PlusAPI.publish_tool") -@patch("crewai.cli.tools.main.extract_available_tools", return_value=["SampleTool"]) +@patch("crewai.cli.tools.main.available_exports", return_value=["SampleTool"]) def test_publish_failure( - mock_extract_available_tools, + mock_available_exports, mock_publish, mock_open, mock_listdir, @@ -274,9 +274,9 @@ def test_publish_failure( read_data=b"sample tarball content", ) @patch("crewai.cli.plus_api.PlusAPI.publish_tool") -@patch("crewai.cli.tools.main.extract_available_tools", return_value=["SampleTool"]) +@patch("crewai.cli.tools.main.available_exports", return_value=["SampleTool"]) def test_publish_api_error( - mock_extract_available_tools, + mock_available_exports, mock_publish, mock_open, mock_listdir,