Python: Fix type compatibility check (#1753)

* Fix type compatibility check

* Address comments
This commit is contained in:
Tao Chen
2025-10-28 17:48:01 -07:00
committed by GitHub
Unverified
parent 00a78d7bc6
commit 1fbdcf8268
5 changed files with 218 additions and 55 deletions
@@ -4,7 +4,7 @@ from dataclasses import dataclass
from typing import Any, Generic, TypeVar, Union
from agent_framework._workflows import RequestInfoMessage, RequestResponse
from agent_framework._workflows._typing_utils import is_instance_of
from agent_framework._workflows._typing_utils import is_instance_of, is_type_compatible
def test_basic_types() -> None:
@@ -133,3 +133,86 @@ def test_edge_cases() -> None:
assert is_instance_of({}, dict[str, int]) # Empty dict should be valid
assert is_instance_of(None, int | None) # Optional type with None
assert not is_instance_of(5, str | None) # Optional type without matching type
def test_type_compatibility_basic() -> None:
"""Test basic type compatibility scenarios."""
# Exact type match
assert is_type_compatible(str, str)
assert is_type_compatible(int, int)
# Any compatibility
assert is_type_compatible(str, Any)
assert is_type_compatible(list[int], Any)
# Subclass compatibility
class Animal:
pass
class Dog(Animal):
pass
assert is_type_compatible(Dog, Animal)
assert not is_type_compatible(Animal, Dog)
def test_type_compatibility_unions() -> None:
"""Test type compatibility with Union types."""
# Source matches target union member
assert is_type_compatible(str, Union[str, int])
assert is_type_compatible(int, Union[str, int])
assert not is_type_compatible(float, Union[str, int])
# Source union - all members must be compatible with target
assert is_type_compatible(Union[str, int], Union[str, int, float])
assert not is_type_compatible(Union[str, int, bytes], Union[str, int])
def test_type_compatibility_collections() -> None:
"""Test type compatibility with collection types."""
# List compatibility - key use case
@dataclass
class ChatMessage:
text: str
assert is_type_compatible(list[ChatMessage], list[Union[str, ChatMessage]])
assert is_type_compatible(list[str], list[Union[str, ChatMessage]])
assert not is_type_compatible(list[Union[str, ChatMessage]], list[ChatMessage])
# Dict compatibility
assert is_type_compatible(dict[str, int], dict[str, Union[int, float]])
assert not is_type_compatible(dict[str, Union[int, float]], dict[str, int])
# Set compatibility
assert is_type_compatible(set[str], set[Union[str, int]])
assert not is_type_compatible(set[Union[str, int]], set[str])
def test_type_compatibility_tuples() -> None:
"""Test type compatibility with tuple types."""
# Fixed length tuples
assert is_type_compatible(tuple[str, int], tuple[Union[str, bytes], Union[int, float]])
assert not is_type_compatible(tuple[str, int], tuple[str, int, bool]) # Different lengths
# Variable length tuples
assert is_type_compatible(tuple[str, ...], tuple[Union[str, bytes], ...])
assert is_type_compatible(tuple[str, int, bool], tuple[Union[str, int, bool], ...])
assert not is_type_compatible(tuple[str, ...], tuple[str, int]) # Variable to fixed
def test_type_compatibility_complex() -> None:
"""Test complex nested type compatibility."""
@dataclass
class Message:
content: str
# Complex nested structure
source = list[dict[str, Message]]
target = list[dict[Union[str, bytes], Union[str, Message]]]
assert is_type_compatible(source, target)
# Incompatible nested structure
incompatible_target = list[dict[Union[str, bytes], int]]
assert not is_type_compatible(source, incompatible_target)
@@ -519,7 +519,9 @@ class StateTrackingExecutor(Executor):
"""An executor that tracks state in shared state to test context reset behavior."""
@handler
async def handle_message(self, message: StateTrackingMessage, ctx: WorkflowContext[Any, list[Any]]) -> None:
async def handle_message(
self, message: StateTrackingMessage, ctx: WorkflowContext[StateTrackingMessage, list[str]]
) -> None:
"""Handle the message and track it in shared state."""
# Get existing messages from shared state
try: