Compare commits

...

4 Commits

Author SHA1 Message Date
Devin AI
d4480bc694 Enhance run_crew.py with improved logging, error handling, and tests
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-12 13:39:49 +00:00
Devin AI
a7d7edb983 Fix import order in test_run_crew.py to resolve linting issue
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-12 13:33:36 +00:00
Devin AI
64b8708bae Add test for run_crew.py src path addition
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-12 13:31:47 +00:00
Devin AI
92d420dcc4 Fix #2348: Add src directory to sys.path in run_crew.py to resolve ModuleNotFoundError
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-12 13:31:45 +00:00
3 changed files with 190 additions and 2 deletions

View File

@@ -1,5 +1,8 @@
import logging
import subprocess import subprocess
import sys
from enum import Enum from enum import Enum
from pathlib import Path
from typing import List, Optional from typing import List, Optional
import click import click
@@ -8,6 +11,8 @@ from packaging import version
from crewai.cli.utils import read_toml from crewai.cli.utils import read_toml
from crewai.cli.version import get_crewai_version from crewai.cli.version import get_crewai_version
logger = logging.getLogger(__name__)
class CrewType(Enum): class CrewType(Enum):
STANDARD = "standard" STANDARD = "standard"
@@ -48,12 +53,31 @@ def run_crew() -> None:
def execute_command(crew_type: CrewType) -> None: def execute_command(crew_type: CrewType) -> None:
""" """Execute the appropriate command based on crew type.
Execute the appropriate command based on crew type.
Args: Args:
crew_type: The type of crew to run crew_type: The type of crew to run
Note:
Automatically adds the 'src' directory to sys.path if it exists
to ensure proper module resolution.
""" """
# Add the 'src' directory to sys.path to ensure module imports work correctly
cwd = Path.cwd()
src_path = cwd / "src"
logger.debug(f"Current working directory: {cwd}")
if src_path.exists() and str(src_path) not in sys.path:
logger.info(f"Adding {src_path} to sys.path")
sys.path.insert(0, str(src_path))
elif not src_path.exists():
logger.warning(f"src directory not found: {src_path}")
click.secho(
f"Warning: 'src' directory not found at {src_path}. Module imports may fail.",
fg="yellow",
)
command = ["uv", "run", "kickoff" if crew_type == CrewType.FLOW else "run_crew"] command = ["uv", "run", "kickoff" if crew_type == CrewType.FLOW else "run_crew"]
try: try:
@@ -63,6 +87,7 @@ def execute_command(crew_type: CrewType) -> None:
handle_error(e, crew_type) handle_error(e, crew_type)
except Exception as e: except Exception as e:
logger.error(f"Unexpected error: {e}")
click.echo(f"An unexpected error occurred: {e}", err=True) click.echo(f"An unexpected error occurred: {e}", err=True)

View File

@@ -0,0 +1,48 @@
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch
import pytest
from crewai.cli.run_crew import CrewType, execute_command
def test_execute_command_adds_src_to_path():
"""
Test that execute_command correctly modifies sys.path.
Ensures:
1. src directory is added to sys.path when it exists.
2. Original sys.path is preserved for other entries.
3. Command execution proceeds correctly.
"""
# Create a temporary directory with a src subdirectory
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
src_path = temp_path / "src"
src_path.mkdir()
# Change to the temporary directory
original_dir = os.getcwd()
os.chdir(temp_dir)
try:
# Save the original sys.path
original_sys_path = sys.path.copy()
# Mock subprocess.run to avoid actually running the command
with patch("subprocess.run") as mock_run:
# Call execute_command
execute_command(CrewType.STANDARD)
# Check that src_path was added to sys.path
assert str(src_path) in sys.path
# Check that the command was called
mock_run.assert_called_once()
finally:
# Restore the original directory and sys.path
os.chdir(original_dir)
sys.path = original_sys_path

View File

@@ -0,0 +1,115 @@
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch
import pytest
from crewai.cli.run_crew import CrewType, execute_command
@pytest.mark.parametrize("crew_type", [CrewType.STANDARD, CrewType.FLOW])
def test_execute_command_with_different_crew_types(crew_type):
"""
Test that execute_command works with different crew types.
Verifies that the correct command is executed based on the crew type.
"""
# Create a temporary directory with a src subdirectory
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
src_path = temp_path / "src"
src_path.mkdir()
# Change to the temporary directory
original_dir = os.getcwd()
os.chdir(temp_dir)
try:
# Save the original sys.path
original_sys_path = sys.path.copy()
# Mock subprocess.run to avoid actually running the command
with patch("subprocess.run") as mock_run:
# Call execute_command
execute_command(crew_type)
# Check that the correct command was called based on crew_type
expected_command = ["uv", "run", "kickoff" if crew_type == CrewType.FLOW else "run_crew"]
mock_run.assert_called_once_with(expected_command, capture_output=False, text=True, check=True)
finally:
# Restore the original directory and sys.path
os.chdir(original_dir)
sys.path = original_sys_path
def test_execute_command_handles_missing_src_directory():
"""
Test that execute_command handles missing src directory gracefully.
Verifies that the command executes even when the src directory doesn't exist.
"""
# Create a temporary directory without a src subdirectory
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Change to the temporary directory
original_dir = os.getcwd()
os.chdir(temp_dir)
try:
# Save the original sys.path
original_sys_path = sys.path.copy()
# Mock subprocess.run to avoid actually running the command
with patch("subprocess.run") as mock_run:
# Call execute_command
execute_command(CrewType.STANDARD)
# Check that sys.path wasn't modified (since src doesn't exist)
assert sys.path == original_sys_path
# Check that the command was still called
mock_run.assert_called_once_with(["uv", "run", "run_crew"], capture_output=False, text=True, check=True)
finally:
# Restore the original directory and sys.path
os.chdir(original_dir)
sys.path = original_sys_path
def test_execute_command_handles_subprocess_error():
"""
Test that execute_command properly handles subprocess errors.
Verifies that exceptions from subprocess.run are propagated correctly.
"""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
src_path = temp_path / "src"
src_path.mkdir()
# Create a dummy pyproject.toml file
with open(temp_path / "pyproject.toml", "w") as f:
f.write("[project]\nname = \"test\"\n")
# Change to the temporary directory
original_dir = os.getcwd()
os.chdir(temp_dir)
try:
# Save the original sys.path
original_sys_path = sys.path.copy()
# Mock subprocess.run to raise an exception
with patch("subprocess.run", side_effect=subprocess.CalledProcessError(1, [])):
# Call execute_command and verify it handles the error
execute_command(CrewType.STANDARD)
# Verify that sys.path was modified correctly
assert str(src_path) in sys.path
finally:
# Restore the original directory and sys.path
os.chdir(original_dir)
sys.path = original_sys_path