Compare commits

...

2 Commits

Author SHA1 Message Date
Devin AI
529bdbdd83 Address review feedback: Improve UTC compatibility implementation
- Enhance datetime_compat.py documentation
- Add edge case tests for UTC timezone handling
- Update mock patching in tests
- Fix utcnow usage in sqlite.py

Co-Authored-By: Joe Moura <joao@crewai.com>
2025-02-20 02:00:38 +00:00
Devin AI
c6ed4eaaf6 Fix Python 3.10 compatibility: Replace datetime.UTC with timezone.utc
- Created datetime_compat module to provide UTC constant using timezone.utc
- Updated all direct UTC imports to use compatibility module
- Added tests to verify UTC timezone compatibility
- Fixes #2171

Co-Authored-By: Joe Moura <joao@crewai.com>
2025-02-20 01:50:08 +00:00
8 changed files with 77 additions and 11 deletions

View File

@@ -11,6 +11,7 @@ from typing import Any, Dict, Optional, Union
from pydantic import BaseModel
from crewai.flow.persistence.base import FlowPersistence
from crewai.utilities.datetime_compat import UTC
class SQLiteFlowPersistence(FlowPersistence):
@@ -95,7 +96,7 @@ class SQLiteFlowPersistence(FlowPersistence):
""", (
flow_uuid,
method_name,
datetime.utcnow().isoformat(),
datetime.now(UTC).isoformat(),
json.dumps(state_dict),
))

View File

@@ -1,8 +1,7 @@
import ast
import datetime
import json
import time
from datetime import UTC
from datetime import datetime
from difflib import SequenceMatcher
from json import JSONDecodeError
from textwrap import dedent
@@ -18,6 +17,7 @@ from crewai.tools import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
from crewai.utilities import I18N, Converter, ConverterError, Printer
from crewai.utilities.datetime_compat import UTC
from crewai.utilities.events.crewai_event_bus import crewai_event_bus
from crewai.utilities.events.tool_usage_events import (
ToolSelectionErrorEvent,
@@ -158,7 +158,7 @@ class ToolUsage:
self.task.increment_tools_errors()
started_at = time.time()
started_at_trace = datetime.datetime.now(UTC)
started_at_trace = datetime.now(UTC)
from_cache = False
result = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
@@ -506,8 +506,8 @@ class ToolUsage:
event_data = self._prepare_event_data(tool, tool_calling)
event_data.update(
{
"started_at": datetime.datetime.fromtimestamp(started_at),
"finished_at": datetime.datetime.fromtimestamp(finished_at),
"started_at": datetime.fromtimestamp(started_at),
"finished_at": datetime.fromtimestamp(finished_at),
"from_cache": from_cache,
}
)

View File

@@ -1,6 +1,6 @@
import inspect
import os
from datetime import UTC, datetime
from datetime import datetime
from functools import wraps
from typing import Any, Awaitable, Callable, Dict, List, Optional
from uuid import uuid4
@@ -14,6 +14,7 @@ from crewai.traces.models import (
LLMResponse,
ToolCall,
)
from crewai.utilities.datetime_compat import UTC
class UnifiedTraceController:

View File

@@ -1,4 +1,5 @@
from .converter import Converter, ConverterError
from .datetime_compat import UTC
from .file_handler import FileHandler
from .i18n import I18N
from .internal_instructor import InternalInstructor
@@ -22,6 +23,7 @@ __all__ = [
"Printer",
"Prompts",
"RPMController",
"UTC",
"YamlParser",
"LLMContextLengthExceededException",
"EmbeddingConfigurator",

View File

@@ -0,0 +1,23 @@
"""Compatibility module for datetime functionality across Python versions.
This module provides timezone constants that work consistently across different
Python versions, particularly focusing on maintaining compatibility between
Python 3.10 and newer versions.
Notes:
- In Python 3.10, datetime.UTC is not available, so we use timezone.utc
- In Python 3.11+, this provides equivalent functionality to datetime.UTC
- This implementation maintains consistent behavior across versions for
timezone-aware datetime operations
- No known limitations or edge cases between versions
- Safe to use with DST transitions and leap years
- Maintains exact timezone offset (always UTC+00:00)
Example:
>>> from datetime import datetime
>>> from crewai.utilities.datetime_compat import UTC
>>> dt = datetime.now(UTC) # Creates timezone-aware datetime with UTC
"""
from datetime import timezone
UTC = timezone.utc # Equivalent to datetime.UTC (Python 3.11+)

View File

@@ -1,7 +1,7 @@
"""Test Agent creation and execution basic functionality."""
import os
from datetime import UTC, datetime, timezone
from datetime import datetime, timezone
from unittest import mock
from unittest.mock import patch
@@ -18,6 +18,7 @@ from crewai.tools import tool
from crewai.tools.tool_calling import InstructorToolCalling
from crewai.tools.tool_usage import ToolUsage
from crewai.utilities import RPMController
from crewai.utilities.datetime_compat import UTC
from crewai.utilities.events import crewai_event_bus
from crewai.utilities.events.tool_usage_events import ToolUsageFinishedEvent
@@ -916,9 +917,10 @@ def test_tool_result_as_answer_is_the_final_answer_for_the_agent():
@pytest.mark.vcr(filter_headers=["authorization"])
def test_tool_usage_information_is_appended_to_agent():
from datetime import UTC, datetime
from datetime import datetime, timezone
from crewai.tools import BaseTool
from crewai.utilities.datetime_compat import UTC
class MyCustomTool(BaseTool):
name: str = "Decide Greetings"
@@ -928,8 +930,9 @@ def test_tool_usage_information_is_appended_to_agent():
return "Howdy!"
fixed_datetime = datetime(2025, 2, 10, 12, 0, 0, tzinfo=UTC)
with patch("datetime.datetime") as mock_datetime:
with patch("crewai.tools.tool_usage.datetime", autospec=True) as mock_datetime:
mock_datetime.now.return_value = fixed_datetime
mock_datetime.fromtimestamp = datetime.fromtimestamp
mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw)
agent1 = Agent(

View File

@@ -1,5 +1,5 @@
import os
from datetime import UTC, datetime
from datetime import datetime
from unittest.mock import MagicMock, patch
from uuid import UUID
@@ -21,6 +21,7 @@ from crewai.traces.unified_trace_controller import (
trace_flow_step,
trace_llm_call,
)
from crewai.utilities.datetime_compat import UTC
class TestUnifiedTraceController:

View File

@@ -0,0 +1,35 @@
"""Test datetime compatibility module."""
from datetime import datetime, timedelta, timezone
from crewai.utilities.datetime_compat import UTC
def test_utc_timezone_compatibility():
"""Test that UTC timezone is compatible with both Python 3.10 and 3.11+"""
assert UTC == timezone.utc
assert UTC.tzname(None) == "UTC"
# Verify it works with datetime.now()
dt = datetime.now(UTC)
assert dt.tzinfo == timezone.utc
def test_utc_timezone_edge_cases():
"""Test UTC timezone handling in edge cases."""
# Test with leap year
leap_date = datetime(2024, 2, 29, tzinfo=UTC)
assert leap_date.tzinfo == timezone.utc
# Test DST transition dates
dst_date = datetime(2024, 3, 10, 2, 0, tzinfo=UTC) # US DST start
assert dst_date.tzinfo == timezone.utc
# Test with minimum/maximum dates
min_date = datetime.min.replace(tzinfo=UTC)
max_date = datetime.max.replace(tzinfo=UTC)
assert min_date.tzinfo == timezone.utc
assert max_date.tzinfo == timezone.utc
# Test timezone offset calculations
dt = datetime(2024, 1, 1, tzinfo=UTC)
offset = dt.utcoffset()
assert offset == timedelta(0) # UTC should always have zero offset